Detectron2 中的 DeepLabV3 / DeepLabV3+ 语义分割实战指南:Cityscapes 训练、评估与源码解析
【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2
本指南围绕 projects/DeepLab/README.md 展开,系统讲解如何在 Detectron2 项目中复现并应用 DeepLabV3 与 DeepLabV3+ 语义分割模型,涵盖环境安装、8 卡分布式训练、模型评估、Cityscapes 官方结果复现,以及从配置到源码的逐层剖析。读完本文,你将掌握基于 ResNet-103-DC5 主干、ASPP 空洞卷积、深度可分离卷积与 poly 学习率调度在 Detectron2 中训练语义分割模型的完整链路,并能独立修改配置迁移到自己的分割数据集。
一、项目定位:Detectron2 中的 DeepLab 实现
Detectron2 官方仓库以projects/目录承载研究级模型实现,DeepLab 正是其中之一。projects/DeepLab在 Detectron2 的建模框架内完整实现了 DeepLabV3 与 DeepLabV3+ 两种语义分割架构,其核心组件分布在:
- 语义分割头定义:
DeepLabV3Head与DeepLabV3PlusHead - DeepLab 专用 ResNet 主干:
DeepLabStem与build_resnet_deeplab_backbone - 损失函数:
DeepLabCE(hard pixel mining 交叉熵) - 学习率调度 与 求解器构建:
WarmupPolyLR - 配置扩展:
add_deeplab_config - 训练/评估入口:
Trainer
从入口模块 deeplab/init.py 可以看到,该包对外暴露的接口即为上述add_deeplab_config、build_lr_scheduler、build_resnet_deeplab_backbone、DeepLabV3Head和DeepLabV3PlusHead,是接入 Detectron2 训练管线的最小组合。
二、环境安装
DeepLab 项目运行在 Detectron2 之上,因此首先需要安装 Detectron2 本体(官方安装说明见 INSTALL.md,Linux 下编译 C++/CUDA 扩展的细节可参考 detectron2/layers/csrc/README.md)。
# 安装 detectron2(以源码方式编译,DeepLab 依赖其 CUDA 扩展如 SyncBN) python -m pip install -e .安装完成后,无需额外安装独立包——projects/DeepLab下的 Python 代码通过detectron2.projects.deeplab命名空间被导入(见 train_net.py 中的from detectron2.projects.deeplab import add_deeplab_config, build_lr_scheduler),因此工作区根目录必须保持为 detectron2 仓库根目录,以保证包路径可解析。
三、模型训练:8 卡分布式启动命令
README 给出的训练命令如下,进入项目目录后直接以配置文件启动:
cd /path/to/detectron2/projects/DeepLab python train_net.py --config-file configs/Cityscapes-SemanticSegmentation/deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml --num-gpus 8对该命令逐段拆解:
--config-file:指定完整配置文件,此处为 deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml。文件名各段含义:R_103(R-103 主干,即改进型 ResNet-101)、os16(encoder 输出 stride 为 16)、mg124(res5 multi-grid 为 [1, 2, 4])、poly(poly 学习率)、90k(90,000 次迭代)、bs16(batch size 16)。--num-gpus 8:使用 8 块 GPU 做分布式数据并行训练,配合--num-machines、--machine-rank、--dist-url可扩展到多机。
3.1 训练入口的内部逻辑
train_net.py 的setup()函数展示了配置装配顺序,这对自定义配置至关重要:
cfg = get_cfg() add_deeplab_config(cfg) # 注入 DeepLab 专用配置项(ASPP、stem、poly 等) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) # 支持命令行覆盖 cfg.freeze() default_setup(cfg, args)即:先调用add_deeplab_config注册 DeepLab 新增的配置字段(否则cfg不认识ASPP_DILATIONS等键),再合并 YAML 文件与命令行覆盖项。main()在非 eval 模式下创建Trainer并调用trainer.train();launch()负责根据num_gpus自动拉起多进程训练。
3.2 训练数据增强管线
与 Detectron2 通用检测/分割任务不同,语义分割训练需要"裁剪 + 类别面积约束"的增强组合。Trainer.build_train_loader针对SemanticSegmentor架构调用了自定义的build_sem_seg_train_aug(train_net.py):
T.ResizeShortestEdge:按INPUT.MIN_SIZE_TRAIN((512, 768, 1024, 1280, 1536, 1792, 2048) 中随机 choice)缩放到边,限制最大边MAX_SIZE_TRAIN=4096;T.RandomCrop_CategoryAreaConstraint:若INPUT.CROP.ENABLED=True,执行absolute模式的 (512, 1024) 随机裁剪,并约束裁剪区域内单一类别占比不超过SINGLE_CATEGORY_MAX_AREA=1.0(见 deeplab/config.py 的注释:反复重试裁剪,直到没有任何一个 GT 类别在裁剪区域中占比超过该阈值,避免大类别主导训练);T.RandomFlip:随机水平翻转。
四、模型评估
评估与训练共用同一入口,只需追加--eval-only并指定权重路径:
cd /path/to/detectron2/projects/DeepLab python train_net.py --config-file configs/Cityscapes-SemanticSegmentation/deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint--eval-only使main()走评估分支:构建模型 → 通过DetectionCheckpointer加载MODEL.WEIGHTS→ 调用Trainer.test(cfg, model)(train_net.py)。
评估器由Trainer.build_evaluator按数据集的evaluator_type元数据自动选择(train_net.py):
sem_seg→SemSegEvaluator(通用语义分割评估,输出到OUTPUT_DIR/inference);cityscapes_sem_seg→CityscapesSemSegEvaluator(Cityscapes 官方评估脚本,对 19 类细粒度标注计算 mIoU,忽略 ignore 区域)。
推理时,分割头输出的 logits 通过双线性插值上采样common_stride倍回到原图分辨率(见下文forward分析),因此--eval-only命令中的MODEL.WEIGHTS指向任何通过DetectionCheckpointer序列化的 checkpoint 即可。
五、Cityscapes 语义分割结果复现表
README 中给出了使用 ImageNet 预训练初始化的 Cityscapes 模型基准结果,全部以 1024×2048 输出分辨率在 Cityscapes val 集上评估:
| 方法 | 主干 | 输出分辨率 | mIoU | model id | 下载 |
|---|---|---|---|---|---|
| DeepLabV3 | R101-DC5 | 1024×2048 | 76.7 | - | - |
| DeepLabV3 | R103-DC5 | 1024×2048 | 78.5 | 28041665 | model / metrics |
| DeepLabV3+ | R101-DC5 | 1024×2048 | 78.1 | - | - |
| DeepLabV3+ | R103-DC5 | 1024×2048 | 80.0 | 28054032 | model / metrics |
其中 model id 对应训练实验目录名,下载链接指向dl.fbaipublicfiles.com的模型权重与metrics.json训练日志(weights 与 metrics 文件在运行时可通过DetectionCheckpointer加载或直接查看评估曲线)。需要说明:
- R103:并非标准的 ResNet-103,而是将 ResNet-101 的第一个 7×7 卷积替换为 3 个 3×3 卷积(感受野不变、参数量略增、非线性增强)的变体,该修改在多数语义分割论文中被采用,并在 ImageNet 上使用 PyTorch examples 的默认配方预训练;
- DC5:表示在
res5阶段使用空洞(dilated)卷积,使特征图 stride 由 32 降为 16(os16),从而保留更高分辨率的空间信息; - 上述 mIoU 数据直接取自 README 表格,属于官方发布结果,在实际复现时受随机种子、GPU 型号与数值精度影响可能产生小幅浮动。
六、配置深度解析:从 Base 到模型专属配置
6.1 基础配置 Base-DeepLabV3-OS16-Semantic.yaml
该文件继承自仓库级基础配置 configs/Base-RCNN-DilatedC5.yaml(提供 PIXEL_MEAN/STD、SyncBN 等通用项),并声明了语义分割的默认骨架:
MODEL: META_ARCHITECTURE: "SemanticSegmentor" BACKBONE: FREEZE_AT: 0 SEM_SEG_HEAD: NAME: "DeepLabV3Head" IN_FEATURES: ["res5"] ASPP_CHANNELS: 256 ASPP_DILATIONS: [6, 12, 18] ASPP_DROPOUT: 0.1 CONVS_DIM: 256 COMMON_STRIDE: 16 NUM_CLASSES: 19 LOSS_TYPE: "hard_pixel_mining" DATASETS: TRAIN: ("cityscapes_fine_sem_seg_train",) TEST: ("cityscapes_fine_sem_seg_val",) SOLVER: BASE_LR: 0.01 MAX_ITER: 90000 LR_SCHEDULER_NAME: "WarmupPolyLR" IMS_PER_BATCH: 16 INPUT: MIN_SIZE_TRAIN: (512, 768, 1024, 1280, 1536, 1792, 2048) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 4096 MAX_SIZE_TEST: 2048 CROP: ENABLED: True TYPE: "absolute" SIZE: (512, 1024) SINGLE_CATEGORY_MAX_AREA: 1.0 DATALOADER: NUM_WORKERS: 10关键参数说明:
| 配置项 | 默认值 | 作用 |
|---|---|---|
MODEL.SEM_SEG_HEAD.ASPP_CHANNELS | 256 | ASPP 各分支输出通道数 |
MODEL.SEM_SEG_HEAD.ASPP_DILATIONS | [6, 12, 18] | ASPP 中三个空洞卷积的 dilation 系数 |
MODEL.SEM_SEG_HEAD.ASPP_DROPOUT | 0.1 | ASPP 输出上的 dropout 比例 |
MODEL.SEM_SEG_HEAD.COMMON_STRIDE | 16 | 输出相对于输入的下采样倍数(os16) |
MODEL.SEM_SEG_HEAD.NUM_CLASSES | 19 | Cityscapes 细粒度类别数 |
MODEL.SEM_SEG_HEAD.LOSS_TYPE | hard_pixel_mining | 损失类型,可选cross_entropy或hard_pixel_mining |
SOLVER.LR_SCHEDULER_NAME | WarmupPolyLR | poly 学习率调度器(DeepLab 特有) |
INPUT.CROP.SINGLE_CATEGORY_MAX_AREA | 1.0 | 单类别在裁剪区域的最大面积占比约束 |
6.2 DeepLabV3+ 专属配置 deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml
_BASE_: Base-DeepLabV3-OS16-Semantic.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-103.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 101 NORM: "SyncBN" OUT_FEATURES: ["res2", "res5"] RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False SEM_SEG_HEAD: NAME: "DeepLabV3PlusHead" IN_FEATURES: ["res2", "res5"] PROJECT_FEATURES: ["res2"] PROJECT_CHANNELS: [48] NORM: "SyncBN" COMMON_STRIDE: 4 INPUT: FORMAT: "RGB"与 DeepLabV3 配置(deeplab_v3_R_103_os16_mg124_poly_90k_bs16.yaml)相比,V3+ 的关键差异:
- 多级特征输入:
OUT_FEATURES: ["res2", "res5"],低层res2(stride 4)提供高分辨率细节,高层res5输入 ASPP 提供语义信息; - Decoder 通道投影:
PROJECT_FEATURES: ["res2"]、PROJECT_CHANNELS: [48],将 res2 通过 1×1 卷积压缩到 48 通道,再与上采样后的 ASPP 输出拼接; - COMMON_STRIDE 降为 4:DeepLabV3+ 的 encoder-decoder 结构输出 stride 从 16 降到 4,直接输出 4 倍下采样的 logits,在推理时仅需上采样 4 倍即可回到原图;
- DeepLab Stem:
STEM_TYPE: "deeplab"、STEM_OUT_CHANNELS: 128,将标准 7×7 stride 2 卷积替换为 3 个 3×3 卷积(stride 2/1/1),输出通道扩展到 128; - STRIDE_IN_1X1: False:在 res5 空洞阶段,下采样发生在 3×3 卷积而非 1×1 卷积,这是空洞分割标准的
stride_in_1x1=False设定。
6.3 add_deeplab_config:新配置项从哪来
DeepLab 使用的许多配置项(ASPP_*、POLY_LR_*、PROJECT_*、STEM_TYPE、RES5_MULTI_GRID等)并非 Detectron2 内置,而是由 deeplab/config.py 中的add_deeplab_config动态注入:
cfg.SOLVER.POLY_LR_POWER = 0.9 # poly 学习率指数 cfg.SOLVER.POLY_LR_CONSTANT_ENDING = 0.0 # 尾部恒定学习率(0 表示关闭) cfg.MODEL.SEM_SEG_HEAD.LOSS_TYPE = "hard_pixel_mining" cfg.MODEL.SEM_SEG_HEAD.PROJECT_FEATURES = ["res2"] cfg.MODEL.SEM_SEG_HEAD.PROJECT_CHANNELS = [48] cfg.MODEL.SEM_SEG_HEAD.ASPP_CHANNELS = 256 cfg.MODEL.SEM_SEG_HEAD.ASPP_DILATIONS = [6, 12, 18] cfg.MODEL.SEM_SEG_HEAD.ASPP_DROPOUT = 0.1 cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV = False cfg.MODEL.RESNETS.RES4_DILATION = 1 cfg.MODEL.RESNETS.RES5_MULTI_GRID = [1, 2, 4] cfg.MODEL.RESNETS.STEM_TYPE = "deeplab"这是接入任何 Detectron2 训练脚本(如tools/plain_train_net.py)前必须调用的注册步骤——漏掉它会导致cfg.merge_from_file因未知字段而报错。
七、源码级原理剖析
7.1 DeepLabV3+ 分割头:decoder 逐级融合
DeepLabV3PlusHead 注册于SEM_SEG_HEADS_REGISTRY,其核心layers()方法自高到低分辨率反向遍历in_features(代码注释为 "Reverse feature maps into top-down order"):
- 对每个特征
f先做project_conv(res2 为 1×1 投影到 48 通道;res5 为 ASPP 模块); - 将上一级(低分辨率)输出
y双线性上采样到当前特征分辨率,与投影特征拼接; - 拼接结果经
fuse_conv融合——默认是两层 3×3 卷积(semantic_seg.py),若开启USE_DEPTHWISE_SEPARABLE_CONV,则按 Panoptic-DeepLab 的建议用单个 5×5 深度可分离卷积替代两层 3×3 卷积(两者感受野相同,见代码注释); - 最后
predictor(1×1 卷积)输出num_classes通道的 logits。
forward()中训练与推理行为分离(semantic_seg.py):训练时返回(None, {"loss_sem_seg": ...}),推理时对 logits 做scale_factor=common_stride的双线性上采样后返回(C×H×W logits, {})。
from_config中还隐藏一个细节:当开启裁剪训练(INPUT.CROP.ENABLED)时,若裁剪尺寸不能被 encoder stride 整除会直接抛出ValueError;同时 ASPP 的 image pooling 分支会使用train_h // encoder_stride作为全局平均池化的 kernel size,这解释了为何 Cityscapes 配置固定裁剪为 (512, 1024) 且 stride 为 16/4。
7.2 DeepLabV3 分割头:单特征 + ASPP
DeepLabV3Head 结构更简洁,断言len(in_features) == 1:取单一高层特征(默认res5)→ ASPP → 1×1 predictor → 上采样common_stride(16)倍。它没有 decoder 与低层特征融合,因此CONVS_DIM=256直接作为 ASPP 输出通道并被 predictor 消费。
7.3 DeepLabCE:hard pixel mining 损失
LOSS_TYPE: "hard_pixel_mining"对应 loss.py 中的DeepLabCE,其实现逻辑为:
- 先用
reduction="none"的交叉熵计算每个像素的损失; - 取前
top_k_percent_pixels(默认 0.2,即最难的 20%)像素的损失做torch.topk; - 对这批最难像素的损失求均值作为最终 loss。
该策略来自 TensorFlow DeepLab 框架与 DeeperLab 论文,作用是让训练聚焦于难分类像素(如物体边缘、小目标),两个分割头在构造损失时均调用DeepLabCE(ignore_label=self.ignore_value, top_k_percent_pixels=0.2)。若希望使用标准交叉熵,只需将LOSS_TYPE改为cross_entropy。
7.4 DeepLabStem:改进的 ResNet 主干
resnet.py 中的DeepLabStem将标准BasicStem的 7×7 stride 2 卷积替换为3×3(stride 2) → 3×3 → 3×3三段结构,每段后接 ReLU,最后接 3×3 stride 2 max-pool,输出 128 通道。build_resnet_deeplab_backbone通过STEM_TYPE在basic/deeplab间切换,并支持RES5_MULTI_GRID:在 res5 阶段以dilation_per_block = [dilation * mg for mg in res5_multi_grid]逐 block 设置空洞率(即 [1, 2, 4] × 2 = 2/4/8),实现 multi-grid 空洞卷积。res4_dilation=1、res5_dilation=2的组合配合OUT_FEATURES: ["res2", "res5"],正是os16(res5 输出 stride 16)的由来。
7.5 WarmupPolyLR:poly 学习率调度
DeepLab 训练不使用 Detectron2 默认的多步衰减,而是 lr_scheduler.py 中的WarmupPolyLR,其学习率公式为:
lr = base_lr × warmup_factor × (1 - iter / max_iters)^powerpower = 0.9(POLY_LR_POWER),学习率随迭代平滑衰减到 0;- 前
warmup_iters轮执行线性 warmup(默认因子 0.001); - 若设置
POLY_LR_CONSTANT_ENDING > 0,当衰减值低于该阈值时学习率恒定保持为base_lr × constant_ending,避免尾部衰减过慢。
调度器由 build_solver.py 的build_lr_scheduler根据SOLVER.LR_SCHEDULER_NAME == "WarmupPolyLR"路由创建,并被Trainer.build_lr_scheduler接入 Detectron2 训练循环。
八、扩展到自定义数据集
若要在自己的分割数据集上使用本实现,需调整以下配置(均可用--opts命令行覆盖,无需改代码):
- 数据集注册:参考 detectron2/data/datasets/builtin.py 与 cityscapes.py 注册
"xxx_sem_seg_train"/"xxx_sem_seg_val",并在 DATASETS 中替换TRAIN/TEST; - 类别数:
MODEL.SEM_SEG_HEAD.NUM_CLASSES改为实际类别数(背景计入则 +1); - 忽略值:
MODEL.SEM_SEG_HEAD.IGNORE_VALUE默认 -1,数据集中若有 ignore 区域需对应设置; - 裁剪与增强:
INPUT.CROP.SIZE必须能被COMMON_STRIDE整除(DeepLabV3 为 16、DeepLabV3+ 为 4),否则from_config会抛ValueError; - 预训练:
MODEL.WEIGHTS可替换为 detectron2:// 的 ImageNet 权重或其他 checkpoint,DetectionCheckpointer会自动做兼容加载。
九、引用 DeepLab
若在研究中使用了本实现,请按如下 BibTeX 引用原始论文:
DeepLabv3+(Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation,ECCV 2018):
@inproceedings{deeplabv3plus2018, title={Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation}, author={Liang-Chieh Chen and Yukun Zhu and George Papandreou and Florian Schroff and Hartwig Adam}, booktitle={ECCV}, year={2018} }DeepLabv3(Rethinking atrous convolution for semantic image segmentation):
@article{deeplabv32018, title={Rethinking atrous convolution for semantic image segmentation}, author={Chen, Liang-Chieh and Papandreou, George and Schroff, Florian and Adam, Hartwig}, journal={arXiv:1706.05587}, year={2017} }十、小结
projects/DeepLab在 Detectron2 框架内提供了开箱即用的 DeepLabV3/V3+ 语义分割方案:通过add_deeplab_config注入 ASPP、DeepLabStem、multi-grid、poly 学习率等专属配置,通过train_net.py一键完成分布式训练与 Cityscapes 评估,并在 Cityscapes val 上以 R103-DC5 主干取得 DeepLabV3 78.5 mIoU、DeepLabV3+ 80.0 mIoU 的官方结果。结合本文的配置拆解与源码剖析,你可以快速复现官方实验,或将这套 encoder-decoder + 空洞卷积 + hard pixel mining 的组合迁移到自定义语义分割任务中。
【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考