MMDetection v2.22.0 实战:从零训练自定义目标检测模型

📅 2026/7/29 6:36:01
MMDetection v2.22.0 实战:从零训练自定义目标检测模型
1. 项目概述从零上手MMDetection v2.22.0如果你刚拿到一批标注好的图片想用MMDetection这个强大的目标检测工具箱来训练自己的模型但面对官方文档和繁杂的配置文件感到无从下手那你来对地方了。我最近刚用MMDetection v2.22.0完整跑通了一个自定义数据集的训练流程从环境搭建、数据准备、配置修改到模型训练和测试中间踩了不少坑也总结了一套行之有效的“流水线”操作。这篇文章就是我的实战笔记目标很明确让你能避开我走过的弯路用最清晰的步骤在MMDetection v2.22.0上成功训练出第一个属于你自己的目标检测模型。无论你是做学术研究、工业质检还是个人兴趣项目这套流程都能直接套用。我们会聚焦于最常用的Faster R-CNN模型和COCO数据格式因为这是最通用、社区支持最好的路径掌握了它再探索其他模型和格式就会容易得多。2. 环境搭建与MMDetection安装2.1 基础环境准备CUDA、PyTorch与MMCVMMDetection的稳定运行严重依赖底层环境尤其是PyTorch和MMCVOpenMMLab的计算机视觉基础库的版本匹配。v2.22.0版本发布于一段时间其版本依赖相对固定盲目使用最新版PyTorch很可能导致兼容性问题。我的建议是严格按照 MMDetection官方安装文档 中推荐的版本来。以我使用的环境为例操作系统Ubuntu 20.04 LTS 或 Windows 10/11WSL2环境下。纯Windows原生安装会遇到更多编译问题强烈推荐WSL2。CUDA Toolkit11.3。这是经过广泛测试的版本。确保nvidia-smi显示的CUDA版本不低于此。PyTorch1.11.0。使用以下命令安装pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113MMCV这是关键。MMDetection v2.22.0需要mmcv-full而非轻量版的mmcv。我们必须安装与CUDA、PyTorch版本完全匹配的预编译包。pip install mmcv-full1.5.3 -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.11.0/index.html请将URL中的cu113和torch1.11.0替换成你的CUDA和PyTorch版本。安装成功后在Python中运行import mmcv; print(mmcv.__version__)应能正确输出版本号。注意切勿直接pip install mmcv-full这大概率会触发从源码编译过程漫长且极易失败。一定要使用-f指定预编译包的索引地址。2.2 MMDetection安装与验证基础环境就绪后安装MMDetection本身反而很简单。推荐从GitHub克隆特定版本以保证代码一致性。git clone -b v2.22.0 https://github.com/openmmlab/mmdetection.git cd mmdetection pip install -v -e . # “-e”代表可编辑模式安装方便修改源码 # 或者使用requirements文件安装依赖 # pip install -r requirements/build.txt # pip install -r requirements/optional.txt安装完成后进行一个快速的完整性验证。创建一个简单的Python脚本test_install.pyfrom mmdet.apis import init_detector, inference_detector import mmcv config_file configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py checkpoint_file checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth # 下载预训练模型如果尚未下载 # from mmdet.apis import init_detector # 模型会在首次运行时自动下载也可以手动下载到checkpoints目录 model init_detector(config_file, checkpoint_file, devicecpu) # 先用CPU测试 print(MMDetection安装及模型加载测试通过)如果运行没有报错说明核心安装成功。接下来我们需要准备最重要的燃料——你自己的数据集。3. 自定义数据集准备COCO格式详解与制作MMDetection支持多种数据集格式PASCAL VOC、COCO等但COCO格式是社区生态最完善、文档示例最全的格式强烈建议将你的数据转为COCO格式能省去大量适配工作。3.1 COCO数据集格式深度解析COCO格式的核心是一个JSON文件通常命名为annotations.json。它包含以下几个顶级字段images: 一个列表包含所有图像的信息。annotations: 一个列表包含所有目标实例的标注信息。categories: 一个列表定义所有物体类别。下面我们拆解每一个部分并说明如何从你的原始标注比如LabelImg生成的XML转换过来。1.images字段每个图像是一个字典例如{ id: 1, // 图像唯一ID从1开始递增 width: 800, height: 600, file_name: image_001.jpg // 相对于数据集根目录的路径 }你需要遍历你的所有图片为每一张生成这样一个记录。id必须唯一且连续。2.categories字段定义你的检测类别。id通常从1开始0保留给背景MMDetection内部处理。[ {id: 1, name: cat, supercategory: animal}, {id: 2, name: dog, supercategory: animal}, {id: 3, name: person, supercategory: human} ]supercategory可以用于更粗粒度的分组非必需但建议填写。3.annotations字段最关键每个目标实例一个字典{ id: 1, // 标注实例唯一ID全局递增 image_id: 1, // 对应 images 中的 id category_id: 1, // 对应 categories 中的 id bbox: [x, y, width, height], // [左上角x, 左上角y, 宽度, 高度] area: width * height, // bbox面积用于评估指标如AP segmentation: [], // 实例分割多边形坐标目标检测可留空列表 iscrowd: 0 // 0表示单个物体1表示一群物体如人群 }这里最重要的是bbox的格式。COCO使用的是[x, y, width, height]即左上角坐标和宽高。这与某些标注工具如YOLO使用的中心点坐标和归一化宽高不同转换时务必注意。3.2 从常见格式转换到COCO格式的实操脚本假设你的原始标注是PASCAL VOC格式XML文件下面是一个使用Pythonxml.etree.ElementTree进行转换的示例脚本核心逻辑import json import os import xml.etree.ElementTree as ET from PIL import Image def voc_to_coco(voc_annotations_dir, images_dir, output_json_path): images [] annotations [] categories [] # 1. 构建 categories # 这里需要你事先知道所有类别并映射到id category_dict {cat: 1, dog: 2, person: 3} for name, cid in category_dict.items(): categories.append({id: cid, name: name, supercategory: none}) ann_id 1 for img_id, xml_file in enumerate(os.listdir(voc_annotations_dir), 1): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_annotations_dir, xml_file)) root tree.getroot() # 2. 构建 image 信息 filename root.find(filename).text img_path os.path.join(images_dir, filename) with Image.open(img_path) as img: width, height img.size images.append({ id: img_id, width: width, height: height, file_name: filename, }) # 3. 构建 annotation 信息 for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in category_dict: continue # 或者跳过未知类别 xml_box obj.find(bndbox) xmin int(float(xml_box.find(xmin).text)) ymin int(float(xml_box.find(ymin).text)) xmax int(float(xml_box.find(xmax).text)) ymax int(float(xml_box.find(ymax).text)) # VOC是[xmin, ymin, xmax, ymax]需转为COCO的[x, y, width, height] coco_bbox [xmin, ymin, xmax - xmin, ymax - ymin] area coco_bbox[2] * coco_bbox[3] annotations.append({ id: ann_id, image_id: img_id, category_id: category_dict[cls_name], bbox: coco_bbox, area: area, segmentation: [], iscrowd: 0 }) ann_id 1 # 4. 组装成COCO JSON coco_format_json { images: images, annotations: annotations, categories: categories } with open(output_json_path, w) as f: json.dump(coco_format_json, f, indent2) print(f转换完成共 {len(images)} 张图片{len(annotations)} 个标注。) # 使用示例 voc_to_coco(./voc_annotations, ./images, ./annotations/train.json)运行这个脚本你就能得到标准的COCO格式标注文件。记得将图片文件放在images_dir指定的目录下。3.3 数据集目录结构规划一个清晰的数据集目录结构能让后续配置变得简单。我推荐如下结构mmdetection/ ├── data/ │ └── my_dataset/ # 你的数据集根目录 │ ├── annotations/ # 存放COCO格式的JSON文件 │ │ ├── train.json │ │ └── val.json │ └── images/ # 存放所有图片或按train/val放子目录 │ ├── train/ │ │ ├── img1.jpg │ │ └── ... │ └── val/ │ ├── img2.jpg │ └── ...实操心得在制作train.json和val.json时images列表和annotations列表只包含对应划分的数据。categories列表在两个文件中必须完全一致。你可以先用一个脚本生成完整的all.json再根据划分列表拆分成train.json和val.json。4. 配置文件解析与关键修改MMDetection采用模块化、可继承的配置文件系统这是其强大之处也是新手最容易困惑的地方。我们不需要从头写配置而是基于一个基准配置文件进行修改。4.1 配置文件继承机制解读以训练Faster R-CNN为例我们查看configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py会发现它的第一行是_base_ [ ../_base_/models/faster_rcnn_r50_fpn.py, ../_base_/datasets/coco_detection.py, ../_base_/schedules/schedule_1x.py, ../_base_/default_runtime.py ]这表示它继承了四个基础配置文件分别定义了模型结构、数据流水线、训练策略和运行时设置如日志、钩子。我们的修改策略是创建一个新的配置文件继承我们选中的基准配置然后只覆盖需要修改的部分。这样做既保证了配置的完整性又使修改清晰可追溯。4.2 创建自定义配置文件在mmdetection/configs目录下或任何你喜欢的位置建议在项目根目录新建一个configs文件夹创建我们的配置文件例如my_faster_rcnn_r50_fpn_1x_mydataset.py。# my_faster_rcnn_r50_fpn_1x_mydataset.py _base_ ./faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py # 相对于当前文件的路径 # 1. 修改数据集相关配置 dataset_type CocoDataset classes (cat, dog, person) # 务必与你的 categories 中 name 的顺序和内容一致 data_root data/my_dataset/ # 指向你的数据集根目录 # 覆盖 _base_ 中关于数据集的配置 data dict( samples_per_gpu2, # 批大小。根据你的GPU内存调整。如果遇到CUDA out of memory就调小这个值。 workers_per_gpu2, # 数据加载线程数。通常设为CPU核心数但不宜过大。 traindict( typedataset_type, ann_filedata_root annotations/train.json, img_prefixdata_root images/train/, classesclasses # 指定类别 ), valdict( typedataset_type, ann_filedata_root annotations/val.json, img_prefixdata_root images/val/, classesclasses ), testdict( typedataset_type, ann_filedata_root annotations/val.json, # 测试集可以用验证集代替 img_prefixdata_root images/val/, classesclasses ) ) # 2. 修改模型头部类别数 # 找到 _base_ 中模型定义的路径然后修改 roi_head 的 bbox_head model dict( roi_headdict( bbox_headdict( num_classeslen(classes) # 关键必须修改为你的类别数这里是3 ) ) ) # 3. 修改训练策略可选但建议调整 # 学习率根据 batch size 线性缩放是一个经验法则。原配置 base_lr0.02 对应 8 GPUs * 2 imgs/gpu 16 的总batch size。 # 如果你用单卡samples_per_gpu2总batch size2那么学习率应缩放为 0.02 * (2 / 16) 0.0025 optimizer dict(typeSGD, lr0.0025, momentum0.9, weight_decay0.0001) lr_config dict( policystep, warmuplinear, warmup_iters500, warmup_ratio0.001, step[8, 11]) # 学习率在第8和第11个epoch下降 runner dict(typeEpochBasedRunner, max_epochs12) # 总训练轮数 # 4. 修改运行时配置如日志、检查点频率 checkpoint_config dict(interval1) # 每个epoch保存一次检查点 log_config dict(interval50, hooks[dict(typeTextLoggerHook)]) # 每50个iteration打印一次日志 # 指定工作目录训练日志和模型权重将保存在这里 work_dir ./work_dirs/my_faster_rcnn_exp1这个配置文件是核心中的核心。请逐行理解特别是num_classes和lr的修改这是新手最常出错的两个地方。num_classes不修改会导致维度不匹配错误lr设置不当会导致训练不收敛。4.3 配置文件的调试与验证在开始漫长训练之前先用一个极小的设置验证整个数据流和配置是否正确。我们可以创建一个“玩具”配置文件只加载几张图片跑通前向传播。# debug_config.py _base_ ./my_faster_rcnn_r50_fpn_1x_mydataset.py # 覆盖数据加载部分仅用于调试 data dict( samples_per_gpu1, workers_per_gpu1, traindict( type_base_.dataset_type, ann_file_base_.data_root annotations/train.json, img_prefix_base_.data_root images/train/, classes_base_.classes, # 使用更小的流水线加速调试 pipeline[ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeResize, img_scale(1333, 800), keep_ratioTrue), dict(typeRandomFlip, flip_ratio0.5), dict(typeNormalize, **img_norm_cfg), dict(typePad, size_divisor32), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_bboxes, gt_labels]), ] ) )然后运行一个简单的测试脚本python tools/misc/browse_dataset.py debug_config.py --show这个命令会可视化你的数据加载和增强效果确保图片和标注能正确配对、显示。这是排查数据问题最直观的方法。5. 模型训练、监控与测试5.1 启动训练与分布式训练单GPU训练命令很简单python tools/train.py my_faster_rcnn_r50_fpn_1x_mydataset.py如果你有多张GPU可以使用分布式训练以大幅加速。MMDetection基于PyTorch的DistributedDataParallel(DDP) 封装了分布式训练命令./tools/dist_train.sh my_faster_rcnn_r50_fpn_1x_mydataset.py 8 --work-dir ./work_dirs/my_exp这里的8代表使用8个GPU。dist_train.sh脚本会自动处理进程启动、端口分配等繁琐细节。训练日志和模型权重会保存在--work-dir指定的目录如果配置文件中已指定work_dir则以配置文件为准。5.2 训练过程监控与日志解读训练开始后控制台会打印日志。你需要关注以下几个关键信息Loss曲线loss_rpn_cls、loss_rpn_bbox、loss_cls、loss_bbox是Faster R-CNN的主要损失。在训练初期这些loss应该呈现明显的下降趋势。如果loss剧烈震荡或居高不下可能是学习率过高、数据有问题或标注错误。学习率日志会显示当前的学习率。根据lr_config的设置你应该能看到在指定epoch学习率按比例下降。内存使用关注GPU内存占用。如果接近上限可以尝试减小samples_per_gpu或输入图片尺寸。更强大的监控工具是TensorBoard。MMDetection默认集成了TensorBoard日志。在训练命令后加上--tensorboard参数或者在配置文件中添加log_config dict( interval50, hooks[ dict(typeTextLoggerHook), dict(typeTensorboardLoggerHook) # 添加这行 ])训练后在work_dir下会生成tf_logs目录使用tensorboard --logdir ./work_dirs/my_exp/tf_logs即可在浏览器中查看丰富的可视化图表包括Loss曲线、学习率、验证集mAP等这对于分析训练状态至关重要。5.3 模型测试与性能评估训练完成后我们使用验证集评估模型性能。使用以下命令# 单GPU测试 python tools/test.py my_faster_rcnn_r50_fpn_1x_mydataset.py ./work_dirs/my_exp/latest.pth --eval bbox # 多GPU测试 ./tools/dist_test.sh my_faster_rcnn_r50_fpn_1x_mydataset.py ./work_dirs/my_exp/latest.pth 8 --eval bbox--eval bbox指定评估边界框检测的指标。MMDetection会计算COCO风格的一系列指标其中最重要的是AP (Average Precision): IoU阈值从0.5到0.95步长0.05的平均精度。这是COCO的主要评价指标记为AP或AP[.5:.95]。AP50: IoU阈值为0.5时的AP即PASCAL VOC的评价标准。AP75: IoU阈值为0.75时的AP。AP_s, AP_m, AP_l: 分别对应小、中、大尺寸目标的AP。一份合格的训练结果AP和AP50应该达到一个合理的值取决于你的数据集难度和规模。如果AP_s远低于AP_l说明模型对小目标检测不好可能需要调整FPN结构、使用更小的anchor或增加小目标的训练数据。5.4 模型推理与可视化训练出模型后我们当然想看看它的实际检测效果。MMDetection提供了简单的推理APIfrom mmdet.apis import init_detector, inference_detector, show_result_pyplot import mmcv # 指定配置文件和训练好的模型 config_file my_faster_rcnn_r50_fpn_1x_mydataset.py checkpoint_file ./work_dirs/my_exp/latest.pth # 初始化模型 model init_detector(config_file, checkpoint_file, devicecuda:0) # 对单张图片进行推理 img test.jpg # 或者 img mmcv.imread(test.jpg) result inference_detector(model, img) # 可视化结果 # show_result_pyplot函数会直接显示图片适合在Jupyter Notebook中使用 # show_result_pyplot(model, img, result, score_thr0.3) # score_thr是显示分数阈值 # 更常用的方式将结果绘制到图片上并保存 vis_img model.show_result(img, result, score_thr0.3, showFalse) mmcv.imwrite(vis_img, result.jpg) print(检测结果已保存至 result.jpg)你可以调整score_thr来过滤低置信度的检测框这个值需要根据你的具体应用场景来权衡召回率和精度。6. 实战避坑指南与高级技巧6.1 常见错误与解决方案KeyError: ‘xxx’ is not in the fields或AssertionError: Thenum_classes(xx) shared by all branches must be the same问题这是最经典的错误。根本原因是模型的num_classes没有修改或者修改得不彻底。Faster R-CNN的num_classes需要同时在roi_head.bbox_head和rpn_head如果RPN也有分类头的话新版通常不需要中修改。最稳妥的方法是像我们之前那样在配置文件中通过model.dict()覆盖。解决仔细检查配置文件确保model.roi_head.bbox_head.num_classes已正确设置为你的类别数。使用print(model)打印模型结构来确认。CUDA out of memory (OOM)问题GPU内存不足。解决减小配置中的samples_per_gpu批大小。减小输入图片尺寸。在train_pipeline和test_pipeline中找到Resize操作将img_scale改小例如从(1333, 800)改为(800, 600)。使用梯度累积Gradient Accumulation。这需要在优化器配置和train_cfg中设置对新手稍复杂但可以有效模拟大batch size训练。尝试更轻量的模型如RetinaNet或FCOS。Loss为NaN或训练不收敛问题学习率设置不当、数据存在极端值如坐标超出图像范围、标注错误。解决首要检查数据使用browse_dataset.py脚本仔细检查标注框是否合理。确保bbox坐标非负且不超过图像宽高。调整学习率根据你的batch size按线性缩放规则调整lr。如果loss爆炸变成NaN大幅降低学习率如除以10再试。使用预训练权重确保你的配置中load_from指向了在COCO上预训练的模型权重MMDetection会自动下载或你需要手动指定路径。从随机初始化开始训练检测器非常困难。验证集mAP为0或极低但训练loss正常下降问题过拟合或者训练集和验证集分布差异极大。解决增加数据增强的多样性如更多的随机裁剪、颜色抖动。检查是否不小心在验证集上使用了训练时的数据增强如RandomFlip。验证集流水线应该只有Resize,Normalize,Pad,DefaultFormatBundle等确定性操作。使用更小的模型或添加正则化如Dropout但检测模型中不常用。确保训练轮数max_epochs没有过多。6.2 性能调优与进阶技巧数据增强策略调优MMDetection的数据增强流水线pipeline非常灵活。对于小数据集增强是防止过拟合的关键。你可以在配置文件的train_pipeline中添加或调整增强操作例如train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeResize, img_scale[(1333, 800), (1600, 960)], multiscale_moderange, keep_ratioTrue), # 多尺度训练 dict(typeRandomFlip, flip_ratio0.5), dict(typeRandomCrop, crop_size(0.8, 0.8), crop_typerelative_range), # 随机裁剪 dict(typePhotoMetricDistortion, # 光度失真 brightness_delta32, contrast_range(0.5, 1.5), saturation_range(0.5, 1.5), hue_delta18), dict(typeNormalize, **img_norm_cfg), dict(typePad, size_divisor32), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_bboxes, gt_labels]), ]注意增强不是越多越好需要根据你的任务特性调整。工业质检可能不需要RandomFlip而自然场景目标检测则需要。学习率策略与优化器选择除了StepLR还可以尝试CosineAnnealingLR余弦退火它通常能带来更好的收敛效果和最终精度。将lr_config修改为lr_config dict( policyCosineAnnealing, warmuplinear, warmup_iters500, warmup_ratio0.001, min_lr_ratio1e-5 # 最小学习率 )对于优化器SGD with momentum是检测任务的主流但也可以尝试AdamW尤其在小数据集或训练不稳定时。模型微调与冻结骨干网络如果你的数据集与预训练数据集如COCO差异很大或者数据量很小可以考虑冻结骨干网络Backbone的前几层只训练后面的网络层以防止过拟合和加速训练。# 在配置文件中修改 model dict( backbonedict( frozen_stages2, # 冻结前2个stageResNet有4个stage norm_cfgdict(typeBN, requires_gradFalse), # 冻结BN层的统计量 norm_evalTrue), ... )训练初期验证集指标提升很快但后续可能遇到瓶颈此时可以解冻部分层继续训练。6.3 部署与落地考量训练出一个指标不错的模型只是第一步要真正用起来还需要考虑部署。模型转换MMDetection模型通常需要转换为其他格式以便部署。常用的有ONNX: 使用tools/deployment/pytorch2onnx.py脚本转换可以获得一个跨平台的中间表示。TorchScript: 使用PyTorch自带的torch.jit.trace或torch.jit.script进行转换适合在PyTorch生态内部署。TensorRT (for NVIDIA GPUs): 通过ONNX中转或直接使用MMDeploy等工具链可以极大提升推理速度。速度与精度权衡在配置文件中选择不同的Backbone如将r50换成r18或mobilenetv2和Neck如修改FPN的通道数可以显著影响模型速度和精度。MMDetection的Model Zoo提供了大量预训练模型及其性能指标可以作为选型参考。构建简易推理服务对于原型验证可以快速搭建一个基于Flask或FastAPI的Web服务将上面提到的推理代码封装成API方便其他系统调用。走到这一步你已经完成了从数据准备到模型训练、评估和初步部署的完整闭环。MMDetection的功能远不止于此它支持数十种检测算法、实例分割、全景分割等高级任务。但掌握这套基于Faster R-CNN和COCO格式的标准流程就像掌握了打开宝库的钥匙你可以自信地去探索更复杂的模型和任务了。记住遇到问题多查官方文档和GitHub Issues社区里很可能已经有现成的解决方案。