MMDetection3.0自定义数据集目标检测全流程实战

发布时间:2026/9/16 23:18:06

MMDetection3.0自定义数据集目标检测全流程实战 做目标检测项目十有八九绕不开MMDetection。最近我在搞一个钢材表面缺陷检测的小项目需要把MMDetection3.0和自定义数据集这套训练流程完整走一遍图片自己拍标注自己标标注完还得转成模型能吃的格式然后改配置、调参数、盯loss、看mAP。3.0相比2.x改动非常大配置体系、底层Runner、依赖库全换了网上很多教程还停留在旧版本照着抄第一轮就会在环境那一步报错。这篇文章就把我从环境搭建、数据标注、配置改写到最终训练出可用模型的全过程完整记录下来。如果你正准备把手里的自定义数据集塞进MMDetection3.0照这条路线走应该能省下不少折腾的时间。1. 环境准备版本不对后面全是泪1.1 MMDetection 3.0 和 2.x 到底差在哪先说结论千万别用老教程里的安装方式去装3.0。MMDetection 2.x 依赖的是mmcv-full而 3.x 一开始就换成了mmengine。mmengine 相当于一个全新的训练引擎把所有训练循环、Hook、Logger 都统一了这就是为什么 2.x 的很多脚本和配置在 3.x 里跑不起来。所以3.0的安装链条比2.x多了一层mmdet依赖mmcv和mmenginemmcv又依赖 PyTorch 和 CUDA。任何一个环节版本对不上后面就是无穷无尽的报错。这里先给出一套我实测能跑通的组合组件版本说明Python3.83.8/3.9最稳太新的版本容易遇到依赖兼容问题PyTorch1.13.1与CUDA 11.7配对CUDA11.7驱动版本够新就行mmcv2.1.0必须 2.x不能装1.xmmengine0.10.0mmcv 2.x会自动带上mmdet3.2.0用git clone源码方式安装这个组合我跑了分类、检测、分割好几套流程没有出现奇奇怪怪的兼容性问题。当然 PyTorch 2.x 搭配新版 mmcv 2.2 也可以但新手阶段没必要追求最新稳定第一。1.2 安装步骤与环境验证安装命令按顺序执行conda create -n mmdet3 python3.8 -y conda activate mmdet3 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -U openmim mim install mmengine mim install mmcv2.0.0 git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e .这里有个关键点mmcv一定要用mim install来装不要直接pip install mmcv。直接pip装默认是CPU版本训练时一跑就报CUDA错误。mim工具会自动检测你的CUDA和PyTorch版本帮你选对应的预编译包省心很多。如果GitHub拉取速度不理想可以把最后四行换成 gitee 镜像地址来clone路径和源码保持一致就行这个不算丢人省时间才是正事。装完之后一定要验一下环境python -c import mmdet, mmcv, mmengine; print(mmdet.__version__, mmcv.__version__, mmengine.__version__)能同时输出三个版本号说明环境基本通了。如果只装了mmdet没装mmengine这里第一行就会报ModuleNotFoundError。我见过太多人卡在这一步花了大半天排查配置问题结果就是环境没配对。2. 数据准备从一堆图片到COCO格式2.1 标注工具怎么选环境搞定后真正耗费时间的是数据。我这次做的是钢材表面缺陷检测总共6个类别crazing裂纹、inclusion夹杂、patches麻点、pitted_surface氧化铁皮压入、rolled-in_scale轧制氧化皮、scratches划伤。这些缺陷在工业场景里形态差异挺大有的是细长线条有的是团块状标注的时候非常考验耐心。标注工具我对比过几个最终结论是看你用检测还是分割模型工具输出格式上手难度是否支持自动标注适合场景LabelImgVOC XML低否纯检测框快速上手X-AnyLabelingVOC/COCO/YOLO中是需要实例分割或多格式导出labelmeJSON多边形中否多边形分割标注Label Studio多格式中高是团队协作、多模态数据如果你只需要目标检测框LabelImg 就够了界面简单画框效率高。如果后面想跑 Mask R-CNN 这类实例分割模型建议直接用 X-AnyLabeling它支持导出带多边形坐标的JSON再转成COCO的segmentation字段省得二次标注。标注时有几个细节特别容易踩坑文件名不要有中文、不要有空格统一用英文字母加数字命名后面所有脚本都会省事。类别名统一用小写英文不要一会儿Crazing一会儿crazing转换脚本匹配不上会静默丢数据。一张图有多个目标就画多个框框要尽量贴合目标边缘不要为了省事框大一圈会直接影响模型收敛。边缘处只露出一半的缺陷对象建议直接跳过不标或者标了之后在筛选阶段去掉不然会给模型传递很差的监督信号。2.2 标注转COCO一个脚本搞定MMDetection3.0对COCO格式支持最友好几乎开箱即用。我建议不管标注工具导出什么格式都统一转成COCO JSON。COCO标注文件的核心就是三个数组images存图片信息annotations存框和多边形categories存类别ID和名称。LabelImg 默认导出的是Pascal VOC XML格式我写了一个转换脚本核心逻辑就是遍历XML文件读取每个目标的bbox坐标换算成COCO格式的[x, y, width, height]然后填进JSONimport json import os import xml.etree.ElementTree as ET from PIL import Image label_map { crazing: 1, inclusion: 2, patches: 3, pitted_surface: 4, rolled-in_scale: 5, scratches: 6, } def convert_voc_to_coco(xml_dir, img_dir, json_out): images [] annotations [] ann_id 1 xml_files sorted([f for f in os.listdir(xml_dir) if f.endswith(.xml)]) for img_id, xml_file in enumerate(xml_files, start1): tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): continue with Image.open(img_path) as img: width, height img.size images.append({ id: img_id, file_name: img_name, width: width, height: height, }) for obj in root.findall(object): cls obj.find(name).text if cls not in label_map: continue category_id label_map[cls] 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 if w 0 or h 0: continue annotations.append({ id: ann_id, image_id: img_id, category_id: category_id, bbox: [xmin, ymin, w, h], area: w * h, iscrowd: 0, }) ann_id 1 categories [{id: v, name: k} for k, v in label_map.items()] coco { images: images, annotations: annotations, categories: categories, } with open(json_out, w) as f: json.dump(coco, f) if __name__ __main__: convert_voc_to_coco(labels/xml, images, instances_train.json)这个脚本有几个地方我特意做了保护w 0 or h 0的框直接跳过避免脏数据污染训练图片文件不存在时跳过而不是让脚本崩溃label_map里没有的类别直接continue防止XML里混入未定义的对象。如果你用的是 X-AnyLabeling 或 labelme导出的是带多边形坐标的JSON那就需要额外处理segmentation字段。一定要把annotations里的bbox和area从多边形顶点坐标计算出来不要直接填0。area在训练时用于计算一些统计量填错会影响部分模型的表现。2.3 目录组织与数据检查转换完成后目录结构建议这样组织data/steel/ ├── annotations/ │ ├── instances_train.json │ └── instances_val.json ├── images/ │ ├── train/xxx.jpg │ └── val/xxx.jpg训练集和验证集按7:3或8:2划分划分时注意打乱顺序直接把前几百张归为训练、后几百张归为验证是不行的模型会学不到泛化能力。验证集不需要太大但要保证每个类别至少都有几张。数据检查这一步很多人会跳过我强烈建议写一个统计脚本看看每个类别各有多少个框、验证集里每张图的框数量分布。我之前就遇到过验证集里某个类别一个都没有的情况mAP指标全红了还不知道问题在哪。检查几个关键点类别ID是否从1开始0是COCO格式里保留给背景的。每张图的file_name是否和images目录下的实际文件名完全一致。图片尺寸和JSON里记录的宽高是否一致。训练集和验证集的图片是否有交叉交叉了就是数据泄漏指标虚高没有意义。3. 修改配置让模型认识你的数据集3.1 认识3.0的配置继承体系MMDetection3.0的配置文件体系和2.x有本质区别。3.0的配置是“继承覆盖”的一个config文件可以同时继承多个基础配置比如模型结构、数据集、优化策略、运行时参数都是独立文件通过_base_字段聚合到一起。这样做的好处是自定义数据集根本不需要从头写config只需要继承官方现成的基础配置然后覆盖掉数据集路径和类别数就行了。我第一次用3.0时还在到处找“数据集类要怎么写”后来才发现根本不用重新注册数据集类直接用BaseDataset加上metainfo就能搞定。自定义config文件建议放在configs/steel/目录下和官方配置分开管理避免污染源码目录。3.2 自定义config完整示例以 Faster R-CNN ResNet50 FPN 为例完整配置文件如下_base_ [ ../_base_/models/faster-rcnn_r50_fpn.py, ../_base_/datasets/coco_detection.py, ../_base_/schedules/schedule_1x.py, ../_base_/default_runtime.py ] data_root data/steel/ metainfo dict( classes(crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches), palette[(220, 20, 20), (0, 0, 220), (0, 220, 220), (220, 0, 220), (220, 220, 0), (20, 220, 0)] ) train_dataloader dict( batch_size4, num_workers2, datasetdict( data_rootdata_root, metainfometainfo, ann_fileannotations/instances_train.json, data_prefixdict(imgimages/train/) ) ) val_dataloader dict( batch_size4, num_workers2, datasetdict( data_rootdata_root, metainfometainfo, ann_fileannotations/instances_val.json, data_prefixdict(imgimages/val/) ) ) test_dataloader val_dataloader val_evaluator dict( typeCocoMetric, ann_filedata_root annotations/instances_val.json, metricbbox ) test_evaluator val_evaluator model dict( roi_headdict( bbox_headdict(num_classes6) ) ) load_from checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth逐段说下这几处改动的含义metainfo里定义的classes必须和转换脚本里的label_map完全一致顺序都不能乱。palette是每个类别在可视化时的颜色3.0里不写也能跑但写上有助于区分结果。train_dataloader和val_dataloader里的ann_file、data_prefix要仔细对。data_root是统一的前缀ann_file相对data_root是annotations/instances_train.json图片路径相对data_root是images/train/。路径写错最常见的报错就是FileNotFoundError而且经常是训练跑了一会儿才报。model里的num_classes6是整份配置里最容易漏改的一处。Faster R-CNN 的改动位置在roi_head.bbox_head如果你是RetinaNet就是bbox_head.num_classesMask R-CNN还得加一行roi_head.mask_head.num_classes。漏了的话会报一个很显眼的错The num_classes (80) in Shared2FCBBoxHead does not match the classes (6) in metainfo看到这个就知道去哪改了。3.3 预训练权重与加载方式load_from指定的是预训练权重路径。用COCO预训练权重做初始值对小数据集效果提升非常明显相当于模型已经知道“什么是边缘、什么是纹理”只需要在你的数据集上微调最后几层就行。我把权重文件提前下载好放到checkpoints/目录然后在load_from里写本地路径。如果你不想用预训练直接删掉这一行就行但效果会明显变差尤其是数据量只有几百张的时候。这里有个经验预训练权重的下载速度取决于网络状况如果经常断建议用支持断点续传的下载工具或者找镜像地址。不要在load_from里写一个很长的URL命令等它慢慢下浪费训练机时间。4. 开始训练跑通第一个epoch4.1 训练命令与日志监控环境、数据、配置都准备齐了终于可以启动训练了。在mmdetection源码根目录执行python tools/train.py configs/steel/faster-rcnn_r50_fpn_steel.py --work-dir work_dirs/steel --auto-scale-lr--work-dir指定训练日志和权重保存目录建议每个项目单独建一个避免多个实验混在一起。--auto-scale-lr是3.0里很实用的功能当你把batch size从默认值调小后学习率会自动按比例调整否则大学习率配小batch容易让loss直接飞成nan。启动后会在终端打印出模型结构、参数量、数据集统计信息。第一次跑建议盯着日志看前几个iteration重点关注loss_rpn_cls、loss_cls、loss_bbox这几个值。正常情况下loss会从几开始随着训练逐步下降如果一开始就是几个很大的数字后面很难收敛。训练日志会同步写到work_dirs/steel/下的.log文件里可以另开一个终端实时查看tail -f work_dirs/steel/20240201_120000.log日志里每一行都是类似这样的格式Epoch(train) [1][ 50/200] lr: 0.0010 eta: 1:50:00 time: 0.45 data_time: 0.08 memory: 2350 loss_rpn_cls: 0.2301 loss_cls: 0.5123 loss_bbox: 0.3124time是单次迭代耗时data_time是数据加载耗时。如果data_time占time的很大比例说明数据加载是瓶颈可以调大num_workers或者检查硬盘读取速度。4.2 显存不足与训练卡死的排查训练中最常见的问题就是显存爆掉。我用的显卡是24G显存batch_size4完全没问题但如果你的卡只有8G就需要做三件事一是调小batch_size比如从4改成2甚至1。二是开启混合精度训练在训练命令后面加--amp支持AMP的模型会有明显加速且显存占用降低。三是配置梯度累积在config里改optim_wrapper dict(accumulative_counts4)这个的意思是每4个batch累积一次梯度再更新参数等效于把batch size从2扩展成8显存却不会成倍增长。训练卡死不报错的情况也要留意。有一次我num_workers调成8数据加载直接卡住不动进程占用CPU但就是不跑。把num_workers调回2或4就好了。另外shuffle在验证集dataloader里要设成False不然每次验证结果不可复现。4.3 训练提速的小技巧如果你只是想快速验证流程有没有跑通有几个让训练跑快的小技巧先用小模型测试比如把backbone改成faster-rcnn_r18_fpn跑一个epoch只要十几分钟验证数据、配置没问题后再换大模型正式训练。数据增强不要一开始就上全套MMDetection默认的数据增强已经够用增加额外增强只会拖慢速度。把验证间隔调大一点比如每5个epoch验证一次能省出不少时间。在default_runtime.py里改default_hooks.checkpoint.interval和训练循环的val_interval。日志打印频率默认是50个iter一次不用改看趋势足够了。5. 评估与推理看模型效果到底行不行5.1 用test.py做指标评估训练完12个epoch之后模型权重保存在work_dirs/steel/epoch_12.pth。此时需要用测试集评估最终效果在mmdetection根目录执行python tools/test.py configs/steel/faster-rcnn_r50_fpn_steel.py work_dirs/steel/epoch_12.pth --eval bbox--eval bbox表示只评估检测框的指标。如果你的任务是分割模型改成--eval bbox segm就能同时评估分割指标。输出会给出一个表格IoU metric: bbox Average Precision (AP) [ IoU0.50:0.95 | area all | maxDets100 ] 0.421 Average Precision (AP) [ IoU0.50 | area all | maxDets100 ] 0.683 Average Precision (AP) [ IoU0.75 | area all | maxDets100 ] 0.462 Average Precision (AP) [ IoU0.50:0.95 | area small | maxDets100 ] 0.173 Average Precision (AP) [ IoU0.50:0.95 | areamedium | maxDets100 ] 0.451 Average Precision (AP) [ IoU0.50:0.95 | area large | maxDets100 ] 0.538钢材缺陷很多是细小的裂纹属于小目标范畴所以我会特别关注AP_small这一行。第一次跑出来的AP_small往往很低只有0.1几这时候不要慌这是小目标检测的正常现象。可以尝试把输入图片的尺寸调大比如把config里的img_scale从(1000, 600)调整成(1333, 800)小目标的特征会更明显。5.2 单图推理与结果可视化指标看着差不多之后用真实图片验证一下效果把模型跑在单张测试图上from mmdet.apis import init_detector, inference_detector config_file configs/steel/faster-rcnn_r50_fpn_steel.py checkpoint_file work_dirs/steel/epoch_12.pth # 初始化模型 model init_detector(config_file, checkpoint_file, devicecuda:0) # 推理单张图片 img_path data/steel/images/val/ISIC_0000001.jpg result inference_detector(model, img_path) # 可视化并保存 model.show_result(img_path, result, out_fileresult.jpg)在MMDetection3.0里show_result会自动从模型的metainfo读取类别名称和配色不需要手动传class_names这点比2.x方便很多。如果你在Jupyter Notebook里跑可以直接传入showTrue在notebook里显示图片。打开result.jpg之后重点关注几个方面有没有漏检、有没有误检框和目标的贴合程度怎么样。如果模型把背景当成缺陷大概率是训练数据里背景太杂如果某些缺陷类别普遍漏检大概率是这类样本量太少或者形态差异太大需要针对性补充数据。6. 高频报错与避坑速查6.1 高频报错对症速查表整个流程走下来下面这些报错是我自己踩过、或者帮同事排查时遇到的最高频的问题整理成一张表报错信息可能原因解决方案ModuleNotFoundError: No module named mmcv环境没装mmcv或装的是CPU版用mim install mmcv2.0.0重装AttributeError: NoneType object has no attribute xxx数据路径配错图片或标注没读到逐级检查data_root、ann_file、data_prefixAssertionError: The num_classes (80) in ...忘了改roi_head.bbox_head.num_classes按模型类型找到对应head把类别数改成你自己的RuntimeError: CUDA out of memorybatch_size太大或显存不足调小batch、开--amp、设置梯度累积FileNotFoundError: xxx.json标注文件路径和config不一致确认JSON文件确实存在检查路径拼接KeyError: xxx is not in the model registry模型组件未注册或config名写错检查_base_引用的模型文件是否存在ValueError: could not convert string to float: xxxXML里有异常字符或标注文件编码有问题检查对应XML文件内容统一UTF-8编码这里面最恶心的是第二种报错信息指不到具体位置提示NoneType但不说哪里为空。我当时的排查办法是在config里把data_root的每一层路径都手动ls一遍最终发现是images/train/目录名写成了train_images/一个字母之差浪费了我半小时。6.2 我踩过的那些隐蔽的坑除了上面能直接定位的报错还有一些坑是“不报错但结果不对”这类更危险。第一是验证集太小。我第一次只分了15张图做验证结果mAP每次评估波动非常大同一套权重连续评估两次都能差好几个点。后来把验证集扩到50张左右指标才稳定下来。第二是类别名大小写不统一。标注时有人用了Crazing有人用了crazing转换脚本里label_map只匹配小写结果一部分目标被静默丢弃。这类问题不报错只有统计每个类别的框数量时才能发现。第三是忘记设置验证评估指标。训练完之后跑tools/test.py没加--eval bbox程序只加载模型然后直接结束什么指标都不输出。这时候不是模型坏了是命令少了参数。第四是可视化时类别颜色看不出差异。palette里如果用了相近的颜色多类别同时出现时根本分不清谁是谁。我后来统一用红、绿、蓝、黄、紫、青这类高区分度颜色视觉效果一下就好了。写在最后整套流程跑下来我的一个明显感受是MMDetection 3.0 的配置体系虽然初看复杂但只要理解了“继承覆盖”这个核心思想自定义数据集反而比2.x更省事。最花时间的不是代码而是数据整理和版本对齐。建议第一次跑的时候不要把目标定在“我要上什么高大上的模型”先用Faster R-CNN或RetinaNet把全套流程跑顺一次比刷十个教程都管用。我自己的习惯是流程跑通之后马上把数据转换脚本和config模板存好下次换个数据集改改路径和类别就完事。这篇基本把我踩过的坑都写进去了如果你在实操中遇到别的问题欢迎留言交流。
延伸阅读

更多相关文章

2026/9/16 23:18:06

YuE2:AR-NAR混合架构的轻量高效文本生成模型

1. “YuE”不是拼写错误,而是当前AI生成领域一个正在快速演进的技术代号最近在Hugging Face Spaces、GitHub Trending和几个主流AI技术社区里,“YuE”这个词频繁出现在模型卡片、论文复现项目和推理服务部署文档中。它既不像传统Python库那样有清晰的PyP…

2026/9/16 23:18:06

多普勒雷达强度数据处理:ShowRadarData源码解析与PPI显示

简介:面向多普勒雷达强度数据的读取与可视化,压缩包内提供了一套VC工程,涵盖雷达回波样本数据(dat)、可执行程序、C源码、VS工程配置(dsp/dsw)以及调试辅助文件,文件总数为13个&…

2026/9/17 0:08:14

CI/CD流水线安全门禁实战:从漏洞扫描到自动化阻断

把DevSecOps比喻成给软件交付过程装一套自动安检系统,那道真正拦人的闸机就是安全门禁——扫描仪检测到违禁品,闸机必须锁死,否则前面装再多摄像头都是摆设。我见过太多团队上了SonarQube、接了Trivy,结果流水线里留了个“仅记录不…

2026/9/17 0:08:14

电动汽车充电智能调度:多目标优化与Matlab实现

1. 项目背景与核心价值去年参与某园区微电网项目时,我第一次深刻意识到电动汽车充电调度对电网负荷的冲击。晚上7点园区充电桩集中启动时,变压器负载率直接从40%飙升至85%,差点触发过载保护。这个经历让我开始关注如何通过智能调度实现"…

2026/9/17 0:03:13

Python+Django构建行政复议在线预约系统开发实践

1. 项目背景与核心价值行政复议在线预约系统是"互联网政务服务"背景下提升行政效率的重要工具。传统行政复议申请往往需要当事人亲自前往行政机关提交材料,耗时耗力且容易因材料不全反复跑动。这个Python实现的在线预约系统,本质上是通过技术手…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/17 0:03:13

WiFi密码安全测试:从原理到实战的字典暴力破解指南

1. 写在前面:我为什么要研究WiFi密码这件事先交代一下背景。我身边有不少朋友,家里的WiFi密码常年是"12345678"或者"88888888",问就是"好记"。直到有一次,隔壁邻居蹭网蹭到我家路由器后台都进不去&…

2026/9/17 0:03:13

redis-py服务控制与监控函数实战:从ping到slowlog的巡检指南

我用 redis-py 写了快五年的业务代码,坦白说,真正让我觉得这个客户端“像一个成熟工具箱”的,不是 get/set 那套基本操作,而是它那批专门做服务控制与状态监控的辅助函数。日常开发里,大家把redis.Redis(host..., deco…

2026/9/17 0:03:13

SpringBoot+Vue3实现中小企业设备管理系统开发实践

1. 项目概述与核心价值中小企业设备管理系统是制造业、服务业等领域的基础信息化工具。传统设备管理往往依赖Excel表格或纸质记录,存在数据孤岛、流程混乱、维护成本高等痛点。这套基于Java SpringBootVue3MyBatis的技术方案,通过前后端分离架构实现了设…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码