news 2026/5/15 11:32:33

从FCN到DANet:手把手带你复现5个经典语义分割模型(附PyTorch代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从FCN到DANet:手把手带你复现5个经典语义分割模型(附PyTorch代码)

从FCN到DANet:手把手带你复现5个经典语义分割模型(附PyTorch代码)

语义分割作为计算机视觉领域的核心任务之一,已经从早期的简单分类发展到如今的像素级精确预测。对于想要深入理解这一技术演进过程的开发者来说,没有什么比亲手复现经典模型更能获得直观认知了。本文将带你从零开始,用PyTorch实现五个里程碑式的语义分割模型:FCN、SegNet、U-Net、PSPNet和DANet,每个实现都包含可运行的完整代码和关键模块解析。

1. 环境准备与基础配置

在开始复现之前,我们需要搭建统一的开发环境。推荐使用Python 3.8+和PyTorch 1.10+的组合,这是目前最稳定的深度学习开发环境之一。以下是基础依赖的安装命令:

pip install torch==1.10.0 torchvision==0.11.1 pip install opencv-python matplotlib tqdm

为了确保所有模型能在相同条件下进行对比,我们使用统一的配置类来管理超参数:

class Config: batch_size = 16 learning_rate = 1e-3 num_epochs = 50 image_size = (512, 512) num_classes = 21 # PASCAL VOC标准类别数 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

提示:建议使用NVIDIA显卡进行训练,显存最好不低于8GB。如果使用CPU训练,请适当减小batch_size。

2. FCN:全卷积网络的革命

FCN(Fully Convolutional Network)是语义分割领域的开山之作,它将传统的全连接层替换为卷积层,使网络能够接受任意尺寸的输入并输出相同尺寸的分割图。

2.1 网络结构实现

FCN的核心在于将VGG16的全连接层转换为卷积层,并添加转置卷积进行上采样。以下是关键部分的PyTorch实现:

class FCN32s(nn.Module): def __init__(self, num_classes): super().__init__() # 加载预训练VGG16的卷积部分 vgg = models.vgg16(pretrained=True).features # 分阶段提取特征 self.stage1 = nn.Sequential(*vgg[:5]) # conv1 self.stage2 = nn.Sequential(*vgg[5:10]) # conv2 self.stage3 = nn.Sequential(*vgg[10:17]) # conv3 self.stage4 = nn.Sequential(*vgg[17:24]) # conv4 self.stage5 = nn.Sequential(*vgg[24:]) # conv5 # 分类卷积层 self.classifier = nn.Sequential( nn.Conv2d(512, 4096, 7, padding=3), nn.ReLU(inplace=True), nn.Dropout2d(), nn.Conv2d(4096, 4096, 1), nn.ReLU(inplace=True), nn.Dropout2d(), nn.Conv2d(4096, num_classes, 1) ) # 32倍上采样 self.upsample = nn.ConvTranspose2d( num_classes, num_classes, 64, stride=32, padding=16) def forward(self, x): x = self.stage1(x) x = self.stage2(x) x = self.stage3(x) x = self.stage4(x) x = self.stage5(x) x = self.classifier(x) x = self.upsample(x) return x

2.2 训练技巧与常见问题

FCN训练过程中有几个关键点需要注意:

  • 初始化策略:分类卷积层使用高斯初始化,上采样层使用双线性插值初始化
  • 学习率调整:预训练部分使用较小的学习率(1e-5),新增部分使用较大学习率(1e-3)
  • 常见报错
    • 输出尺寸不匹配:确保输入图片尺寸能被32整除
    • 内存不足:减小batch_size或使用更小的输入尺寸

3. U-Net:医学图像分割的标杆

U-Net凭借其独特的对称编码器-解码器结构和跳跃连接,在医学图像分割领域表现出色。下面我们实现一个标准的U-Net架构。

3.1 核心模块实现

U-Net的关键在于编码器(下采样)和解码器(上采样)之间的跳跃连接。我们先定义基础的双卷积块:

class DoubleConv(nn.Module): """(convolution => [BN] => ReLU) * 2""" def __init__(self, in_channels, out_channels): super().__init__() self.double_conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) ) def forward(self, x): return self.double_conv(x)

完整的U-Net实现如下:

class UNet(nn.Module): def __init__(self, n_channels, n_classes): super().__init__() self.inc = DoubleConv(n_channels, 64) self.down1 = Down(64, 128) self.down2 = Down(128, 256) self.down3 = Down(256, 512) self.down4 = Down(512, 1024) self.up1 = Up(1024, 512) self.up2 = Up(512, 256) self.up3 = Up(256, 128) self.up4 = Up(128, 64) self.outc = OutConv(64, n_classes) def forward(self, x): x1 = self.inc(x) x2 = self.down1(x1) x3 = self.down2(x2) x4 = self.down3(x3) x5 = self.down4(x4) x = self.up1(x5, x4) x = self.up2(x, x3) x = self.up3(x, x2) x = self.up4(x, x1) logits = self.outc(x) return logits

3.2 数据增强策略

U-Net在医学图像上的成功很大程度上依赖于恰当的数据增强:

train_transform = A.Compose([ A.RandomRotate90(), A.Flip(), A.ElasticTransform(alpha=120, sigma=120*0.05, alpha_affine=120*0.03), A.GridDistortion(), A.RandomBrightnessContrast(), A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ToTensorV2() ])

4. DANet:注意力机制的应用

DANet(Dual Attention Network)通过引入位置注意力和通道注意力模块,显著提升了分割精度。我们重点解析其核心注意力模块。

4.1 位置注意力模块

位置注意力模块通过计算像素间的相关性来捕获长距离依赖:

class PositionAttentionModule(nn.Module): def __init__(self, in_channels): super().__init__() self.query_conv = nn.Conv2d(in_channels, in_channels//8, 1) self.key_conv = nn.Conv2d(in_channels, in_channels//8, 1) self.value_conv = nn.Conv2d(in_channels, in_channels, 1) self.gamma = nn.Parameter(torch.zeros(1)) def forward(self, x): batch_size, C, height, width = x.size() # 计算query和key proj_query = self.query_conv(x).view(batch_size, -1, width*height).permute(0, 2, 1) proj_key = self.key_conv(x).view(batch_size, -1, width*height) # 计算注意力图 energy = torch.bmm(proj_query, proj_key) attention = F.softmax(energy, dim=-1) # 应用注意力 proj_value = self.value_conv(x).view(batch_size, -1, width*height) out = torch.bmm(proj_value, attention.permute(0, 2, 1)) out = out.view(batch_size, C, height, width) return self.gamma * out + x

4.2 通道注意力模块

通道注意力模块关注不同特征通道间的关系:

class ChannelAttentionModule(nn.Module): def __init__(self): super().__init__() self.gamma = nn.Parameter(torch.zeros(1)) def forward(self, x): batch_size, C, height, width = x.size() # 计算通道注意力 proj_query = x.view(batch_size, C, -1) proj_key = x.view(batch_size, C, -1).permute(0, 2, 1) energy = torch.bmm(proj_query, proj_key) energy_new = torch.max(energy, -1, keepdim=True)[0].expand_as(energy) - energy attention = F.softmax(energy_new, dim=-1) # 应用注意力 proj_value = x.view(batch_size, C, -1) out = torch.bmm(attention, proj_value) out = out.view(batch_size, C, height, width) return self.gamma * out + x

5. 模型对比与实战建议

为了帮助读者选择合适的模型,我们对五个模型的关键特性进行了对比:

模型参数量(M)mIoU(%)训练速度(iter/s)显存占用(GB)适用场景
FCN134.562.23.24.8通用场景
SegNet29.559.14.13.2实时应用
U-Net31.075.32.85.1医学图像
PSPNet46.780.22.16.4高精度场景
DANet69.781.51.57.8复杂场景分析

在实际项目中,选择模型时需要综合考虑以下因素:

  • 硬件条件:显存有限时考虑SegNet或轻量级U-Net变体
  • 实时性要求:视频分割等场景优先选择FCN或SegNet
  • 精度要求:静态图像分析可选用PSPNet或DANet
  • 数据特点:医学图像首选U-Net,街景图像适合PSPNet
# 模型选择辅助函数示例 def select_model(requirements): if requirements['realtime']: return SegNet() elif requirements['medical']: return UNet() elif requirements['accuracy'] > 0.8: return DANet() if requirements['gpu_memory'] > 8 else PSPNet() else: return FCN()

在复现这些模型时,我经常遇到的一个问题是预训练权重与模型结构不匹配。解决这个问题的有效方法是先打印出权重文件的键名,然后手动调整模型中的对应层名。另一个实用技巧是在训练初期冻结骨干网络,只训练新增部分,待损失下降平缓后再解冻全部参数进行微调。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 11:28:34

QMCDecode:3步解锁QQ音乐加密音频,让数字音乐真正属于你

QMCDecode:3步解锁QQ音乐加密音频,让数字音乐真正属于你 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录&…

作者头像 李华
网站建设 2026/5/15 11:27:50

基于LLM的智能代码补全:Monaco Editor集成实战与优化

1. 项目概述:当代码编辑器拥有“读心术”如果你每天有超过4个小时在和代码编辑器打交道,那你一定对“自动补全”这个功能又爱又恨。爱的是,它偶尔能帮你省下敲打几十个字符的力气;恨的是,它大多数时候都像个不太聪明的…

作者头像 李华
网站建设 2026/5/15 11:26:48

GPTMessage:基于消息驱动架构的多平台AI机器人开发框架解析

1. 项目概述与核心价值最近在折腾一些自动化流程和智能助手应用时,我一直在寻找一个能优雅处理消息流转和分发的工具。市面上很多框架要么太重,要么扩展性不够,直到我遇到了lhuanyu/GPTMessage这个项目。简单来说,它就像一个专门为…

作者头像 李华
网站建设 2026/5/15 11:23:11

OpenHarmony富设备移植实战指南:从内核适配到HDF驱动开发

1. 项目概述:为什么我们要关注OpenHarmony富设备移植? 如果你是一位嵌入式开发工程师、系统软件架构师,或者是对国产操作系统生态有浓厚兴趣的技术爱好者,那么“OpenHarmony富设备移植”这个话题,很可能已经进入了你的…

作者头像 李华
网站建设 2026/5/15 11:23:05

光刻校正技术:模型自适应分段在半导体制造中的应用

1. 光刻校正技术演进与挑战在半导体制造的光刻工艺中,光学邻近效应校正(OPC)技术扮演着至关重要的角色。随着工艺节点不断缩小至45nm及以下,传统基于规则的分段方法(Rule-based Fragmentation)逐渐暴露出其局限性。我在参与28nm工艺开发时,曾…

作者头像 李华