☰
YOLO自定义模型训练全流程:数据、参数、指标与部署实战
2026/10/26 12:45:42 网站建设 项目流程

做目标检测这行时间久了,会发现一个挺有意思的现象:很多人第一次接触 YOLO 系列,都是拿官方权重跑个图或者接个摄像头,看着框出来的人、车、狗,觉得"这东西真神"。但真正落到自己手上的活儿,情况立刻变了——产线上要认的是某种特定型号的零件,工地上要认的是某种特定颜色的安全帽和反光衣,农田里要认的是某种特定生长阶段的果实。这些目标在通用模型眼里要么根本不存在,要么类别对不上。这时候唯一的出路就是:拿自己的数据,训一个自定义模型。

这篇内容就是把"yolo训练自定义模型"这件事从零到一拆开讲。包括数据怎么收、怎么标、目录怎么组织、配置文件怎么写、每个超参背后的含义、指标怎么读、训练崩了怎么救、训练完怎么导出部署。适合两类人:一类是刚学完检测基础、第一次自己准备数据集的新手;另一类是已经能跑通流程、但模型效果总是差一口气、想搞清楚"为什么"的中级选手。我会把踩过的坑和参数背后的算术都摆出来,能直接抄的部分我会给具体数值和命令。

1. 训自定义模型这件事,先想清楚再动手

动手之前最浪费时间的不是训练,而是方向错了还硬训。我见过太多次这样的情况:花了三天标了两千张图,训完发现 mAP 挺好看,实际场景一跑全是误检,最后回头一看是数据集本身的问题。所以在敲第一条训练命令之前,有几个判断必须先做完。

1.1 先确认通用模型到底差在哪

不是所有需求都需要训自定义模型。如果你的目标属于常见的八十类之一(人、车、瓶子、椅子这类),只是场景比较特殊,比如俯拍、逆光、远距离,那优先考虑的是微调而不是重训,甚至有时候只调推理端的置信度阈值和输入分辨率就能救回来。真正需要自定义模型的情况通常是三类:

第一类是类别不存在。你要检测的是某种特定工业零件、某种特定品级的农产品、某种特定的手势或状态,通用模型压根没有这个类别。

第二类是类别粒度不够。通用模型有"人"这一大类,但你要区分的是"戴安全帽的人"和"没戴安全帽的人";通用模型有"车",但你要分轿车、SUV、皮卡、工程车。这种细粒度需求通用模型给不了。

第三类是场景分布差异极大。比如红外图像、X 光片、显微镜下的细胞、夜间监控,这些图像的纹理和色彩统计与自然图像差别很大,通用权重的特征提取器基本处于"失效"状态,迁移过来的收益有限,需要更多轮次的微调甚至部分层重训。

判断清楚属于哪一类,直接决定了后面的策略:第一类、第二类走常规微调即可,几百到几千张图就够;第三类要考虑图像预处理(直方图均衡、伪彩映射)和更激进的数据增强,样本量要求也更高。

1.2 别从零训,迁移学习是默认选项

有人会问:既然要自定义,为什么不从随机初始化开始训?原因很实在——数据量不够。从零训一个检测网络,公开研究的经验值是需要十万量级以上的标注框才能收敛到可用水平。个人或小团队手上通常只有几百到几千张图,从零训的结果一定是严重过拟合,训练集 loss 掉得飞快,验证集一塌糊涂。

迁移学习的逻辑是:主干网络在大型通用数据集上学到的边缘、纹理、形状特征,在绝大多数视觉任务里都是通用的。你要训的只是"把这些通用特征组合成我要的类别"这一层映射。所以标准做法是加载预训练权重(yolov8s.pt、yolov5s.pt这类),然后在自己数据上继续训。这样几百张图、几十轮就能出效果。

这里有个细节值得注意:加载预训练权重时,检测头(head)的最后输出层会因为类别数变化而随机初始化,这是正常的,框架会自动处理。反而是主干和 neck 部分的权重被完整继承。所以你会观察到训练前几轮 loss 掉得特别快,那就是检测头在快速适配新类别。

1.3 版本与规模怎么选

版本选择上没有绝对答案,但有几个务实的判断维度。

从工程成熟度看,v5 系列的生态最完整,导出、量化、各种部署端的踩坑记录最多,遇到问题容易搜到答案;v8 之后改成了 anchor-free 结构,配置更简洁,训练脚本一体化程度高,yolo一条命令搞定训练验证导出;更新的版本在同精度下参数更少,但周边资料相对少一些。

从模型规模看,n/s/m/l/x 这五档(不同版本命名略有差异)本质上是宽度和深度的缩放。我的经验是:先用最小的那一档跑通全流程,确认数据没问题、标签没错、指标能涨,再换大模型。直接用最大档开始训的风险是,一旦数据有问题,你会花几小时甚至一天才等到一个糟糕的结果,而小模型二十分钟就能告诉你"这数据不行"。

具体到选型,可以按下面的思路走:

场景推荐规模输入尺寸说明
边缘设备、实时性优先n 或 s416~640帧率优先,精度靠数据和增强补
服务器推理、精度均衡m640~800大多数工业场景的甜点区
离线批量处理、精度优先l 或 x800~1280速度不敏感时用来榨精度
小目标密集场景m 或 l1024 以上单纯换大模型不如提分辨率

最后一行值得展开说:小目标检测效果差,换更大模型往往不如提高输入分辨率有效。因为小目标的问题本质是"像素太少,特征在下采样过程中被抹掉了",提高输入尺寸能直接增加目标占用的像素数,而换大模型只是增加了容量,不解决信息丢失。

2. 数据这一关,决定模型的天花板

训模型这行有句话:模型结构决定下限,数据决定上限。我自己的体会是,同样的模型配置,数据集质量从"凑合"提升到"规范",mAP 能差十几个点,而且这个差距是后面怎么调参都补不回来的。所以这一章我会讲得细一些。

2.1 采集:数量不是第一位,"像"才是

新手最容易犯的错是——从网上爬一堆图,标完就训。结果模型在测试集上表现不错,一上真实场景就崩。原因是采集的数据和实际部署时的数据分布不一致。

正确的采集原则是:训练集的图像分布要尽可能覆盖部署时会遇到的全部情况。具体要覆盖的维度包括:

  • 光照:白天、黄昏、夜间、逆光、强曝光、阴影区
  • 视角:正视、俯视、侧视、大角度倾斜
  • 距离/尺度:目标在画面中占比从 5% 到 80% 都要有
  • 背景:不同场地、不同材质背景、有干扰物和无干扰物
  • 遮挡:完整目标、部分遮挡、严重遮挡
  • 成像设备:如果是多台相机或不同型号设备,都要采

数量上给个参考区间:单一类别、场景相对固定(比如固定工位的质检),每类 300~500 个实例通常能看到明显效果;场景开放、变化大(比如户外、多场地),每类至少1000~2000 个实例,而且实例数比图片数更重要——一张图里有 10 个目标,抵得上十张各有一个目标的图。

这里有个实用技巧:先用小样本跑一轮,看模型错在哪,再针对性补数据。我通常的做法是先标 200 张,训 50 轮,把验证集上的误检和漏检图调出来看,会发现错误高度集中在某几类场景(比如都是夜间、都是远距离)。然后专门去补这几类场景的数据,比盲目堆数量效率高得多。

2.2 标注:一致性比精确度更重要

标注这块,我说几个从实际项目里总结出来的硬规矩。

第一,边界框要贴着目标的外接矩形,不要留太多空白,也不要切掉目标。留白会让模型学到"目标周围有背景"的错误关联,切掉则会让模型低估目标尺寸。工业质检这类任务里,如果目标是规则形状,贴边误差控制在 2~3 个像素内比较理想。

第二,同一类别只能有一种叫法。这是最容易出问题的地方,尤其是多人协作标注时。比如"安全帽"这个类,有人写helmet,有人写hat,有人写安全帽,最后数据集里会出现三个类,每个类的样本量都只有三分之一,训练效果直接腰斩。解决办法是提前写一份标注规范文档,把类别名、拼写、大小写、是否允许缩写全部固定下来,所有人严格照抄。

第三,遮挡目标怎么标要有统一策略。通常的做法是:可见部分超过 30%~50% 就标完整框(按推断的完整目标框),低于这个比例标为"忽略区域"或者干脆不标。关键是全体标注人员用同一个阈值,不能有人标有人不标。

第四,负样本(背景图)要专门准备一批。也就是画面里没有目标、但环境和正样本相似的图。这类图能显著降低误检率,比例大概占总数据的 5%~10%。我遇到过最典型的误检案例:模型把墙上的海报认成了目标物,加了三十张同场景无目标的背景图之后,这个误检基本消失。

2.3 目录结构与数据配置文件的写法

YOLO 系列的数据组织方式大同小异,标准结构长这样:

dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── ... │ ├── val/ │ └── test/ # 可选 ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ └── ... │ ├── val/ │ └── test/ └── data.yaml

关键点是images 和 labels 的目录结构必须严格镜像,images/train/0001.jpg对应labels/train/0001.txt,文件名(不含扩展名)必须完全一致。框架是靠路径推导找标签的,路径对不上就会静默地认为"这张图没有标签",然后你就得到一个"训练集有图但全是背景"的诡异结果。

标签文件的格式是每行一个目标:

<class_id> <x_center> <y_center> <width> <height>

后四个值都是归一化到 0~1 的相对坐标,相对于图片宽高。class_id从 0 开始。举个具体例子,一张 1280×720 的图里有个框,左上角 (320, 180),右下角 (640, 540),那么:

  • 中心点 x = (320+640)/2 = 480,归一化 480/1280 =0.375
  • 中心点 y = (180+540)/2 = 360,归一化 360/720 =0.5
  • 宽 = 640-320 = 320,归一化 320/1280 =0.25
  • 高 = 540-180 = 360,归一化 360/720 =0.5

最终这一行是:0 0.375 0.5 0.25 0.5。手工算容易错,所以绝对不要手写,用标注工具或脚本转换。

data.yaml的写法:

path: /home/user/dataset # 数据集根目录,绝对路径最稳 train: images/train val: images/val test: images/test # 可选 names: 0: helmet 1: vest 2: person

names的索引必须从 0 连续,不能跳号。如果是从别的格式转换过来,务必检查一遍类别映射有没有错位——类别错位是"训练正常但结果荒谬"的头号元凶。

2.4 划分比例与样本均衡

训练/验证/测试的常规比例是8:1:1 或 7:2:1。验证集不能太小,否则指标波动大,你没法判断一次训练到底是真的提升了还是运气好。1000 张图的话,验证集至少留 100 张,最好 150 张。

划分时有个必须注意的点:要按场景划分,不能随机打散。如果你的数据是从视频里抽帧得到的,相邻帧之间几乎一样,随机划分会导致验证集里全是训练集的"近亲",指标虚高,实际部署打脸。正确做法是按视频段、按采集批次、按场地来划分,保证验证集里的场景在训练集中没出现过。

样本不均衡的处理,我一般先用过采样:把稀有类别的图在训练列表里重复几次(不改文件,改一个 txt 列表或复制软链接)。如果差距特别大(比如 100:1),再考虑对稀有类做针对性的数据增强,比如多做一些旋转、缩放、拼接。不建议一上来就上 focal loss 之类的损失改造,调参成本高,收益不一定划算。

3. 配置文件与训练参数:把每个数字讲明白

参数这块是重灾区,很多教程只给一串命令让你抄,抄完出问题完全不知道怎么改。我把关键的几个拆开说,每个都讲清楚它在控制什么、什么情况下该动。

3.1 模型配置文件与类别数修改

如果用命令行加载预训练权重训练(推荐),框架会自动根据data.yaml里的类别数重建检测头,不需要手改模型结构文件。但如果你要改网络结构(换主干、改 neck、调 anchor),就需要编辑模型 yaml。

模型 yaml 里的nc字段是类别数,必须和data.yaml里的类别数一致。scales段控制 n/s/m/l/x 各档的深度、宽度、最大通道数,比如:

nc: 3 scales: s: [0.33, 0.50, 1024] m: [0.67, 0.75, 768]

三个数分别是 depth_multiple、width_multiple、max_channels。想做一个"比 s 稍大、比 m 稍小"的模型,就可以自己插一档,比如[0.50, 0.625, 1024]。这种微调在算力受限时挺有用。

anchor 这块要区分版本:老版本是 anchor-based,训练开始时会用 k-means 在自己的数据集上重新聚类 anchor,这个步骤是自动的,不用管;新版本是 anchor-free,没有这个概念,别去照抄老教程里的 anchor 调参。

3.2 关键超参:每一个都在控制什么

epochs(训练轮数):一轮 = 完整过一遍训练集。经验值:小数据集(<2000 张)300 轮左右,中等数据集 100~200 轮,大数据集 50~100 轮。但更靠谱的判断依据是看验证集指标是否已经平台化,通常设一个较大的值配合早停(patience 30~50)比较省事。

batch(批大小):受显存限制。粗略估算,640 输入、s 规模模型,单卡 8G 显存大概能跑 batch 8~16,16G 能跑 32~64。显存占用和 batch 基本线性,和输入尺寸是平方关系——把 imgsz 从 640 提到 1280,显存大约翻四倍。这个平方关系是很多人显存爆炸时想不通的地方。

imgsz(输入尺寸):训练和推理的尺寸要一致,否则精度会掉。默认 640。小目标多就往上提,但要注意上面的平方关系。另外输入尺寸最好是 32 的整数倍(下采样总倍数的要求),非 32 倍数的值会被自动 pad。

lr0(初始学习率):最关键的参数。经验公式是随 batch 线性缩放:lr = 0.01 × batch / 64。也就是说 batch=64 时用 0.01,batch=16 时用 0.0025,batch=8 时用 0.00125。很多人直接抄lr0=0.01配 batch=8,结果训练前期 loss 震荡甚至发散,就是这个原因。

lrf(最终学习率系数):学习率衰减到lr0 × lrf。默认 0.01,也就是最后阶段学习率是初始值的 1%。这个值别设太大,否则训练末期 loss 降不下去。

warmup_epochs:前几轮用极小的学习率线性爬升。作用是避免训练刚开始、检测头还是随机初始化时用大学习率把主干权重带崩。默认 3 轮左右,小数据集可以设 1~2 轮。

optimizer(优化器):SGD 泛化通常略好但收敛慢,AdamW 收敛快但需要更小的学习率(大概lr0=0.001量级)。小数据集、赶时间用 AdamW;追求最后零点几个点的精度用 SGD。

weight_decay(权重衰减):正则项,抑制过拟合。默认 0.0005。如果发现训练集 loss 降但验证集 loss 上升,可以适当加大到 0.001。

patience(早停耐心值):验证集指标连续多少轮没提升就停。设 30~50。这个参数能帮你省下大量无意义的训练时间。

workers(数据加载线程):CPU 核数允许的话设 8 或 16。如果 GPU 利用率上不去(nvidia-smi看显存占用高但 GPU 利用率低),八成是数据加载瓶颈,加 workers 能缓解。

3.3 数据增强参数:别一刀切地开满

增强参数是最容易被"抄默认值"带偏的地方。默认配置面向通用场景,你的场景需要自己判断。

mosaic(马赛克拼接):把四张图拼成一张。这是提升小目标检测和背景鲁棒性的利器,但要注意——如果你的目标总是完整出现、且尺寸较大,mosaic 会把目标切碎,反而有害。训练后期通常建议关掉(框架里叫close_mosaic,最后 10~15 轮关闭),让模型适应真实分布。

mixup:两张图按比例混合。能提升泛化,但会让标签变模糊(框内混入另一张图的内容),小数据集上容易过拟合到混合模式。默认关闭,需要时开 0.1~0.15。

hsv_h / hsv_s / hsv_v:色调、饱和度、明度的随机扰动。如果颜色是判别依据(比如靠颜色区分不同类别的零件),hsv_h 一定要调小甚至关掉,否则模型学不到颜色特征。如果颜色无关、只是光照变化大,hsv_v可以调到 0.5 左右。

degrees(旋转):默认 0。对于航拍、遥感、显微镜图像,目标朝向任意,可以开到 90 甚至 180。对于自然场景(人、车),开大了反而有害,因为"倒过来的人"在真实数据里不存在。

translate(平移)、scale(缩放):控制目标位置和尺寸扰动,默认 0.1 / 0.5 比较通用。小目标多的时候把 scale 调到 0.3~0.9,增加小尺寸样本。

flipud / fliplr(上下/左右翻转):左右翻转默认 0.5,适合绝大多数场景。但上下翻转要谨慎——文字、仪表、有明显上下语义的目标,翻转后会变成不合理样本。上下翻转默认 0,别乱开。

copy_paste:把实例抠出来贴到别的图上,对稀有类别特别有效。需要实例分割标签支持,纯检测任务用起来麻烦,可以跳过。

3.4 训练命令与断点续训

小模型先跑通的命令大致长这样:

yolo detect train \ data=/home/user/dataset/data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=640 \ batch=16 \ lr0=0.0025 \ lrf=0.01 \ patience=40 \ optimizer=AdamW \ workers=8 \ project=runs/train \ name=helmet_v1 \ amp=True

老版本(v5 系)的命令风格是:

python train.py --img 640 --batch 16 --epochs 200 \ --data data/helmet.yaml --weights yolov5s.pt --workers 8 \ --hyp data/hyps/hyp.custom.yaml

amp=True是混合精度训练,能省 30%~40% 显存,精度损失可以忽略,除非你用的是很老的卡,否则都建议开。

训练中断了要续训,关键是保留last.pt:

# v8 系 yolo detect train resume=True model=runs/train/helmet_v1/weights/last.pt # v5 系 python train.py --resume runs/train/helmet_v1/weights/last.pt

断点续训会恢复优化器状态和 epoch 计数,不是简单地重新加载权重。另外实际项目中我习惯把project和name按日期和实验内容命名,比如20240612_helmet_v1_imgsz800,跑十几次实验之后你会感谢自己。

4. 指标怎么读,什么时候该停

训练跑起来只是开始,真正考验判断力的是看结果。这一章讲怎么从曲线和指标里读出"下一步该干什么"。

4.1 损失曲线的三种典型形态

健康形态:训练 loss 单调下降并逐渐趋缓,验证 loss 跟着下降,最后在一个小范围内波动。两条线之间始终有一定间距(泛化间隙),这个间距是正常的。

过拟合形态:训练 loss 一路下探接近 0,验证 loss 在某个点之后掉头向上。看到这个形态,先做三件事:加增强(mosaic、mixup、scale)、加 weight_decay、减少模型规模或减轮数。数据量太少的话,最有效的还是补数据。

欠拟合形态:两条线都降得很慢,最后停在一个较高的值。可能原因:学习率太小、轮数不够、模型容量不足、标签有系统性错误。这时候先确认标签——用可视化工具把标签画到图上抽查 20 张,我遇到过训练不动的案例最后发现是坐标没归一化。

还有一种特殊形态:loss 剧烈震荡或者 NaN。这通常是学习率太大、batch 太小配了太大的 lr0、或者数据里有异常值(比如宽高为 0 的框)。先降 lr0 一个数量级试试,不行再查数据。

4.2 mAP、Precision、Recall 到底该盯哪个

  • Precision(精确率):报出来的框里有多少是对的。低 Precision = 误检多。
  • Recall(召回率):真实目标里有多少被找出来了。低 Recall = 漏检多。
  • mAP50:IoU 阈值 0.5 时的平均精度,衡量"框得大致对"。
  • mAP50-95:IoU 从 0.5 到 0.95 每隔 0.05 算一次取平均,衡量"框得准不准"。这个指标对定位精度要求高。

盯哪个取决于业务。质检类、抓拍类通常要求高 Precision(误报成本高),安防、缺陷检测通常要求高 Recall(漏报成本高)。这两者在固定的置信度阈值下是跷跷板,提升一个往往牺牲另一个。

我在项目里的判断顺序是:先看 mAP50-95 的整体趋势判断模型有没有在学,再看 Recall 是否够用,最后看 Precision。如果 Recall 上不去,八成是数据覆盖不够或者置信度阈值问题;如果 Precision 上不去,先查背景图够不够、有没有标错的负样本。

顺便说一句,mAP50 到 0.9 以上但 mAP50-95 只有 0.4 的情况非常常见,这通常说明模型能找到目标但框不准。如果是大目标,可能是标注框本身就不够贴合;如果是小目标,多半是分辨率不够。

4.3 验证集可视化:比看数字有用十倍

数字看趋势,看图找原因。训练完我必做的一件事是把验证集的预测结果画出来,按置信度从高到低排,重点看三类:

  1. 高置信度的误检:模型很确信但错了的,这类最能暴露数据问题。常见的坑是背景里有相似纹理、或者某个类别的定义有歧义。
  2. 低置信度的漏检:真目标但置信度只有 0.1~0.3 的,通常出现在遮挡、小目标、低对比度的情况下,说明这类样本训练不足。
  3. 框位置偏移的:框住了但偏,看是系统性偏移(标注习惯问题)还是随机偏移(回归能力不足)。

这一步能省下大量盲目调参的时间。我有个项目,mAP 卡在 0.6 上不去,调了两天参数没效果,把验证图拉出来一看,模型把相邻的两个同类目标合并成一个框了——原因是数据里存在大量密集目标但标注时把紧挨着的两个标成了一个。改标注策略、补标了 200 张密集图,mAP 直接上到 0.78。

4.4 学习率与训练的配合节奏

学习率不是设了就不管。如果你用固定学习率(不推荐),很可能出现前期学得太慢、后期在最优点附近反复横跳。标准做法是余弦退火或线性衰减,让学习率从 lr0 平滑降到 lr0×lrf。

这里有个实战技巧:如果你做的是微调已有模型(比如在自己的数据上继续训一个已经很接近的模型),初始学习率要比从头训小得多,大概 1e-4 到 1e-5 量级,否则会把已经学好的特征冲掉。我见过有人拿一个训好的模型做增量学习,lr0 还是 0.01,结果训完精度还不如原来的,就是这个问题。

5. 常见问题和排查实录

这一章是我这些年攒下来的问题清单,基本覆盖了新手到中级选手会撞的绝大部分坑。遇到问题时建议按顺序排查,因为很多现象是连锁的。

5.1 训练跑起来但完全不收敛

表现是 loss 在最初几轮之后就基本不动了,或者小幅震荡。排查顺序:

第一步,确认标签对不对。写个十行脚本,把标签画回原图上,抽查 20 张覆盖不同类别和场景的图。重点看:框的位置对不对、类别 id 对不对、有没有出现坐标为 0 或者超过 1 的异常值。这一步能解决 70% 的"不收敛"问题。

第二步,确认 data.yaml 的路径。用绝对路径。相对路径在不同工作目录下执行时的解析结果不一样,很容易出现"训练正常但实际加载的是空数据集"——表现就是 loss 一开始就恒定不变,因为全是背景。

第三步,确认类别数一致。data.yaml里的 names 数量要和标签里的最大 class_id 对得上。如果标签里有 class_id=5 但 names 只定义了 3 个类,越界的标签会被丢弃或报错。

第四步,调学习率。先降到原来的 1/10 试一轮。如果 loss 开始下降,说明原来是学习率太大。

第五步,检查图片格式。有些图是 16 位深度、CMYK 色彩空间、或者带透明通道,读进来会出问题。统一转成 8 位 RGB 的 jpg/png。

5.2 指标很高但实际跑起来一塌糊涂

这是最让人心态崩的情况。原因通常有三个:

验证集泄漏:验证集和训练集来自同一段视频或同一批次,图像高度相似。解决办法是按采集批次/场景划分数据。

测试环境和训练环境差异大:训练用的是清晰白天图,部署是夜间摄像头。这个只能靠补数据解决,没有别的捷径。

推理参数没调:训练时用的imgsz=640,推理时传了原始 1920×1080 的图,框架内部做了缩放但 letterbox 策略如果没对齐,精度会掉。另外置信度阈值默认 0.25、NMS 的 IoU 默认 0.45,这两个值在部署时一定要根据实际场景重新调。

调阈值有个简单方法:在验证集上跑一遍推理,把置信度从 0.05 到 0.9 扫一遍,画 Precision-Recall 曲线,找到满足业务要求的那个点。比如要求 Precision ≥ 0.95,就在曲线上找到对应的阈值。

5.3 小目标总是漏检

小目标是检测任务的老大难。按性价比排序的处理方式:

手段成本效果说明
提高 imgsz中(显存)高640→1024 通常有显著提升
增加小目标样本高(标注)高针对性补数据最根本
关闭或减少 mosaic 关闭时机低中后期关闭让模型适应真实尺寸
增大 scale 增强范围低中scale 上调到 0.9
换更大模型中中不如提分辨率直接
切图推理(SAHI 思路)低高推理时把大图切块分别检测再合并

最后一条值得单独说:如果部署时图像分辨率很高(比如 4K 监控),而目标很小,训练时用 640、推理时切图往往比训练时硬上 1280 更划算。切图推理不改模型,只是在推理阶段把大图切成带重叠的小块分别检测,最后用 NMS 合并。代价是推理耗时增加,但精度提升很直接。

5.4 过拟合与欠拟合的具体处方

过拟合(训练集好、验证集差):

  • 加数据(最有效)
  • 增强拉满:mosaic=1.0、mixup=0.15、scale=0.9、translate=0.2
  • weight_decay 从 0.0005 提到 0.001
  • 用更小的模型规模
  • 加 dropout(改模型 yaml)
  • 早停

欠拟合(两边都差):

  • 加轮数
  • 加大模型
  • 提学习率(小心发散)
  • 检查标签质量
  • 减少过强的增强(增强太猛会让模型学不到东西,尤其是小数据集)

这里有个反直觉的点:小数据集上增强开太猛会导致欠拟合。因为每一轮看到的都是变形后的样本,模型很难学到稳定的特征。所以增强强度要和数据量匹配,几百张图的时候别把 mosaic、mixup、旋转全开满。

5.5 导出与部署的坑

训练完的best.pt不能直接扔到所有端上,通常要导出。以 ONNX 为例:

yolo export model=runs/train/helmet_v1/weights/best.pt \ format=onnx \ opset=12 \ simplify=True \ imgsz=640

几个必须注意的点:

opset 版本:不是越高越好。部署端的推理引擎对 opset 的支持有上限,选一个目标引擎支持的稳定版本,12 是比较通用的选择。

动态维度:默认导出的 batch 是固定的。如果需要变 batch,加dynamic=True,但有些推理引擎对动态维度的支持不好,会拖慢速度。

预处理对齐:导出的模型只包含网络前向,不包含 letterbox 预处理和 NMS 后处理。部署端必须自己实现这两步,而且要和训练时完全一致。letterbox 的填充值(通常是 114)、缩放比例、坐标还原方式,任何一处不一致都会让精度明显下降。这是部署环节最常见的精度损失来源。

量化:INT8 量化能提速 2~4 倍,但需要校准数据集,而且精度通常掉 1~3 个点 mAP。如果业务对精度敏感,先试 FP16,通常几乎无损。

5.6 常见问题速查表

现象最可能原因快速验证处理
loss 恒定不变数据路径错/标签空打印数据集长度改绝对路径,检查标签文件
loss 变 NaN学习率过大/异常框降 lr 重跑lr0 降 10 倍,过滤异常标签
只有背景类被预测类别 id 错位可视化标签修 data.yaml 的 names
mAP 卡在 0.3 左右标注框不贴合看验证集可视化重标或放宽 IoU 评估
某类召回极低该类样本太少统计各类实例数补数据或过采样
误检集中在某背景缺负样本看误检图加 5%~10% 背景图
训练慢、GPU 利用率低数据加载瓶颈nvidia-smi 看利用率加 workers,图预缩放
显存爆imgsz 太大算平方关系降 imgsz 或 batch,开 amp
导出后精度掉预处理不一致对比 pt 和 onnx 输出对齐 letterbox 和 NMS

6. 一些没写进文档的经验

最后分享几条我觉得挺值钱、但各类文档里基本不会提的细节。

第一条,先做一个"最小可用数据集"。不要一上来就追求几千张标注完美的数据。先挑 100~200 张覆盖主要场景的图,标完训 50 轮,看能不能出基本效果。如果能,说明流程通了,再往上堆数据;如果不能,问题一定在流程或标注上,这时候堆再多数据也是白堆。这个习惯帮我省下过无数次"标了三天发现白标"的时间。

第二条,把实验记录当回事。每次训练至少记下:数据集版本、模型规模、imgsz、batch、lr0、增强配置、最终 mAP50-95、以及一句"这次改了什么"。用个 txt 或者表格就行。训到第十几次实验的时候,你会发现自己完全不记得哪次用了什么参数,而往往最优解就藏在某次随手改的配置里。

第三条,类别定义要"可判"而不是"可描述"。定类别的时候问自己一句:标注员看这张图,能不能在 3 秒内确定它属于哪一类?如果不能,说明这个类别定义有问题,需要拆分或者合并。我见过把"轻微污渍"和"明显污渍"分成两类的项目,标注一致性极差,模型怎么训都上不去,最后合并成一类反而效果好了。

第四条,训练完之后一定要在"真实部署链路"上测一遍。不是验证集,是真正从摄像头/文件读图、走完预处理、推理、后处理、画框的完整流程。我踩过的最深的坑就是:验证集 mAP 0.85,上线后业务方说"一半都认不出来",最后发现是部署端的图像读取用了 BGR 而训练用的是 RGB,通道顺序反了。这种问题在验证集上永远暴露不出来。

第五条,别迷信"再训几轮就好了"。当验证指标连续 30 轮不动的时候,再训 200 轮也不会有变化。这时候该做的是回过头看数据、看可视化、看阈值,而不是加大 epochs。训练时间是最不值钱的成本,但也是最容易让人产生"我在努力"的错觉的东西。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询