FCN-ResNet18 语义分割完整实现详解

news/2025/11/7 18:50:30/文章来源:https://www.cnblogs.com/inian/p/19200636

好的!我来把这段代码整理成博客园风格的笔记,一段代码一段讲解:

FCN-ResNet18 语义分割完整实现详解

1. 导入必要的库

import torch
import torchvision
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

代码说明

  • torch:PyTorch深度学习框架
  • torchvision:提供预训练模型和数据集
  • nn:神经网络模块
  • F:函数式接口
  • d2l:《动手学深度学习》工具库

2. VOC数据集类别和颜色定义

VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle','bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog','horse', 'motorbike', 'person', 'potted plant', 'sheep','sofa', 'train', 'tv/monitor'
]VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128],[128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0],[192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128],[192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0],[128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]
]

代码说明

  • VOC_CLASSES:21个语义类别名称
  • VOC_COLORMAP:每个类别对应的RGB颜色值
  • 背景为黑色[0,0,0],飞机为红色[128,0,0]
  • 这是PASCAL VOC数据集的官方定义

3. 创建颜色到标签的映射字典

colormap2label = torch.zeros(256 ** 3, dtype=torch.long)
for i, colormap in enumerate(VOC_COLORMAP):# 将RGB颜色转换为唯一索引:R*256^2 + G*256 + Bcolor_index = (colormap[0] * 256 + colormap[1]) * 256 + colormap[2]colormap2label[color_index] = i

代码说明

  • 创建大小为256³的查找表(覆盖所有RGB颜色)
  • 将每个VOC颜色映射到对应的类别ID
  • 例如:黑色[0,0,0] → 索引0 → 类别0(背景)

4. 高效的标签转换函数

def voc_label_indices_fast(colormap, colormap2label):"""使用查找表快速将RGB标签图转换为类别ID"""# colormap: (H, W, 3) RGB图像# 将RGB图像转换为索引:R*256^2 + G*256 + Bindices = (colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256 + colormap[:, :, 2]# 使用查找表直接映射到类别IDreturn colormap2label[indices]def preprocess_mask(mask, colormap2label):"""预处理掩码:RGB → 类别ID"""if mask.dim() == 3 and mask.shape[-1] == 3:  # 如果是RGB图像mask = voc_label_indices_fast(mask, colormap2label)return mask

代码说明

  • voc_label_indices_fast:批量处理整个图像,比逐像素循环快很多
  • 利用向量化操作一次性计算所有像素的索引
  • preprocess_mask:封装函数,自动判断输入格式

5. 双线性插值卷积核

def bilinear_kernel(in_channels, out_channels, kernel_size):factor = (kernel_size + 1) // 2center = factor - 1 if kernel_size % 2 == 1 else factor - 0.5og = torch.arange(kernel_size).reshape(-1, 1), torch.arange(kernel_size).reshape(1, -1)filt = (1 - torch.abs(og[0] - center) / factor) * (1 - torch.abs(og[1] - center) / factor)weight = torch.zeros((in_channels, out_channels, kernel_size, kernel_size))weight[range(in_channels), range(out_channels), :, :] = filtreturn weight

代码说明

  • 创建双线性插值权重核
  • 中心权重最大,向边缘逐渐减小
  • 用于初始化转置卷积,实现平滑的上采样

6. 构建FCN-ResNet18网络

# 1) 加载预训练 ResNet18,做 encoder
pretrained_net = torchvision.models.resnet18(pretrained=True)
net = nn.Sequential(*list(pretrained_net.children())[:-2])# 2) segmentation head
num_classes = 21
net.add_module('final_conv', nn.Conv2d(512, num_classes, kernel_size=1))
net.add_module('transpose_conv',nn.ConvTranspose2d(num_classes, num_classes,kernel_size=64, padding=16, stride=32))# 3) 初始化反卷积为双线性插值
net.transpose_conv.weight.data.copy_(bilinear_kernel(num_classes, num_classes, 64))

代码说明

  • 编码器:使用ResNet18(去掉最后两层)
  • 1×1卷积:将512特征通道转换为21个类别通道
  • 转置卷积:32倍上采样,恢复原始分辨率
  • 双线性初始化:避免棋盘伪影,加速收敛

7. 演示颜色映射过程

def demonstrate_colormap2label():"""演示colormap2label的使用方法"""print("=== 演示colormap2label映射 ===")test_colors = [[0, 0, 0],        # 背景 - 黑色[128, 0, 0],      # 飞机 - 红色[0, 128, 0],      # 自行车 - 绿色[192, 128, 128],  # 人 - 灰色[255, 255, 255]   # 不在VOC中的颜色]for color in test_colors:color_index = (color[0] * 256 + color[1]) * 256 + color[2]class_id = colormap2label[color_index].item()if class_id == 0 and color != [0, 0, 0]:class_name = "未知类别"else:class_name = VOC_CLASSES[class_id]print(f"RGB{color} -> 索引{color_index} -> 类别{class_id}: {class_name}")# 运行演示
demonstrate_colormap2label()

输出示例

=== 演示colormap2label映射 ===
RGB[0, 0, 0] -> 索引0 -> 类别0: background
RGB[128, 0, 0] -> 索引8388608 -> 类别1: aeroplane
RGB[0, 128, 0] -> 索引32768 -> 类别2: bicycle
RGB[192, 128, 128] -> 索引12632256 -> 类别15: person
RGB[255, 255, 255] -> 索引16777215 -> 类别0: 未知类别

8. 加载VOC数据集

batch_size = 32
crop_size = (320, 480)
print("\n加载VOC数据集...")
train_iter, test_iter = d2l.load_data_voc(batch_size, crop_size)# 检查一个批次
for X, Y in train_iter:print(f"输入图像形状: {X.shape}")  # (batch, 3, H, W)print(f"标签形状: {Y.shape}")      # (batch, H, W) - 已经是类别IDbreak

代码说明

  • d2l.load_data_voc自动处理数据加载和预处理
  • 输入图像:(32, 3, 320, 480) - 批量32,3通道,320×480分辨率
  • 标签:(32, 320, 480) - 每个像素是0-20的类别ID

9. 定义损失函数

def loss(inputs, targets):return F.cross_entropy(inputs, targets, reduction='none').mean(1).mean(1)

代码说明

  • inputs: (N, 21, H, W) - 21个类别的概率图
  • targets: (N, H, W) - 每个像素的真实类别ID
  • 先计算每个像素的交叉熵,然后在空间维度取平均

10. 模型训练

num_epochs = 5
lr = 0.001
wd = 1e-3
devices = d2l.try_all_gpus()print(f"\n开始训练,使用设备: {devices}")
trainer = torch.optim.SGD(net.parameters(), lr=lr, weight_decay=wd)
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)print("训练完成!")

代码说明

  • 训练5个epoch,学习率0.001
  • 使用SGD优化器,权重衰减1e-3
  • 自动检测并使用所有可用的GPU
  • d2l.train_ch13封装了标准的训练流程

关键技术点总结

  1. 全卷积网络:去除全连接层,支持任意尺寸输入
  2. 编码器-解码器结构:ResNet18编码 + 转置卷积解码
  3. 双线性初始化:转置卷积权重初始化为双线性插值
  4. 逐像素分类:每个像素独立进行21分类
  5. 颜色映射:RGB标签图 → 类别ID图

这个实现展示了现代语义分割网络的核心思想,结合了迁移学习和端到端训练的优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/959122.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《代码大全 2》观后感(六):错误处理 —— 代码的 “安全气囊”

过去写代码时,我总觉得 “错误处理” 是 “可有可无的附加项”—— 只要自己测试时没遇到报错,就不用写 try-catch,不用判断空值。但读了《代码大全 2》中 “错误处理” 的章节,才意识到错误处理是代码的 “安全气…

在龟骨的第二次课的讲解

本节课将围绕环境搭建→效率工具→目标管理三大模块,帮你从“新手”到“规范开发者”。效率加速器:快捷键实战 底层逻辑:快捷键的本质是减少鼠标操作 基础快捷键 Ctrl + C/V/X:复制/粘贴/剪切(举例:写文档时复制…

P5610 解题报告

P5610 解题报告 简要题意 一个长为 \(n\) 的非负整数序列 \(a\),支持以下两个操作:1 l r x:把区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)。 2 l r:查询区间 \([l,r]\) 的和。本题强制在线。 数据范围: \(1\le…

fcitx5里有趣的东西

apt source fcitx5-pinyin 即可飞速下载。 emoji.txt ⛑ anquanmao 🪁 aoxiang 🌏 aozhou 🕗 badian ... chaizi.txt 亖 erer 亗 shaner 亝 leier 什 renshi 仂 renli 仃 rending 仄 changren

自定义MCP Server

1. MCP Server 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency>&l…

英语_错题集_25-11

**答案:** annoying **解析:** 这里需要填入一个形容词来描述“我的小弟弟”的特点。动词 “annoy” 意为“使烦恼”,其形容词形式 “annoying” 表示“令人烦恼的”,符合句意。句子意思是:“我的小弟弟有时真的很…

Ai元人文随想:守护时光的印记

Ai元人文随想:守护时光的印记 岐金兰 论古树保护的文化、生态与精神价值第一章:活态史诗——古树作为历史的无言见证者 在村口,在庙前,在深山幽谷之中,古树以一种超越人类纪年的姿态屹立着。它们不是普通的植物,…

浅谈模拟系列算法

模拟着火/模拟火灾算法 算法名称:模拟火退 研究时间:2023年10月17日19点 算法引入:发现了模拟退火算法中出现的正确性和复杂度问题,故研发模拟火退算法进行优化。 注意到模拟退火算法中出现了较为严重的正确性问题…

第三十六篇

今天是11月7号,今天没课

Ai元人文随想:三值纠缠中的人文关怀

Ai元人文随想:深入探讨三值纠缠思维中蕴含的深刻人文关怀 岐金兰 灵魂的测量仪:三值纠缠思维中的人文之光 在技术理性日益主导、算法试图将人类简化为一行行代码的时代,一种名为“三值纠缠”的思维模型,却悄然进行…

R语言实现多组样本两两t检验的完整教程

t检验的核心思想是通过样本均值与方差的比较,评估两个总体均值是否存在显著差异。当有三个或更多组数据时,单次t检验已不再适用,因此通常的做法是先进行方差齐性检验与单因素方差分析(ANOVA),如果总体差异显著,…

实用指南:TensorFlow深度学习实战(40)——图神经网络(GNN)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2023QDEZ男人八题赛后总结

这里是QDEZ男人八题赛后总结博客。 这是QDEZ OI 出题组的最后一场公开赛,作为行将就木的退役选手,在此再次提醒大家: 今年欢笑复明年,不知退役在眼前! 比赛于线下机房举行,本次有___人参加比赛,感谢大家的参与。…

学习差的孩子,有必要用学习机吗?

学习差的孩子用学习机是智商税?松鼠AI双线方案给出提分答案 一、打破偏见:学习成绩差的学生,更需要专业 AI 学习机 “学习机是智商税” 的说法,本质上是对 “单一工具依赖” 的否定,而非对 AI 教育价值的否定。对…

CSP-S2023游记

赛前第三天: 白天狂摆一天文化课,整个人都进不到学习状态里,摆摆摆摆摆,晚上在机房里进行了 CSP 动员大会,大家都写下了自己祝福的话或者是自己挂分的秘籍,为了让这些话有灵魂,我加上了两句曹学(最后这两句曹学…

2025苏州驾驶证培训推荐榜:摩托车驾驶证培训、A2驾驶证培训、大车A1驾驶证培训、大车B2驾驶证培训,省心学车选这些

在苏州,驾驶证培训已成为成年人提升生活技能的热门选择,而规模适中、专注服务的小体量培训机构,因教学灵活、关注度高更受青睐。2025 年最新盘点,精选口碑突出的小型驾驶证培训机构,其中苏州从欢机动车贸易有限公…

2025佛山钢管厂家推荐榜:防腐钢管、大口径钢管、螺旋钢管工厂采购选型不踩坑

在工业制造、建筑装修、机械配套等领域,钢管作为基础材料的需求持续稳定,而小型钢管企业凭借灵活适配、精准服务的优势,成为众多采购方的优选。为帮助行业伙伴精准筛选靠谱合作方,本文整理 2025 年优质小型钢管企业…

不谈离散数学基本定理

本文半娱乐向半学术向 先列出定理:1.对于 \(\forall x,y \in \mathbb{Z},x<y\),有 \(x+1\le y\)2.\(\forall a,b\in\mathbb{Z},a<b,x>1\),则有 \(x^a<x^b\)3.\(\forall i\in\{1,2\cdots,n\},a_i\in\mat…

现代Linux网络命令简介

都是字符界面的、很小的。 iftop: /usr/sbin/iftop display bandwidth usage on an interface by host nethogs: /usr/sbin/nethogsNet top tool grouping bandwidth per process nload: /usr/bin/nloaddisplays the c…

深谈王书童变换

首先大家肯定都听过这个名字,毕竟发明者比较著名,他就是和鲍林,泡利三人能得三次诺贝尔奖的著名化学家王书童,不过他这个变换更多的应用于数学领域,所以大多人不太熟悉这个定理,就让我来给大家打通一下! 首先先…