如果用一句话概括MMDetection,我会说它是一个把目标检测从“自学成才”变成“按图索骥”的工具箱。前几年想做目标检测,光是数据加载、模型搭建、训练循环这些基础代码就能折腾好几个星期;现在用MMDetection,你可以在一天之内把环境配置好,用官方预训练权重跑通推理,再花一两天整理好标注数据,直接开始训练自己的检测器。这篇指南写给零基础想入门目标检测的朋友,目标是让你用最小的代价跑通一个完整的检测流程:环境搭建、配置文件解读、数据准备、训练与测试。不管你之前有没有做过深度学习,只要会基本的Python语法,能上手操作命令行,跟着走一遍,就知道整个流程里各个组件是怎么配合的。
1. 为什么推荐从MMDetection入门
1.1 目标检测工具箱到底解决了什么问题
很多新手对“目标检测”的第一印象是“在图片里画框框”,这个理解没错,但工程落地的时候画框只是最后一步。你需要处理标注数据、设计网络结构、写损失函数、配优化器、管理日志、算mAP、做可视化,每一步都有大量细节。MMDetection把这些环节统一收口,变成一个“配置驱动”的框架:你写一个Python配置文件,它就知道用什么模型、读什么数据、训多少轮、怎么评估。
框架给你提供的核心能力有三块。
第一是模型库。检测器从经典的Faster R-CNN、SSD,到一阶段网红模型,再到Mask R-CNN、Cascade R-CNN这类进阶结构,框架里基本都有实现和预训练权重。你不需要从论文复现代码,直接用官方实现就能复现论文效果。
第二是标准化的数据流程。训练集、验证集、测试集的划分,图片读取、缩放、翻转、归一化这些预处理操作,都封装成一个个可插拔的pipeline组件。你要换一种数据增强,改一行配置就行,不用重写数据加载器。
第三是训练与评估闭环。训练日志、模型权重保存、验证集指标计算、各类可视化工具都是现成的。对小白来说,这意味着你少走很多“造轮子”的弯路,可以把精力集中在理解模型和数据上。
1.2 从零手写检测器 vs 使用成熟框架的取舍
我知道有些同学会纠结:用现成框架会不会变成“调包侠”?手写一个Faster R-CNN是不是更能提升能力?
我的建议很直接:第一遍学习,不要手写。手写一个能用的检测器,涉及anchor生成、RoIAlign、NMS、RPN、正负样本分配这些模块,每一个都够你调试好几天。很多人在把loss调到收敛之前就已经放弃了,挫败感特别强。而用MMDetection先跑通一个完整流程,你知道了一个检测系统长什么样,回头再去读源码、手写模块,其实更容易看懂每一步在干什么。
你可以把框架理解成一本带标准答案的习题集。先照着答案做一遍,理解“哦,原来这一步是干这个的”,再合上答案自己做。直接蒙头做,可能要卡在第一步很久。这对初学阶段来说,效率差太多了。
1.3 小白学习路线的整体规划
既然标题写了“基础实现指南(一)”,说明这个系列是有规划的。我给一个建议路线。
第一阶段,也就是本篇要讲的,搞定三件事:环境能跑、看懂配置文件、用公开数据集或自己标注的小数据集训练出一个模型。在这个阶段你不要追求把源码里每个类都看懂,重点是“流程通”。
第二阶段,研究核心组件。比如Faster R-CNN的RPN和RoIHead到底怎么协作、数据pipeline里每个变换做了什么、训练时的正负样本怎么分配。这时候可以读源码、改模型结构、换backbone。
第三阶段,做自定义项目。接一个真实场景的数据集,调参与优化,把它部署成可用服务。
很多人一开始就冲着第三阶段去,结果基础没打牢,遇到问题都不知道从哪里查。跟着这个路线走,至少不会出现“模型训练出来但不知道它是怎么训练出来的”这种窘境。
2. 环境准备:搞定版本匹配才是第一道坎
2.1 先装Python与PyTorch:版本不能乱来
我见过太多新手第一章就栽在环境上,原因不是操作难,而是版本不匹配。MMDetection的版本和PyTorch、CUDA、mmcv之间有一张“对应关系表”,你按老版本的教程装,跑了新版本框架,出来一堆莫名其妙的报错。
先说Python。建议直接用Miniconda或Anaconda管理环境。不要用系统自带的Python,因为后面你会在不同项目里用不同版本的依赖,隔离环境能减少很多麻烦。
conda create -n mmdet python=3.8 conda activate mmdet我习惯用Python 3.8或3.9,不是越新越好,新的Python版本可能导致一些依赖库还没适配。装完之后,接着要确定你的CUDA环境。如果你有NVIDIA显卡,先在终端里执行nvidia-smi,看右上角的CUDA Version,这个数字代表你的显卡驱动支持的最高CUDA版本。比如显示CUDA 12.1,你就可以安装对应或更低版本的PyTorch。
PyTorch的安装命令在它的官网首页就能找到,注意选对CUDA版本。例如在CUDA 12.1的环境中,你可以用类似下面的命令:
conda install pytorch torchvision pytorch-cuda=12.1 -c pytorch -c nvidia装完之后一定要验证一下,确保PyTorch能调用GPU。这一步很多人跳过了,结果训练到一半才发现用的是CPU,速度慢得离谱,那才是真的浪费时间。
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"看到True,说明GPU可用。顺便记一下torch.__version__,后面选mmcv版本要用。
2.2 mmcv / mmengine / mmdet 的三方匹配关系
MMDetection本身依赖两个关键库:mmcv和mmengine。mmengine是底层的训练框架,负责配置解析、日志、hook这些基础功能;mmcv提供了图像处理、CNN算子这些基础组件。说白了,前者管“怎么训练”,后者管“底层运算”,MMDetection管“检测模型长什么样”。
这三个库的版本必须互相匹配。最稳妥的方式是用官方提供的openmim工具来安装,它能帮你解析依赖关系,自动安装匹配的版本。
pip install -U openmim mim install mmengine mim install mmcv如果你不想用openmim,也可以直接pip安装,但一定要去查看官方文档里的版本对照表。打个比方,如果你的mmdet是3.x版本,那就需要配合2.x以上的mmcv和0.7以上的mmengine,具体的对应关系以官方文档为准。
我个人的经验是:不要用pip自动安装mmcv带后缀的完整包时什么版本都交给它默认处理,务必指定装和你的CUDA版本匹配的预编译包。否则你装了一个CPU版的mmcv,后面跑训练时某些算子会报错,或者直接在GPU上无法运行。
2.3 安装与验证:跑通第一个import
环境装得对不对,不是看到安装成功就完事。我推荐在安装完MMDetection之后,进入Python环境做一次完整的导入验证:
git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e .这里的-e是以开发模式安装,意思是你对源码做的修改会即时生效。之后进入Python:
import mmcv import mmengine import mmdet from mmdet.utils import register_all_modules register_all_modules() print('所有模块导入成功')如果这一步能顺利通过,说明绝大多数依赖已经配好。要注意里面那个register_all_modules()很重要,MMDetection里很多自定义组件靠装饰器注册,如果不调用它,后面加载配置时可能会报“找不到某某类”的错误。
到这里,环境就搭好了。环境是最没技术含量却又最消耗耐心的环节,我强调一下:遇到报错先看版本,再看到底是哪一步引入的问题,别急着去网上复制粘贴什么“万能解决方案”。
3. 理解配置系统:MMDetection的“说明书”
3.1 配置文件长什么样:五个核心板块
MMDetection的使用核心是配置文件。你可以把配置文件理解成一张“订单”,告诉框架你要什么菜、用什么料、做几份。这个文件本质是一个Python字典,它运行时会被解析成各种对象,组织成一个完整的训练流程。
打开一个标准的配置文件,比如Faster R-CNN的示例配置,你会发现主要内容可以分成这么几块:
model:模型结构,包括backbone、neck、roi_head这些组件及其参数。train_dataloader、val_dataloader、test_dataloader:数据从哪里读、批次大小、pipeline处理方式。optim_wrapper:优化器类型、学习率、动量等。param_scheduler:学习率怎么变化,比如多少轮之后衰减一次。train_cfg、val_cfg、test_cfg:训练多少轮、验证频率、测试时的必要参数。
框架的设计哲学是“配置即代码”。绝大多数实验调整,比如换backbone、改学习率、换数据集,只需要改配置文件,不需要改源码。你用着用着就会发现,真正需要自己写代码的场景其实很少。
3.2 数据集的注册与pipeline流水线
新手最容易卡住的地方是数据部分。配置里关于数据集的部分,核心是指定一个type字段,告诉框架去注册表里找对应的数据集类。比如type='CocoDataset',就是使用MMDetection内置的COCO格式读取器。
接下来是pipeline。它定义的是“一张图片从磁盘读到内存后经历哪些操作”。常见的流程是:
- 读取图片和标注;
- 随机缩放,让尺寸在一定范围内变化;
- 随机翻转,做数据增强;
- 归一化;
- 把图片和标注转成模型需要的张量格式。
pipeline里的每个操作都对应一个类,比如RandomFlip、Resize、Normalize。这些操作是串起来的,顺序也有讲究,比如镜像翻转之后标注坐标会变化,所以RandomFlip内部会同步处理bbox的坐标。
我建议你第一次用的时候不要乱删pipeline组件。很多新手觉得RandomFlip没用,删了之后发现训练指标掉了,还找不到原因。数据增强本身就是训练流程的一部分,删改之前要想清楚目的。
3.3 小白最容易忽略的checkpoint与日志配置
配置里还有一类字段经常被忽略,比如default_scope、log_level、checkpoint_config相关的逻辑。它们不直接影响模型效果,但特别影响使用体验。
训练时,框架默认会保存epoch阶段的模型权重。如果你训练中途停了,可以用最近的一个权重接着训练,不用从头开始。日志方面,训练过程会在终端打印每个epoch的loss和当前的learning rate,同时会保存到work_dir目录下。你要养成看日志的习惯——如果loss一直不降,早发现问题早停,不要白跑半天。
我还建议新手在配置里开启visualizer的可视化选项,它会在验证阶段把预测结果画出来存成图片。你训练完去work_dir里翻翻这些图,比打印一百行日志都直观,能一眼看出模型是哪里学得不对。
4. 实操:从下载代码到训练出第一个模型
4.1 准备自己的数据集:先看懂Coco格式
很多教程拿公开数据集演示,轮到你自己项目时,还是要面对自定义数据集。这里我先带你过一遍COCO格式,因为MMDetection默认用这套格式。
COCO格式的标注是一个JSON文件,最核心的有三个字段:
images:每张图片的id、file_name、width、height;annotations:每个目标的image_id、bbox、category_id、area等;categories:类别的id和name。
特别注意bbox是[x, y, width, height]格式,不是很多同学熟悉的[x1, y1, x2, y2]。如果你用数据标注工具导出成别的格式,格式转换这一步非常容易踩坑。我自己就遇到过类别ID从0开始、而COCO要求从1开始的情况,结果训练出来的模型所有预测结果都偏移了一位,排查了整整半天。
一个常见的数据集目录长这样:
data/my_coco/ ├── annotations/ │ ├── train.json │ └── val.json ├── train/ │ ├── img_001.jpg │ └── ... └── val/ └── ...如果还没有标注数据,建议先用开源的标注工具人工标注几百张,跑通一个流程再说。很多同学非要等标注了几万张才开始训练,其实先跑通几百张的小规模实验,确定方案可行,再扩大数据量,是更务实的做法。
4.2 写一个属于自己的配置文件
我们不做复杂改动,就用MMDetection自带的Faster R-CNN配置做底子,然后覆盖自己的数据集信息。新建一个文件my_faster_rcnn.py,内容如下:
_base_ = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py' # 你的数据路径和类别数 data_root = 'data/my_coco/' num_classes = 3 # 修改模型头部的类别数 model = dict( roi_head=dict( bbox_head=dict(num_classes=num_classes))) # 修改训练数据配置 train_dataloader = dict( dataset=dict( data_root=data_root, ann_file='annotations/train.json', data_prefix=dict(img='train/'))) # 修改验证数据配置 val_dataloader = dict( dataset=dict( data_root=data_root, ann_file='annotations/val.json', data_prefix=dict(img='val/'))) # 测试阶段复用验证集配置 test_dataloader = val_dataloader # 调整训练轮数和学习率,小数据集可以先少训几轮 train_cfg = dict(max_epochs=12) optim_wrapper = dict(optimizer=dict(lr=0.001))注意_base_这个字段,它表示继承自哪个基础配置。这样你就不用把一大份参数全抄过来,只需要改动和项目相关的部分。这是MMDetection非常实用的设计:实验配置可以像积木一样互相继承。
如果你训练时发现类别数不对,或者数据加载出问题,返回检查这个文件里三个data_root相关的字段有没有配对。对应不上就会报路径错误,这部分是初学者出错率最高的地方。
4.3 跑通训练、验证与推理全流程
配置写好了,就可以开始训练了。命令行很简单:
python tools/train.py my_faster_rcnn.py --work-dir work_dirs/my_faster_rcnn第一次训练会下载backbone在ImageNet上的预训练权重。如果你的网络不太好,这一步可能会卡住,建议提前把权重文件下载好放到指定目录。否则你会看到模型迟迟不启动,日志一直停在“Downloading”。
训练过程中终端会滚动输出类似这样的信息:
2024/01/01 10:00:00 - mmengine - INFO - Epoch [1][50/100] lr: 1e-3 eta: 0:30:00 loss: 0.852看到loss在逐步下降,说明训练正常。这时候不用干等,可以打开另一个终端看GPU占用:
nvidia-smi显存占用率合理就OK。训练结束后,work_dirs/my_faster_rcnn下会有一堆epoch_*.pth权重文件。取最优或者最后一轮的权重,用它跑验证集:
python tools/test.py my_faster_rcnn.py work_dirs/my_faster_rcnn/epoch_12.pth --eval bbox输出里会显示mAP、AP50这些指标。看到AP50有数值,说明你的模型已经能从图片中检测出物体了。
最后是推理,拿一张自己的图片来测试:
python demo/image_demo.py demo/test.jpg my_faster_rcnn.py work_dirs/my_faster_rcnn/epoch_12.pth --out-file result.jpg打开输出的result.jpg,看到检测框画在目标上,这个基础流程就算跑通了。很多同学到了这一步会特别有成就感,这其实也是建立信心的关键节点。
5. 常见问题与排查实录
5.1 版本冲突类的报错
版本问题是新手遇到的第一个大坑,而且报错往往千奇百怪。比如AttributeError: module 'mmcv' has no attribute 'xxx',十有八九是mmcv版本和你框架要求的版本不一致。
还有一种情况是,你明明装了GPU版PyTorch,训练时却提示找不到CUDA。这通常是因为mmcv装成了CPU版本。检查方式很简单:
python -c "from mmcv.ops import sigmoid_focal_loss; print('mmcv ops ok')"如果报错,说明mmcv的编译版本不对,需要用匹配CUDA和PyTorch的预编译包重新安装。
版本排查思路优先顺序:先看框架官方的版本对应表,再确定mmcv、mmengine是否匹配,最后检查PyTorch的CUDA版本是否可用。不要一上来就搜报错信息,那样往往找到的是另一个版本下的解决方案,可能越改越乱。
5.2 训练过程异常的排查思路
训练能正常启动,但loss是NaN,这个问题也比较常见。我列一个排查顺序:
- 学习率是否太大:先降到原来的十分之一试试;
- 标注数据是否有问题:比如有无穷大的坐标值、空标注、类别ID越界;
- 数据增强是否过于激进:某些光线变换或裁剪操作可能导致异常值;
- 是否混合了不匹配的数据集格式:检查
ann_file指向的JSON是不是COCO格式。
还有一种情况是训练loss下降很慢,甚至不下降。这时候不要盲目找优化器的问题,先看看是不是类别不均衡,或者数据集太小。我见过有同学拿10张图去训练目标检测,跑了几十轮loss还在神游,这其实不是代码问题,而是数据量远低于正常需求。
5.3 显存与性能相关提示
显存溢出是最常见的一个错误,报错文本通常是CUDA out of memory。很多人第一反应是换显卡,但其实在代码层面可以做几件事:
- 把
batch_size从2改成1,这是最直接有效的; - 修改配置文件里的
train_dataloader = dict(batch_size=1); - 换更小的输入尺寸,比如把输入从1333x800改成800x600;
- 开启
auto_scale_lr自动调整学习率,减小显存压力时同步调整学习率,避免训练不稳。
另外我建议时刻留意nvidia-smi的输出,看显存是不是被其他程序占着。我遇到过模型跑不动,结果发现是另有一个进程吃掉了大半显存,和代码本身没关系。
还有一些性能问题不是显存,而是速度:训练太慢。检查nvidia-smi里GPU利用率如果不到50%,大概率是数据加载成了瓶颈。这时候可以调整train_dataloader的num_workers,比如从2改成4或8,让数据读取并行化。新手通常会忘记这个参数,结果GPU一直在等数据,利用率上不去。
6. 初学阶段最后要养成的几个习惯
到这里,你已经能跑完一个完整的MMDetection流程了。我再分享几个我在实际使用中摸索出来的小习惯,这些习惯能帮你少走不少弯路。
第一个习惯是一定要做小规模验证。不要在几百上千张图片直接开训,而是先取一小份数据,比如50张训练图、10张验证图,快速跑几个epoch确认配置没问题,再放完整数据集。小规模验证能把配置错误、路径错误、类别数错误这类问题早早暴露出来,避免你等大任务跑了一整天后发现白跑了。
第二个习惯是把每次实验记录下来。框架会自动保存日志和权重,但我建议你自己在work_dirs外再建一个说明文本,记清楚这一次实验改了哪些配置项、最终指标是多少、有什么观察。不要高估自己的记忆力,三天之后你很可能忘了当前这个权重是用什么配置训出来的。这一点对做对比实验尤其重要。
第三个习惯是重视预训练权重。MMDetection官方提供的backbone权重都是在大规模分类数据集上预训练过的,能让你的检测任务收敛更快、效果更好。如果你在训练时为了省事把init_cfg删掉了,效果会差一大截。这是很多人忽略的地方。
另外,很多同学问要不要去看源码。我的建议是:先跑通流程,等你想改某一个模块、或者模型效果不理想想知道原因的时候,再针对性看对应源码。比如你想搞清楚预测框是怎么解码出来的,就去看bbox_head.py;想弄明白训练时标签怎么分配,就去看assigner目录。按需阅读,比从第一行代码顺序看下去效率高很多,也更能坚持。
这个系列后面我打算继续讲模型结构拆解、数据增强实战、训练调参技巧这些话题。基础流程你打通之后,后面的深入学习就不会那么迷茫了。第一次跑通检测模型的成就感,是真的会让人上瘾的。