干过目标检测训练的朋友,肯定对hyp.scratch.yaml不陌生。它在某开源检测框架的cfg/hyps目录下,名字拆开就是 hyperparameters(超参数)+ scratch(从零开始)+ yaml(配置文件格式)。说白了,它就是一份“从零训练模型”的配方单。我最早接触它的时候一脸懵,以为调参就是把 learning rate 改大改小,后来才发现这个文件里藏着优化器、损失权重、数据增强、标签平滑等一整套互相牵制的设置。
这份文件解决的是很多新手的核心痛点:为什么同一份数据集,别人跑出来 mAP 就是比你高?为什么损失曲线一直震荡就是不收敛?其实很多问题不在模型结构,而在超参数的组合方式。这篇内容适合所有刚上手目标检测、对调参感到头疼的读者,我会从文件本身的参数含义讲起,再到一次模拟项目X里的真实改动记录,最后附上我踩过的坑和排查清单。读完你至少能把这份配置看明白,并且知道自己改的每一个数字到底在干什么。
1. hyp.scratch.yaml 到底是什么,为什么每个项目都默认带它
1.1 一个文件搞懂框架的“出厂设置”
先明确一个概念:这个 YAML 文件不是网络结构文件,不定义模型有多少层、用什么激活函数,它定义的是“怎么训练”这件事。模型结构由model目录下的配置决定,而hyp.scratch.yaml管的是学习率多少、权重衰减多少、数据增强用到什么程度、各类损失按什么比例相加。相当于同一道菜谱,食材由模型文件决定,但火候、调味、下锅顺序全由这个文件决定。
命名为scratch的意思是,它对应“从零开始训练”的场景,不加载任何预训练权重。与之对应的,同目录下还会看到其他命名的配置,例如针对微调场景或不同优化器(如 Adam)的配置。scratch这一份通常是大多数人默认使用的基线配置,也是官方在各大数据集上反复试出来的经验值组合。它的价值在于:
- 给你一套大概率能收敛的起点;
- 保证多个实验之间可对比;
- 让你后面做消融实验时有参照组。
我见过很多同学上来就乱改,把学习率从 0.01 改成 0.1,把 Mosaic 关掉,结果训练三个 epoch 损失直接飞到 NaN,然后怀疑模型有问题。实际上这类情况 90% 是超参数组合出了问题。
1.2 为什么“从零训练”这一档值得认真对待
“从零训练”听起来像是最笨的方法——明明有很多预训练权重,为什么还要随机初始化从头学?在特定场景下,从零训练反而是更稳妥的选择。比如你做的是工业质检,目标是你工厂里那些特定缺陷,跟 ImageNet 上的自然图像差异极大;又比如你用的是修改过的检测头、自定义的 anchor 或特殊的分支结构,这种情况下预训练权重可能不仅帮不上忙,反而会因为特征分布不匹配拖慢收敛。
我之前在模拟项目X里试过直接套预训练权重去微调,前三轮 loss 降得飞快,但后面 mAP 一直卡在 0.7 上不去。后来换回scratch从零训练,虽然前期下降慢,但最终 mAP 反而涨了 3 个多点。原因不复杂:预训练权重来自通用数据,而你的数据分布如果偏离太远,不仅没法“站在巨人肩膀上”,还可能要被拖着重走弯路。
所以,不要一看到“从零”就觉得低级。当你的数据集足够大(通常几千张以上)、任务域跟预训练数据差异明显,或者网络结构有较大改动时,hyp.scratch.yaml这套从零训练的参数组合,反而值得你优先研究。
2. 文件里每个参数在说什么:逐项拆解
2.1 优化器与学习率:训练节奏的“总控台”
打开hyp.scratch.yaml,最先看到的就是lr0。它代表初始学习率,常见默认值是0.01。这个名字很多人误以为是“学习率等于 0.01”,其实它是起点。配合lrf(最终学习率因子),模型会在训练过程中按照余弦退火策略,把学习率从lr0逐渐降到lr0 * lrf。如果lr0=0.01、lrf=0.01,那最终学习率就是0.0001,相差两个数量级。
为什么需要这种“先大后小”的节奏?用个生活化的类比:一开始你对地形完全不熟,步子迈大一点可以快速探路;到了后期,你已经接近局部最优解了,步子还迈那么大就容易跨过头,所以要走小碎步精细调整。momentum(动量)默认0.937,作用是在梯度下降时保留一部分上一次的方向信息,抑制震荡。你可以把它理解为“惯性”,越大越不容易被个别异常样本带偏,但太大也会导致转向困难。
weight_decay(权重衰减)默认0.0005,它的本质是对大权重施加惩罚,抑制过拟合。不是越大越好,如果你发现模型欠拟合、训练 loss 降不下去,可以考虑适当减小;反过来,训练 loss 很低但验证集表现差,也就是过拟合明显时,可以适当加大。
warmup_epochs是预热轮数,默认3.0。前 3 个 epoch 学习率会从一个很小的值逐步升到lr0。这像运动前热身,目的是避免刚开始的几轮大步更新把随机初始化的权重冲坏。配套的warmup_momentum(预热期动量,默认0.8)和warmup_bias_lr(预热期偏置学习率,默认0.1)也按此逻辑设计。
我在实际使用中总结了一套判断方法:如果你的 batch size 偏小(比如小于 16),建议把lr0适当调低到0.005左右;因为小 batch 的梯度噪声更大,太高的学习率容易震荡。如果你用的是大 batch(比如 64 以上),默认的0.01通常问题不大。
2.2 损失权重:模型学习的“指挥棒”
hyp.scratch.yaml中box、cls、dfl是检测模型最核心的损失权重,而pose、kobj等参数只有做姿态估计这类任务时才会用到。以检测为例:
box: 边界框回归损失的权重,默认常见7.5cls: 分类损失的权重,默认常见0.5dfl: 分布焦点损失的权重,默认常见1.5
这几个数字背后的含义是:总损失 =box倍框回归损失 +cls倍分类损失 +dfl倍分布损失。box权重最大,说明这个框架在训练时更强调把框“框得准”,因为目标检测的最终输出就是框。分类损失次之,分布损失垫底。
不要一上来动这三兄弟。它们互相之间的比例一旦失衡,会出现很典型的现象:框位置很准但类别老错,或者类别对但框的位置漂得离谱。我见过有人为了提高分类准确率,把cls从0.5调到2.0,结果整体 F1 反而跌了,就是因为框回归受影响后,定位不好,类别再对也没意义。
正常情况下这套默认值已经能应对大多数任务。真要调,我建议遵循一个原则:一次只动一个权重,并且改完必须跑完整轮训练验证,不要中途看几个 epoch 就下结论。损失权重的变化通常在训练后期才体现出来。
还有一个容易忽略的参数nbs(名义批大小),常见默认64。它的作用是让损失值不直接受实际 batch size 影响。比如你实际 batch size 只有 16,框架会按sqrt(64/16)之类的比例去缩放损失梯度,使不同 batch size 下的训练节奏保持一致。这解释了为什么有人换了个显卡、改了 batch size,发现训练曲线走势完全不同,其实就是nbs和实际 batch 的比例变了。
2.3 数据增强参数:给模型制造“新题型”
数据增强是hyp.scratch.yaml里占比最大的一部分,也是很多人最容易随手乱调的部分。它的核心思想是:模型不是靠背题,而是靠刷题,你给的题型越多样,它学到的通用规律越多,泛化能力越强。但题出得太难,它可能直接心态崩掉。
拿颜色类增强来说,hsv_h(色相)、hsv_s(饱和度)、hsv_v(明度)默认常见0.015/0.7/0.4。这三个值表示的是变化幅度,不是绝对目标值。比如hsv_h=0.015意味着每张图的色相会在原有基础上随机偏移正负 0.015 的范围。这个量级不算大,目的是让模型对环境光照变化不那么敏感。工业质检场景下,如果你的产品颜色本身是品质判定的关键,就需要把hsv_h调小甚至改成 0;否则模型会学到“颜色变一点没关系”,导致错误判级。
几何增强方面,translate=0.1(平移)、scale=0.5(缩放)、degrees=0.0(旋转)、shear=0.0(错切)、perspective=0.0(透视)、flipud=0.0(上下翻转)、fliplr=0.5(左右翻转)。这套组合的含义是:模型在训练时会随机看到轻微平移、任意比例缩放的图片,左右翻转有一半概率触发,但基本不做旋转和透视变换。为什么旋转默认是 0?因为在很多自动驾驶和无人机航拍场景中,物体是有明确朝向的,车不会倒着开、人不该头朝下。上下翻转同理,地面上的东西不会突然跑到天上。如果你的任务确实需要旋转不变性,比如圆形零件缺陷检测,再考虑逐步增加degrees。
mosaic=1.0是马赛克增强,把 4 张图拼成 1 张再训练。它极大地丰富了每张训练图的上下文,是最强的增强手段。但它的副作用是,如果目标太小,拼图后目标被切碎导致学习困难。mixup=0.0和copy_paste=0.0默认是关闭的,mixup 是把两张图按比例混合,copy_paste 是把一个样本的实例抠到另一张图上。这些增强有它们适用的场景,但都需要更多调参经验支撑,建议新手不要贸然开启。
我个人的原则是:增强参数调起来最保守的方式是“只减不增”。比如你的数据集本身已经很干净、目标很规整,先保持默认增强跑一轮看效果。如果 mAP 不满意,再逐项判断是哪里不足,而不是一次性把degrees、shear、mixup全打开,否则你根本不知道是哪个参数起了反作用。
2.4 其他容易被忽略的“角落参数”
label_smoothing(标签平滑)默认常见0.0,也就是关闭。它的作用是让模型不要对训练集标签过于自信,把 one-hot 标签变成带噪概率,比如把[0, 1]平滑成[0.05, 0.95]。训练数据越脏、标注噪声越大,这个值的收益越明显。但如果你的数据标注质量很高,开着反而可能略微掉点。
cache参数通常不在hyp文件里,而在训练命令中,它表示是否把图片缓存到内存/磁盘以加速读取。这个跟hyp.scratch.yaml里的超参不是一回事,但因为它写在 YAML 旁边,很多人误以为它是增强参数。记住:cache只影响数据读取速度,不影响训练效果。
workers也不是增强参数,而是数据加载线程数。它不出现在hyp文件里,但有些同学会把进程数拉到 16 导致 OOM,我后面在排查清单里会详细讲。
3. 从零手调一份 hyp.scratch.yaml 的完整过程
3.1 我的调参顺序与整体思路
拿到一个新数据集,我不会直接开始改参数,而是先保证能够“跑通、收敛、看 baseline”。也就是说第一步永远是:用默认配置完整跑一轮训练,记录最终的 mAP、 Precision、 Recall、各类别单类 AP。这一步非常关键,因为默认配置代表官方在各种数据集上调出来的平均水平,它跑出来的结果就是后续所有调整的参照物。很多人上来就调参,调了半天也不知道自己改的到底是正向还是负向,就是没有 baseline 导致的。
我的调参顺序基本分三个阶段:
先调数据增强:如果训练 loss 很低但验证集 AP 差,大概率是增强不够,模型过拟合了,这时候优先考虑加大
hsv、scale、translate,而不是急着调weight_decay。反过来,如果训练 loss 一直下不去,可能是增强过猛、任务太难,可以把mosaic暂时关掉或降低scale幅度。再调优化器相关:如果损失曲线震荡剧烈或收敛太慢,再考虑
lr0、warmup_epochs、momentum。先确认当前学习率是不是太大或太小,而不是无脑改大。最后动损失权重:只有当模型整体行为出现了明显偏科,比如框定位准但分类混淆,或小目标总漏检时,才去动
box/cls/dfl。损失的权重是“组合拳”,尽量保持它们的相对比例不变。
这套顺序的核心逻辑是:数据增强影响“模型能看到什么”,优化器影响“模型怎么朝最优走”,损失权重影响“模型优先学什么”。优先级从高到低,是因为前两者的问题会掩盖第三者的影响。在增强不够、训练都不收敛的情况下调损失权重,得到的结果非常不可靠。
3.2 一次真实项目的参数改动记录
以模拟项目X为例,任务是检测流水线上的金属表面划痕,类别只有 3 类:浅划痕、深划痕、凹坑。数据集总共 1.6 万张,目标尺寸普遍偏小,最大目标也只有整图的 8% 左右。用默认hyp.scratch.yaml跑了 100 轮之后,baseline 得到 mAP 0.72,单看凹坑类只有 0.58,明显偏低。
第一轮改动:我判断凹坑类 AP 低的最大原因是目标小、数量少,也就是正负样本极度不均衡。这时候如果直接加大cls权重,容易导致模型对背景区域过度敏感。我先做的是削弱mosaic到0.8,因为小目标在 mosaic 拼接后更容易被压缩得小到看不见,而且被切碎后标注面积损失严重。同时加大scale到0.7,确保模型在训练中能经常看到不同尺度的目标。
第二轮改动:观察训练曲线后发现,训练集 mAP 到 0.89,验证集只有 0.75,属于典型过拟合。我把hsv_s从0.7提到0.9、hsv_v从0.4提到0.5,更多变化的光照和饱和度强行让模型去学形状特征而不是颜色统计规律。同时把weight_decay从0.0005提到0.0008,让大权重的惩罚稍微强烈一些。这个组合把验证集 mAP 拉到了 0.78。
第三轮改动:消融之后发现深划痕和凹坑两类边界特征相近,模型容易把浅划痕误判成深划痕。我把cls从0.5微调到0.7,分类损失占比加大后,类别混淆减少。最终 100 轮训练做到 mAP 0.80,凹坑类从 0.58 涨到 0.66,涨幅最明显。
写这个例子的目的是让你看到,每一次参数改动都是“问题驱动”的,而不是“感觉驱动”。我几乎从不同时改超过三个参数。并且每次只改一个方向:要么增强,要么防过拟合,要么调权重比例。记录下每轮改动和结果,一段时间后回看,你对自己数据的理解会比任何“默认值”都深。
4. 训练起不来的常见问题与排查清单
4.1 Loss 不降反升:先别急着改参数
训练到第 10 个 epoch,损失曲线不仅没降,还越走越高,甚至出现NaN。这种情况我遇到过不下五次,大部分原因不是参数,而是数据。
先排除一个隐藏雷区:数据集里是否存在损坏的图片、空的标注文件或异常的标注框?有一张图片的标注框坐标是负值,或者宽高为 0,就可能在计算损失时产生无穷值,导致整套训练崩掉。所以第一反应应该是检查数据,而不是改配置。
排除了数据问题后,再看学习率。如果你用的是默认lr0=0.01,但实际 batch size 很小,梯度噪声会偏大,学习率就显得过高。处理方式有两种:一种是把lr0降到0.005或0.002;另一种是加大warmup_epochs,比如从3.0改成5.0,让模型在更长的时间内热身上来,比直接降学习率更平滑,效果往往也更好。
还有一个我踩过不止一次的坑:从某个 epoch 开始 loss 突然剧烈上升,升完又慢慢降回来。这种周期性飙升往往跟学习率调度节点有关。当学习率降到非常低时,模型进入“坍缩”状态,某些层权重开始极端化,随后调度器进入下一个阶段,学习率重新回升,模型又被救回来。如果你的lrf设得太小,比如0.001,会出现这种情况。我的建议是不要让最终学习率低于lr0的万分之一,也就是lrf不要低于0.01。
4.2 模型过拟合的典型特征与对策
验证集 mAP 和训练集 mAP 差距超过 15 个点,基本可以断定过拟合。这在中小数据集上尤其常见。遇到这种情况,优先考虑增强,其次是权重衰减,最后才是调小模型。
增强方面,首选加大颜色类增强——它不改变目标结构,风险最小。把hsv_s、hsv_v分别调大 0.1 是安全操作。其次考虑加大scale和translate,它们能扩展目标尺度和位置分布,但不要猛加到改变语义。fliplr已经默认 0.5,不建议放到 1.0,因为左右翻转太频繁会让模型学不到方向特征。如果数据集有目标朝向要求,甚至可以改成 0。
权重衰减方面,从0.0005起步,每次增加0.0003左右观望效果。加太多会把模型推向欠拟合,表现是训练 loss 和验证 loss 同时偏高、mAP 上不去。
如果你试了增强和权重衰减都没明显改善,最后可以考虑调小模型或引入更严格的正则化方式。但这个时候已经属于结构性调整,不在hyp.scratch.yaml的范围内了,需要动模型定义文件。
4.3 显存、速度与精度的三角平衡
有人为了加快训练,把mosaic关掉,结果发现 mAP 掉了一截。有人为了塞下更大的 batch,把图片尺寸调小,也会导致精度下降。这些都是典型的只盯着速度和显存、忽略了增强和输入分辨率带来的精度影响。
实际上hyp.scratch.yaml里的增强参数会对训练速度产生明显影响:mosaic需要同时拼接多张图,代价是每个 step 的 CPU 预处理时间增加;copy_paste涉及实例抠图,更慢。如果你的训练已经变成 CPU 瓶颈,可以优先关掉copy_paste(反正默认就是 0),或者把mosaic频率降到0.5~0.8,而不是直接置 0。
显存优化跟超参数关系不大,主要看 batch size、workers线程数、图像 size 和模型参数量。如果你把workers设得太高,比如 16,内存和显存可能同时爆掉。经验值:workers一般为物理核心数的一半,最大不超过 8。batch size 则建议维持nbs=64对应的量级,如果显存不够,可以在hyp文件里同步降低nbs,让框架按比例调整学习率,而不是直接改lr0。
4.4 小目标检测失效的排查方向
很多人跑通用数据集有不错的效果,一换到无人机视角、工业缺陷这类小目标密集场景就歇菜。小目标失效首先要反思的不是超参数,而是模型本身的感受野和下采样倍数。如果下采样后小目标的特征图区域只剩 1~2 个像素,调任何超参数都救不回来。
在确保模型结构允许的情况下,hyp.scratch.yaml里最值得调的是:加大scale范围(比如0.5→0.8),多制造小尺度样本;降低mosaic频率,防止小目标在拼接过程中被过度压缩;适当降低hsv幅度,避免颜色失真干扰本来就微弱的纹理特征。如果条件允许,把输入分辨率提高比任何增强都更管用,但这已经超出超参数范畴了。
5. 用这套配置,我踩过的几个坑
第一坑:运维同学嫌 Mosaic 增强太占用 CPU,直接置 0,说“让训练快点跑完”。结果模型在小目标上 AP 掉了 10 个点,赶上没调过的 baseline。Mosaic 不是普通的锦上添花,它是这套配置中数据多样性最重要的来源之一,不要为了提速轻易全关。
第二坑:因为数据集标注有噪声,我把label_smoothing从0.0开到0.2,指望模型“温和一点”。但由于我的类别数很少,平滑到 0.2 反而把正负标签的差距拉得过小,模型变得犹豫不决,输出一堆低置信度预测。后来改成0.05才看到改善。标签平滑的取值跟类别数有关,类别越多,单类概率对样本形成的监督越弱,平滑值可以相应设大;类别少,平滑值必须收敛保守一些。
第三坑:怀疑weight_decay越大正则化越强,于是从默认0.0005一口气调到0.01,直接导致模型欠拟合到什么都学不出来。后面才知道,weight_decay每提升一个数量级,影响都比想象中大得多。我现在的习惯是每次只加0.0002~0.0003,两步没效果就换方向。
最后分享一个我个人的工作习惯:每改一个参数,我会在本地留着这张配置的备份,文件名带上简短的改动说明,比如hyp.scratch.yaml.box75_cls07_scale08。等积累了十几份这样的配置后,横向对比起来非常直观,相当于用文件命名建立了一份自己的调参日志。超参数调优没有银弹,能做的就是建立 baseline、单点改动、观察完整训练曲线、记录结论,然后在下一次遇到类似数据时少走弯路。