- 人工智能
- 计算机视觉
- 深度学习
- 模型评测
【免费下载链接】mmdetection
OpenMMLab Detection Toolbox and Benchmark
导读
本文是一份面向 MMDetection(OpenMMLab Detection Toolbox)开发者的端到端实战指南,完整讲解"如何在标准数据集上训练、测试与推理自定义检测模型"。全文以 Cityscapes 数据集上训练一个定制版Cascade Mask R-CNN R50为例:用自研 Neck(以AugFPN为示例)替换默认的FPN,并在训练管线中注入Rotate/TranslateX自动数据增强。读完本文,你将掌握四大核心能力:标准数据集的准备与格式转换、基于注册机制自定义模型组件、编写继承式配置文件、以及使用tools/train.py与tools/test.py完成完整的训练-测试闭环。
整体流程只需四步:① 准备标准数据集 → ② 编写自定义模型模块 → ③ 编写配置文件 → ④ 训练、测试与推理。下文将结合仓库源码逐一深入。
第一步:准备标准数据集
1.1 数据目录结构
本文以标准 Cityscapes 数据集为例。MMDetection 建议将数据集根目录软链接到$MMDETECTION/data下,目录结构如下:
mmdetection ├── mmdet ├── tools ├── configs ├── data │ ├── coco │ │ ├── annotations │ │ ├── train2017 │ │ ├── val2017 │ │ ├── test2017 │ ├── cityscapes │ │ ├── annotations │ │ ├── leftImg8bit │ │ │ ├── train │ │ │ ├── val │ │ ├── gtFine │ │ │ ├── train │ │ │ ├── val │ ├── VOCdevkit │ │ ├── VOC2007 │ │ ├── VOC2012如果你的目录结构不同,则需要相应修改配置文件中的路径。另一种更优雅的做法是通过环境变量指定数据根目录,从而完全避免改动配置文件:
export MMDET_DATASETS=$data_root配置解析时会用$MMDET_DATASETS替换数据根路径,这一机制使得同一份配置可以无缝迁移到不同机器、不同数据挂载点。从仓库中的 基础数据集配置 可以看到,数据根路径默认值即为data/cityscapes/,并通过data_root变量引用:
dataset_type = 'CityscapesDataset' data_root = 'data/cityscapes/'1.2 将 Cityscapes 标注转换为 COCO 格式
MMDetection 的 Cityscapes 实例分割配置依赖 COCO 格式的标注文件(例如annotations/instancesonly_filtered_gtFine_train.json),因此需要先用仓库自带的转换脚本tools/dataset_converters/cityscapes.py完成格式转换:
pip install cityscapesscripts python tools/dataset_converters/cityscapes.py ./data/cityscapes --nproc 8 --out-dir ./data/cityscapes/annotations./data/cityscapes:Cityscapes 数据根目录;--nproc 8:使用 8 个进程并行转换,可显著提速;--out-dir ./data/cityscapes/annotations:转换后 JSON 标注的输出目录。
1.3 预训练权重准备
目前configs/cityscapes下的配置文件使用COCO 预训练权重初始化模型。如果网络不可用或较慢,请务必提前手动下载预训练模型,否则训练开始阶段会因为无法加载权重而报错。这一机制在自定义配置中通过load_from字段指定(详见第四步),它只加载权重、不参与模型结构定义。
第二步:编写自定义模型(以 AugFPN Neck 为例)
第二步的核心是"用你自己的模块替换或扩展现有检测器"。这里假设我们要在 Cascade Mask R-CNN R50 检测器中实现一个新的 Neck ——AugFPN,替换默认的FPN。
2.1 新建 AugFPN 模块文件
首先在mmdet/models/necks/下新建文件augfpn.py:
import torch.nn as nn from mmdet.registry import MODELS @MODELS.register_module() class AugFPN(nn.Module): def __init__(self, in_channels, out_channels, num_outs, start_level=0, end_level=-1, add_extra_convs=False): pass def forward(self, inputs): # implementation is ignored pass这里最关键的一行是@MODELS.register_module()装饰器。从仓库的 注册表实现 可以看到,MODELS是 MMDetection 提供的 17 个注册节点之一,它是 MMEngine 根注册表的子注册表:
# manage all kinds of modules inheriting `nn.Module` MODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmdet.models'])注册机制的本质是建立"字符串类型名 → 类对象"的映射。装饰器把AugFPN类以'AugFPN'为键登记进MODELS注册表,之后配置文件里type='AugFPN'就会被自动解析为这个类。你可以参照仓库中真实的 FPN 实现 来理解一个标准 Neck 的接口约定——其__init__签名恰好包含示例中出现的全部参数:
@MODELS.register_module() class FPN(BaseModule): def __init__( self, in_channels: List[int], out_channels: int, num_outs: int, start_level: int = 0, end_level: int = -1, add_extra_convs: Union[bool, str] = False, ... ) -> None:从 FPN 的源码实现可以进一步理解这些参数的含义:
in_channels:各尺度输入特征图的通道数列表(与 backbone 的out_indices输出一一对应);out_channels:每个输出尺度统一的通道数;num_outs:输出特征金字塔的层数;start_level/end_level:从 backbone 的哪一层开始/结束构建金字塔,end_level=-1表示取到最后一层;add_extra_convs:是否在金字塔之上追加下采样卷积(如 RetinaNet 场景),源码中True会被归一化为'on_input'。
FPN 的__init__中还包含长度断言(如num_outs >= num_ins - start_level),新实现的 Neck 建议同样保留这类输入校验,避免配置错误在训练中途才暴露。
2.2 导入模块(两种方式二选一)
方式一:修改mmdet/models/necks/__init__.py
在 necks 包初始化文件 中追加一行导入,并同步加入__all__:
from .augfpn import AugFPN仓库中每个内置 Neck(FPN、PAFPN、DyHead等)都是通过这种集中导出的方式完成注册的,例如:
from .fpn import FPN ... __all__ = ['FPN', 'BFP', ...]方式二:在配置文件中使用custom_imports(推荐,零侵入)
不想改动仓库源码时,可以在配置文件里动态导入新模块:
custom_imports = dict( imports=['mmdet.models.necks.augfpn'], allow_failed_imports=False)custom_imports会在配置加载阶段主动执行指定模块的导入,从而触发@MODELS.register_module()完成注册。这种方式让自定义代码完全独立于仓库本体,便于后续升级、复用与分发。
2.3 在配置中替换 Neck
配置层面只需要把neck字段的type换成'AugFPN'并传入所需参数:
neck=dict( type='AugFPN', in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5)对比仓库中 Cascade Mask R-CNN 基础配置 里默认的 FPN 配置,可以直观看到两者结构完全一致、仅type不同,这正是注册机制带来的"即插即用"体验:
neck=dict( type='FPN', in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5),第三步:编写自定义配置文件
3.1 配置继承策略
第三步是为自己的训练设定准备一份完整配置。假设配置文件位于configs/cityscapes/目录下,命名为cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py,通过_base_字段继承三份基础配置,从而只突出必要的修改点:
# The new config inherits the base configs to highlight the necessary modification _base_ = [ '../_base_/models/cascade-mask-rcnn_r50_fpn.py', '../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py' ]三份基础配置分别提供:模型结构(cascade-mask-rcnn_r50_fpn.py)、数据加载与评测(cityscapes_instance.py)、默认运行时设置(default_runtime.py)。
3.2 模型部分:替换 Neck 与调整类别数
model = dict( # set None to avoid loading ImageNet pre-trained backbone, # instead here we set `load_from` to load from COCO pre-trained detectors. backbone=dict(init_cfg=None), # replace neck from defaultly `FPN` to our new implemented module `AugFPN` neck=dict( type='AugFPN', in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5), # We also need to change the num_classes in head from 80 to 8, to match the # cityscapes dataset's annotation. This modification involves `bbox_head` and `mask_head`. roi_head=dict( bbox_head=[ dict( type='Shared2FCBBoxHead', in_channels=256, fc_out_channels=1024, roi_feat_size=7, # change the number of classes from defaultly COCO to cityscapes num_classes=8, bbox_coder=dict( type='DeltaXYWHBBoxCoder', target_means=[0., 0., 0., 0.], target_stds=[0.1, 0.1, 0.2, 0.2]), reg_class_agnostic=True, loss_cls=dict( type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)), dict( type='Shared2FCBBoxHead', in_channels=256, fc_out_channels=1024, roi_feat_size=7, # change the number of classes from defaultly COCO to cityscapes num_classes=8, bbox_coder=dict( type='DeltaXYWHBBoxCoder', target_means=[0., 0., 0., 0.], target_stds=[0.05, 0.05, 0.1, 0.1]), reg_class_agnostic=True, loss_cls=dict( type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)), dict( type='Shared2FCBBoxHead', in_channels=256, fc_out_channels=1024, roi_feat_size=7, # change the number of classes from defaultly COCO to cityscapes num_classes=8, bbox_coder=dict( type='DeltaXYWHBBoxCoder', target_means=[0., 0., 0., 0.], target_stds=[0.033, 0.033, 0.067, 0.067]), reg_class_agnostic=True, loss_cls=dict( type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)) ], mask_head=dict( type='FCNMaskHead', num_convs=4, in_channels=256, conv_out_channels=256, # change the number of classes from default COCO to cityscapes num_classes=8, loss_mask=dict( type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))要点解读:
backbone=dict(init_cfg=None):显式关闭 ImageNet 预训练主干初始化,改为通过下文load_from加载 COCO 预训练的完整检测器权重,以获得更稳定的训练起点;- 三阶段级联
bbox_head与mask_head全部从 80 类改为 8 类:Cityscapes 实例分割数据集只有 8 个类别,类别数不匹配会直接导致网络输出维度与标注维度不一致。三个Shared2FCBBoxHead的target_stds依次递减([0.1,0.1,0.2,0.2]→[0.05,...]→[0.033,...]),这是 Cascade R-CNN 由粗到精回归的标准设计,与 基础配置 保持一致,仅类别数不同; - 如果只做检测不做分割,可同时考虑将
mask_head移除,但本文示例是完整的实例分割模型。
3.3 数据部分:自定义训练管线(AutoAugment)
重写train_pipeline,在原有RandomResize/RandomFlip基础上插入AutoAugment自动增强:
# over-write `train_pipeline` for new added `AutoAugment` training setting train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True, with_mask=True), dict( type='AutoAugment', policies=[ [dict( type='Rotate', level=5, img_border_value=(124, 116, 104), prob=0.5) ], [dict(type='Rotate', level=7, img_border_value=(124, 116, 104)), dict( type='TranslateX', level=5, prob=0.5, img_border_value=(124, 116, 104)) ], ]), dict( type='RandomResize', scale=[(2048, 800), (2048, 1024)], keep_ratio=True), dict(type='RandomFlip', prob=0.5), dict(type='PackDetInputs'), ] # set batch_size per gpu, and set new training pipeline train_dataloader = dict( batch_size=1, num_workers=3, # over-write `pipeline` with new training pipeline setting dataset=dict(pipeline=train_pipeline))AutoAugment的底层实现位于 augment_wrappers.py,它继承自RandomChoice:调用时从policies列表中等概率随机挑选一个增强策略作用于图像。其policies参数结构为List[List[dict]]——外层列表的每个元素是一条候选策略,内层是若干按顺序执行的基础变换。这里定义了两条策略:
- 策略 1:仅
Rotate(level=5,概率 0.5); - 策略 2:
Rotate(level=7)后接TranslateX(level=5,概率 0.5)。
Rotate与TranslateX注册于 geometric.py,img_border_value=(124, 116, 104)指定旋转/平移后填充的边界颜色(即图像边缘填充色)。所有几何增强都会同步更新gt_bboxes、gt_masks等标注,因此无需担心标注错位问题。
3.4 优化器、学习率与训练循环
# Set optimizer optim_wrapper = dict( type='OptimWrapper', optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)) # Set customized learning policy param_scheduler = [ dict( type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), dict( type='MultiStepLR', begin=0, end=10, by_epoch=True, milestones=[8], gamma=0.1) ] # train, val, test loop config train_cfg = dict(max_epochs=10, val_interval=1)- 优化器:SGD(
lr=0.01, momentum=0.9, weight_decay=0.0001),外层用OptimWrapper包装,这是 MMEngine 的标准写法; - 学习率策略:先线性热身(前 500 iter 从
0.001倍起步线性上升到目标学习率),再使用MultiStepLR在第 8 个 epoch 将学习率衰减为原来的0.1(gamma=0.1),共训练 10 个 epoch; - 训练循环:
max_epochs=10,每个 epoch 结束做一次验证(val_interval=1)。
3.5 加载 COCO 预训练权重
# We can use the COCO pre-trained Cascade Mask R-CNN R50 model for a more stable performance initialization load_from = 'https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth'load_from指定从 OpenMMLab 模型库下载 COCO 上预训练的 Cascade Mask R-CNN R50 权重文件。与init_cfg(结构初始化)不同,load_from属于权重级别的"迁移初始化",只加载参数、跳过结构差异(例如 head 的类别数不同会导致对应层被自动跳过),配合backbone=dict(init_cfg=None)使用即可实现"COCO 检测器 → Cityscapes 微调"的迁移学习范式。
第四步:训练新模型
配置就绪后,一条命令即可启动训练:
python tools/train.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.pytools/train.py的入口实现位于 tools/train.py,它提供了一系列实用的命令行参数,可在不修改配置文件的情况下调整训练行为:
| 参数 | 作用 |
|---|---|
config(位置参数) | 训练配置文件路径 |
--work-dir | 指定日志与模型权重保存目录 |
--amp | 开启自动混合精度训练 |
--auto-scale-lr | 根据 batch size 自动缩放学习率 |
--resume | 断点续训;不指定值时自动从 work-dir 中最近的 checkpoint 恢复 |
--cfg-options | 以key=value形式覆盖任意配置项(支持嵌套与列表值) |
--launcher | 分布式启动器(none/pytorch/slurm/mpi) |
日志、checkpoint 和可视化结果默认输出到./work_dirs/<config文件名>/目录,训练过程中的验证指标也会按val_interval=1的设定在每个 epoch 结束后打印。
更详细的多卡训练(如tools/dist_train.sh)与调度器用法,可进一步参考仓库的 训练指南。
第五步:测试与推理
使用tools/test.py加载训练好的权重(例如第 10 个 epoch 的 checkpoint)完成测试:
python tools/test.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py work_dirs/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes/epoch_10.pth测试将依据配置中继承自 cityscapes_instance.py 的评测器输出结果——该基础配置同时启用了两个评测器:
CocoMetric:基于 COCO 格式标注计算bbox(检测)与segm(分割)指标;CityScapesMetric:输出 Cityscapes 官方风格的实例分割评测结果。
tools/test.py同样支持--cfg-options、--launcher等参数,且支持--out保存预测结果、--format-only仅格式化输出用于官方评测提交(配置中注释部分给出了test集与format_only的示例写法)。
对于单张图片的推理演示,仓库 demo 目录 提供了image_demo.py等脚本,可用于快速可视化模型效果。更完整的测试与推理细节可参考 测试指南。
深入理解:这套自定义流程背后的机制
注册机制(Registry)是自定义的基石
MMDetection 3.x 的模块化核心是 MMEngine 的注册表体系。mmdet/registry.py 中定义了MODELS、DATASETS、TRANSFORMS、OPTIMIZERS、PARAM_SCHEDULERS、HOOKS、METRICS等 17 个注册节点,分别管理模型、数据、变换、优化器、学习率调度器、钩子与评测指标。任何被@REGISTRY.register_module()装饰的类,都可以通过配置文件中的type字符串被实例化——这就是为什么自定义 Neck 只需要"写一个类 + 注册 + 在配置里改 type"三步即可无缝接入既有检测器。
训练管线中的每个环节都可替换
本文示例仅自定义了 Neck 与数据增强,但同样的范式可推广到检测器的每个组件:
- Backbone:新增骨干网络(对应
BACKBONES或MODELS注册); - Head:自定义检测/分割头(如
bbox_head、mask_head); - Loss:自定义损失函数;
- Optimizer / 学习率调度器 / Hook:自定义训练时运行配置。
详细的组件定制指南见 Customize Models,运行时设置(优化器、梯度裁剪、训练调度、钩子等)的定制见 Customize Runtime Settings。
总结
本文以"Cityscapes + Cascade Mask R-CNN R50 + AugFPN + AutoAugment"为完整案例,走通了 MMDetection 自定义模型开发的全部环节:数据准备与格式转换 → 基于MODELS注册机制实现新 Neck → 通过_base_继承编写配置文件(替换 Neck、调整类别数、注入自动增强、自定义优化与调度)→ 使用tools/train.py训练 → 使用tools/test.py测试与推理。整个过程清晰地体现了 MMDetection 3.x"注册机制 + 配置驱动"的设计哲学:任何组件都可通过"实现一个类 + 注册 + 修改配置"三步完成替换,而无需改动框架核心代码。这套方法论可直接迁移到 backbone、head、loss、optimizer、hook 等所有组件,是二次开发与算法研究的通用起点。
- 人工智能
- 计算机视觉
- 深度学习
- 模型评测
【免费下载链接】mmdetection
OpenMMLab Detection Toolbox and Benchmark
相关推荐
MMDetection 模型微调完全指南:基于 COCO 预训练权重在新数据集上微调(以 Cityscapes 为例)
MMDetection 模型微调完全指南:基于 COCO 预训练权重在新数据集上微调(以 Cityscapes 为例) 在 MMDetection 中,使用在大
人工智能计算机视觉深度学习模型评测MMDetection 模型训练实战指南:从标准数据集到自定义数据集的完整训练流程
MMDetection 模型训练实战指南:从标准数据集到自定义数据集的完整训练流程 本文是 MMDetection 官方用户指南中 docs/en/user_g
人工智能计算机视觉深度学习模型评测基于 MMDetection 在 Cityscapes 数据集上训练与评估目标检测、实例分割模型实战指南
基于 MMDetection 在 Cityscapes 数据集上训练与评估目标检测、实例分割模型实战指南 Cityscapes 是面向城市场景理解的经典街景数据
人工智能计算机视觉深度学习模型评测
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考