☰
MMDetection从零到实战:环境搭建、数据准备与训练推理全攻略
2026/10/5 7:52:28 网站建设 项目流程

MMDetection是目标检测领域绕不开的一个工具箱,但很多初学者卡在第一步——环境装不上、数据格式搞不懂、配置文件看不懂。这篇教程会从零开始,把MMDetection的完整使用链路拆开来讲,包括环境搭建、数据准备、配置修改、训练推理和排错经验,内容偏实战,照着做就能跑通。

1. 为什么是MMDetection:入门之前先搞清楚框架边界

目标检测的框架不少,Detectron2、PaddleDetection、MMDetection三足鼎立。MMDetection之所以被用得最多,主要有三个原因:算法覆盖全、模块解耦彻底、社区活跃度高。截止目前,它内置了超过两百个检测模型和训练配置,从两阶段的Faster R-CNN、单阶段的YOLO系列、DETR这类Transformer系列,到Mask R-CNN、Cascade R-CNN、ATSS、FCOS等,基本学术界出现的经典算法都有官方实现。这意味着你做对比实验、复现论文、改造模型,都不需要从零写网络结构。

另一个核心优势是它的模块化设计。整个框架把模型拆成Backbone、Neck、Head、Loss等独立组件,每个组件都可以单独替换和组合。比如你想把ResNet50换成Swin Transformer,只需要在配置文件里改一行;想把FPN换成PAFPN,也是一行的事情。这种设计带来的不仅是代码复用,更是实验灵活性的质变——你能在同一个代码框架里公平对比不同改进点的增益,这是工程上和科研上都非常看重的能力。

不过有一点需要注意:不是所有任务都适合用MMDetection。如果你的目标是纯工业落地、追求极致推理速度,可能更适合直接用TensorRT或OpenVINO做部署优化;如果你是做非常规的检测任务(比如不规则倾斜目标检测、全景分割),虽然MMDetection有RotatedBox和Panoptic的扩展,但生态成熟度不如其核心检测任务。所以入门之前,先明确你的场景属于哪一类,避免学了框架之后发现用不上。

从学习曲线的角度看,MMDetection的配置文件体系是最大的门槛,但也是最大的财富。它的所有参数都通过config文件暴露,高度规范化。这个框架适合的人群很广:研究生复现论文、算法工程师做业务调优、开发者在项目中集成检测能力,都能从中找到对应的用法。本篇教程按“环境—数据—配置—训练—推理—排错”这条主线展开,接下来逐层深入。

2. 环境搭建:版本匹配比安装本身更考验耐心

2.1 版本对应关系决定了你能不能一次装通

MMDetection安装最核心的难点不在于执行pip install,而在于版本之间的匹配。它依赖PyTorch和MMCV,而MMCV又分为mmcv和mmcv-lite两个变体,不同MMDetection版本对MMCV的版本号还有严格限制。我见过太多人在装环境时卡了一下午,最后发现是版本不兼容。

先明确当前主流使用的版本分支。以MMDetection 2.x系列为例,它依赖的mmcv-full版本一般要求在1.4.0到1.7.x之间,PyTorch要求在1.5到1.13之间,Python版本则建议3.7到3.9(更高版本有时会遇到编译问题)。而MMDetection 3.x系列则做了比较大的架构调整,将mmcv和mmdet的版本绑定方式改成了mmcv>=2.0.0,并且和mmengine这个新的训练引擎绑定。

这里给出一个核心原则:先确定cuda和PyTorch,再确定MMCV,最后确定MMDetection。CUDA版本决定PyTorch版本,PyTorch版本决定MMCV编译方式,MMCV版本决定MMDetection版本上限。链路不能反着来,反着装大概率会出问题。

2.2 安装步骤:从CUDA到MMDetection的完整命令

第一步,检查硬件驱动支持的最高CUDA版本:

nvidia-smi

如果显示CUDA Version: 11.8,说明驱动支持到11.8。然后查看本机是否已安装合适的CUDA toolkit:

nvcc --version

我建议直接用conda创建一个干净的环境,避免污染已有环境:

conda create -n mmdet python=3.8 conda activate mmdet

第二步,安装PyTorch。以CUDA 11.8为例:

pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117

为什么这里选择1.13.1而不是更新版本?因为这是2.x系列MMDetection下兼容性最好的PyTorch版本之一。如果你对版本对应关系不够熟悉,又不想花大量时间折腾,直接用官方文档中推荐的组合即可。

第三步,安装MMCV。这里有一个关键选择:装mmcv-full还是mmcv。2.x系列必须安装带编译优化的mmcv-full:

pip install mmcv-full==1.7.2 -f https://download.openmmlab.com/mmcv/dist/cu117/torch1.13/index.html

注意这里的-f参数指定了预编译包的下载地址。使用预编译包可以跳过本地编译,安装速度快很多。如果你的CUDA版本或PyTorch版本不在预编译包的列表里,那就只能从源码编译:

git clone https://github.com/open-mmlab/mmcv.git cd mmcv MMCV_WITH_OPS=1 pip install -e .

预编译包能解决的尽量用预编译包,本地编译耗时且容易出问题。编译过程中常见的报错是Killed(内存不足)或gcc: error(编译器版本问题),处理起来很麻烦。

第四步,安装MMDetection。推荐用源码安装的方式,因为后续调试模型、修改源码都会用到:

git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -r requirements/build.txt pip install -e .

执行完后,验证安装是否成功:

import mmdet print(mmdet.__version__)

能正常输出版本号,说明核心安装完成。这里我补充一个容易被忽略的细节:MMDetection源码安装之后,配置文件和预训练权重会自动放在mmdetection目录下的checkpoints文件夹里,而不是放在site-packages里。这样做的意义是方便你直接查看源码结构和修改工具脚本,但也意味着不要轻易移动mmdetection目录的位置,否则权重路径会失效。

2.3 常见环境报错与对应处理

安装过程中最常见的三个报错,我逐个说明处理方式。

报错一:ModuleNotFoundError: No module named 'mmcv'。这个大概率是MMCV没装上,或者装的版本不对。检查一下是mmcv还是mmcv-full,2.x系列必须要后者。

报错二:ImportError: libGL.so.1: cannot open shared object file。这是缺少OpenGL依赖库,常见于Ubuntu服务器环境:

apt-get update apt-get install -y libgl1-mesa-glx libglib2.0-0

报错三:RuntimeError: CUDA error: no kernel image is available for execution on the device。这说明PyTorch的CUDA版本和你的GPU驱动不兼容,检查驱动版本,降低CUDA版本重新安装。

安装阶段的总体心态是:不要期望一次成功,而是按“驱动—CUDA—PyTorch—mmcv—mmdet”的顺序排查,每一步都验证通过再走下一步。

3. 数据准备:从官方数据集到自己数据集的完整链路

3.1 先跑通官方Demo:用COCO子集练手

数据准备的第一个阶段,建议先不要碰自定义数据集,而是跑通官方Demo。MMDetection官方提供了一些小型数据集的配置,比如configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py。但COCO完整数据集有20多GB,下载一次很耗时。这里有一个更轻量的方案:使用COCO数据集的mini版本,比如coco_data子集中的instances_train2017.json可以只保留前1000张图片。

下载COCO数据集的命令:

mkdir -p data/coco cd data/coco wget http://images.cocodataset.org/zips/train2017.zip wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip

如果你只是想快速验证环境没问题,也可以只下载annotations,然后用一个小脚本来抽取子集。不过更推荐的方式是直接用MMDetection自带的demo脚本跑推理,用一张测试图片(比如在demo/demo.jpg中自带的图片)验证模型可以正常加载和推理:

python demo/image_demo.py demo/demo.jpg configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth --device cuda:0

从网上下载训练好的权重,跑通推理,这步的意义是验证你的环境链路完整,为后续自定义数据的每一步打基础。

3.2 自定义数据集:从标注工具到COCO格式的转换

跑通Demo之后,就要开始准备自己的数据了。MMDetection默认支持COCO格式的数据标注文件(一个JSON文件),同时也兼容VOC格式。但建议统一使用COCO格式,因为它结构清晰,且在实例分割、关键点检测等任务上都有统一的表示方式。

整个自定义数据准备流程,我按步骤拆解:

第一步,数据采集与整理。把所有图片放到同一个目录下,命名尽量规范。比如images/train/、images/val/、images/test/。图片数量建议训练集和验证集按9:1或8:2划分。

第二步,标注工具选择。常用的工具有LabelImg(矩形框标注)、Labelme(多边形/矩形标注)、X-AnyLabeling等。如果你做的是矩形框检测,LabelImg足够用,它可以直接导出Pascal VOC格式的XML文件。如果你做的是实例分割,推荐Labelme,它导出的是JSON格式的多边形坐标。

第三步,格式转换。LabelImg导出的VOC格式并不能直接交给MMDetection用,需要转成COCO格式。MMDetection仓库里提供了一个通用的转换脚本:tools/dataset_converters/目录下的脚本。但对于大多数人来说,自己写转换脚本更灵活。

整理COCO JSON的格式其实并不复杂,核心包含5个key:images、annotations、categories、info、licenses。其中images是一组字典,每个字典包含id、file_name、width、height等字段;annotations里每一个字典对应一个标注框,包含id、image_id、category_id、bbox、area、iscrowd等字段;categories则定义类别列表和对应的id。

这里有一个非常关键的细节:类别id必须从1开始,而不是从0开始。虽然COCO数据集的原始类别是从1到80,但人群习惯从0编号。如果直接用0作为第一个类别的id,训练时会出现诡异的收敛问题。具体表现是训练loss下降正常,但验证时AP特别低甚至为0。这个坑坑了我两天,排查到最后发现就是类别id从0导致的问题。原因是MMDetection里有num_classes和categories的映射逻辑,后台使用0作为背景类,如果前景类和背景类id冲突,整个训练逻辑就乱了。

第四步,检查数据标注的有效性。转换完之后一定要做一次数据可视化检查,不要急着训练。方法有两种:一是写脚本把标注框画在图片上,随机抽查几十张;二是使用MMDetection提供的tools/analysis_tools/browse_dataset.py脚本:

python tools/analysis_tools/browse_dataset.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py --output-dir vis_output

这一步能帮你发现很多肉眼看不到的问题:标注框坐标超出图像边界、宽高为负数、重复标注等。这些脏数据如果不处理,训练时会严重影响效果。

3.3 数据增强:如何让模型在小数据集上不轻易过拟合

数据准备阶段有一个很少在入门教程里被重视但实际很重要的环节:数据增强策略。MMDetection在config里通过train_pipeline字段来控制。

默认的train_pipeline一般包括LoadImageFromFile(加载图片)、LoadAnnotations(加载标注)、Resize(缩放)、RandomFlip(随机翻转)、Normalize(归一化)、DefaultFormatBundle(格式打包)和Collect(收集)。这些基础方案对标准baseline是够用的,但如果你的数据量只有几百张、上千张,很容易过拟合。这时候可以考虑加入更激进的数据增强:

  • RandomCrop:随机裁剪,挺适合遮挡严重的场景,让模型学习局部特征。
  • PhotoMetricDistortion:调整亮度、对比度、饱和度、色相,模拟不同光照条件。
  • RandAugment或AutoAugment:自动数据增强策略,在大量实验中被证明能提升鲁棒性。
  • Mosaic(在最新的YOLOX和RTMDet配置中常见):把四张图拼成一张,显著提高小目标检测效果。

值得提醒的是,数据增强不是加得越多越好。增强强度过大会引入过多的分布偏移,导致训练不稳定。我的经验是先从基础增强开始,实验跑通后再逐个加增强项,每加一项都对比验证集AP的变化,效果变好就保留,变差就回退。

4. 配置文件:真正决定训练逻辑的核心部分

4.1 Config体系的工作方式:继承与覆盖

配置文件(config)是MMDetection的命脉,但也是新手最容易一头雾水的地方。一个模型的完整训练配置往往由多个文件拼接而成,通过继承关系来复用公共设置。这和面向对象编程里的继承非常相似——子类配置文件通过_base_字段来引用基础配置,然后按需覆盖特定字段。

拿经典的configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py为例,它的_base_包含三个部分:

_base_ = [ '../_base_/models/faster_rcnn_r50_fpn.py', '../_base_/datasets/coco_detection.py', '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' ]

这四个文件分别定义了模型结构、数据集配置、训练调度、运行环境。当你新建一个自己的config文件时,只需要继承这些基础配置,然后覆盖需要修改的字段,比如num_classes、data_root、batch_size等。

理解这个继承机制非常重要。很多同学在修改配置文件时,直接在完整的config基础上改,改完发现训练跑出来的还是原来的行为,原因就是没有正确覆盖基类中的字段,或者改错了层级。建议先运行:

python tools/analysis_tools/get_flops.py configs/xxx.py

这样能看到模型最终聚合后的完整配置。

4.2 关键字段逐项解析:model、dataset、schedule

一个配置文件的核心逻辑分为三个部分:模型怎么搭、数据怎么喂、怎么训练。

Model部分,要看三个子字段:type决定检测器类型(Faster R-CNN还是RetinaNet等),backbone决定主干网络(ResNet、Swin等),neck决定特征融合方式(FPN等),rpn_head和roi_head分别对应两阶段检测器中的区域提议网络和感兴趣区域分类回归网络。以num_classes为例,假设你有3个类别(加上背景是4个),需要分别修改rpn_head和roi_head下的num_classes。为了便于后续维护,建议在config文件的头部定义:

num_classes = 3

然后在整个config中引用这个变量。这样做的好处是后续调整类别数时只用改一处,不需要全局查找替换。

Dataset部分,核心参数包括data_root(数据根目录)、ann_file(标注文件路径)、img_prefix(图片路径)、pipeline(数据增强流程)、samples_per_gpu和workers_per_gpu。

这部分最容易出的问题就是路径错误。MMDetection在拼接路径时使用的规则是img_prefix + 'train2017/'这种形式,如果你把图片放在data/coco/train2017/下,配置里img_prefix写成了data/coco/,那实际就会找data/coco/train2017/train2017/,这肯定报错。

Schedule部分,optimizer设定优化器和初始学习率,lr_config设定学习率调整策略,runner设定最大训练轮次。以Faster R-CNN的标准配置为例:

optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) lr_config = dict( policy='step', warmup='linear', warmup_iters=500, warmup_ratio=0.001, step=[8, 11] ) runner = dict(type='EpochBasedRunner', max_epochs=12)

这里的lr=0.02对应的是batch_size=16(8卡x2)的情况。如果你的batch_size不是16,学习率应该按比例缩放。推荐线性缩放规则:lr_new = lr_base * (batch_size_new / batch_size_base)。假设你只用单卡且batch_size=2,理想的学习率大约是0.02 * 2 / 16 = 0.0025。如果不调整学习率,容易出现训练震荡或者收敛缓慢的问题。

4.3 从零搭建一个新Detector的config:实践示例

假设你要用Mask R-CNN在自定义数据集上训练一个3类实例分割模型,一个基础的config文件长这样:

_base_ = [ '../_base_/models/mask_rcnn_r50_fpn.py', '../_base_/datasets/coco_instance.py', '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' ] num_classes = 3 model = dict( roi_head=dict( bbox_head=dict(num_classes=num_classes), mask_head=dict(num_classes=num_classes) ) ) dataset_type = 'CocoDataset' data_root = 'data/custom/' data = dict( samples_per_gpu=2, workers_per_gpu=2, train=dict( type=dataset_type, ann_file='annotations/instances_train.json', img_prefix='images/train/', classes=('cat', 'dog', 'bird') ), val=dict( type=dataset_type, ann_file='annotations/instances_val.json', img_prefix='images/val/', classes=('cat', 'dog', 'bird') ) ) optimizer = dict(lr=0.0025)

这个配置文件体现了几个关键点:第一,model部分通过仅覆盖roi_head中需要修改的字段,保持了基类中backbone和neck的默认设置;第二,dataset部分指定了自定义数据集的路径,同时通过classes字段告诉数据加载器类别名称和顺序;第三,optimizer的学习率按batch_size做了线性缩放。

有一点要特别留意:classes的传入顺序必须与标注JSON中categories的顺序一致。MMDetection在构建类别映射时,用的是列表的顺序来给类别编号,顺序不对会导致训练出来的模型类别对应错乱。比如你把('cat', 'dog', 'bird')写成了('dog', 'cat', 'bird'),训练器的loss依然会收敛,但推理时把猫识别成狗,看起来模型完全没法用。排查这个问题的方式是查看类别名称和概率输出,不要只看AP指标。

5. 训练、评估与可视化:从命令行到结果解读

5.1 模型训练:单卡与多卡的正确打开方式

训练之前,先把预训练权重准备好。MMDetection在checkpoints目录下通常需要预训练好的backbone权重,这对于提高收敛速度和最终精度都很有帮助,尤其是在自定义数据集较小的情况下。官方provides下载脚本或直接通过curl下载:

mkdir -p checkpoints wget -c https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth -P checkpoints/

训练的命令统一走tools/train.py脚本。单卡训练:

python tools/train.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py --work-dir work_dirs/faster_rcnn_r50_fpn_1x_coco --auto-scale-lr

多卡训练使用torch.distributed.launch(对应旧版本PyTorch)或torchrun(对应新版):

bash tools/dist_train.sh configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py 8 --work-dir work_dirs/xxx

这里的8表示使用8张GPU。有一个细微但重要的差别:--work-dir参数指定了训练日志和权重文件的保存位置。如果你不指定,默认会生成在工作目录下以时间戳命名的文件夹。我建议每次实验都显式指定并统一命名为work_dirs/实验名称_日期,方便后续对比。

训练过程中要重点观察日志中的loss值变化。刚开始的几百个iter,loss下降非常快(从几下降到1左右),之后逐渐变缓,这是正常现象。如果loss在整个训练过程中都在0级别附近徘徊,可能是学习率过大;如果loss下降过慢、波动过大,可能是学习率过小或数据有问题。

5.2 看曲线、读日志:训练过程怎么判断是否健康

训练时不要只是傻等,要懂得实时监控训练状态。MMDetection在work_dirs下会生成一个时间戳.log.json文件,这是训练过程中的全部日志记录。你可以用脚本解析:

import json import matplotlib.pyplot as plt logs = [] with open('work_dirs/xxx.log.json', 'r') as f: for line in f: logs.append(json.loads(line)) loss_values = [x['loss'] for x in logs if 'loss' in x] epochs = [x['epoch'] for x in logs if 'loss' in x] plt.plot(epochs, loss_values) plt.xlabel('Epoch') plt.ylabel('Loss') plt.savefig('loss_curve.png')

如果不想自己写,MMDetection也提供了可视化工具:

python tools/analysis_tools/analyze_logs.py plot_curve work_dirs/xxx.log.json --keys loss --out loss_curve.png

观察曲线时有几个判断标准:训练loss和验证损失差距过大(比如训练loss在0.2以下,验证loss在1以上),说明过拟合,考虑增加数据增强或正则化;训练loss和验证损失都在高位下不去,说明欠拟合,考虑增大模型容量或增加训练轮数;曲线剧烈震荡,考虑降低学习率或增大batch_size。

5.3 测试与可视化推理:用训练好的模型跑自己的图片

训练完成后,首先要做定量评估。测试命令:

python tools/test.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py work_dirs/xxx/latest.pth --eval bbox

如果是实例分割任务,改为--eval bbox segm。评估结果会输出mAP、AR等指标。重点关注mAP@0.5和mAP@0.5:0.95这两个指标:前者是通用的宽松指标,后者更严格,也更接近实际应用中的检测质量感受。

接下来做定性可视化。用训练好的模型推理一张图片:

from mmdet.apis import init_detector, inference_detector import mmcv config_file = 'configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py' checkpoint_file = 'work_dirs/xxx/latest.pth' model = init_detector(config_file, checkpoint_file, device='cuda:0') result = inference_detector(model, 'test.jpg') model.show_result('test.jpg', result, out_file='result.jpg')

对于目标检测任务,show_result会在图片上画出预测框和类别分数。这里有一个容易被忽略的点:对不同尺度的图片,显示的框大小可能让你误判模型效果。如果要更仔细地评估模型在特定图像上的表现,可以用model.show_result加上score_thr=0.3这类阈值参数来调节置信度阈值。

6. 自定义模型和模块的修改思路

6.1 在配置文件里调整模型结构:加一个注意力模块

很多人在跑通默认的Faster R-CNN之后,就开始尝试改进模型。最基础的需求是在backbone后面加一个注意力模块。这里我不能直接放完整的自定义代码,但可以说清楚思路:使用MMDetection的注册器机制。

以一个假设场景为例,你想在ResNet50的layer4之后插入一个坐标注意力模块(CoordAttention)。做法是在mmdet/models/backbones下编写一个新模块,通过@BACKBONES.register_module()注册。然后在配置文件中指定:

model = dict( backbone=dict( type='MyResNet50', ... ) )

注册器是MMDetection和MMEngine的精华机制,它维护一个“字符串类型名到模块类”的映射。配置文件里填写type='MyResNet50'时,注册器会根据字符串去找对应的类。只要你正确导入了自定义模块,注册器就能识别。这个机制意味着你不需要修改框架核心代码,就能随时插入自己的模块。

从这里可以延伸出一个重要的学习方法:学会阅读已注册的模块源码,比会改配置文件更重要。以FasterRCNN类为例,它的前向传播逻辑、rpn_head和roi_head的交互方式,都在源码里有清晰的注释。

6.2 损失函数修改和后处理参数调整

除模型结构外,训练中还经常要调整损失函数。MMDetection把损失函数抽象成了loss_cls、loss_bbox、loss_mask等字段。如果你想给分类损失加一个focal loss的变体,只需修改roi_head中的配置:

model = dict( roi_head=dict( bbox_head=dict( loss_cls=dict(type='FocalLoss', use_sigmoid=True, gamma=2.0, alpha=0.25), loss_bbox=dict(type='L1Loss', loss_weight=1.0) ) ) )

改动配置就能改变训练目标,这是MMDetection框架对科研和调参极大友好的地方。同时,test_cfg和train_cfg中还有很多后处理参数,比如nms_iou_thr(NMS的IoU阈值)、score_thr(得分阈值)、max_per_img(每张图最大输出框数)。这些参数直接影响最终检测结果的召回率和精确度,实际调优时非常有用。

6.3 深度学习项目的目录组织方式

当自定义代码量变大后,建议不要直接修改mmdetection仓库源码,而是建立自己的项目目录:

my_project/ ├── configs/ │ └── my_faster_rcnn_r50_fpn.py ├── mmdet_custom/ │ ├── models/ │ │ ├── backbones/ │ │ ├── necks/ │ │ └── dense_heads/ │ └── datasets/ └── tools/

然后在自定义模块的__init__.py中显式导入模块,这样才能保证注册器能识别到你的类。

7. 常见训练问题和排错经验

这一节对新手来说价值最高,因为你在训练中遇到问题可能在官方文档里找不到直接答案,但大概率在这个列表里。

7.1 数据加载相关:Dataset not found、图像格式错误

报错信息:FileNotFoundError: data/coco/annotations/instances_train2017.json does not exist。

这通常是路径配置错误。检查conf文件中的data_root和ann_file是否拼写正确,以及文件是否真的在这个目录下。

报错信息:AttributeError: 'NoneType' object has no attribute 'shape'。

这通常是某张图片加载失败。常见原因包括图片损坏、扩展名大小写不一致(比如.JPG和.jpg混淆)、图片是RGBA四通道但未做转换。排查方法是检查数据的完整性,脚本遍历图片目录并逐一尝试用OpenCV读取:

import cv2 import glob import os img_files = glob.glob('data/custom/images/train/*.jpg') for img_file in img_files: img = cv2.imread(img_file) if img is None: print(f'Broken image: {img_file}')

7.2 显存溢出:如何用梯度累积减小显存压力

新手最常见的问题就是CUDA out of memory。显存不够,通常的解决方案是降低batch_size,但这样会影响模型收敛效果,尤其是BN层的统计量会变得不稳定。更稳妥的方案是使用梯度累积(gradient accumulation),等效于增大batch_size但不增加显存消耗。

MMDetection原生支持梯度累积。配置文件中有两个参数:optimizer_config里的grad_clip和accumulative_counts。后者在MMDetection 2.x的某些版本中可能不直接暴露,但可以通过自定义hook或直接修改训练脚本实现。如果不想改动代码,最简单的方案是在runner配置里添加梯度累积相关的字段。

在自定义数据集很小的情况下,我还会建议直接调整图像缩放尺寸来减小显存压力,比如从默认的(1333, 800)改成(800, 600)。这样虽然损失了一些精度,但显存占用显著降低,跑通流程没有问题,后续再逐步增加分辨率。

7.3 训练精度始终很低:先检查数据还是先调参

模型训练完发现mAP很低,这是最让人困惑的情况。按我的经验,优先级应该是:先检查数据,再检查配置,最后再调参。

数据检查要确认几个方面:类别id是否从1开始;标注框是否和图片中的物体对得上;训练集和验证集的图片是否存在信息泄漏(比如同一物体的不同裁剪同时出现在训练集和验证集);是否存在大量的背景框(即标注框占据了图片的大部分面积)。

配置检查要确认:类别数量是否和数据集一致;学习率是否随batch_size调整过;预训练权重是否成功加载。MMDetection训练时会在日志里输出load checkpoint from ...,你可以检查权重加载信息中是否出现了unexpected key或者missing key的警告。

调参这个环节排在最后是因为没有前两者的保证,调参有效果也是偶然的,不具可解释性。

8. 从入门到实战:一个完整的项目案例复盘

以我最近做一个安全帽佩戴检测项目为例,完整梳理整个流程。这个项目的目标是在监控视频画面上检测工人是否佩戴安全帽,类别只有两个:helmet和head。

数据集有大约2000张图片,标注工作用了LabelImg,导出VOC格式后自己写了转换脚本变成COCO格式。训练集1500张,验证集500张。类别id从1开始,1是helmet,2是head。

配置文件选择了faster_rcnn_r50_fpn_1x_coco.py作为基础,修改了num_classes=2,data_root指向项目数据目录,学习率从0.02调整到0.005(batch_size从16降到4)。训练了12个epoch,验证集mAP@0.5大概在91.2%。

在这个项目里遇到的比较有意思的问题是:硬帽子和头的遮挡关系。因为安全帽戴在头上,很多标注框是重叠的。一开始模型在预测时总会出现帽子框和头框同时输出的情况,业务上要求如果帽子框存在并覆盖头部,就不应该再输出头框。最后通过在test_cfg中设置合适nms_iou_thr以及一套业务层面的过滤逻辑解决了这个问题。

这个项目给我的体会是,算法精度只是其中一环,真正的业务难点往往在数据定义和后处理逻辑上。目标检测框架能帮你解决“在图片中找到物体并分类”的问题,但如何定义“找到”和“分类”的规则,这就是工程问题了。

9. 后续学习路径建议

跑通MMDetection的完整流程只是入门的开始。从这里继续深入,推荐按以下三个方向进阶:

第一方向,熟悉框架内部机制。读configs/_base_/models下的核心模型代码,理解FasterRCNN的前向逻辑,尝试实现一个简单的自定义head。这个阶段的目标是摆脱“只会改config”的层次,能够修改模型结构。

第二方向,追踪新模型。MMDetection官方支持RTMDet、DINO、Mask2Former等新模型。阅读这些模型的论文和对应代码,理解它们相对于baseline的改进点在哪里。在配置文件中切换到这些模型,对比它们在你自己的数据集上的表现。这个习惯能让你保持对目标检测技术前沿的跟进。

第三方向,工程化能力。模型训练完成后,部署是很多实际项目绕不开的环节。学习如何使用MMDeploy将MMDetection模型转换为ONNX或TensorRT格式,并完成服务化部署。同时可以尝试MMDetection的增量训练功能,在已有模型的基础上继续训练,适应新的数据分布,这在真实业务中非常常见。

以上,就是MMDetection入门到实战比较完整的经验了。工具本身在快速迭代,但这些工作流和排错思路是通用的,希望能帮你减少踩坑时间。

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

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

立即咨询