1. 项目概述从零上手MMDetection v2.22.0如果你刚拿到一批自己的图片数据想用当下主流的检测框架MMDetection来训练一个模型却对着官方文档和一堆配置文件感到无从下手那么这篇笔记或许能帮你理清思路。我最近刚用MMDetection v2.22.0完整跑通了一个自定义数据集的训练流程从环境搭建、数据准备、配置修改到训练调试中间踩了不少坑也总结了一套比较顺畅的实操路径。MMDetection作为OpenMMLab旗下的明星项目其模块化设计和丰富的模型库确实强大但正因为其高度可配置性对新手来说如何将官方示例适配到自己的任务上往往是最头疼的第一步。这篇内容不会面面俱到地讲解源码而是聚焦于“如何用起来”目标是让你能快速复现一个训练流程看到损失曲线下降并在自己的图片上跑出检测框。2. 环境搭建与核心依赖解析2.1 系统环境与PyTorch选择我的实验环境是Ubuntu 20.04配备了RTX 3090显卡。选择MMDetection v2.22.0这个版本主要是因为它是一个长期支持版本社区资料和解决方案相对丰富且与PyTorch 1.8到1.11的兼容性都经过充分测试稳定性有保障。这里第一个关键点就是PyTorch版本必须严格匹配。官方推荐使用PyTorch 1.8到1.11我选择了PyTorch 1.10.0 CUDA 11.3的组合。你可以通过PyTorch官网的历史版本安装命令来精确安装比如pip install torch1.10.0cu113 torchvision0.11.1cu113 torchaudio0.10.0cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html。安装后务必用python -c “import torch; print(torch.__version__, torch.cuda.is_available())”验证CUDA是否可用。注意不要盲目安装最新版的PyTorch。我曾尝试过PyTorch 1.12结果在编译MMDetection的CUDA扩展时遇到了不兼容的错误回退到1.10.0后问题消失。如果你的CUDA版本是11.6或更高可能需要对应地选择PyTorch 1.12但务必先查阅MMDetection仓库的Issue区确认有无已知的兼容性问题。2.2 MMDetection的安装与验证安装MMDetection本身并不复杂但步骤顺序很重要。我强烈建议使用MIMOpenMMLab的管理工具来安装它能更好地处理依赖关系。以下是标准步骤安装OpenMMLab系列基础包pip install openmim使用MIM安装MMCVMMCV是MMDetection的核心依赖必须安装完整版包含CUDA算子。命令是mim install mmcv-full1.6.0 -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html。请注意这里的版本号cu113/torch1.10.0需要根据你的CUDA和PyTorch版本进行修改。这是最容易出错的一步版本不对会导致运行时出现各种“未定义符号”的错误。克隆MMDetection仓库并安装git clone -b v2.22.0 https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e . # “-e”代表以可编辑模式安装方便你修改源码安装完成后运行一个简单的验证脚本至关重要。我通常会创建一个verify.py文件内容如下from 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’ model init_detector(config_file, checkpoint_file, device‘cuda:0’) img ‘demo/demo.jpg’ result inference_detector(model, img) print(‘验证通过模型加载和推理成功’)这个脚本会下载一个预训练模型并进行一次推理。如果它能正常运行并打印成功信息说明你的MMDetection环境基本就绪。如果报错错误信息通常会直接指向缺失的依赖或版本冲突。3. 自定义数据集准备与格式转换3.1 数据组织与标注规范你的数据可能来自各种渠道自己拍摄的、网络爬取的、或是公开数据集的一部分。无论来源如何在喂给MMDetection之前都需要整理成固定的格式。MMDetection原生支持COCO和PASCAL VOC格式我推荐使用COCO格式因为它更通用且MMDetection对其支持最好很多工具和可视化代码都基于COCO格式。假设你的原始数据是一堆jpg图片和对应的标注文件可能是XML、TXT或JSON。你需要将它们转换成COCO格式的单个annotations.json文件。这个JSON文件的结构如下{ “images”: [ { “id”: 1, “file_name”: “image_001.jpg”, “height”: 600, “width”: 800 }, // ... 更多图片信息 ], “annotations”: [ { “id”: 1, “image_id”: 1, // 对应上面的图片ID “category_id”: 1, // 类别ID从1开始 “bbox”: [x, y, width, height], // [左上角x, 左上角y, 框宽, 框高] “area”: width * height, // 框面积 “iscrowd”: 0 // 通常是0 }, // ... 更多标注框信息 ], “categories”: [ { “id”: 1, “name”: “cat” }, { “id”: 2, “name”: “dog” } // ... 你的所有类别 ] }images数组记录所有图片的基本信息annotations数组记录每一个目标框一个图片可以有多个框categories数组定义你的所有类别。这里的id必须从1开始连续编号0是留给背景类的。3.2 实用格式转换脚本与数据划分手动构造这个JSON文件非常繁琐。我写了一个通用的转换脚本模板假设你的原始标注是类似YOLO格式的每图一个TXT文件内容为class_id x_center y_center width height坐标已归一化可以这样转换import json import os from PIL import Image def convert_yolo_to_coco(yolo_img_dir, yolo_label_dir, output_json_path): images [] annotations [] categories [{id: 1, name: your_class_1}, {id: 2, name: your_class_2}] # 修改为你的类别 ann_id 1 for img_id, img_name in enumerate(os.listdir(yolo_img_dir), 1): img_path os.path.join(yolo_img_dir, img_name) with Image.open(img_path) as img: width, height img.size images.append({ “id”: img_id, “file_name”: img_name, “height”: height, “width”: width }) label_path os.path.join(yolo_label_dir, os.path.splitext(img_name)[0] ‘.txt’) if os.path.exists(label_path): with open(label_path, ‘r’) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) 5: cat_id, x_center, y_center, w, h map(float, parts) # 将归一化坐标转换为COCO的绝对坐标 x (x_center - w/2) * width y (y_center - h/2) * height w w * width h h * height annotations.append({ “id”: ann_id, “image_id”: img_id, “category_id”: int(cat_id) 1, # YOLO类别从0开始COCO从1开始 “bbox”: [x, y, w, h], “area”: w * h, “iscrowd”: 0 }) ann_id 1 coco_format { “images”: images, “annotations”: annotations, “categories”: categories } with open(output_json_path, ‘w’) as f: json.dump(coco_format, f) print(f“转换完成共 {len(images)} 张图片{len(annotations)} 个标注框。”)使用前请根据你的实际标注格式调整解析逻辑。转换完成后你需要将数据集划分为训练集和验证集。一个常见的做法是按8:2或9:1的比例随机划分图片并生成对应的train.json和val.json。切记划分是在图片层面进行的你需要确保同一个图片的所有标注框都进入同一个集合。实操心得在划分数据集前最好先做一次简单的数据分析。用脚本统计一下每个类别的实例数量、标注框的宽高分布。如果某些类别样本极少比如少于10个或者标注框的尺寸差异极大既有占满全图的大目标又有几个像素的小目标这些都会对模型训练造成挑战你可能需要后续通过数据增强或调整模型锚框Anchor来应对。4. 配置文件深度解析与关键修改4.1 配置文件结构与继承机制MMDetection的强大和复杂都体现在其配置系统上。它的配置文件采用Python格式并支持继承这既提高了灵活性也增加了理解难度。以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’ ]这表示该配置文件继承了四个基础配置文件分别定义了模型结构、数据加载、训练策略和运行时设置如日志、钩子。我们的修改原则是尽量不直接修改_base_中的原始文件而是在当前配置文件中通过覆盖重新赋值的方式来定制。这样既保持了原始配置的纯净也清晰记录了我们所做的改动。4.2 针对自定义数据集的必要修改点你需要修改的地方主要集中在数据data和模型头部model.roi_head.bbox_head两部分。修改数据集路径和类别数 在配置文件中找到data字典通常它已经通过_base_引入了COCO数据集的设置。我们需要覆盖train、val和test的ann_file和img_prefix。# 假设你的数据放在 mmdetection/data/my_dataset/ 下 data_root ‘data/my_dataset/’ data dict( samples_per_gpu2, # 根据你的GPU内存调整俗称batch_size workers_per_gpu2, # 数据加载线程数通常设为CPU核心数的一半 traindict( type‘CocoDataset’, ann_filedata_root ‘annotations/train.json’, img_prefixdata_root ‘images/train/’, # 继承自_base_的其他参数保持不变 ), valdict( type‘CocoDataset’, ann_filedata_root ‘annotations/val.json’, img_prefixdata_root ‘images/val/’, ), testdict( type‘CocoDataset’, ann_filedata_root ‘annotations/val.json’, # 测试集可以用验证集代替 img_prefixdata_root ‘images/val/’, ) )修改模型头部的类别数 这是最关键的修改如果忘记模型输出通道数对不上训练会直接报错。找到model配置部分修改roi_head中的bbox_head对于Faster R-CNN系列或bbox_head和mask_head对于Mask R-CNN。你需要将num_classes参数修改为你的实际类别数。model dict( roi_headdict( bbox_headdict( type‘Shared2FCBBoxHead’, # ... 其他参数 num_classes10, # 修改为你的类别数例如10类 ) ) )特别注意对于RetinaNet、YOLO等单阶段检测器修改位置可能不同通常在bbox_head的anchor_generator和bbox_head本身。一个简单的查找方法是在配置文件中全局搜索num_classes找到所有值不为80COCO类别数的地方都改成你的类别数。调整学习率可选但重要 当你的数据集规模和COCO差异很大时需要调整学习率。一个经验法则是按线性比例缩放new_lr base_lr * (new_batch_size / base_batch_size)。如果基础配置的samples_per_gpu即batch size是2学习率是0.02你改成了4那么新学习率可以尝试设为0.04。修改位置在optimizer配置中optimizer dict(type‘SGD’, lr0.04, momentum0.9, weight_decay0.0001) # 调整lr5. 模型训练、监控与调试实战5.1 启动训练与多GPU支持修改好配置文件后假设保存为configs/my_config/faster_rcnn_my_dataset.py就可以启动训练了。最基本的单GPU训练命令是python tools/train.py configs/my_config/faster_rcnn_my_dataset.py如果你有多张GPU强烈建议使用分布式训练它能显著加快速度。MMDetection基于PyTorch的DistributedDataParallel (DDP) 封装了启动脚本./tools/dist_train.sh configs/my_config/faster_rcnn_my_dataset.py 8 --work-dir ./work_dirs/my_exp这里的8代表使用8张GPU。--work-dir指定了实验日志、模型权重和配置备份的保存目录。训练开始后控制台会打印损失值、学习率等信息。5.2 训练过程监控与可视化仅仅看命令行输出是不够的。MMDetection默认集成了TensorBoard和MMDetVisualization等可视化工具。我习惯使用TensorBoard来监控训练过程。在启动训练时配置文件中log_config部分已经设置了TensorBoard钩子。你只需要在另一个终端运行tensorboard --logdir ./work_dirs/my_exp然后在浏览器打开localhost:6006你就可以看到实时的损失曲线loss_rpn_cls,loss_rpn_bbox,loss_cls,loss_bbox等、学习率曲线、以及验证集上的mAP平均精度曲线。观察这些曲线是诊断训练问题的关键损失不下降可能学习率太高/太低、数据标注有严重错误、模型复杂度与数据不匹配。训练损失下降但验证集mAP不升典型的过拟合现象可能需要增加数据增强、使用更重的正则化如Dropout、或提前停止。损失出现NaN通常是学习率过高、数据中存在异常值如坐标超出图像范围、或梯度爆炸。5.3 模型测试与推理演示训练完成后模型权重会保存在work_dirs/my_exp/latest.pth最新epoch或按epoch命名的文件中。你可以使用以下命令在验证集上评估模型性能# 单GPU测试 python tools/test.py configs/my_config/faster_rcnn_my_dataset.py ./work_dirs/my_exp/latest.pth --eval bbox # 多GPU测试 ./tools/dist_test.sh configs/my_config/faster_rcnn_my_dataset.py ./work_dirs/my_exp/latest.pth 8 --eval bbox--eval bbox表示评估边界框检测的指标如mAP。结果会打印出来并与TensorBoard中的曲线相互印证。更直观的方式是看模型在单张图片上的推理效果。MMDetection提供了一个方便的API和演示脚本from mmdet.apis import init_detector, inference_detector, show_result_pyplot import mmcv config_file ‘configs/my_config/faster_rcnn_my_dataset.py’ checkpoint_file ‘./work_dirs/my_exp/latest.pth’ model init_detector(config_file, checkpoint_file, device‘cuda:0’) img ‘test.jpg’ # 你的测试图片路径 result inference_detector(model, img) # 将结果可视化并保存 out_file ‘result.jpg’ show_result_pyplot(model, img, result, out_fileout_file, score_thr0.3) # score_thr是置信度阈值通过调整score_thr你可以控制显示框的严格程度这对于观察模型在不同置信度下的表现非常有用。6. 常见问题排查与性能调优经验6.1 训练初期常见错误与解决KeyError: ‘xxx’ is not in the fields或AssertionError: The ‘num_classes’ (80) in Shared2FCBBoxHead … does not matches the length of ‘class_name’ (10) in CocoDataset问题这是最典型的新手错误意味着数据集类别数class_name长度和模型头部num_classes设置不一致。解决仔细检查并确保配置文件中model.roi_head.bbox_head.num_classes的值与你数据集中categories的数量完全一致。同时检查data配置中的classes参数如果显式指定了是否与你的类别列表匹配。RuntimeError: CUDA out of memory问题GPU内存不足。解决首先降低data.samples_per_gpu即batch size。如果降到1还不够可以尝试使用更小的输入图像尺寸修改配置中train_pipeline和test_pipeline里的Resize步骤例如将img_scale(1333, 800)改为(800, 600)。使用更小的骨干网络例如将ResNet-50换成ResNet-34或ResNet-18。启用梯度检查点Gradient Checkpointing在模型配置中添加with_cpTrue但这会以时间换空间。FileNotFoundError: [Errno 2] No such file or directory: ‘…/annotations/instances_val2017.json’问题数据路径配置错误代码找不到你的标注文件或图片。解决使用绝对路径或确保相对路径正确。在配置文件中用os.path.join或直接写绝对路径来定义data_root。用简单的Python脚本打印出拼接后的完整路径验证文件是否存在。6.2 模型性能调优思路当模型能跑起来但精度不高时可以从以下几个方向尝试优化数据层面数据增强MMDetection在train_pipeline中内置了丰富的增强策略如RandomFlip、RandomCrop、PhotoMetricDistortion光度畸变等。对于小数据集可以适当增强这些操作的强度或概率。但要注意过度增强也可能损害性能。类别平衡如果某些类别样本极少可以考虑使用ClassBalancedDataset包装器或者采用过采样Oversampling的策略。模型层面预训练权重务必使用在ImageNet或COCO上预训练的骨干网络权重。这能极大加速收敛并提升最终精度。在配置文件中通过load_from参数指定预训练权重路径。MMDetection提供了许多模型的预训练权重可以在Model Zoo中找到。锚框Anchor匹配对于目标尺寸分布特殊的数据集如都是细长形目标默认的锚框尺寸可能不匹配。可以分析数据集中标注框的宽高比和尺度分布然后调整rpn_head.anchor_generator中的scales和ratios参数。训练策略学习率策略如果验证集精度很早就停滞可以尝试使用CosineAnnealing或Step学习率衰减策略在配置文件的lr_config部分修改。权重衰减与优化器默认的SGD优化器配合0.0001的权重衰减weight decay对于大多数情况是好的起点。如果模型过拟合严重可以尝试增大权重衰减或换用AdamW优化器需调整学习率通常更小。训练轮数schedule_1x.py默认训练12个epoch在COCO上。对于更小的数据集可能需要更少的轮数否则容易过拟合。可以观察验证集mAP曲线当其在连续多个epoch不再上升时即可考虑停止。6.3 一个实用的调试流程建议当训练结果不理想时建议采用分步排查法过拟合一个小数据集从你的数据集中随机抽取50-100张图片关闭所有数据增强用很高的学习率如0.1训练几个epoch。目标是让训练损失快速下降到接近0。如果连这个小数据集都无法过拟合训练精度达不到100%说明代码、数据加载或损失计算可能存在根本性错误。在完整训练集上训练第一步成功后恢复正常的训练设置数据增强、正常学习率在完整数据集上训练。此时应关注验证集指标。分析预测结果将验证集上预测错误的案例可视化出来。是定位不准框歪了还是分类错了或者是根本漏检了针对性地调整模型如调整NMS阈值、分类损失权重或数据如增加困难样本。迭代优化基于分析结果进行有针对性的调整如修改锚框、增加数据增强类型、调整损失函数权重等然后重新训练验证。这个过程往往需要多次迭代。最后关于模型选择对于刚入门和数据集不大的情况我建议从经典的Faster R-CNN或单阶段的RetinaNet开始它们结构清晰调试方便性能也比较稳定。等熟悉了整个流程后再尝试更复杂的模型如Cascade R-CNN、Dynamic R-CNN或者基于Transformer的检测器如DETR会更有把握。记住MMDetection是一个工具箱第一步是先学会熟练使用一两件核心工具解决实际问题之后再探索整个仓库的丰富内容。