ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MMDetection实战指南:从环境搭建到自定义数据集训练

MMDetection实战指南:从环境搭建到自定义数据集训练 刚入坑目标检测那会儿我对着 MMDetection 的源码硬啃了整整两周从“这框架怎么这么多文件”到“原来每个模块都能自由替换”中间踩过的坑比代码行数还多。现在回头看这框架的设计思路其实非常清晰——它就是把目标检测里那些固定套路数据加载、模型搭建、训练流程、评测逻辑全部模块化让你不用从零写网络结构也不用重复造轮子。这篇文章就按照我从零到一跑通 MMDetection 的完整路径来写从环境搭建到自定义数据集训练再到结果分析和常见问题排查全部覆盖。适合刚接触目标检测、想快速上手 MMDetection 的朋友也适合那些已经在用但总是报错、搞不清配置逻辑的人。1. 为什么选择 MMDetection不只是“省事”这么简单说起目标检测框架现在市面上确实有不少选择Detectron2、PaddleDetection、YOLOv5 系列还有 MMDetection。但如果你要在科研或者工程里做算法对比实验MMDetection 的优势非常明显它把模型、数据集、训练策略、评测指标全部解耦想换 backbone 就换 backbone想改 anchor 配置就改 anchor 配置不用动其他代码。这一点在对比实验里太重要了——我在一篇论文里对比了 8 个检测器全部靠改配置文件完成总共没写过一行网络定义代码。1.1 MMDetection 的核心设计一切皆模块MMDetection 基于 PyTorch 构建但它没有把代码写成“一个大类跑到底”而是拆成了几个核心模块detector检测器骨架、backbone特征提取网络、neck特征融合层、head检测头、loss损失函数、data pipeline数据流水线。每个模块都有统一接口你在配置里指定用哪个类框架就自动加载对应的实现。这个思想很像搭积木Faster R-CNN 和 SSD 在 backbone、neck、head 上各有不同但框架层面都是同一套流程。理解了这一点你就知道为什么 MMDetection 的配置系统是核心中的核心。它不止是调参工具更是一个“代码生成器”——配置文件里写什么类框架就实例化什么网络。曾经有个同事为了换一个 attention 模块把整个 heads 文件重写了一遍后来发现其实只需要注册一个新类然后配置里改一行type就行。1.2 它能帮你解决什么问题如果你要从零写一个 Faster R-CNN光 RPN 的 anchor 生成逻辑、Proposal 筛选、RoIAlign 实现、NMS 后处理这些代码加起来没有 3000 行下不来而且你还得保证它和原 paper 一致才能公平对比。MMDetection 替你把这些都做好了并且实现精度和官方论文基本对齐。更重要的一点是它的工具链完整训练日志可视化、模型评估COCO mAP、VOC mAP、模型导出、推理 demo、特征图可视化官方都提供了现成脚本。这意味着你只需要关注“我该改哪里”而不用关心“这行代码怎么实现”。2. 环境搭建版本对齐比什么都重要MMDetection 最让人头大的不是模型代码而是环境依赖。PyTorch、CUDA、MMCV 三者版本必须严格匹配否则各种奇奇怪怪的报错会把你折磨到怀疑人生。我第一次装的时候就是因为 Cython 版本不对编译 MMCV 直接失败折腾了两天才发现是 Python 版本的问题。2.1 推荐的安装环境组合先说结论直接用这套组合能避开 90% 的坑# 基础环境 Python 3.8 CUDA 11.3 PyTorch 1.10.0 torchvision 0.11.0 mmcv-full 1.4.8 mmdetection 2.25.0 mmdet 2.x 分支如果你有 GPU 且 CUDA 版本不是 11.3也没关系核心是确保 PyTorch 版本和 MMCV 版本匹配。MMCV 官方文档有个版本对应表安装前先去查一下不要凭感觉来。2.2 一步步安装流程首先创建虚拟环境这里我强烈建议用 conda而不是直接装在系统 Python 里conda create -n openmmlab python3.8 -y conda activate openmmlab然后安装 PyTorch。这里要特别注意CUDA 版本决定了 PyTorch 的安装命令用官网的 pip 命令即可pip install torch1.10.0cu113 torchvision0.11.0cu113 -f https://download.pytorch.org/whl/torch_stable.html接着安装 MMCV。MMDetection 依赖 MMCV 提供的编译好的 CUDA 算子比如 RoIAlign、Deformable Conv 这些所以不能直接pip install mmcv要安装mmcv-full而且要选择和 PyTorch/CUDA 对应的版本pip install mmcv-full1.4.8 -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html最后克隆 MMDetection 源码并安装git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection git checkout v2.25.0 pip install -r requirements/build.txt pip install -v -e .pip install -v -e .这行命令是开发模式安装它会去编译一些扩展算子比如nms、sigmoid_focal_loss这些。如果这一步报错大概率是 Cython 没装先执行pip install cython再重新来。注意编译扩展时不要急着跳过。MMDetection 里很多算子是在运行时才调用的如果编译阶段没成功训练时会出现module mmdet has no attribute nms之类的诡异报错。2.3 验证环境是否正常安装完成后跑一下官方自带的推理 demo如果能正常出结果说明环境没问题from mmdet.apis import init_detector, inference_detector config_file configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py checkpoint_file checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth # 初始化模型 model init_detector(config_file, checkpoint_file, devicecuda:0) # 推理一张图片 result inference_detector(model, demo/demo.jpg)下载权重文件时注意官网的下载链接在 GitHub Release 里如果下载慢可以用镜像或者代理但不要用未经验证的第三方来源防止权重被篡改。3. 数据准备COCO 格式是绕不开的坎MMDetection 默认支持 COCO 和 VOC 两种数据格式但实际项目里你大多数时候需要把自己的数据转成 COCO 格式。这一步虽然是纯体力活但出错率极高我见过太多人花费大量时间标注和导出数据结果训练时一直报KeyError: images或者IndexError。3.1 COCO 格式的核心结构COCO 标注文件是一个 JSON里面核心字段就四个images图片信息、annotations标注信息、categories类别信息还有一个可选的licenses。大多数错误都出在字段名不一致比如有些人把category_id写成了class_id有人把bbox里的坐标写成了[x1, y1, x2, y2]但 COCO 要求是[x, y, width, height]。这里我建议写一个自定义转换脚本时先构造一条最小示例验证再用脚本批量转换def convert_bbox(x1, y1, x2, y2): 把 xyxy 格式转成 xywh return [x1, y1, x2 - x1, y2 - y1] def build_annotation(img_id, ann_id, category_id, bbox_xyxy, area, iscrowd0): x1, y1, x2, y2 bbox_xyxy bbox convert_bbox(x1, y1, x2, y2) return { id: ann_id, image_id: img_id, category_id: category_id, bbox: bbox, area: area, iscrowd: iscrowd, segmentation: [] }3.2 数据文件夹结构训练前需要把数据和标注文件按以下结构放好MMDetection 默认从data/coco读取数据你可以在配置里修改data_root来指向自定义路径data/ └── coco/ ├── annotations/ │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ └── val2017/如果你的数据只有一部分图片不需要下载完整的 2017 数据集只要保证标注文件的images字段里的file_name能和实际图片对应上即可。3.3 小数据集也能用 COCO 格式很多人有个误解觉得 COCO 格式是大数据集专用的。其实 COCO 格式本质上就是一个 JSON 文件和数据集大小无关。我训练一个 500 张图片的 PCB 缺陷检测项目时就是手动构造了一个简化版的 COCO JSON效果没有任何问题。关键在于确保数据加载器能正确读取图片路径和标注信息。4. 配置文件拆解看懂这 3 个部分就掌握一半MMDetection 的配置系统是分层继承的base目录里存着一堆“默认配置”你新建的配置文件通过_base_字段继承它们然后只写你要覆盖的部分。这种设计非常优雅但也让新手困惑——明明一个配置文件只有几十行结果训练时模型结构、优化器策略全都变了这就是因为继承的关系。4.1 模型配置模型配置定义了检测器的完整结构包括backbone、neck、rpn_head、roi_head这些子模块。以 Faster R-CNN 为例核心配置长这样model dict( typeFasterRCNN, backbonedict( typeResNet, depth50, num_stages4, out_indices(0, 1, 2, 3), frozen_stages1, norm_cfgdict(typeBN, requires_gradTrue), norm_evalTrue, stylepytorch ), neckdict( typeFPN, in_channels[256, 512, 1024, 2048], out_channels256, num_outs5 ), rpn_headdict( typeRPNHead, in_channels256, feat_channels256, anchor_generatordict( typeAnchorGenerator, scales[8], ratios[0.5, 1.0, 2.0], strides[4, 8, 16, 32, 64] ), bbox_coderdict( typeDeltaXYWHBBoxCoder, target_means[0.0, 0.0, 0.0, 0.0], target_stds[1.0, 1.0, 1.0, 1.0] ), loss_clsdict(typeCrossEntropyLoss, use_sigmoidTrue, loss_weight1.0), loss_bboxdict(typeL1Loss, loss_weight1.0) ), roi_headdict( typeStandardRoIHead, bbox_roi_extractordict( typeSingleRoIExtractor, roi_layerdict(typeRoIAlign, output_size7, sampling_ratio0), out_channels256, featmap_strides[4, 8, 16, 32] ), bbox_headdict( typeShared2FCBBoxHead, in_channels256, fc_out_channels1024, roi_feat_size7, num_classes80 ) ) )新手最容易犯的错误是只改num_classes却忘了检查roi_head里 bbox head 的num_classes也改了。如果你只改了整体配置而把num_classes漏掉训练时 loss 会正常计算但推理时类别数对不上结果全是错的。4.2 数据配置数据配置定义了训练集和验证集的划分、数据预处理流水线pipeline、批量大小、线程数等。常见的数据增强操作有随机翻转、随机裁剪、多尺度训练等。data dict( samples_per_gpu2, workers_per_gpu2, traindict( typeCocoDataset, ann_filedata/coco/annotations/instances_train2017.json, img_prefixdata/coco/train2017/, pipeline[ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeResize, img_scale(1333, 800), keep_ratioTrue), dict(typeRandomFlip, flip_ratio0.5), dict(typeNormalize, mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375], to_rgbTrue), dict(typePad, size_divisor32), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_bboxes, gt_labels]) ] ) )这里的size_divisor32很重要。FPN 的下采样倍率最高是 32 倍如果图片宽高不是 32 的整数倍特征图尺寸对不上训练时会出现尺寸不匹配的报错。Pad会把图片补到 32 的倍数。4.3 训练配置训练配置包括优化器、学习率调度器、总训练轮数、日志打印间隔、评估间隔等。对于自定义数据集最需要调整的是学习率和训练轮数。optimizer dict(typeSGD, lr0.02, momentum0.9, weight_decay0.0001) optimizer_config dict(grad_clipNone) lr_config dict( policystep, warmuplinear, warmup_iters500, warmup_ratio0.001, step[8, 11] ) runner dict(typeEpochBasedRunner, max_epochs12) evaluation dict(interval1, metricbbox)学习率0.02是针对 8 张卡每张卡 2 张图batch size 总共 16设置的。如果你只有一张显卡batch size 总共才 2学习率必须相应调整否则容易发散。经验法则学习率按 batch size 线性缩放即新学习率 0.02 * (新batch size / 16)。如果 batch size 为 2大概用0.0025起步比较稳。5. 训练实操从一条命令到完整训练闭环配置写好后训练就变成一条命令的事情。但真正跑起来后你需要学会看日志、看曲线、及时发现问题并止损。5.1 训练启动命令python tools/train.py configs/my_configs/my_faster_rcnn_r50_fpn_1x_coco.py --work-dir work_dirs/my_faster_rcnn--work-dir指定日志和权重文件的输出目录强烈建议每次实验都指定一个独立目录避免多个实验混在一起。训练开始后终端会输出类似下面的日志2023-05-20 10:30:12,658 - mmdet - INFO - Epoch [1][50/1000] lr: 0.002500, eta: 3:20:15, time: 0.45, data_time: 0.12, memory: 3450, loss_rpn_cls: 0.6843, loss_rpn_bbox: 0.1234, loss_cls: 0.8756, loss_bbox: 0.2345, loss: 1.9178从这些字段里你能实时看到当前 epoch、迭代数、学习率、预计剩余时间、每步耗时、数据加载耗时、显存占用以及各分支 loss。data_time如果占比过高说明数据加载是瓶颈需要调大workers_per_gpu或检查磁盘 IO 性能。5.2 损失曲线怎么看训练过程中MMDetection 会把 loss 记录到日志文件里也支持通过 TensorBoard 实时查看。启动 TensorBoard 的命令tensorboard --logdir work_dirs/my_faster_rcnn在浏览器里打开http://localhost:6006你就能看到各分支 loss 的曲线。正常来说loss_rpn_cls、loss_cls会快速下降然后在低位震荡loss_bbox下降速度相对平缓。如果 loss 出现剧烈波动或者直接爆到几十万基本可以确定是学习率过大或者数据标注有误。5.3 评估指标与 checkpoint 管理evaluation dict(interval1, metricbbox)表示每隔一个 epoch 就在验证集上做一次完整评估计算 COCO mAP。MMDetection 会在验证集上记录最佳模型并保存三个 checkpointlatest.pth最新模型、best_bbox_mAP_epoch_x.pth验证集 mAP 最高模型、以及 best 模型对应的best_bbox_mAP_epoch_x.json评估结果文件。我在实际项目中习惯用 best 模型做最终测试而不是用最后一个 epoch 的模型。因为目标检测训练到最后几个 epoch有时候模型会过拟合验证 mAP 反而不如中间某个 epoch。MMDetection 默认就是保存验证集最优模型这个设计非常省心。6. 模型推理从权重文件到业务落地训练完模型你需要验证模型效果输出检测结果可视化图片。MMDetection 提供了测试脚本也有非常灵活的 Python API方便你集成到自己的项目里。6.1 测试脚本的使用python tools/test.py configs/my_configs/my_faster_rcnn_r50_fpn_1x_coco.py work_dirs/my_faster_rcnn/best_bbox_mAP_epoch_12.pth --eval bbox --show-dir result_images--eval bbox会在验证集上计算 COCO 标准指标输出类似Average Precision (AP) [ IoU0.50:0.95 | area all | maxDets100 ] 0.378 Average Precision (AP) [ IoU0.50 | area all | maxDets100 ] 0.580 Average Precision (AP) [ IoU0.75 | area all | maxDets100 ] 0.405 Average Recall (AR) [ IoU0.50:0.95 | area all | maxDets100 ] 0.440如果你不想要完整评估只是简单看看效果可以用--show-dir把预测结果画到原图上保存下来。6.2 Python API 自定义推理实际应用里你一般不会在命令行里做推理而是把检测功能集成到自己的服务或脚本里。下面这段代码是标准的 API 用法import cv2 from mmdet.apis import init_detector, inference_detector config_file configs/my_configs/my_faster_rcnn_r50_fpn_1x_coco.py checkpoint_file work_dirs/my_faster_rcnn/best_bbox_mAP_epoch_12.pth model init_detector(config_file, checkpoint_file, devicecuda:0) img cv2.imread(test_images/car.jpg) result inference_detector(model, img) # result 是一个 list每个元素对应一个类别的检测框 # 每个元素是 shape (N, 5) 的数组每行是 [x1, y1, x2, y2, score] # 保存可视化结果 from mmdet.apis import show_result_pyplot show_result_pyplot(model, img, result, score_thr0.3, out_fileresult.jpg)有一个细节要注意inference_detector输入图片如果是 BGR 格式用 cv2 读取框架内部会按to_rgbTrue的配置自动转换不需要你手动处理。但如果你用 PIL 读取RGB 格式就得在调用前确认配置里的to_rgb是否匹配否则颜色通道错乱检测效果会莫名其妙变差。6.3 推理速度的优化思路如果是工业落地场景推理耗时是硬指标。MMDetection 默认推理流程包括完整的后处理NMS如果你想提升速度几个方向供参考用 TensorRT 转换模型在 GPU 上能提升 1.5 到 3 倍速度调整输入尺寸比如从(1333, 800)降到(1000, 600)精度损失不大但速度提升明显调整 NMS 的score_thr提前过滤低置信度框减少计算量。7. 常见问题与排查技巧实录这部分是我最想分享的因为 MMDetection 的报错信息有时候特别不友好一个KeyError能让你查半天。下面这些问题都是我实际踩过的按出现频率排序。7.1 CUDA 内存不足显存不足是训练中最常见的问题尤其在 batch size 较大或输入图片分辨率较高时。解决方法优先级从高到低降低samples_per_gpu从 2 改为 1降低输入图片尺寸把img_scale从(1333, 800)改为(1000, 600)如果你的显卡只有 8GB建议直接用轻量级模型如 RetinaNet ResNet50而不是大尺寸的 Cascade R-CNN开启梯度累积optimizer_config dict(typeGradientCumulativeOptimizerHook, cumulative_iters4)用时间换显存。注意显存不够时不要盲目减小workers_per_gpu这个参数只影响数据加载进程数不影响显存占用。7.2KeyError: gt_labels这个报错 90% 是数据集的标注文件里没有annotations对应的类别标签或者图片没有对应的标注信息。MMDetection 的LoadAnnotations流水线期望每张图片都有gt_bboxes和gt_labels如果你的数据集中有背景图片无目标需要在配置里加skip_img_without_annotationsTruetraindict( typeCocoDataset, ... skip_img_without_annotationsTrue )7.3 类别数不匹配导致 mAP 为 0如果训练正常结束但验证 mAP 一直是 0先检查是不是num_classes没有改成你的数据集类别数。COCO 的 80 类和一个 3 类的小数据集计算的输出维度和损失函数完全不同但训练过程可能照常进行只是结果全错。7.4 训练速度特别慢如果你的 GPU 利用率很低用nvidia-smi查看可能是数据加载速度跟不上。优先调整workers_per_gpu从 2 调到 4 或 8并检查存储介质是否为机械硬盘。训练数据量大的话建议把图片放到 SSD 上。此外可以在配置里启用shuffleFalse来测试数据加载是否是瓶颈对验证集操作但训练集不要关 shuffle。7.5 下载预训练权重时被墙国内下载 mmdetection 官方权重很慢常见的做法是配置代理或者从镜像站下载。但要注意如果你下载的预训练权重和应用配置不完全匹配加载时会报 shape mismatch。这种情况下需要手动修改加载 weights 的方式或者换一个完全对应的权重文件。7.6 常见问题速查表最后整理一个速查表方便大家排查报错现象可能原因解决方案KeyError: gt_labels标注文件缺类别字段检查 JSON 中 annotations 的字段CUDA out of memorybatch size / 输入尺寸过大降低 samples_per_gpu 或 img_scaleRuntimeError: size mismatch类别数不一致检查配置里的 num_classesmodule mmdet has no attribute nms扩展算子没编译重新执行pip install -v -e .训练时 loss 为 NaN学习率过大 / 数据异常降低学习率检查标注 bbox 是否合理验证时 mAP 为 0类别不匹配 / 模型加载错误核对 num_classes、权重路径推理结果为空score_thr 设置过高降低 score_thr 或检查模型配置8. 写在最后几个提升效率的小习惯MMDetection 框架本身没那么难难的是它背后那一套庞大的模块化体系和配置继承关系。如果你刚开始接触我建议不要一上来就用自己的数据集跑训而是先用官方配置和 COCO 子集把整个流程走通再逐步替换成自己的数据。另外每改一个配置一定要先看日志里打印出的模型结构是否和预期一致因为配置继承经常会导致你以为改了某个参数实际上没生效。还有一个我后来才养成的习惯每个实验都在work_dirs目录下保存一份当时的配置文件副本用日期命名。这样三个月后回看实验结果还能准确知道当时用的什么配置不用在几十份文件里翻找。这个小习惯帮我省了太多时间。如果你在训练过程中遇到什么奇怪的报错欢迎在评论区留言我会根据自己的经验帮你排查。
返回列表