mmsegmentation 中的 CGNet:轻量级上下文引导语义分割网络的实现与配置详解
2026/9/15 15:32:29 网站建设 项目流程

mmsegmentation 中的 CGNet:轻量级上下文引导语义分割网络的实现与配置详解

【免费下载链接】mmsegmentationOpenMMLab Semantic Segmentation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmsegmentation

导读

本文以 configs/cgnet/README.md 为核心,结合 mmsegmentation 仓库中 CGNet 的源码、配置与测试,系统讲解这一面向移动端/嵌入式场景的轻量级语义分割网络:从论文核心思想(Context Guided Block 的局部特征 + 上下文联合 + 全局上下文增强)到开源实现的三阶段结构、核心超参数语义、Cityscapes 上的复现配置与训练/推理命令。读完本文,你将掌握如何在 mmsegmentation 中加载 CGNet 模型、读懂其完整配置链(backbone → decode head → 数据管线 → 优化器),并能在自己项目中按需调整网络宽度、深度与上下文感受野。

CGNet 是什么:轻量化与分割精度的平衡

CGNet(Context Guided Network)是发表在IEEE Transactions on Image Processing(2020)上的轻量级语义分割网络,论文题为CGNet: A Light-weight Context Guided Network for Semantic Segmentation(arXiv:1811.08201)。

其出发点非常明确(见 README 中 Abstract):移动设备对语义分割模型的需求持续增长,但当时 SOTA 网络参数量巨大,不适合移动端部署;而一些低内存占用模型又沿袭了分类网络的思路,忽略了语义分割"上下文关系至关重要"这一内在特性。CGNet 的核心主张是:在所有网络阶段都捕获上下文信息,并用精心设计控制参数量与内存占用。论文报告:在 Cityscapes 上不做任何后处理与多尺度测试,CGNet 以不到 0.5 M 参数即可取得 64.8% 的 mean IoU(原文数据,供背景参考)。

在 mmsegmentation 仓库中,CGNet 以 backbone 的形式接入标准EncoderDecoder框架(configs/base/models/cgnet.py),配合FCNHead作为分割头,并提供了两套 Cityscapes 训练配置与对应权重,模型集合记录于 configs/cgnet/metafile.yaml。

核心模块:Context Guided Block 的源码级解析

CGNet 的构建基础是 CG Block,完整实现在 mmseg/models/backbones/cgnet.py。从代码结构看,CG Block 由四个组件构成(对应源码 docstring 中的说明):

  1. 局部特征提取器f_loc3x3卷积,padding=1groups=channels(深度可分离卷积),捕获当前位置的局部细节;
  2. 周围上下文提取器f_sur3x3空洞卷积,padding=dilationdilation=dilation,同样按通道分组,通过扩张率扩大感受野以捕获"周围上下文";
  3. 联合特征提取:将局部与上下文特征在通道维拼接(torch.cat([loc, sur], 1)),经 BN 与 PReLU 激活后得到联合特征joi_feat
  4. 全局上下文提取器f_glo:即GlobalContextExtractor类,先做AdaptiveAvgPool2d(1)全局池化,再经Linear(channel → channel/reduction) → ReLU → Linear(channel/reduction → channel) → Sigmoid生成逐通道权重,对联合特征做通道级重标定(x * y),类似轻量的 SE 模块。
# mmseg/models/backbones/cgnet.py 中 ContextGuidedBlock 的核心前向逻辑(简化) out = self.conv1x1(x) # 1x1 降维/升维投影 loc = self.f_loc(out) # 局部特征 sur = self.f_sur(out) # 周围上下文(空洞卷积) joi_feat = torch.cat([loc, sur], 1) # 联合特征 joi_feat = self.bn(joi_feat) joi_feat = self.activate(joi_feat) out = self.f_glo(joi_feat) # 全局上下文增强 if self.skip_connect: return x + out # 残差连接

代码细节上还有几个值得注意的点(均有测试覆盖,见 tests/test_models/test_backbones/test_cgnet.py):

  • 深度可分离卷积f_locf_sur均为groups=channels的分组卷积且bias=False,这是 CGNet 控制参数量的关键手段之一;
  • 下采样模式downsample=Trueconv1x1变为3x3stride=2的卷积,并通过bottleneck(1x1 卷积)把拼接后的2*channels通道映射回out_channels
  • skip_connect:仅在下采样块中关闭(self.skip_connect = skip_connect and not downsample),普通块保留残差连接;
  • 可选 checkpointwith_cp=True时使用torch.utils.checkpoint以显存换速度(训练期生效);
  • 参数约束GlobalContextExtractor要求reduction >= 1 and channel >= reduction,测试用例中ContextGuidedBlock(8, 8)会因此触发AssertionError

网络整体结构:三阶段 + 原始输入注入

CGNet主干(mmseg/models/backbones/cgnet.py)整体分为三个阶段:

  • Stage 0(stem):3 个3x3卷积组成的下采样堆叠,同时用InputInjectionAvgPool2d(3, stride=2, padding=1))把原始输入分别下采样 2 倍(inject_2x)与 4 倍(inject_4x),与主干特征torch.cat后经 BN+PReLU,保留原始图像高频信息——这是 CGNet 的设计特点之一(原始输入注入);
  • Stage 1num_blocks[0]个 CG Block(默认 3 个),首个块downsample=True将分辨率降为 1/2;第一个块的输出down1会与当前特征、inject_4x注入的原始输入拼接;
  • Stage 2num_blocks[1]个 CG Block(默认 21 个),首个块再次下采样,最终输出为2*num_channels[2](默认 256)通道。

forward返回三阶段特征列表output,其中第三阶段(in_index=2)被FCNHead用作分割头的输入。测试用例验证了默认配置下输入2x3x224x224时三阶段输出形状分别为[2, 35, 112, 112][2, 131, 56, 56][2, 256, 28, 28]——注意拼接输入注入后,通道数并不等于num_channels本身(35 = 32 + 3,131 = 128 + 3)。

CGNet类的关键构造参数及默认值如下:

参数默认值含义
in_channels3输入图像通道数
num_channels(32, 64, 128)三阶段特征通道数
num_blocks(3, 21)Stage 1 与 Stage 2 中 CG Block 的数量
dilations(2, 4)两阶段f_sur空洞卷积的扩张率,决定上下文感受野
reductions(8, 16)两阶段GlobalContextExtractor的通道压缩比
norm_cfgdict(type='BN', requires_grad=True)归一化层配置
act_cfgdict(type='PReLU')激活函数(PReLU 会自动注入num_parameters
norm_evalFalseTrue时训练中冻结 BN 统计量
with_cpFalse是否启用梯度 checkpoint 以节省显存

构造参数有严格校验:num_channels必须为长度 3 的 tuple、num_blocks/dilations/reductions必须为长度 2 的 tuple,非法输入会抛AssertionError(测试用例对此做了覆盖)。

完整配置解读:以 Cityscapes 复现配置为例

模型与数据基础配置

CGNet 的基础模型配置位于 configs/base/models/cgnet.py,它定义了:

  • 归一化SyncBNeps=1e-03),适合多卡训练;
  • 数据预处理器SegDataPreProcessor,使用 Cityscapes 的均值[72.39, 82.91, 73.16]std=[1,1,1](即仅做减均值)、bgr_to_rgb=True
  • backboneCGNet,默认(32, 64, 128)通道、(3, 21)个块、dilations=(2, 4)reductions=(8, 16)
  • decode_headFCNHeadin_channels=256(对应 Stage 2 输出)、num_convs=0concat_input=Falsedropout_ratio=0num_classes=19(Cityscapes 类别数),并配CrossEntropyLoss与一份 Cityscapes 类别权重class_weight(用于缓解类别不平衡);
  • 推理模式test_cfg=dict(mode='whole'),即整图前向推理,不做滑动窗口。

训练配置一:680x680 裁剪

configs/cgnet/cgnet_fcn_4xb4-60k_cityscapes-680x680.py 继承了模型/数据集/运行时三份基础配置,并做了以下定制:

crop_size = (680, 680) data_preprocessor = dict(size=crop_size) model = dict(data_preprocessor=data_preprocessor) train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations'), dict(type='RandomResize', scale=(2048, 1024), ratio_range=(0.5, 2.0), keep_ratio=True), dict(type='RandomCrop', crop_size=crop_size), dict(type='RandomFlip', prob=0.5), dict(type='PackSegInputs') ] test_pipeline = [ dict(type='LoadImageFromFile'), dict(type='Resize', scale=(2048, 1024), keep_ratio=True), dict(type='LoadAnnotations'), # 注释在 Resize 之后加载,GT 不做缩放 dict(type='PackSegInputs') ] train_dataloader = dict(batch_size=8, num_workers=4, dataset=dict(pipeline=train_pipeline))

训练采用迭代制(IterBasedTrainLoop):total_iters=60000,每 4000 次迭代验证并保存 checkpoint;优化器为 Adam(lr=0.001, eps=1e-08, weight_decay=0.0005),学习率调度使用 PolyLR(power=0.9eta_min=1e-4、按迭代衰减)。

训练配置二:512x1024 全分辨率

configs/cgnet/cgnet_fcn_4xb8-60k_cityscapes-512x1024.py 与上一份配置差异很小但值得对比:

  • crop_size = (512, 1024):直接采用 Cityscapes 原生分辨率(而非 680x680 方形裁剪),因此没有重写 train/test pipeline,直接继承基础数据集的默认管线;
  • train_dataloader = dict(batch_size=8):batch size 从 4 提升到 8(配合 4 卡共 32),以补偿更大输入尺寸带来的迭代吞吐差异;
  • 优化器、PolyLR、60000 次迭代、4000 间隔验证等设置与 680x680 版本完全一致。

从 configs/cgnet/metafile.yaml 可以确认两套配置在 4 张 V100 上的训练资源:680x680 版本(4x4=16 总 batch)显存约 7.5 GB,512x1024 版本(4x8=32 总 batch)显存约 8.3 GB。

官方复现结果

README 的 Results 表格记录了在 Cityscapes 上的复现成绩(单尺度 mIoU 与多尺度+翻转 mIoU):

方法BackboneCrop SizeLr schdMem (GB)Inf time (fps)设备mIoUmIoU(ms+flip)配置
CGNetM3N21680x680600007.530.51V10065.6368.04cgnet_fcn_4xb4-60k_cityscapes-680x680.py
CGNetM3N21512x1024600008.331.14V10068.2770.33cgnet_fcn_4xb8-60k_cityscapes-512x1024.py

其中 512x1024 配置的 mIoU(68.27 / 70.33)明显高于 680x680(65.63 / 68.04),说明输入分辨率对分割精度的直接影响;两套配置的权重与训练日志下载地址均记录在 configs/cgnet/metafile.yaml 中。值得注意的是,上述推理速度(约 30 fps)与显存数据以 V100 单卡为基准,实际部署到移动端/嵌入式设备时需要结合具体硬件重新评估。

实战:训练、测试与推理

mmsegmentation 的标准工具入口位于 tools/train.py 与 tools/test.py,训练 CGNet 的最小命令为:

# 单卡训练(680x680 配置) python tools/train.py configs/cgnet/cgnet_fcn_4xb4-60k_cityscapes-680x680.py # 多卡分布式训练(4 卡,对应 metafile 中的官方设置) bash tools/dist_train.sh configs/cgnet/cgnet_fcn_4xb4-60k_cityscapes-680x680.py 4 # 测试并输出 mIoU python tools/test.py configs/cgnet/cgnet_fcn_4xb4-60k_cityscapes-680x680.py /path/to/checkpoint.pth

如需在推理时启用多尺度 + 翻转(README 中的mIoU(ms+flip)指标),可在测试命令中追加--tta参数(依赖配置中提供的 TTA 数据管线,具体以当前仓库版本支持为准)。演示脚本 demo/image_demo.py 提供了对单张图片直接调用模型进行分割可视化的入口。

训练前请先按照 README.md 的安装指引准备环境(mmengine、mmcv 等依赖),并确保 Cityscapes 数据集已按 configs/base/datasets/cityscapes.py 的路径约定放置。

如何针对自己的场景调整 CGNet

基于上文对源码与配置的拆解,可以给出以下可落地的调参思路(均有代码依据):

  1. 控制模型大小num_channels=(32, 64, 128)是论文默认的 M3N21(21 对应num_blocks[1])配置。减小num_channelsnum_blocks可进一步压缩参数量;需要注意的是 backbone 输出通道需与decode_head.in_channels保持一致(默认 256 = 2 × 128)。
  2. 调整上下文感受野dilations直接决定f_sur空洞卷积的扩张率,reductions决定全局上下文提取器的压缩比。它们分别控制"周围上下文"与"全局上下文"的强度。
  3. 显存受限时:开启with_cp=True(梯度 checkpoint),或用norm_eval=True冻结 BN 统计量;训练阶段仍可保持较低 batch size。
  4. 更换数据集:修改num_classesclass_weight(如无先验可置None)以及 configs/base/datasets 下的数据管线与预处理器均值/方差。

引用

如果研究中使用了 CGNet,README 提供了论文 BibTeX(即Wu et al., IEEE TIP 2020的 CGNet 论文)。在 mmsegmentation 中引用 CGNet 模型时,也可参考 CITATION.cff 中对整个项目的引用方式,并在论文中标注复现配置(模型、训练迭代数、batch size、输入分辨率等),以保证实验可复现。

【免费下载链接】mmsegmentationOpenMMLab Semantic Segmentation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmsegmentation

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询