☰
mmdetection训练自定义数据集:从数据标注到模型推理全流程指南
2026/10/15 6:50:07 网站建设 项目流程

在目标检测这个圈子里折腾了几年,我发现很多人入门时最头疼的不是算法原理,而是“怎么把自己手里的数据跑进一个成熟的框架里”。mmdetection作为目前工业界和学术界都用得比较多的检测工具箱,功能确实强大,但配置文件的复杂度也让不少新手望而却步。这篇文章我想以实际项目为主线,把从数据标注到模型训练、再到最后的验证推理这条完整链路拆开来讲,希望你看完能少踩一些我当年踩过的坑。

快速上手mmdetection训练自己的数据集

1. 为什么要用mmdetection:不只是因为“大家都在用”

1.1 一个配置驱动的工具箱解决了什么问题

先说说mmdetection解决的核心痛点。如果你之前自己写过检测模型,一定经历过这样的场景:换一个backbone要改网络结构,换一个数据集要重写数据加载器,想试一下新的训练策略又得动一堆底层代码。整个项目代码和实验配置揉在一起,每换一个想法都要动手术。

mmdetection把这些问题拆开了。它把网络结构分成几个独立模块:backbone负责提取特征,neck负责特征融合,head负责最终的任务预测。这些模块通过配置文件组合,你只需要在config里指定用哪个backbone、哪个neck、哪个head,框架就能自动把网络搭起来。数据集的加载方式、训练策略、评估指标也全部由配置控制,整个训练流程就像搭积木。我记得第一次用mmdetection时,想把ResNet50换成一个更轻量的backbone,只需要在配置里改一行字段,训练就能继续跑,这个体验相比之前改代码的方式确实省心太多。

这种设计思路最大的好处是实验管理的规范化。每次实验的完整配置都留存在一个.py文件里,过几个月回来看还能清清楚楚知道当时用了什么模型、什么数据、什么训练策略。对比一下不同实验,也只是配置文件之间的差异,复现结果变得很容易。

1.2 版本选择的经验之谈

mmdetection目前常用的主分支是v2.x和v3.x系列。v2.x时代广泛使用,网上资料多,很多老项目都基于它;v3.x则统一了配置风格,和mmdet、mmseg、mmpose这些兄弟库的交互更顺畅。

如果你是完全的新手,我更建议从v3.x开始。原因不是v2.x不好,而是v3.x的文档结构更清晰,且官方持续在维护。特别要注意的是版本配套问题:不同版本要求不同的PyTorch版本和MMCV版本,装错版本会让你在import阶段就报出各种莫名其妙的错。我是建议先按官方文档的要求安装指定版本,环境配好以后尽量固定下来,不要轻易升级。

2. 数据准备:标注格式和数据集划分是训练的“地基”

2.1 标注工具的选择和注意事项

训练自己的检测模型,第一步是拿到带有标注的数据。常见的标注工具我试过LabelImg和Labelme,简单说下区别。LabelImg生成Pascal VOC的XML格式标注文件,操作方式就是画矩形框,基本不用学就会。Labelme则更灵活,支持多边形标注,输出为JSON文件。如果你的任务需要的不止矩形框,比如实例分割或者旋转框检测,用Labelme这类工具会更合适。

标注阶段最容易犯的错误我列一下:

  • 画框太随意,没有紧贴目标边缘,导致模型学到的位置信息不准
  • 类别设置不统一,同一个物体有人标成“cat”,有人标成“Cat”
  • 漏标小目标,小目标本身难检测,漏标会进一步加剧漏检

标注是一个需要反复检查的环节。我通常的做法是第一遍先把所有图片粗标一遍,第二遍按类别过滤,逐个图片检查是否有标错或漏标,相当于质检流程。数据质量直接决定模型上限,这块多花时间是值得的。

2.2 把标注转成mmdetection需要的格式

mmdetection主分支原生支持两种主流数据格式:COCO格式和Pascal VOC格式。如果你用的是LabelImg生成的XML,推荐转成COCO格式。COCO格式本质是一个JSON文件,里面存放了图片信息、标注框信息和类别信息,结构如下:

{ "images": [ {"id": 1, "file_name": "001.jpg", "width": 640, "height": 480} ], "annotations": [ {"id": 1, "image_id": 1, "category_id": 1, "bbox": [50, 40, 120, 80], "area": 9600, "iscrowd": 0} ], "categories": [ {"id": 1, "name": "cat"} ] }

很多刚入门的朋友不理解,为什么不能直接用XML训练,非要转成JSON?核心原因在于读取速度。COCO JSON加载后可以一次性建立图片索引和标注索引,而XML需要逐个解析文件,在数据量上万之后训练速度差距很明显。同时COCO格式是检测方向的“通用语言”,之后做评测、做可视化都比较方便。

下面这段转格式的脚本我实测过,可以根据标注文件结构直接修改使用。核心逻辑是遍历XML文件,把每个object的name映射为类别ID,再计算bbox坐标。VOC的bbox格式是xmin、ymin、xmax、ymax,而COCO需要的是x、y、width、height,转换时注意不要搞混:

import xml.etree.ElementTree as ET import json import os def voc_to_coco(xml_dir, output_path): categories = [] cat_set = set() images = [] annotations = [] ann_id = 1 for idx, xml_file in enumerate(os.listdir(xml_dir)): if not xml_file.endswith('.xml'): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() filename = root.find('filename').text size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) images.append({ 'id': idx + 1, 'file_name': filename, 'width': width, 'height': height }) for obj in root.findall('object'): name = obj.find('name').text if name not in cat_set: cat_set.add(name) categories.append({'id': len(cat_set), 'name': name}) bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) w = xmax - xmin h = ymax - ymin annotations.append({ 'id': ann_id, 'image_id': idx + 1, 'category_id': len(cat_set), 'bbox': [xmin, ymin, w, h], 'area': w * h, 'iscrowd': 0 }) ann_id += 1 coco = {'images': images, 'annotations': annotations, 'categories': categories} with open(output_path, 'w', encoding='utf-8') as f: json.dump(coco, f, ensure_ascii=False)

2.3 数据集划分和目录组织

转好格式后要划分训练集、验证集、测试集。一个常见的比例是8:1:1,但要注意划分时保证每个类别在每个集合中都有一定数量的样本。如果随机划分导致某个类别在验证集中只出现一两次,评估结果会很不稳定。

目录组织上,我习惯按mmdetection约定来建:

data/ └── mydataset/ ├── annotations/ │ ├── train.json │ ├── val.json │ └── test.json ├── train/ ├── val/ └── test/

这里的train/、val/存放对应划分的图片,annotations存放JSON标注。后面配置数据路径时,只要指定data_root和这几个关键names,框架就能自动找到对应的文件和标注。别小看目录规划,项目做久了你就知道一个整洁的数据目录能给自己省多少时间。

3. 配置文件修改:理解关键字段是训练成功的基石

3.1 选择适合自己任务的模型和backbone

拿到数据后的下一个问题是:用什么模型来做训练?mmdetection官方提供了很多现成配置,覆盖了从单阶段检测器到两阶段检测器再到Transformer类检测器。

如果你是第一次拿自己的数据集跑,我的建议是从Faster R-CNN或者Mask R-CNN这种两阶段模型开始。原因有两点:

  1. 两阶段模型的训练稳定性比单阶段模型好,不容易出现loss爆炸问题
  2. 官方配套的预训练权重资源充足,基础版本适配良好

Backbone选择上,ResNet50是公认的“万金油”。它不算最强,但胜在均衡:显存占用适中,训练速度合理,精度表现也不错。如果你对性能有更高要求,后期可以换Swin Transformer或者ConvNeXt这类更强的backbone,但入门阶段没必要追求极致。

3.2 从官方配置改到自定义数据:必须改的字段

在mmdetection中,config文件是整个实验的“总开关”。以我常用的一个自定义配置为例,核心修改点集中在三个部分:模型配置、数据集配置、训练配置。

模型配置中,最需要关注的是num_classes。这个字段在head定义里,必须改成你实际数据集的类别数。这里有个常见的坑:如果你用的是COCO预训练权重,原本的num_classes是80,你改成自己的类别数后,最后的分类层必须重新初始化。mmdetection通常会自动检测并初始化,但你要确保加载权重的strict参数设置为False,否则会报shape不匹配的错误。

数据集配置中,核心要改的是data_root、ann_file、img_prefix和classes。这部分我建议直接看一个现成的配置示例:

data_root = 'data/mydataset/' metainfo = { 'classes': ('cat', 'dog'), } train_dataloader = dict( dataset=dict( type='CocoDataset', data_root=data_root, ann_file='annotations/train.json', data_prefix=dict(img='train/'), metainfo=metainfo, ) )

这里有一个重要细节:如果类别不传metainfo,代码会用默认的80类COCO类别来解析你的JSON,导致category_id映射错乱。第一跑的时候可能不报错,但训练出的模型预测结果完全不对。这个坑我在多人协作的项目里见到过不止一次。

训练配置中,有几个参数值得新手注意:

  • optimizer的lr:默认配置一般是针对8卡训练的,你如果只有1张卡,最好把学习率调小一些。比如默认lr=0.02对应8卡,单卡可以改成0.0025,按比例缩放能保持训练稳定性
  • epochs和val_interval:根据自己的数据量来定,数据量大可以多训练几个epoch
  • checkpoint_config和log_config:用于保存模型和记录日志,建议开启

3.3 修改配置时的常见报错和解法

改完配置文件,运行训练命令时最容易出现两类报错。

第一类是类别数不匹配。报错信息通常是assert num_classes == xxx failed。这往往是因为模型head的num_classes没有改对。解决方案是检查配置文件中的roi_head或bbox_head部分,确认num_classes等于你的实际类别数再加背景类(如果实现需要的话)。mmdetection的配置中,如果指定了metainfo中的classes,部分模型会自动推断类别数,但手动检查一遍更稳妥。

第二类是数据路径错误。报错一般会在加载数据时出现FileNotFoundError。排查思路很简单:先确认data_root路径是否正确,再用python检查一下标注文件是否存在。我在实践中发现,很多路径问题其实是相对路径基准搞错了,mmdetection的路径都是以项目根目录为基准来解析的。

4. 训练和验证:从命令行到可视化全流程

4.1 单卡训练的完整命令

环境配置好、config修改好之后,训练就开始成为一个纯操作流程。单卡训练的命令如下:

python tools/train.py configs/my_config/mask_rcnn_resnet50_fpn_custom.py

这是最简单的启动方式。训练开始后,你会看到终端输出每个iteration的loss、lr等指标。这里叮嘱一句:第一次训练我强烈建议先用少量数据跑通整个流程,比如每个集合只放几十张图,确定从数据到训练的整条链路没问题后,再上完整数据。这个“小数据试跑”的习惯为我节省了大量排查时间。

关于多卡训练,如果机器有多张GPU,可以使用以下命令:

bash tools/dist_train.sh configs/my_config/mask_rcnn_resnet50_fpn_custom.py 4

这里的4表示使用4张卡。但要注意,多卡训练时batch_size和lr的配置要成比例调整。例如单卡batch_size=2、lr=0.0025,四卡时total_batch_size变成8,学习率应该同步调到0.01,否则收敛效果会受影响。

4.2 训练过程监控和checkpoint管理

训练过程中,除了肉眼观察loss曲线外,我习惯同时关注学习率的变化和每个val epoch后的bbox_mAP指标。mmdetection默认在每次验证后打印完整的评估结果,包括mAP、AP50、AP75等。mAP = 0.5:0.95的数值是最主要参考指标,AP75关注更精确的定位能力。

checkpoint的保存策略要提前规划。默认配置下,框架会保存每个epoch结束时的模型权重,但如果你的数据集比较大,这种保存方式会迅速占满磁盘。我通常是每5个epoch保存一次,同时只保留最近的3个checkpoint,配置方式如下:

default_hooks = dict( checkpoint=dict( type='CheckpointHook', interval=5, max_keep_ckpts=3 ) )

这里的max_keep_ckpts=3能自动删除旧的checkpoint,降低磁盘压力。保存的文件通常会包含epoch_{n}.pth文件和best_mAP相关文件,命名规则清晰,方便你回看实验。

4.3 训练后进行推理和可视化

训练完成后,用测试集进行推理是检验模型真实效果的方式。推理命令如下:

python tools/test.py configs/my_config/mask_rcnn_resnet50_fpn_custom.py work_dirs/epoch_12.pth

这个命令会在测试集上计算各类指标,你也可以加--show-dir参数,把带预测框的图片保存到指定目录,直观地看看模型的预测效果。我每次看完指标,都会再去翻一翻可视化图片,特别是看那些漏检、误检样本,能快速定位模型的问题所在。

如果需要推理单张图片,可以这样写:

from mmdet.apis import init_detector, inference_detector import mmcv config_file = 'configs/my_config/mask_rcnn_resnet50_fpn_custom.py' checkpoint_file = 'work_dirs/epoch_12.pth' model = init_detector(config_file, checkpoint_file, device='cuda:0') result = inference_detector(model, 'demo/test.jpg') model.show_result('demo/test.jpg', result, out_file='demo/output.jpg')

inference_detector返回的是一个包含多个数组的结果结构,每个数组对应一个类别的边框和分数。通过遍历可以提取出所有置信度大于某个阈值的检测框,用于后续的业务逻辑。

5. 常见问题与排查技巧实录

5.1 训练不收敛的排查思路

训练loss不下降是大家问得比较多的问题。我遇到过几种典型情况:

第一种是学习率设置不合理。学习率太大,loss曲线会震荡甚至发散;学习率太小,loss下降像蜗牛一样。解决方法是先看官方配置的lr,再结合自己的batch size按比例缩放,训练前几百个iteration多观察几次loss变化趋势。

第二种是数据或标注问题。标注框和图片不匹配,比如框坐标超出图像边界,会导致模型在训练时无法正常计算损失。用可视化脚本把标注框画在图片上查看,是最直接的排查方式。

第三种是类别不平衡问题。如果某个类别占比过高,模型会倾向于把所有目标预测成这个类别。常见的处理办法是做类别重加权采样,或者增加难例挖掘。

5.2 显存不足和训练中断

显存不足在训练检测模型时几乎一定会遇到。ResNet50加FPN结构在默认输入尺寸下通常需要5-6GB显存,如果你的显卡只有8GB,设置太小batch_size,训练就会报CUDA out of memory。

这里有几个可操作的解决方案:

  1. 减小输入图片尺寸:配置中的img_scale可以调整,比如从(1333, 800)改成(1000, 600),显存占用明显下降,但精度也会有一定损失
  2. 减小batch_size:单卡可以从2降到1,配合梯度累积来保持等效batch size
  3. 使用混合精度训练:mmdetection支持amp训练,开启后显存占用可以减少约30%,同时训练速度也会提升

训练中断是另一个常见问题。如果因为断电或显存溢出导致训练中断,可以基于最新checkpoint进行resume:

python tools/train.py configs/my_config/mask_rcnn_resnet50_fpn_custom.py --resume work_dirs/epoch_10.pth

这一步能省去从头再训练的时间。我的习惯是定期备份最佳checkpoint,同时开启resume机制,这样即使中途异常也能快速恢复。

5.3 结果评估指标低时的优化方向

如果训练完成后测试指标不理想,先不要急着换模型。我一般的排查顺序是:

  1. 确认测试集标注质量和训练集的分布一致性
  2. 在训练集上做一次小规模评测,如果训练集上指标也很低,说明模型拟合能力不足,需要增加训练轮数或加大模型容量
  3. 在训练集上指标很高但测试集很低,说明过拟合,考虑加数据增强、加正则化或减少模型复杂度
  4. 检查是否存在类别不平衡导致的指标被“稀释”

这些都是成熟的做法,但实际操作起来最关键的是耐心记录每次实验的变化。我专门有一个实验记录表格,记录每次调整的参数、指标结果和训练时间。没有记录,调参就像在黑夜里摸路。

6. 最后的经验分享

我个人在实际跑完多个自定义数据集项目后,最深的体会是:mmdetection这类工具框架,真正的门槛在于配置的理解,而不是代码的编写。建议第一次跑的时候把官方给的config从头到尾读一遍,不懂的字段就查文档,而不是直接复制粘贴。这个“读懂配置”的过程,远比敲代码更重要。

还有一个小技巧:跑新数据前,先用官方示例配置和一张图片把整个流程跑通,确认环境没问题后,再切换到自己的数据。这样能最大化减少变量,出了问题也能快速定位是环境问题还是数据问题。希望这篇内容能帮你少走一些弯路,把宝贵的时间花在更有价值的模型迭代和数据优化上。

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

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

立即咨询