开篇导读 很多人会用 YOLOv5 训练命令但指标一掉就只会加epoch、改lr、换增强。结果是参数改了一堆却说不清到底哪一步真正生效也无法复现实验收益。 这篇文章不讲玄学调参。我会按源码执行路径把 YOLOv5 从参数读取、权重加载、数据管线、损失计算到 best.pt 保存的关键机制讲透并给出可直接照做的优化与排错方法。 你将获得三样东西 1一张训练主链路图知道问题该去哪里查 2一份高杠杆改点清单知道优先改什么 3一套单变量实验顺序知道怎么证明改动有效。 YOLOv5训练源码精讲万字详细版 一、本文目标与阅读说明 ---------------------- 这篇文章的目标非常明确帮你从会用 YOLOv5 训练命令升级到能独立诊断问题、能系统性优化效果、能自信解释每一改动的原因。 很多人遇到效果不达标时第一反应是调参数——加大 batch、改学习率、换增强策略结果改了一圈说不清哪个有效指标升了不知道为什么升降了不知道为什么降。这是因为没有建立训练链路视角没有把 YOLOv5 训练系统当作一个整体来理解。 本文按 YOLOv5 源码的真实执行顺序组织内容从训练入口开始依次覆盖参数来源、预训练权重加载机制、冻结训练策略、输入尺寸约束、优化器与学习率、训练数据管线含增强与 cache、损失函数设计与调优、验证与模型保存逻辑最后给出排错清单和推荐实验顺序。 每个核心章节都会讲清楚机制是什么what、为什么这样设计why、什么情况下该改when以及怎么改、改了会怎样how。读完这篇你不仅能理解每个模块在训练链路中的位置还能独立完成一次有对照的优化实验。 二、训练主链路全解析先建全局地图再深入细节 -------------------------------------------- 在深入每个模块之前先把整条链路看清楚。YOLOv5 训练流程可以浓缩为以下十个关键节点 节点1参数解析。命令行参数 配置文件hyp.yaml合并产生最终生效的 opt 对象。这一步决定了一切后续行为的起点。 节点2模型构建。依据 cfg 文件yaml 格式的网络结构描述构建模型实例。如果提供了预训练权重则进入节点3。 节点3预训练权重加载。尝试加载 checkpoint按键名匹配 shape 匹配的交集逻辑赋值参数。匹配部分继承预训练权重未匹配部分随机初始化。 节点4冻结层处理。如果指定了 freeze 参数则将对应层的 requires_grad 设为 False使这些参数在反向传播时不更新。 节点5优化器与调度器初始化。包括 optimizerSGD / Adam / AdamW、lr_scheduler余弦退火或阶梯衰减、EMA指数滑动平均、AMP混合精度等可选组件。 节点6Dataset / Dataloader 构建。Dataset 负责加载图片、读取标签、执行在线数据增强Dataloader 负责多 worker 并行读取、打乱顺序、collate_fn 拼 batch。 节点7每轮训练epoch loop。前向传播得到预测张量 → build_targets 为 GT 分配正负样本 → compute_loss 计算 box/obj/cls 三类损失 → 反向传播更新参数 → EMA 更新影子权重。 节点8验证。每隔 N 个 epoch或每个 epoch跑一次 val将模型通常是 EMA 权重切换到 eval 模式跑推理并统计 P/R/mAP。 节点9模型保存。按 fitness 指标判断是否更新 best.pt同时保存 last.pt 供断点续训。 节点10断点续训resume。下次启动时从 last.pt 恢复 epoch 进度、optimizer 状态、scheduler 状态、EMA 影子权重。 理解这张图的价值在于任何训练异常loss 不降、指标抖动、发散等都可以先定位到具体节点再在该节点内部排查。例如 loss 发散 → 可能是 lr 过高或增强过强 → 对应节点5或节点6。建立了这张地图才能系统性地排错而不是盲目试参。 三、训练入口参数来源的完整梳理 -------------------------------- YOLOv5 训练入口的行为本质上是多来源参数合并 环境检查的过程。很多我改了参数但好像没生效的问题都出在这一步的理解不够清晰。 参数来源一共有三个层次按优先级从高到低排列 第一层命令行参数Command Line Arguments。用户在执行 python train.py 时传入的参数比如 --batch 16、--img 640、--epochs 300、--data coco128.yaml 等。这些参数会被解析到 opt 字典中。 第二层配置文件hyp.yaml。位于 data/hyps/hyp.scratch-low.yaml 或 hyp.detection.yaml 等路径。包含数据增强概率flip、mosaic、mixup、hsv、损失权重box、cls、obj、优化器参数lr0、lrf、momentum、weight_decay等。 第三层resume 快照opt.yaml。当使用 --resume 参数恢复训练时run/expN/opt.yaml 文件中保存的历史参数快照会覆盖当前命令行参数。这是很多人最容易踩的坑。 举一个典型场景你想在续训时把 batch 从 16 改成 32命令写成 python train.py --resume runs/exp12/weights/last.pt --batch 32。但你可能发现最终生效的 batch 仍然是 16。这是因为 resume 时opt.yaml 中的 batch 参数优先级更高会把你的 --batch 32 覆盖掉。 正确的做法有两个 方法一完全新开一个 run不使用 resume 参数只改 batch 参数。 方法二先手动编辑 opt.yaml 中的 batch 参数再执行 resume。 实战建议每次启动训练时在训练日志开头打印 opt 的关键字段batch、imgsz、lr0、weight_decay、epochs确认这些值和你预期的一致。养成这个习惯可以避免 70% 的参数不生效类问题。 环境检查部分也会执行一些辅助操作git diff 查看代码是否有未提交修改、依赖版本检查如 torch 版本、AMP 可用性检测cuDNN 是否支持 fp16等。如果你的环境比较干净且确定没问题这些检查在调试阶段可以临时注释掉以加快启动速度但生产环境建议保留。 四、预训练权重加载交集加载机制的深度理解 ------------------------------------------ 这是 YOLOv5 迁移学习能力的核心也是最容易被误用的模块。很多结构改动后的训练失败根因都在预训练权重加载这一步。 YOLOv5 的预训练权重加载机制本质上是一个集合交集操作。加载过程分三步 第一步加载 checkpoint。用 torch.load() 将 .pt 文件反序列化为字典。checkpoint 中通常包含model模型状态字典、optimizer优化器状态、training_results训练历史、epoch当前轮次等。 第二步构建当前模型。用 cfg 文件yaml 格式的模型结构描述实例化一个全新的模型。这个新模型有自己的参数字典state_dict keys和参数形状。 第三步取交集加载。遍历 checkpoint 中的每个参数键值对检查两个条件条件A键名在当前模型中是否存在条件B形状shape是否完全一致。只有两个条件同时满足才执行赋值。如果键名不存在或者 shape 不匹配该参数就被跳过以当前模型的初始化权重替代。 举个例子假设你在 YOLOv5m 的预训练权重基础上改了检测头的通道数比如把 3 个 anchor 改成 5 个。新模型中对应的 head 层参数 shape 发生了变化checkpoint 中的对应键虽然名字相同但 shape 不一致所以这些层不会被加载而是使用新初始化。匹配的层如 backbone 的大部分参数则完全继承了预训练权重。 这一机制的设计非常优雅它允许你在预训练权重的基础上做结构改动而不需要每次都从零训练。具体来说 如果你只改了检测头层head layer而 backbone 不变那么 backbone 的权重基本可以完全继承匹配率很高训练收敛会很快。 如果你改动了 backbone 的中间层匹配率会下降但只要改得不多预训练收益仍然可观。 如果你用 YOLOv5m 的权重去初始化 YOLOv5s 规模的模型匹配率会非常低因为通道数不一致几乎等同于从零训练。 实战操作中加载完成后你应该关注日志中的输出。YOLOv5 会在加载完成后打印类似这样的信息 Transfering params [%d/%d] (%.1f%%) ... loaded 这里的百分比就是交集加载的匹配率。如果匹配率低于 80%建议重新评估结构改动是否过大或者考虑使用更接近目标结构的预训练权重。 另外还有一个细节加载权重时YOLOv5 通常会先将 checkpoint 加载到 CPU再用 .to(device) 移到目标设备。这种两步策略比直接加载到 GPU 更稳定可以避免某些设备上因为 GPU 初始化顺序导致的问题。如果你在自定义推理代码中遇到权重加载报错可以检查是否遵循了这个CPU 反序列化 → 再迁移的原则。 五、冻结训练策略从原理到分阶段解冻实战 ---------------------------------------- 冻结训练Freeze Training是迁移学习中最稳定、最常用的技巧之一。它的核心原理很简单通过将指定层的 requires_grad 设为 False使这些参数在反向传播时不被更新。 freeze 策略的价值在于三个方面 第一小数据集场景下直接全量训练容易过拟合冻结 backbone 可以减少需要学习的参数规模降低过拟合风险。 第二大数据集跨任务迁移时backbone 提取的特征通常已经有通用性边缘、纹理、局部形状等不需要大幅调整冻结可以加快收敛。 第三显存优化冻结层的梯度不需要保存可以显著减少显存占用在显存受限时可以跑更大的 batch。 YOLOv5 中 freeze 参数的指定方式是通过命令行传入层索引或层名。例如 --freeze 0 10 表示冻结第 0 层到第 10 层具体对应 backbone 的前 11 层。冻结实现的核心代码逻辑类似于 for k, v in model.named_parameters(): if int(k.split(.)[1]) freeze_epochs: # 按层索引判断 v.requires_grad False 注意冻结和 BatchNorm 的关系需要额外关注。冻结层的 BatchNorm 统计量均值和方差是固定的来源于预训练阶段。如果你冻结了 backbone 但 BN 仍在训练模式下更新统计量可能会造成不一致。YOLOv5 中冻结操作通常也包含了对应 BN 层的 eval() 模式切换。 常见的 freeze 策略有以下几种适用场景各不相同 策略一全冻结full freeze。所有层都冻结只训练 head 层。适用场景数据集极小几百张、领域差异极大比如用 ImageNet 预训练权重去训一个医学影像检测。优点是收敛极快缺点是头部能力受限。 策略二部分冻结partial freeze。冻结 backbone 前 N 层解冻后 N 层和 head。例如冻结 0-9 层backbone 的前10层解冻 10 层以后和 head。适用场景小到中等数据集、领域有一定差异。这是 YOLOv5 的默认做法对大多数场景都比较稳妥。 策略三分阶段解冻gradual unfreezing。第一阶段冻结 backbone 只训 head第二阶段解冻 backbone 后几层较低学习率微调第三阶段全量解冻整体微调。适用场景中等规模数据集、想最大化预训练收益。这个策略被多个论文验证优于一次性全量训练但需要手动控制阶段切换。 策略四解冻后低学习率low-lr fine-tuning。不解冻全部层只以极低学习率通常是预训练 lr 的 1/10 到 1/100微调 backbone 后几层和 head。适用场景大数据集、领域接近的场景如用 COCO 预训练权重去训同类别的产品检测。 判断何时该解冻的经验法则 观察 head losscls/obj是否进入平台期连续 5 个 epoch 变化小于 1%同时训练集 loss 仍有下降空间。如果两者同时满足可以尝试解冻。 解冻时建议将学习率降低为原来的 1/10同时打开 warmup 避免参数大幅震荡。 六、输入尺寸为什么必须是32的倍数 ---------------------------------- 这是 YOLOv5 的高频面试题也是影响训练稳定性和推理一致性的真实工程约束。 先从下采样链路说起。YOLOv5 的 backbone 包含 5 次下采样操作每次 stride2 的卷积或池化操作都会将特征图尺寸减半。所以从输入图像到最终特征图经历了 2^5 32 倍的下采样。 对应三个检测头80x80、40x40、20x20 80x80 特征图对应输入图像中每个格子覆盖 8x8 像素区域640/808 40x40 特征图对应输入图像中每个格子覆盖 16x16 像素区域640/4016 20x20 特征图对应输入图像中每个格子覆盖 32x32 像素区域640/2032 这三个格子尺寸分别对应小目标、中目标、大目标的检测。 如果输入尺寸不是 32 的倍数会怎样假设你输入 608x608608/3219可以整除没问题但如果你输入 640x500500/3215.625不能整除代码通常会做两种处理 处理方式一自动 padding。将短边 pad 到最近的 32 倍数比如 500 pad 到 512变为 640x512。但这改变了图像内容的宽高比可能导致目标形变降低检测精度。 处理方式二报错或警告。有些配置下会要求严格对齐直接报错。 多尺度训练的场景下这个问题更复杂。YOLOv5 支持在训练时随机选择多个尺度比如 320-640 之间但每个采样的尺寸仍然要求是 stride 的倍数。如果选了一个 544544/3217可以但选了一个 576576/3218也可以这两个尺度产生的特征图网格不同分别是 18x18 和 17x17 在 stride32 下网络要能正确处理这种变化。 建议的工程实践 在数据预处理阶段将所有图像 resize 到统一的 32 倍数尺寸比如 640x640 或 416x416避免训练和推理时的隐式 padding 行为。 如果你需要处理任意长宽比的图像优先使用 letterbox 方式保持宽高比灰色填充到 32 倍数而不是简单的拉伸 resize。 七、优化器、学习率、EMA、AMP决定训练能不能稳住 ------------------------------------------------ 这四个组件共同决定了训练过程的稳定性和最终模型的泛化能力。下面逐一讲解每个模块的设计原理和调优思路。 7.1 优化器选择与 weight decay 的正确理解 YOLOv5 支持 SGD、Adam 和 AdamW 三种优化器默认使用 SGDmomentum0.937。为什么默认是 SGD 因为在大 batch 训练场景下SGD 的泛化性能通常优于 Adam。SGD 通过动量momentum累积历史梯度方向形成一个更稳定的更新方向适合大规模目标检测这种需要强泛化能力的任务。 Adam 和 AdamW 的优势在于自适应学习率每个参数有自己的 lr收敛更快但泛化能力有时不如 SGD。适合小 batch8-16、短训练周期几十个 epoch、或者数据量较小的场景。 weight decay简称 wd也叫 L2 正则化是优化器中一个容易被误用的参数。很多人直接照抄网上的 weight_decay0.0005却不理解这个数值在不同 batch 下代表什么意义。 weight decay 的实际正则强度和 batch size 以及有效 batch 数nbsnominal batch size有关。在 YOLOv5 中effective batch size batch × accumulation_steps而 weight decay 的影响会被这个有效 batch 缩放 effective_wd wd × (effective_batch / nbs) 例如如果你把 batch 从 16 改到 64扩大 4 倍其他参数不变那么有效正则强度就增强了约 4 倍。如果你的模型在 batch64 时出现过拟合可以适当降低 wd而不是改 lr。 7.2 学习率调度warmup 和余弦衰减 warmup预热是训练初期的一个关键阶段。在前 N 个 epoch 内学习率从极低值通常是 lr0 的 1/1000逐步上升到 lr0。这个设计的目的是 训练初期模型参数是随机初始化的或者从预训练权重继承的此时梯度方向很不稳定。如果直接用大学习率参数更新幅度过大可能直接跳到 Loss 的一个很差的位置甚至导致发散。warmup 让参数在初期用小步试探逐步建立稳定的梯度方向。 warmup 之后通常接余弦退火cosine annealing或者阶梯衰减step LR。余弦退火的优点是 lr 曲线平滑从 warmup 结束时的 lr0 缓慢下降到接近零让模型在训练末期有充足时间在 Loss 谷底找到最优解。阶梯衰减则是在特定 epoch 将 lr 降低一个比例比如降低到原来的 1/10适合你想精确控制训练曲线的场景。 调优建议如果你发现训练初期 loss 波动很大发散或者震荡可以增加 warmup epoch 数默认是 3 个。如果你发现训练后期 loss 平台后无法再下降可以尝试余弦退火替代阶梯衰减。 7.3 EMA稳定验证指标的利器 EMAExponential Moving Average指数滑动平均维护一套模型参数的影子副本。更新公式是 theta_ema decay × theta_ema (1 - decay) × theta_current 其中 decay 通常取 0.9999非常接近1。这意味着 EMA 权重在每次更新时只吸收当前权重 0.01% 的变化因此变化非常缓慢。 为什么 EMA 通常带来更好的泛化因为 EMA 本质上是一个时间平均操作。训练末期的参数往往在一个最优解附近波动直接取最后一个 checkpoint 可能恰好取到了波峰。EMA 通过平滑抵消了这种高频波动取到的是历史上多次更新的平均位置通常更接近真实的最优解。 在 YOLOv5 中验证时默认使用 EMA 权重进行推理。保存 best.pt 时也是 EMA 权重参与 fitness 计算。所以你最终拿到的 best.pt 实际上是 EMA 影子权重而非训练末期的即时权重。 7.4 AMP省显存提吞吐的正确姿势 AMPAutomatic Mixed Precision自动混合精度的核心是前向传播用 FP16 计算节省显存和加速梯度更新用 FP32保证精度不损失。 CUDA 的 tensor core 对 FP16 矩阵运算做了硬件加速FP16 的矩阵乘法比 FP32 快将近 8 倍取决于具体硬件。同时 FP16 的显存占用是 FP32 的一半可以跑更大的 batch。 YOLOv5 中的 AMP 通过 torch.cuda.amp.autocast 和 GradScaler 实现。autocast 自动决定哪些操作用 FP16、哪些用 FP32通常卷积用 FP16loss 计算和梯度更新用 FP32。GradScaler 负责在 loss scaling 阶段防止 FP16 下的下溢问题梯度值太小超出 FP16 表示范围。 需要注意的风险某些硬件配置老显卡、驱动版本过旧下 AMP 可能不稳定。如果你在训练中遇到 NaN loss先关闭 AMP 试试--half False。 八、数据管线从 Dataset 到 batch 的完整数据流 ----------------------------------------------- 训练数据管线是70%的玄学问题所在。理解这一部分的价值不仅在于排查问题更在于知道如何通过调整数据策略来提升模型效果。 8.1 Dataset 的职责 YOLOv5 的 Dataset 类通常在 utils/datasets.py 中负责以下职责 第一文件索引。遍历 images 目录建立图片路径列表。同时在 labels 目录下查找对应名称的 .txt 标签文件YOLO 格式class_id cx cy w h全部归一化到 0-1。 第二标签解析。读取 .txt 文件每行代表一个 GT 框。解析后存储为 [class_id, cx, cy, w, h] 的列表。 第三图像加载与预处理。读取图片文件做 resizeletterbox 或简单拉伸、通道顺序转换BGR→RGB、归一化/255.0。 第四在线数据增强。增强操作在这一步执行具体包括 Mosaic、MixUp、几何变换旋转、平移、缩放、翻转、颜色域增强HSV 调整、亮度、对比度等。 8.2 文件命名的常见坑 YOLO 数据集有一个隐式约定images/ 和 labels/ 目录下的文件名必须完全对应扩展名不同。如果文件名出现以下问题会导致找不到标签或找不到图片的报错 坑1文件名含有多余空格或隐藏字符。比如 img .jpgimg 和 .之间有空格或者文件名校验时看起来正常但实际含有多余字符。 坑2文件名含有多余点号。比如 img..jpg两个点某些路径解析逻辑会截取错误。 坑3大小写不匹配。Windows 系统文件名大小写不敏感但 Linux 服务器训练环境通常大小写敏感。images/ 下是 cat.jpglabels/ 下是 Cat.txt在服务器上就会找不到。 坑4图片和标签扩展名不统一。比如图片用 .JPG大写标签用 .txt小写解析逻辑可能无法匹配。 建议的工程实践写一个数据校验脚本在训练前检查所有图片和标签是否一一对应文件名是否符合规范。这比训练到一半才发现数据问题要高效得多。 8.3 cache 机制加速加载的双刃剑 YOLOv5 支持标签 cache 功能。当第一次加载数据集时程序会解析所有标签并将结果缓存到磁盘.cache 文件类似 labels_cache.cache.pkl。后续训练启动时直接从 cache 文件读取标签不再重新解析每个 .txt 文件可以大幅加速 Dataset 初始化。 这个机制的坑在于如果你修改了标签文件改坐标、改类别、新增标注但 cache 文件没有删除程序会继续读取旧的 cache导致你改了标注但指标没变化。 正确的做法是每次修改标签后手动删除对应的 .cache 文件。或者在 YOLOv5 中使用 --cache-images disk 参数将图片也缓存到内存或磁盘这样标签修改也必须同步更新 cache。 8.4 collate_fn 与 image index对齐 batch 的关键机制 Dataset 返回的是单张图片及其 GT 标签列表。collate_fn 的作用是把 N 个样本通常 N batch size合并成一个 batch 张量。 这个过程中最关键的一个细节是 image index图像索引。GT 标签在拼 batch 时会在每条 GT 记录前插入一个 image index表示该 GT 属于 batch 中的哪一张图片。 为什么要这样做因为 loss 计算时batch 内的 N 张图片是一起前向传播的输出是 N 张图合并的预测结果。计算 loss 时必须知道某条 GT 该和 batch 中哪一张图的预测结果进行匹配。如果缺少这个 image indexGT 和预测会错位导致 loss 计算完全错误。 这个细节非常适合作为面试表达素材我不仅知道 YOLO 标签格式是 class_id cx cy w h还知道 batch 拼接时为什么要加 image index 来做 GT 与预测的对齐。 九、数据增强从原理到实验顺序的全链路讲解 ------------------------------------------ 数据增强是 YOLOv5 训练中最强大也最容易被误用的模块。很多人一味叠加增强手段却不清楚每个增强的作用边界和叠加效果最终导致离线指标好看但上线效果差。 9.1 为什么增强不是越多越好 数据增强的核心目标是让模型在训练时见过的分布尽可能接近它在现场推理时会遇到的分布。 真实数据集有三个常见局限样本有限采集成本高、场景单一白天多、夜晚少、目标形态单一正面多、侧面少。增强是用低成本的图像变换模拟高成本的真实场景多样性。 但如果增强过度反而会让训练分布偏离真实分布。例如把 mosaic 概率设到 1.0每张图都是4张合成同时把 mixup 也开启每张图都是多张图的混合。这会让模型长期处于一种过度人工的分布中训练学到的特征不够贴近真实单一目标最终泛化到真实场景时效果反而下降。 所以增强的原则是宁缺毋滥宁可少用有效的也不要滥用全部。 9.2 YOLOv5 增强的执行顺序非常重要 YOLOv5 中增强的执行顺序不是随意排列的理解这个顺序对调试和设计增强策略非常关键。以默认配置为例mosaic 启用 第一步Mosaic 拼接。将 4 张图片及其标签按九宫格方式拼接成 1 张。这张合成图通常覆盖了更大的上下文区域模拟了多目标场景。 Mosaic 增强的关闭方式有两种方法一是在 hyp.yaml 中将 mosaic 参数设为 0方法二是在代码中将相关布尔变量置 False。 第二步MixUp 混合。在 mosaic 之后随机以一定概率将当前图和另一张图做加权混合像素级 blend。对应参数 mixup默认 0.0不启用。当你的数据集较小、目标重叠较多时开启 mixup 有助于增加多样性。 第三步几何变换。包括随机旋转-5°到5°、随机平移、随机缩放。这些变换会同时作用于图像和对应的 GT 框坐标。关键要求是几何变换必须同步变换标签坐标否则 GT 和图像就错位了。 第四步颜色域增强。包括 HSV 空间调整色调H、饱和度S、亮度V的随机偏移、亮度调整、对比度调整。颜色增强只改变像素值不改变坐标因此不需要同步更新标签。 第五步翻转。水平翻转vertical flip是 YOLO 中最常用的翻转方式。翻转概率在 hyp.yaml 中由 fliplr 参数控制默认 0.5。注意YOLO 通常不做垂直翻转因为大多数拍摄场景中目标不会有上下翻转做垂直翻转反而会引入错误的分布。 9.3 常见增强手段及其适用场景 翻转Flip适合对称目标车辆、行人、商品。不适合不对称目标手势识别中的左右手不能互换。 旋转Rotation小角度旋转±15°适合模拟摄像头轻微倾斜。过大的旋转角度如 180°只有在摄像头可能倒装的场景下才启用。旋转后的黑边处理会引入干扰区域需要额外处理。 Mosaic4图拼接大幅增加 batch 内多样性适合多目标检测。可以同时模拟背景多样性和多目标共现。但 mosaic 会让每张子图变小对小目标的检测难度实际是增加的。 MixUp两图混合像素级 blend适合类别之间边界模糊的场景。但会模糊目标的轮廓对定位精度有一定负面影响。 HSV 调整模拟不同光照条件白天/阴天/室内/室外。几乎所有场景都建议保留属于低成本高收益的增强。 马赛克Mosaic9 / 九宫格YOLOv5 源码中有 Mosaic9 的实现入口。9张图拼接上下文信息更丰富但标签处理更复杂对超密集目标场景有一定帮助对大多数场景收益不明显。 Albumentations 扩展增强如果需要更多增强手段如雨雾模拟、运动模糊、色彩空间变换可以接入 Albumentations 库。需要注意区分颜色域增强模糊、压缩、噪声只改像素不需改坐标几何增强旋转、透视、缩放必须同步更新 GT 框坐标。 9.4 增强实验的正确顺序 建议按以下顺序做增强实验每次只开一个观察 3-5 个 epoch 的趋势 第一步关闭所有复杂增强mosaic0、mixup0用最基础的增强HSV、翻转跑基线。记录基线的 P/R/mAP。 第二步打开 mosaicmixup 保持关闭观察指标变化。mosaic 通常能带来明显收益因为增加了目标多样性和背景多样性。如果收益明显可以保留。 第三步如果数据集较小或类别较多打开 mixupmosaic 保持开观察收益。mixup 通常对分类有帮助对定位帮助有限。 第四步调整 HSV 参数范围色调偏移量 H、饱和度偏移量 S、亮度偏移量 V。如果你现场的光照变化剧烈可以扩大 HSV 偏移范围。 第五步调整翻转概率。如果你的场景中目标左右不对称如手势、面部表情降低 fliplr。 十、损失函数设计box/obj/cls 三路平衡实战 ------------------------------------------ 损失函数是 YOLOv5 训练中最重要的部分也是最值得深入调优的区域。YOLOv5 的总损失由三类损失加权求和得到 Total Loss box_loss × λ_box obj_loss × λ_obj cls_loss × λ_cls 其中 λ_box、λ_obj、λ_cls 是由 hyp.yaml 中的 box、cls、obj 参数控制的权重项。理解这三路损失的机制是做有效优化的前提。 10.1 box_loss定位质量的衡量 box_loss 负责衡量预测框和 GT 框之间的定位误差。YOLOv5 从 v5.0 开始使用 CIoUComplete IoU作为 box_loss 的核心度量。 回顾一下 IoU 家族的演进逻辑 IoU Loss -log(IoU)。直接用两个框的重叠程度做损失。当两框完全不重叠时 IoU0无法反映分离程度梯度消失。 GIoUGeneralized IoU在 IoU 基础上加入了外接框的惩罚项GIoU IoU - (外接框面积 - 并集面积) / 外接框面积。即使两框完全分离GIoU 也能反映它们的距离远近。 DIoUDistance IoU在 GIoU 基础上进一步加入了中心点距离约束DIoU IoU - (中心点距离² / 外接框对角线²)。这让收敛更快尤其是框大小接近但中心点错开的情况。 CIoUComplete IoU在 DIoU 基础上再加入宽高比约束CIoU IoU - (中心点距离项) - (宽高比项)。同时优化重叠面积、中心点距离、宽高比是目前 YOLO 系列最常用的 box_loss。 理解这个演进逻辑你就知道为什么改 IoU 变体可能带来收益不同 IoU 损失对框的什么方面不够好的敏感度不同。如果你的检测框总是中心点准确但宽高比不对CIoU 比 DIoU 更适合如果框总是和 GT 重叠够但中心点偏得远DIoU 是更好的选择。 10.2 obj_loss目标性判断的置信度学习 obj_loss 衡量这个位置有没有目标的判断能力。这是一个二分类问题本格子是否包含 GT 目标的中心点。 正样本分配规则GT 框中心点落在哪个 Grid Cell哪个 Grid Cell 对应的 Anchor Box 就是正样本置信度 target 1。此外如果同一个 GT 和多个 Anchor 匹配通常取 IoU 最高的那个 Anchor 为正样本。 负样本分配规则不是正样本但与 GT 的 IoU 超过某个阈值的格子为忽略样本通常 target -1不参与 loss 计算。其余格子为负样本target 0置信度应该接近0。 obj_loss 使用 BCE二元交叉熵或者 Focal BCE 计算。如果启用 Focal Loss通过 hyp.yaml 中的 cls_pw 参数控制则用 Focal BCE 替代标准 BCE以降低易分类负样本的影响。 10.3 cls_loss类别判断的分类学习 cls_loss 衡量如果有目标它属于哪个类别的分类能力。使用 CE交叉熵或 Focal CE 计算。 类别不平衡时可以通过 class weights类别权重处理稀有类赋予更高的权重防止被头部类别主导。YOLOv5 的 hyp.yaml 中通常不直接配置 class weights需要在 compute_loss 代码中启用通常是一个可选项。 10.4 多尺度头损失平衡 YOLOv5 有三个检测头P3/P4/P5分别对应小目标80x80、中目标40x40、大目标20x20。这三个头分别负责不同尺度的目标大小差异很大直接用同一权重可能导致某类目标训练不充分。 多尺度头损失平衡的调节方式在 compute_loss 时给不同检测头的损失乘以不同的系数。YOLOv5 源码中默认的平衡系数是 [4.0, 1.0, 0.4]分别对应小目标头、中目标头、大目标头。 如果你发现小目标漏检严重可以尝试提高 P3 头的权重比如从 4.0 改到 6.0 或 8.0。 如果你发现大目标检测不稳定可以适当提高 P5 头的权重。 10.5 Focal Loss 的作用与拆分策略 Focal Loss 的设计目标是解决难易样本不平衡问题。在 YOLO 中正样本目标区域远少于负样本背景区域且大量负样本是简单背景模型已经很容易判断这里没有目标。如果用标准 BCE这些简单负样本贡献的损失累积起来会淹没正样本的梯度。 Focal Loss 的公式FL(p_t) -α_t × (1 - p_t)^γ × log(p_t) 其中 (1-p_t)^γ 是调制因子。当样本容易分类时p_t 接近 1调制因子趋近于 0大幅降低易样本的损失贡献当样本难以分类时p_t 接近 0调制因子接近 1损失基本不变让模型继续聚焦于难样本。 工程上有一个非常有价值的拆分策略先关注 obj目标检出再关注 cls类别分类。具体做法是 第一阶段obj_loss 用 Focal BCEcls_loss 用普通 BCE。优先保证能检出来分类交给第二阶段。 第二阶段在基线稳定后将 cls_loss 也切换为 Focal CE强化分类能力。 这种拆分策略适合漏检代价高、误分类代价相对低的业务场景如安监、质检。 10.6 IoU 变体替换的实验方法 替换 IoU 变体CIoU→DIoU→SIoU→EIoU 等是 YOLOv5 调优中一个经常被提到的手段但很多人替换后没有观察到收益原因是实验方法不对。 正确的做法是单因素替换 有对照。每次只替换 IoU 变体固定其他所有参数lr、batch、增强、权重初始化跑至少 20-30 个 epoch记录完整曲线。不同 IoU 变体的收敛速度不同短期内可能看不出差异但长期训练后可能有显著差异。 SIoUSCYLLA-IoU是近年来提出的新变体引入了Angle cost和Distance cost的重新设计在某些数据集上比 CIoU 收敛更快。EIoU 则进一步拆分了宽高比损失项在目标形状多样的数据集上可能更有效。 十一、验证与 best.pt 保存机制 ------------------------------ 很多人把 best.pt 等同于某个指标最高的 epoch这是一个常见的误解。理解 best.pt 的保存机制对正确评估模型效果和制定保存策略至关重要。 11.1 fitness 综合评分机制 YOLOv5 的 best.pt 不是按单个指标保存的而是按 fitness适应度综合评分决定。fitness 是一个加权组合典型的计算方式是 fitness 0.1 × P 0.9 × mAP0.5:0.95 或者更复杂的变体 fitness w1 × P w2 × R w3 × mAP0.5 w4 × mAP0.5:0.95 其中 w1、w2、w3、w4 是权重参数可以在源码中修改。权重的设计反映了什么对你更重要 如果你更看重查全率Recall可以提高 R 的权重防止模型为了追求高精度而漏检。 如果你更看重高精度Precision可以适当降低 Recall 的权重。 如果你在 COCO 格式数据集上评测mAP0.5:0.95 是最重要的指标建议给它最高权重。 这就解释了为什么最高 Recall 未必是 bestRecall 最高的那一轮可能 Precision 很低导致综合 fitness 并不是最高的。 11.2 业务目标映射到 fitness 权重 不同的业务场景对 P/R 的偏好不同 场景一医疗影像辅助诊断。漏检假阴性可能导致重大风险此时应该降低 fitness 中 Recall 的权重阈值或者单独设置Recall 优先的保存规则。宁可多报一些可疑区域Precision 降低也不要漏掉真正的病灶。 场景二商品识别计数。需要精准计数误检假阳性会直接导致计数错误此时应该更看重 Precision。可以适当牺牲一些 Recall。 场景三通用目标检测监控、安防。没有明显的 P/R 偏好使用默认的 fitness 权重组合通常就是最优选择。 11.3 验证阈值对指标观感的影响 YOLOv5 验证时会使用 conf_thres 和 iou_thres 两个阈值 conf_thres置信度阈值只保留置信度高于此值的预测。设得越高Precision 越高但 Recall 越低。 iou_thresNMS 阈值NMS 时 IoU 超过此值的框会被抑制。设得越低重复框越少但可能误删重叠目标。 这两个阈值的选择直接影响验证指标。实战建议做横向对比实验时所有模型的验证阈值必须完全一致包括 conf_thres 和 iou_thres否则对比结论不可靠。 通常 mAP0.5 是最鲁棒的指标因为它只评估 IoU0.5 下的 P/R 曲线下面积对阈值选择的敏感性低于 mAP0.5:0.95它评估 IoU 从 0.5 到 0.95 的平均值。 十二、排错清单从现象到根因的系统定位 ------------------------------------- 本节整理了 YOLOv5 训练中最常见的四类问题及其完整排查路径。 12.1 问题一参数改了但指标没变化 排查步骤 第一步检查启动日志中的最终生效参数。在训练开始时会打印 opt 字典确认关键参数batch、imgsz、lr、weight_decay、增强开关是否和预期一致。 第二步如果使用了 resume检查 opt.yaml 是否覆盖了你的修改。打开 run/expN/opt.yaml对比其中的参数和你的预期。 第三步如果修改的是 hyp.yaml增强参数确认训练时是否真的读取了这个文件。可以在 hyp.yaml 中加一个唯一的注释比如加一行 # DEBUG_TEST_001然后确认训练日志中是否出现了这个注释。 第四步如果以上都没问题检查代码缓存如果有 .pyc 文件或 __pycache__ 目录可能运行的是旧代码。执行 rm -rf __pycache__ 清理后再试。 12.2 问题二改了标签但指标不变 排查步骤 第一步检查 .cache 文件是否仍然存在。如果存在删除它并重新启动训练。 第二步抽样可视化。用 YOLOv5 自带的 plot_val_results.py 或者手动写一个脚本来可视化 GT 框确认标签是否正确读取。 第三步检查图片和标签的对应关系。确认 images/ 下的图片文件名和 labels/ 下的标签文件名完全一致注意扩展名。 第四步确认 Dataset 的 cache 参数设置。如果使用了 --cache-images确保图片和标签的 cache 都刷新了。 12.3 问题三自写推理结果和官方推理差距大 排查步骤 第一步预处理一致性排查。这是差距最大的来源。检查颜色通道顺序YOLO 用 RGBOpenCV 默认 BGR、归一化系数/255.0 还是 /255 还是 /1、resize 方式letterbox 保持宽高比 vs 直接拉伸、padding 颜色BGR 还是 RGB值是多少。 第二步后处理一致性排查。检查 conf_thres 和 iou_thres 是否一致。检查 NMS 实现是否和 YOLOv5 的 torchvision.ops.nms 等价注意 YOLOv5 的 NMS 做了多类别处理不是直接调用单类别 nms。 第三步模型加载确认。检查加载 best.pt 时是否正确切换到 eval 模式model.eval()。检查是否漏了 .float() 或 .half()如果官方用 FP16你用 FP32数值精度差异可能累积。 第四步输出张量解析。YOLOv5 的输出是 [batch, 3, 85, H, W]不是 [batch, 85, H, W, 3]确认你解析输出张量的 reshape 顺序是否正确。 12.4 问题四训练 loss 发散或剧烈抖动 排查步骤 第一步梯度爆炸检测。在训练循环中加入梯度范数打印torch.nn.utils.clip_grad_norm_。如果 grad_norm 超过 100通常说明梯度爆炸。处理方法调低学习率lr0 降低 5-10 倍或者打开梯度裁剪YOLOv5 中可用 --gr 等参数控制。 第二步学习率是否过高。观察第一个 epoch 的 loss 曲线。如果第一个 epoch loss 就非常高比如超过 10说明 lr 过高。YOLOv5 默认 lr00.01SGD对于小 batch 或小模型来说可能过高可以尝试 0.001。 第三步增强是否过强。关闭 mosaic 和 mixup只用基础的 HSV 和翻转看 loss 曲线是否恢复正常。如果恢复正常说明增强强度超过了模型当前能力的接受范围。 第四步batch size 与 weight decay 的配合。减小 batch 后如果训练变得不稳定检查 weight_decay 是否需要同比降低参考 effective_wd 的计算公式。 十三、推荐实验顺序一次只改一个核心因素 ------------------------------------- 做 YOLOv5 优化时最大的浪费是同时改了很多东西然后说不清哪个有效。下面的实验顺序是经过验证的高效优化路径每次只改一个核心因素确保你能准确定位每个改动的收益。 第一阶段数据正确性验证基石。在开始任何优化之前必须确认数据本身没问题。操作跑 1-2 个 epoch 的训练然后可视化 10-20 张样本的 GT 框用 YOLOv5 自带的 plot_labels 或手写脚本。检查GT 框是否画在正确位置、类别标签是否和图片内容一致、图片读取是否正常。如果数据有问题优化一切都白搭。 第二阶段可信基线建立。这一步的目的是在最干净的条件下获得一个基准指标用于后续对照。操作关闭 mosaic、关闭 mixup、增强只保留基础的 HSV 和水平翻转跑完一个完整的训练周期通常 300 epochs记录 best.fitness 和对应 epoch。增强越少离线指标通常越好看因为训练难度降低了但泛化能力未必最好。所以基线的意义在于基准而不是最优。 第三阶段增强逐项恢复。每隔 3-5 个 epoch 开启一个新的增强手段记录每个增强单独带来的变化。比如 baseline只有 HSV flip → mosaic → mixup → augment 的其他选项 通过这个过程你会知道每个增强在你的数据集上带来的真实收益是正还是负。 第四阶段损失权重调优。观察哪个尺度小/中/大目标的 AP 最低针对性地调高对应检测头的损失权重。比如 P3 (80x80) 的 AP 明显低于其他两个就将对应权重从 4.0 改到 6.0 再到 8.0观察 5 个 epoch 的趋势。 第五阶段IoU 变体和 Focal 策略。如果基础优化都已做完、指标仍有提升空间可以尝试替换 IoU 损失CIoU→DIoU→SIoU→EIoU和 Focal Loss 的拆分策略。每次替换后跑 20-30 个 epoch 观察长期趋势。 第六阶段fitness 规则校准。根据业务目标重新设定 fitness 权重组合确保 best.pt 的保存逻辑和验收标准一致。比如业务更关心 Recall就把 fitness 中 Recall 的权重调高。 第六步之后你就有了一个完整对照的实验记录可以写报告、写论文、或者做下一步优化规划。 十四、面试与汇报的表达升级从会调参到会优化 ----------------------------------------------- 能清晰表达 YOLOv5 训练链路和优化思路是区分调参工程师和算法工程师的重要标志。下面给出几套可直接使用的表达模板。 模板一适用于面试算法岗 我做过 YOLOv5 的系统性优化核心思路是链路诊断 单变量实验。具体来说我会在训练初期先固定数据正确性标签、cache、增强然后关闭复杂增强建立基线再逐项恢复增强观察单变量收益。在损失优化阶段我会先分析多尺度头的 AP 分布针对性调整头部损失权重。IoU 变体替换和 Focal 策略则放在最后做长期对照实验。 模板二适用于晋升答辩或项目汇报 本次 YOLOv5 训练的优化工作分为三个层面数据层面通过增强逐项恢复实验确定 mosaicHSV 是对我们数据集收益最高的组合损失层面通过头部损失平衡调整将小目标 AP 提升了 X%工程层面通过 fitness 规则重设确保 best.pt 保存逻辑与业务验收标准一致。 模板三适用于描述具体问题的诊断过程 当发现训练 loss 抖动后我依次排查了梯度范数排除爆炸、学习率从 0.01 降到 0.001、增强强度关闭 mosaic 后恢复稳定、weight decay 缩放batch 从 16 改到 64 后降低了 wd。最终通过梯度裁剪和降低 wd 的组合解决了问题并验证了在更大 batch 下模型收敛更稳定mAP 提升了约 X%。 这类表达的核心是说清楚现象→排查→假设→实验→结论的完整过程而不是只给我改了什么参数、结果变好了。 十五、最终总结 ------------ YOLOv5 训练源码学习的真正价值不在于背函数名而在于建立三个能力 第一个能力链路视角。能说出参数从哪来到哪去能定位任何训练异常到具体模块能解释为什么这个参数这样设置。 第二个能力系统优化。不是调一个参数等结果而是有计划地建立基线、逐项实验、记录对照最终用数据说服自己和他人这个改动有效。 第三个能力工程闭环。能从训练到推理全链路保持一致性预处理/后处理/阈值能设计符合业务目标的 fitness 规则能用排错清单快速定位问题根因。 当你同时具备这三个能力你就已经不是会跑 YOLO了而是会优化 YOLO。这才是技术深水区的核心竞争力。 完