1. 开篇:为什么选择MMYOLO,以及它能帮你解决什么问题?

你好,我是老张,一个在AI和计算机视觉领域摸爬滚打了十来年的工程师。这些年,我见过太多朋友和团队,想为自己的业务(比如生产线上的零件质检、果园里的果实计数、或者小区里的宠物识别)训练一个专属的目标检测模型,但往往在第一步就被劝退了。要么是觉得YOLO这类框架太复杂,配置起来一头雾水;要么是卡在数据标注、模型调参这些环节,感觉无从下手。

如果你也有类似的困扰,那今天这篇文章就是为你准备的。我们不谈那些高深的理论,就聊怎么动手干。我会带你用MMYOLO这个框架,走完从零开始构建一个自定义目标检测模型的完整流程。MMYOLO是OpenMMLab开源的一套YOLO算法工具箱,它的最大好处就是“统一”和“工程化”。什么意思呢?它把YOLOv5、YOLOv6、YOLOv7、YOLOv8等主流YOLO系列算法都集成在了一个框架下,用一套统一的配置和接口来管理。这意味着你学一次,就能玩转几乎所有YOLO模型,切换模型就像换件衣服一样简单,再也不用为每个算法单独搭建一套环境、学一套代码了。

更重要的是,MMYOLO提供了大量现成的、工业级的工具链,从数据准备、可视化分析、模型训练到最终部署,都有非常完善的脚本支持。这能帮你省下大量自己造轮子的时间,把精力真正聚焦在你的业务逻辑和数据上。无论你是刚入门的新手,还是想寻找更高效工程化路径的开发者,相信这篇全链路指南都能给你带来实实在在的帮助。我们这就开始吧。

2. 万事开头难:数据准备与标注的实战技巧

任何AI模型,数据都是地基。地基没打好,后面模型再高级也是空中楼阁。对于目标检测任务,数据准备的核心就两件事:收集图片和标注框。听起来简单,但里面门道不少。

2.1 构建你的专属数据集目录

首先,你得有个清晰的数据存放结构。MMYOLO推荐使用类似COCO的格式,但前期我们可以更灵活。我建议你先建立一个这样的文件夹结构,这会让你后续的所有操作都井井有条:

./data/my_project/
├── images/               # 存放所有原始图片
│   ├── img_001.jpg
│   ├── img_002.png
│   └── ...
└── labels/               # 存放标注文件(初期用labelme的.json格式)
    ├── img_001.json
    ├── img_002.json
    └── ...

my_project换成你的项目名,比如product_defect或者street_cat。把所有原始图片都扔进images文件夹。注意图片格式尽量统一(如.jpg或.png),避免一些冷门格式可能带来的读取问题。

2.2 告别纯手标:用“预标注”大幅提升效率

纯手动标注几百张图片可能还能忍受,但面对成千上万张图片时,那绝对是体力活。这里我分享一个能提升数倍效率的技巧:使用训练好的通用模型进行“预标注”。

MMYOLO自带了一个非常实用的脚本 demo/image_demo.py。你可以先用一个在COCO等大型数据集上预训练好的模型(比如YOLOv5-s),对你的图片进行一轮初步推理。虽然这个通用模型不认识你的具体类别(比如“焊接瑕疵”),但它能检测出“物体”。你可以把这些检测框作为初始结果保存下来。

具体操作是,你先准备好一个预训练模型,然后运行:

python demo/image_demo.py ${你的图片文件夹路径} \
  ${模型配置文件,如 configs/yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py} \
  ${预训练权重路径} \
  --out-dir ${输出文件夹路径}

运行后,脚本会生成带预测框的图片。虽然类别不对,但框的位置可以给你一个很好的参考。接下来,你再用标注工具(比如我们马上要讲的LabelMe)打开这些图片和对应的预测结果,你只需要做两件事:1. 把错误的框删掉;2. 把框的类别改成你定义的(比如从“person”改成“defect”)。这比从零开始画框要快太多了,我实测下来,效率至少能提升3-5倍。

2.3 手把手用LabelMe完成精细标注

预标注之后,我们进入精细调整和补充标注阶段。这里我强烈推荐LabelMe,它开源、免费、跨平台,而且生成的JSON格式能被MMYOLO完美转换。

安装LabelMe非常简单,用conda创建一个独立环境,避免污染你的主环境:

conda create -n labelme python=3.8
conda activate labelme
pip install labelme==5.1.1

启动LabelMe时,我习惯用一些参数让标注过程更流畅:

labelme ./data/my_project/images \
  --output ./data/my_project/labels \
  --autosave \
  --nodata

解释一下这几个参数:

  • --output:指定标注文件(.json)的保存路径。如果这个路径下已经有某个图片的标注文件,LabelMe会直接加载,方便你接着上次的工作继续。
  • --autosave:强烈建议加上。开启后,每标注完一个目标,文件会自动保存,你再也不用担心忘点保存然后前功尽弃了。
  • --nodata:这个参数至关重要。默认情况下,LabelMe会把图片的base64编码也存入json文件,导致单个标注文件非常大。加上--nodata后,json里只存标注信息,文件大小会缩小几十甚至上百倍,后续处理和传输都方便很多。

标注时,有几点经验之谈:

  1. 框要贴得紧:尽量让边界框紧贴目标边缘,但也不要太紧以至于切掉目标的一部分。
  2. 类别名要统一:比如你定义类别叫“cat”,那所有猫的标注都叫“cat”,不要一会儿“cat”一会儿“kitty”。
  3. 困难样本和漏标:对于非常模糊、遮挡严重的目标,也要尽力标出来,这是模型学习的难点。预标注漏掉的小目标,要手动补上。

3. 数据格式转换与划分:为训练做好准备

标注好的LabelMe JSON文件还不能直接用于训练,我们需要把它转换成MMYOLO(或者说COCO)能识别的格式。

3.1 一键转换:从LabelMe到COCO

MMYOLO贴心地提供了转换脚本,一行命令就能搞定:

python tools/dataset_converters/labelme2coco.py \
  --img-dir ./data/my_project/images \
  --labels-dir ./data/my_project/labels \
  --out ./data/my_project/annotations/annotations_all.json

这条命令会读取images下的所有图片和labels下对应的json文件,生成一个统一的COCO格式的标注文件annotations_all.json。同时,它还会自动生成一个class_with_id.txt文件,里面记录了你的类别名称和对应的ID,比如:

0 cat
1 dog

踩坑提醒:如果你的数据集有多个类别,建议先手动创建好这个class_with_id.txt文件,并在转换时通过--class-id-txt参数指定。这样可以确保类别ID的稳定,避免自动生成时顺序错乱。

3.2 可视化检查:千万别跳过这一步!

转换完成后,千万别急着往下走。一定要用可视化工具检查一下转换是否正确。这是避免“垃圾进,垃圾出”的关键一步。

python tools/analysis_tools/browse_coco_json.py \
  --img-dir ./data/my_project/images \
  --ann-file ./data/my_project/annotations/annotations_all.json

运行后,会弹出一个窗口,随机显示图片和其上的标注框。你需要仔细看看:

  • 框的位置对不对?有没有偏移?
  • 类别标签对不对?
  • 有没有漏标的目标在JSON里却出现了?(这说明标注文件可能有问题) 花十分钟做这个检查,可能为你节省后面几天调试模型的时间。

3.3 科学划分训练集、验证集和测试集

接下来,我们需要把全部数据划分成训练集、验证集和测试集。验证集用于在训练过程中评估模型,防止过拟合;测试集用于最终评估模型的真实泛化能力,在整个训练过程中模型“没见过”这些数据。

MMYOLO的划分脚本同样强大:

python tools/misc/coco_split.py \
  --json ./data/my_project/annotations/annotations_all.json \
  --out-dir ./data/my_project/annotations \
  --ratios 0.7 0.15 0.15 \
  --shuffle \
  --seed 42
  • --ratios:这里我设置了0.7, 0.15, 0.15,意味着70%的数据用于训练,15%用于验证,15%用于测试。如果你的数据量非常大(比如10万张),测试集比例可以再小一点(如5%)。如果数据量很少(比如只有几百张),可以考虑只划分训练集和验证集(--ratios 0.8 0.2),用交叉验证等方法。
  • --shuffle:一定要打乱数据,避免数据顺序带来的偏差(比如前70%都是A场景,后30%都是B场景)。
  • --seed:设置一个随机种子(比如42),这样每次划分的结果都是一致的,便于复现实验。

执行完后,你的annotations文件夹里会多出train.json, val.json, test.json(如果按两份划分就是trainval.json和test.json)。至此,数据部分的“脏活累活”就基本完成了,你的数据集已经是一个MMYOLO-ready的标准化格式了。

4. 模型配置与训练:让你的数据“跑”起来

数据准备好了,现在我们来“喂养”模型。这是最核心也最让人兴奋的环节。

4.1 创建你的专属配置文件

MMYOLO采用基于Python的配置文件系统,非常灵活。我们不需要从头写,而是“继承”并修改一个基线配置。以最常用的YOLOv5-s模型为例,我们在configs/目录下新建一个文件夹custom_dataset/,然后创建我们的配置文件,比如叫yolov5_s_1xb32-100e_myproject.py。

_base_ = '../yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py' # 继承基线配置

# 以下是需要根据自己情况修改的核心参数
max_epochs = 100  # 训练总轮次,小数据集可以多训几轮
data_root = './data/my_project/'  # 你的数据集根目录
work_dir = './work_dirs/yolov5_s_100e_myproject'  # 训练日志和模型权重保存路径

# 加载预训练权重,这是提升收敛速度和精度的关键!
load_from = 'https://download.openmmlab.com/mmyolo/v0/yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco/yolov5_s-v61_syncbn_fast_8xb16-300e_coco_20220918_084700-86e02187.pth'

# 根据你的GPU显存调整batch size。原配置是8卡x16=128,单卡32可能太大。
# 如果训练时爆显存(OOM),逐步调小这个值(如16, 8, 4)。
train_batch_size_per_gpu = 16
train_num_workers = 4  # 数据加载线程数,建议设为GPU数量的4倍

# 学习率需要随batch size调整。原base_lr是针对batch_size=128设置的。
# 公式:你的学习率 = 原始学习率 * (你的batch_size / 原始batch_size)
base_lr = _base_.base_lr * (train_batch_size_per_gpu / 128.0)

# !!!最重要的部分:定义你的数据!!!
class_name = ('cat', 'dog')  # 你的类别,必须和class_with_id.txt里一致
num_classes = len(class_name)
metainfo = dict(
    classes=class_name,
    palette=[(220, 20, 60), (119, 11, 32)]  # 每个类别可视化时的颜色,随便设,别重复就行
)

# 模型结构调整:告诉检测头你的类别数
model = dict(
    bbox_head=dict(
        head_module=dict(num_classes=num_classes),
        # loss_cls的权重可以根据类别数调整,这是一个常用技巧
        loss_cls=dict(loss_weight=0.5 * (num_classes / 80 * 3 / _base_.num_det_layers)),
    )
)

# 数据加载配置
train_dataloader = dict(
    batch_size=train_batch_size_per_gpu,
    num_workers=train_num_workers,
    dataset=dict(
        _delete_=True,  # 完全覆盖基类中的dataset配置
        type='RepeatDataset',  # 如果数据量很少(<1000张),可以用这个重复数据
        times=5,  # 重复5次,相当于数据扩增了5倍
        dataset=dict(
            type=_base_.dataset_type,
            data_root=data_root,
            metainfo=metainfo,  # 注入类别信息
            ann_file='annotations/train.json',  # 训练标注文件
            data_prefix=dict(img='images/'),
            filter_cfg=dict(filter_empty_gt=False, min_size=32), # 不过滤空标签,过滤小目标
            pipeline=_base_.train_pipeline,
        ),
    ),
)

# 验证和测试配置
val_dataloader = dict(
    dataset=dict(
        metainfo=metainfo,
        data_root=data_root,
        ann_file='annotations/val.json',
        data_prefix=dict(img='images/'),
    )
)
test_dataloader = val_dataloader  # 测试集配置通常和验证集一样

val_evaluator = dict(ann_file=data_root + 'annotations/val.json')
test_evaluator = dict(ann_file=data_root + 'annotations/test.json')

# 优化器配置
optim_wrapper = dict(optimizer=dict(lr=base_lr))

# 训练策略钩子
default_hooks = dict(
    checkpoint=dict(
        type='CheckpointHook',
        interval=5,  # 每5个epoch保存一次权重
        max_keep_ckpts=3,  # 只保留最新的3个权重文件,节省空间
        save_best='auto',  # 自动根据主要评估指标(如mAP)保存最佳模型
    ),
    logger=dict(type='LoggerHook', interval=50),  # 每50个iteration打印一次日志
)

# 训练周期配置
train_cfg = dict(
    max_epochs=max_epochs,
    val_begin=20,  # 前20个epoch不验证,因为初期模型太差,验证意义不大
    val_interval=5,  # 每5个epoch验证一次
)

这个配置文件看起来有点长,但核心就是改了几个地方:数据路径、类别、batch size和学习率。其他部分都继承了YOLOv5-s在COCO上的成熟配置,保证了训练稳定性。

4.2 启动训练与监控

配置好后,训练就是一行命令的事:

python tools/train.py configs/custom_dataset/yolov5_s_1xb32-100e_myproject.py

训练开始后,你会在终端看到损失(loss)逐渐下降。更推荐的做法是使用MMYOLO集成的可视化工具,比如TensorBoard或WandB。你只需要在配置文件中加入或修改visualizer配置,就能在浏览器里实时看到损失曲线、学习率变化、验证集mAP等指标,非常直观。

训练过程中常见的坑与对策:

  • Loss不下降或NaN:最常见的原因是学习率(base_lr)设得太高了。尝试将其除以10再训练。也可能是数据有问题,回去检查一下标注。
  • 显存不足(OOM):降低train_batch_size_per_gpu,或者减小模型输入图像尺寸(在配置文件中修改train_pipeline里的Resize尺度)。
  • 验证集精度(mAP)一直很低:可能是模型复杂度不够(数据集大但模型小),尝试换用更大的模型(如YOLOv5-m或YOLOv5-l)。也可能是数据量太少,考虑用RepeatDataset或更多数据增强。

4.3 模型测试与性能分析

训练完成后,我们会在work_dir下找到保存的最佳模型(通常是best_coco_bbox_mAP_epoch_xx.pth)。用这个模型在测试集上跑一下,看看最终效果:

python tools/test.py \
  configs/custom_dataset/yolov5_s_1xb32-100e_myproject.py \
  ./work_dirs/yolov5_s_100e_myproject/best_coco_bbox_mAP_epoch_xx.pth

命令会输出详细的评估指标,其中最关键的是Average Precision (AP) @[ IoU=0.50:0.95 ],也就是常说的mAP,它综合反映了模型在不同IoU阈值下的检测精度。AP@0.5和AP@0.75也值得关注,分别代表宽松和严格的评估标准。

如果测试结果不理想,别灰心,这是调参的开始。你可以:

  1. 分析错误样本:MMYOLO的tools/analysis_tools/analyze_results.py可以帮你可视化测试集上预测错误(漏检、误检)的样本,直观地看到模型在哪里“犯了错”。
  2. 调整Anchor尺寸:对于自定义数据集,默认的Anchor(先验框)尺寸可能不匹配。使用tools/analysis_tools/optimize_anchors.py脚本,基于你的数据集重新聚类生成Anchor,有时能带来明显的精度提升。
  3. 数据增强:在配置文件的train_pipeline里增加或调整数据增强策略,如Mosaic, RandomAffine, MixUp等,能有效提升模型鲁棒性。对于小目标检测,谨慎使用大尺度的随机裁剪。

5. 模型部署:让算法真正落地应用

模型训练好了,精度也达标了,最后一步就是把它部署到实际的应用环境中,可能是服务器、边缘计算盒子,甚至是手机。MMYOLO主要推荐两种部署方式,我这里重点讲一下更通用、更强大的MMDeploy方案。

5.1 为什么需要部署框架?

你可能会问,我直接用PyTorch模型(.pth文件)加载推理不行吗?行,但在生产环境往往不够。PyTorch模型依赖完整的PyTorch环境,体积大,推理速度也未必最优。部署框架(如TensorRT, OpenVINO, ONNX Runtime)能对模型进行编译优化、算子融合、量化(FP16/INT8),在不损失太多精度的情况下,大幅提升推理速度、降低资源消耗。MMDeploy就是OpenMMLab统一的模型部署工具箱,支持将MMYOLO模型转换到十多种后端推理引擎。

5.2 使用Docker构建标准化部署环境

部署环境复杂,为了可复现性,强烈建议使用Docker。MMDeploy提供了现成的Dockerfile。

# 1. 克隆MMDeploy仓库
git clone -b dev-1.x https://github.com/open-mmlab/mmdeploy.git
cd mmdeploy

# 2. 构建Docker镜像(国内用户建议加上--build-arg USE_SRC_INSIDE=true使用国内源加速)
docker build docker/GPU/ -t mmdeploy:gpu --build-arg USE_SRC_INSIDE=true

这个过程会下载基础镜像并安装所有依赖,时间可能较长,请耐心等待。

5.3 模型转换:从PyTorch到TensorRT

镜像构建好后,我们启动容器,并将本地的MMYOLO项目目录挂载进去:

export MMYOLO_PATH=/path/to/your/mmyolo  # 替换成你的MMYOLO绝对路径
docker run --gpus all --name mmyolo-deploy -v ${MMYOLO_PATH}:/root/workspace/mmyolo -it mmdeploy:gpu /bin/bash

进入容器后,安装MMYOLO和必要的库,然后进行模型转换。这里以转换为TensorRT FP16模型为例:

cd /root/workspace/mmdeploy
python ./tools/deploy.py \
  ${MMYOLO_PATH}/configs/deploy/detection_tensorrt-fp16_dynamic-192x192-960x960.py \
  ${MMYOLO_PATH}/configs/custom_dataset/yolov5_s_1xb32-100e_myproject.py \
  ${MMYOLO_PATH}/work_dirs/yolov5_s_100e_myproject/best_coco_bbox_mAP_epoch_xx.pth \
  ${MMYOLO_PATH}/data/my_project/images/a_test_image.jpg \
  --work-dir ./work_dirs/my_project_trt_deploy \
  --device cuda:0 \
  --dump-info

这条命令做了几件事:1)加载你的PyTorch模型和配置;2)导出为ONNX中间格式;3)使用TensorRT编译优化,生成最终的.engine推理引擎文件。--dump-info会额外导出部署所需的配置文件(deploy.json, pipeline.json)。

5.4 性能验证与推理测试

转换完成后,我们必须在部署环境下验证其速度和精度:

python tools/test.py \
  ${MMYOLO_PATH}/configs/deploy/detection_tensorrt-fp16_dynamic-192x192-960x960.py \
  ${MMYOLO_PATH}/configs/custom_dataset/yolov5_s_1xb32-100e_myproject.py \
  --model ./work_dirs/my_project_trt_deploy/end2end.engine \
  --speed-test \
  --device cuda

输出会包含平均推理时间(如24.10 ms)和FPS,以及精度指标(mAP)。对比之前PyTorch的测试结果,你会发现TensorRT版本的速度通常有显著提升(可能快2-5倍),而精度(mAP)可能会有极小幅度的下降(例如0.005),这在工程上是完全可以接受的。

最后,用一张图片测试一下部署模型的效果:

python ${MMYOLO_PATH}/demo/deploy_demo.py \
  ${MMYOLO_PATH}/data/my_project/images/test.jpg \
  ${MMYOLO_PATH}/configs/custom_dataset/yolov5_s_1xb32-100e_myproject.py \
  ./work_dirs/my_project_trt_deploy/end2end.engine \
  --deploy-cfg ${MMYOLO_PATH}/configs/deploy/detection_tensorrt-fp16_dynamic-192x192-960x960.py \
  --out-dir ./output \
  --device cuda:0

如果一切顺利,你会在./output目录下看到一张画着预测框的图片。至此,你的自定义目标检测模型就完成了从数据到部署的完整闭环。

6. 进阶与避坑:一些实战中的经验之谈

走完上面的全流程,你已经成功搭建了一个可用的系统。但要想做得更好,这里还有一些我踩过坑后总结的经验。

关于小目标检测:如果你的目标物体在图片中占比非常小(比如航拍图像中的车辆),默认配置可能效果不佳。你可以尝试:1)增大模型输入分辨率(如从640x640提高到1280x1280),但这会大幅增加计算量和显存消耗;2)使用更专注于小目标检测的模型变体,或者修改FPN(特征金字塔)结构,增强浅层特征;3)在数据增强中减少大尺度的随机缩放和裁剪,避免小目标在增强过程中“消失”。

关于数据不平衡:如果你的数据中“猫”的图片有1000张,“狗”只有100张,模型会严重偏向“猫”。解决方法:1)在train_dataloader中使用ClassBalancedDataset等采样器;2)对少数类图片进行过采样或数据增强;3)在损失函数中为少数类设置更高的权重。

关于模型选择:MMYOLO集成了众多YOLO版本,新手常问哪个最好。我的建议是:从YOLOv5-s或YOLOv8-n开始。它们速度快、精度不错、社区资源丰富。如果精度不够,再逐步换用更大的模型(如YOLOv5-m/l, YOLOv8-m/l)。对于边缘设备,可以尝试更轻量的版本(如YOLOv5-n, YOLOv8-n)。切换模型在MMYOLO中极其简单,通常只需在配置文件中修改_base_指向另一个模型的配置文件,并调整一下load_from的预训练权重路径即可,大部分数据配置都是通用的。

关于持续迭代:模型上线不是终点。你需要建立一个数据闭环:收集模型在实际场景中的错误预测案例(难例),重新标注,加入到训练集中,重新训练模型。这个过程循环几次,模型的鲁棒性和精度会有质的飞跃。MMYOLO完善的工具链能让这个迭代过程非常顺畅。

这条路我走过很多遍,从最初的磕磕绊绊到现在的驾轻就熟,关键就在于理解每个环节的目的,并善用工具。希望这份详尽的指南能帮你避开我当年踩过的那些坑,更快地将你的AI想法落地成实实在在的应用。如果在实践过程中遇到具体问题,不妨去MMYOLO的GitHub仓库提个Issue,社区非常活跃,很多开发者都很乐意帮忙。祝你训练顺利!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐