用Deeplabv3在Cityscapes上做语义分割:从数据预处理到可视化测试的全流程保姆级教程
用Deeplabv3在Cityscapes上实现语义分割从数据解析到效果优化的全链路实践当你第一次面对Cityscapes数据集里3000多张高分辨率街景图像时可能会被复杂的目录结构和几十种物体类别搞得晕头转向。作为自动驾驶领域最权威的语义分割基准数据集Cityscapes对初学者来说既是宝藏也是挑战。本文将带你用PyTorch版的Deeplabv3从数据预处理开始一步步构建完整的语义分割流水线重点破解那些官方教程里不会告诉你的实战细节。1. 深入理解Cityscapes数据集结构Cityscapes数据集解压后呈现的目录树就像一座精心设计的迷宫。在leftImg8bit文件夹里train/val/test三个子目录分别存放着训练集、验证集和测试集图像而每个子目录下又按城市名称进一步分类。这种层级结构看似繁琐实则暗藏玄机cityscapes ├── gtFine │ ├── train │ │ ├── aachen │ │ │ ├── aachen_000000_000019_gtFine_color.png │ │ │ ├── aachen_000000_000019_gtFine_instanceIds.png │ │ │ └── ... │ └── val └── leftImg8bit ├── train │ ├── aachen │ │ ├── aachen_000000_000019_leftImg8bit.png │ │ └── ... └── val关键点在于图像文件名中的编码规则以aachen_000000_000019_leftImg8bit.png为例aachen表示城市名第一个数字串000000是序列号第二个000019是帧编号。这种命名方式保证了图像与标注的严格对应关系。注意Cityscapes的标注图像有四种类型_color可视化标注、_instanceIds实例ID、_labelIds分类ID和_polygons.json多边形坐标。我们只需要_labelIds.png用于语义分割训练。2. 数据预处理的黑盒解密大多数教程会直接让你运行preprocess_data.py却很少解释这个脚本到底在做什么。实际上它完成了三个关键转换将原始的_labelIds.png转换为训练所需的单通道掩码图根据cityscapesscripts提供的映射表把34个原始类别合并为19个训练类别生成与输入图像同名的掩码文件到label_imgs目录以下代码片段展示了关键的类别映射逻辑# 原始34类到19训练类的映射表 id_to_trainid { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 1, 8: 2, 9: 0, 10: 0, 11: 3, 12: 4, 13: 5, 14: 0, 15: 0, 16: 0, 17: 6, 18: 7, # ...省略部分映射 255: 255 }常见坑点在于路径配置。建议采用绝对路径并确保目录结构如下your_project ├── cityscapes_data │ ├── leftImg8bit │ └── gtFine └── label_imgs # 预处理后自动生成3. Deeplabv3模型架构调优实战原版Deeplabv3采用ResNet-101为主干网络但对于Cityscapes这样的复杂场景我们可以尝试以下改进改进方案优点实现难度更换为ResNet-152提升特征提取能力★★★☆☆添加SE模块增强通道注意力★★☆☆☆使用空洞空间金字塔多尺度特征融合★★★★☆深度可分离卷积减少参数量★★☆☆☆在model/deeplabv3.py中关键修改点是调整ASPP模块的空洞率class ASPP(nn.Module): def __init__(self, in_channels, out_channels256): super(ASPP, self).__init__() self.conv1 nn.Sequential( nn.Conv2d(in_channels, out_channels, 1, biasFalse), nn.BatchNorm2d(out_channels), nn.ReLU() ) self.conv2 nn.Sequential( nn.Conv2d(in_channels, out_channels, 3, padding6, dilation6, biasFalse), # 调整dilation rate适应Cityscapes场景 nn.BatchNorm2d(out_channels), nn.ReLU() ) # 其他分支...4. 训练策略与指标监控Cityscapes的官方基准使用交叉熵损失但在实际训练中我们发现结合Dice损失能提升小物体的分割效果class HybridLoss(nn.Module): def __init__(self, weightNone): super().__init__() self.ce_loss nn.CrossEntropyLoss(weightweight) def forward(self, pred, target): ce self.ce_loss(pred, target) pred F.softmax(pred, dim1) dice 1 - dice_coeff(pred, target) return ce dice*0.3 # 加权组合训练过程中需要重点监控以下指标mIoU平均交并比各类别IoU的平均值主评估指标Pixel Accuracy整体像素准确率Class Accuracy每个类别的独立准确率建议使用TensorBoard记录训练曲线以下是一个典型的学习率调度策略scheduler torch.optim.lr_scheduler.OneCycleLR( optimizer, max_lr0.01, steps_per_epochlen(train_loader), epochs50, pct_start0.3 )5. 可视化分析与效果优化run_on_seq.py脚本的核心在于将模型输出转换为直观的可视化结果。这个过程包含三个关键步骤模型推理输入图像经过前向传播得到预测张量类别映射将预测的类别ID转换为Cityscapes官方配色结果融合将分割结果与原图叠加显示对于自定义测试集需要特别注意图像尺寸必须与训练尺寸一致默认1024×2048。如果输入分辨率不同可以使用以下预处理代码transform transforms.Compose([ transforms.Resize((1024, 2048)), # 保持宽高比 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])典型的问题场景与解决方案边缘锯齿明显尝试在模型最后添加CRF后处理小物体识别差增加针对小物体的数据增强类别混淆调整损失函数的类别权重在8GB显存的GPU上训练50个epoch大约需要12小时而验证集mIoU通常能达到65%左右。要达到论文报告的78%性能还需要以下技巧使用更大的输入分辨率1536×3072实施OHEM在线难例挖掘添加多尺度测试增强最后提醒Cityscapes的测试集标注是不公开的要获取官方评测结果需要将预测结果提交到他们的评估服务器。本地验证时可以把val集的一部分划为mini-val集用于快速验证模型改动效果。