阶段 1 · 最小学习机器

调参:它能跑了
之后怎么变好

上一章回答「它坏了怎么修」。这一章回答另一半问题:没有 bug,只是还没到最优。 五个动作:看懂曲线、判断该加数据还是加模型、配好 batch(一批样本)与学习率、分清哪些超参值得花时间、 知道什么时候该停手。

1

它没坏,只是还不够好

上一章给过一个数字:从业者 80~90% 的时间花在调试和调参上,只有 10~20% 花在推导数学和实现上。 这一章就是那 80% 的后半。

⚠️ 这一章假设你的代码是对的

如果损失完全不动、或者出现 NaN、或者准确率卡在随机猜测的水平, 那不是调参问题,先去 《症状与病因》。

两章的交界处只有一条铁律:先在小数据上过拟合一个 batch(一小批样本)。 如果连一个 batch 都过拟合不了,别在这两章里找答案,那是 bug。

反过来说:它能跑、只是不够好,那问题就不是「哪儿坏了」,而是「下一步该动哪一个旋钮」。 先把两个词分清:参数是网络自己学的(那些权重),超参数是训练前由你定的 (学习率、batch、层数这些)。这一章说的旋钮,全是后者。

这一章给五件事:一张读懂曲线的图、一条「该加数据还是加模型」的判据、 batch 与学习率(含 warmup)的配法、一张「哪些超参值得花时间」的排序,以及什么时候该停手。

2

核心大图 · 学习曲线诊断器

下面五条曲线是真的训练跑出来的(一个小网络在同一个函数上、用不同配置跑了 200~2400 轮)。 先看图、先猜,再点开答案——每一条建议都带你回到刚才那条曲线的真实读数上,不是「多试试」。

互动 · 选一条曲线,先判断「该加数据还是加模型」

训练损失 验证损失 最低点

曲线形态它在说什么该动哪个旋钮
① 两条都在降,差距小还没到极限加轮数(免费);还有空间就加数据
② 训练降、验证涨开始背答案治过拟合:加数据 / 增强 → 早停 → 加正则
③ 两条都平,而且都高模型学不动这个函数加容量 / 加特征,不是加数据
④ 训练低、验证高且不降两个分布的差距,不是过拟合先查数据管道(数据从原始文件到喂进模型的这一段处理)与验证集构造
⑤ 验证曲线剧烈上下跳步子太大,或验证集太小降学习率 / 加大 batch / 加大验证集
3

batch 翻倍,学习率跟不跟?

答案在下面这张图里,而且它是真的量出来的:固定一组初始参数,算出每一个样本各自的梯度, 再量它们的离散程度(标准差 σ)。

互动 · batch 越大,梯度的噪声越小

🎯 类比

每个样本都在喊一个方向。你有两条路:让每个人喊小声一点(调小学习率), 或者多找几个人来投票(加大 batch),人越多、平均数越稳,噪音按 1/√B 下降。

所以要让「每一步的抖动幅度」保持不变,batch 翻 4 倍,学习率可以翻 2 倍(平方根规则), 而不是翻 4 倍。这就是两条经验法则的由来。 这里的 B 就是 batch size——一次送进网络的那一批样本数。

规则怎么算适用边界(这条最重要)
线性缩放
Goyal et al. 2017
batch ×k → 学习率 ×k 大批量训练、且必须配 warmup(warmup:训练开头的一小段,学习率从很小慢慢升到目标值,下一节详谈)。 它假设每一步的「梯度方差」是主导,所以步长要跟批量成正比, 好让信号(真实下降方向)与噪声(这一批数据带进来的抖动)的比值不变。 原论文用的是 batch 256→8192 配 5 轮 warmup
平方根缩放 batch ×k → 学习率 ×√k 更保守。从上面那个噪声图可以推个大概:噪声按 1/√B 降, 噪声小了,同样安全的前提下步子就可以迈得更大;能加大多少跟噪声降了多少成比例,所以是 √B。 不过严格说,「最优学习率到底怎么随 batch 缩放」并没有定论——大批量后期、 梯度噪声不是主导时(靠近最优点),它比线性规则更贴
不缩放 batch 变了,学习率不动 小 batch 换大 batch 时,等于偷偷减小了有效步长,你会以为「大批量没用」,其实是你没调学习率

同一件事的三种做法 · batch 翻 k 倍时,学习率该乘多少

⚠️ batch 不是越大越好

Keskar et al. 2016 发现:把 batch 从几百提到几千,训练损失更低、但测试效果更差, 大批量容易掉进「又尖又窄」的极小值。这不是玄学,是几何:大批量梯度几乎没有噪声, 于是它只会沿着最陡的方向精确下降,而小批量的噪声会把它从尖锐的坑里推出来。

这也是为什么大模型训练要同时上 warmup + 线性缩放 + 后期降学习率:三个手段合起来才把大批量的好处拿到、坏处压住。

4

为什么都从很慢的步子开始

下面两条曲线是同一个网络、同一个学习率、同一个随机种子跑出来的,唯一的差别是 开头 20 轮的学习率从 0 慢慢升到目标值(这叫 warmup)。看开头发生了什么:

互动 · 同一个学习率,有没有 warmup 的差别

没有 warmup 20 轮 warmup

⚠️ 开头为什么危险

三个原因叠在一起:① 参数是随机的,梯度方向基本是噪声;② 损失离最优很远,梯度很大; ③ momentum 这个「惯性」还没有积累起来,步子的方向几乎完全由当前这一小批数据决定。

这三件事碰到一个大学习率,第一步就可能把参数扔到一个很坏的地方(上图红线的开头)。 warmup 干的就是「先小步走,等方向、幅度和动量都稳下来,再把步子放大」。

调度器形状什么时候用
constant一条水平线调试、小实验。不要用来训练大模型
step走一段降一半,直角台阶经典 CNN 时代(每 30 轮 ÷10)。好懂,但要人工定「什么时候降」
cosine开头缓、中间快、结尾又缓,像半个余弦现在的默认选择。不需要定台阶,结尾自然收慢
linear一路直线降到 0预训练里常见。能不能降到 0 影响很大,留一个尾巴往往更好
WSD
warmup-stable-decay
升一下 → 长时间不动 → 最后猛降现代大模型预训练:中间那段可以随便延长,想停就停,不用重跑
one-cycle先升到峰值再降到底固定算力预算下榨出最好结果(还能顺便用峰值段做 LR finder——让学习率从小到大扫一遍,看损失什么时候降得最快)
5

算笔账 · 该加数据还是加模型

学习曲线告诉你「现在是什么状态」。下一步该动哪一边,就看这笔账: 误差 = 偏差²(系统性想错)+ 方差(对数据太敏感)+ 噪声地板; 再叠上布置考卷时出的问题:分布偏移(训练集和上线数据的分布不一样)、 拿验证集反复挑超参挑出来的过拟合。 第 2 节的前四种形态,是这几项里不同的项在主导(第 ⑤ 种另说)。

听着是五个名词,其实只有两个旋钮在动它们:模型有多大、数据有多少。 下面这张图是真算出来的,重复训练 14 次、对噪声取平均, 量出「训练样本数 → 验证误差」在容量小和容量大时长得完全不一样。

互动 · 训练数据越多,哪些误差会掉、哪些不会

n—训练样本数 · 横轴 容量—模型大小 · 上面滑块 偏差²—系统性想错 · 加数据治不了 方差—对数据敏感 · 加数据能治

换个方向看同一个权衡:固定数据量,把容量往上加,偏差² 和方差会当着你的面换班。

互动 · 容量往上加,偏差和方差真的在换班

偏差²(系统性想错) 方差(对数据太敏感) 合计 = 偏差² + 方差

把账上的项和第 2 节那五条曲线逐一对上,就是这张表:

账上的项它高的时候(第 2 节哪条曲线)加数据能治吗加容量能治吗
不可约误差(噪声地板)两条曲线贴在一起,停在一个抬高的地板上(五条里没有它的单独形态)不能不能
偏差²训练损失本身就高(③ 两条都平、都高)基本不能能
方差训练损失很低、验证损失高,而且验证曲线先降后升(② 训练降、验证涨)能反而更糟
分布偏移验证损失几乎不降、和训练损失差距巨大,曲线一开始就平在高处(④)能,但要加对地方的数据不能
验证集过拟合你按验证集挑了 200 次超参数,它就开始「记住」了(第 2 节没有对应形态,这是自己调出来的)能(换更大,或用交叉验证:把数据轮流切几份,每份都当过验证集)不能

第 2 节的 ⑤(验证曲线剧烈上下跳)不在这笔账里:那是步子太大或验证集太小, 先降学习率、加大 batch。噪声地板也没有自己的曲线形状——它是两条曲线一起停下来时脚底那条线。

微型图解 · 偏差大和方差大,长什么样

💡 一句话记住这笔账

加数据只能压方差,压不动偏差。如果训练损失本身就高(高偏差), 把数据集翻十倍也没用,那是模型不够大;反过来,训练损失很低、验证损失高(高方差),加数据是最稳的解法。

6

超参谱系:哪些值得花时间

调参最大的浪费,是在影响最小的旋钮上反复折腾。下面那张横条图给的是影响力排序; 表里没有这一列,只给每个旋钮的起点——新手最需要的一栏。

示意图 · 八个旋钮通常能带来多少效果

超参数典型范围 → 起点什么时候它才重要
学习率(含调度)1e-5 ~ 1e-2 → 1e-3 ~ 3e-4 永远。这一项调错的代价超过其余所有项之和
正则强度
weight decay / dropout / 增强
wd 0 ~ 0.1 → wd 0.01,先不加 dropout 训练损失已经能压到很低、验证开始涨的时候
模型规模(宽度/深度)取决于任务 → 先小后大 欠拟合时(两条曲线都平且都高)。过拟合时往反方向调
batch size8 ~ 4096 → 32~256(能塞进显存就行) 它的影响几乎全在「和别的参数联动」上:学习率、warmup、batch norm 的统计量
调度器形状cosine / WSD / one-cycle → cosine 训练轮数固定、且你想把最后几个点榨出来的时候
优化器SGD+M / AdamW → AdamW 只有在「要复现某篇论文」或「要多省一点显存」时才是主角
初始化方法He / Glorot → 配 ReLU 用 He,配 tanh 用 Glorot 只有当网络很深、而且你还没加残差连接和归一化时
激活函数ReLU / GELU / SiLU → ReLU 基本不用调。换了它,指标通常只动零点几个点

换个从没做过的新任务,起点从哪来?先抄同类任务公开配置里的学习率,再从它上下扫一遍。

⚠️ 一个诚实的补充:这张表不是免费的

把学习率从 1e-3 调到 3e-4 要跑一次实验,加正则也要跑一次。 所以省时间的做法不是「调得快」,而是「每次实验只动一个旋钮 + 记录」, 以及先用便宜的代理:小模型、小数据、少轮数上做粗筛,最后再在大配置上确认一次。 普通一台电脑也做得起——先用前面那种小配置找出趋势。

为什么网格搜索反而更差、随机搜索好在哪,第 7 节给了一张图。

7

怎么把调参当实验做

上一节最后那句「每次实验只动一个旋钮 + 记录」,它其实是个口号。 下面是把它变成能照着做的做法。这一套不是我编的,来自 Google 的 《Deep Learning Tuning Playbook》(30k stars,目前业界最公认的一份调参规范)。 它的核心主张只有一句:把调参当成做实验,而不是当成碰运气。

① 动手之前:先给你手上的超参分三类

原文里最有用的一个工具,是在每一轮实验开始之前,先把你所有超参分进三个抽屉。 分类不是超参自带的属性,它取决于你这一轮想回答什么问题。先分清三个名字: 科学超参是这一轮想比较的那一个;干扰超参是会影响结果、但不是你要比的, 每个候选都要把它调到最好;固定超参是这一轮先钉死不动的。 点下面三个按钮,看同一批超参怎么换来换去:

互动 · 换一个你想回答的问题,看超参怎么改归属

三条几乎不会错的规则:优化器那一堆(学习率、momentum、调度形状这些)默认都是「干扰」, 因为「当前最优学习率是多少」问不出洞见,改一下管线它就变了; 「用 Adam 还是 SGD」这个选择本身,是「科学」或「固定」; 「要不要加正则」是科学,「正则多强」是干扰。 分错的代价是得出反向的结论:你想测「更深的网络好不好」,却只给浅网络调了学习率, 量到的其实是「学习率没调好的代价」,不是「深度」的效果。

② 试的时候:网格、随机、准随机,同样 9 次试,结果差很多

预算有限时最反直觉的一条结论是:老老实实打网格,是最差的做法。 下面这三张图各试了 9 次,试的是同一个函数。横轴是真正重要的那个超参, 纵轴是另一个几乎不重要的超参。

互动 · 都是 9 次实验,三种铺法的差别

随机搜索真的比网格搜索好?我平时都是网格扫的。

差别在预算有限时怎么花。网格搜索会把时间平分给每一个超参,可大多数超参其实无关紧要;随机搜索让每个超参都有可能被抽到多个不同的值,于是预算自动流向真正重要的那几个。

Bergstra & Bengio 2012 证明了这件事:当只有少数几个超参重要时,随机搜索比网格搜索高效得多—— 网格的 9 个点只在重要的那个轴上落在 3 个位置,另外 6 次实验白花。 Playbook 的建议是探索阶段首选准随机(Sobol / Halton 这类低差异序列:点铺得比随机更均匀), 它不「自适应」,所以事后换目标不用重跑;实验并行跑时,两倍预算的随机搜索就很难被打败。

③ 读结果的时候:只有「各自都调到最好」,比较才算数

假设你要比两个模型 A 和 B,手上还有另一个旋钮 θ(比如学习率)得一起调。 最省事的做法是给两边都用同一个 θ,但这会骗你。 下图里「只试了一个 θ」的那条竖线,结论是 A 赢;把每个模型各自调到最好之后,结论反过来。 这里的 x 就是你这次想比较的那个东西——上面例子里的 A 或 B,也可以是「优化器选 Adam 还是 SGD」。 把每个 x 各自调到最好再比,得到的分数叫隔离分数(isolation),它才是 x 的真实效果。

互动 · 换个 θ 试一次,看结论会不会翻

💡 换个 θ,结论会整个反过来

只拿一个共同的学习率去比两个模型,你比的是「那个学习率恰不恰好」,不是模型本身。 每个候选各自把干扰超参调到最好,得到的才是它真实的效果;拖动上面那个滑块,胜负会翻转。

⚠️ 这套做法也有代价

把干扰超参「各自调到最好」听起来免费,其实很贵: 你要跑的实验数 = 科学超参的取值数 × 干扰超参的搜索量。 所以原文给了三个必须同时满足、又互相争夺预算的目标: ① 科学超参比得够多 ② 干扰超参的空间够宽 ③ 采样够密。 任何一个变好,都要花掉更多实验。

现实里的做法是把一部分干扰超参降级成「固定」,代价是结论从此带上一条前提: 「在激活函数固定为 ReLU 的前提下,更深的网络更好」。 这条前提你必须自己知道、也必须在写结论时带上。

这张表可以直接贴在显示器边上

8

验证集上的好成绩什么时候不算数

前面七节都假设一件事:验证集和线上的数据来自同一个分布。这个假设一旦破了, 验证集上的一切,包括偏差、方差、学习曲线的读法,全部失效。

下面是同一个训练好的模型,在三个验证集上算出来的真实损失。训练集只覆盖 x ∈ [-1, 1]:

这三个验证集各自盖住了输入空间的哪一段

表格里的数字和这张图都是页面加载时真的训练一个小网络、再分别评估三次得到的。

验证集怎么划什么时候适用坑在哪
随机切分 数据是 i.i.d. 的(每个样本互不影响,而且来自同一个分布) 如果数据有时间顺序,随机切分会让验证集偷看未来
时间切分(用旧数据训练、新数据验证) 上线后数据会随时间漂移:推荐、风控、时序预测 指标会明显低于随机切分,那才是真实的线上表现
双验证集(同分布一个 + 目标域一个) 知道上线环境和你训练时的数据不一样(目标域 = 你真正要用它的那种数据) 要两套指标分开看:一个告诉你过没过拟合,另一个告诉你迁移行不行(迁移:在这批数据上学的本事,换到另一种数据上还灵不灵)
按来源/设备/地区切分 数据来自多个采集源(多家医院、多款手机、多个城市) 最接近真实上线的方式:直接留一个来源完全不参与训练

一个可以立刻用的动作:把验证集切成两份,一份随机切、一份按时间或来源切。 看两个数字的差距,那个差距就是「分布偏移税」。差距大,说明你现在调的每一个超参数, 都在优化一个不会在线上出现的分布。

9

什么时候「再调一调」是错的

这一章讲了一整套往上爬的方法。它有一个前提,而且这个前提经常不成立:

你现在的处境继续调参的后果更该做的事
连一个 batch 都过拟合不了 调参调到天荒地老也修不好,那是 bug 去《症状与病因》,按症状查
验证集只有几十个样本 你调的是验证集的噪声。挑 20 次超参数,最好看的那次必然是运气 先扩验证集,或用交叉验证;只看差距很大的改动
训练集和线上分布不一样 所有调参都在优化一个错的分布(第 8 节) 先修数据采集与切分
数据本身有大量标签噪声 / 冲突标签 模型只能学到平均答案,损失卡在地板上不动 洗数据、清冲突样本。数据的收益永远大于超参数
已经连调了 20 次,每个都只涨 0.1 个点 你在过拟合验证集,而不是在改进模型 停下来,换更大的数据/模型,或者接受现状

什么时候算调完了?验证指标连着几轮不涨,而且涨幅小于换随机种子带来的波动。 最后报分用一份从没参与挑超参的测试集。

🎯 类比

调参像调收音机的旋钮。信号清楚的时候,微调能让你听得更清楚; 但如果天线根本没接上(bug)、或者电台在别的频率(分布偏移), 你在旋钮上花的每一分钟都是白费的,而且你会因为「刚才好像清楚了一点」而一直转下去。

10

小结

它对应哪条线 ① 表达力 vs 泛化,调参就是在这两者之间找那个「软」的平衡点: 容量、正则、步长,三个旋钮共同决定模型最终停在哪一边
一句话 调参本质上是在用真实测出来的曲线,判断现在的损失里哪一项在主导, 是偏差、是方差、还是分布偏移。判错了方向,所有实验都是白跑
它牺牲了什么 牺牲了验证集的「干净」。你每按验证集挑一次超参数,它就离「没见过的新数据」远一点, 调参是在花验证集这个预算,而它是有额度的(回扣暗线 B)
🎬 自己验一遍

回到第 2 节那个学习曲线诊断器。先按第 ③ 个按钮(两条都平、都高), 再按第 ② 个(训练降、验证涨),看它给你的建议怎么从「加容量」变成「加数据 / 正则」。 两个形状对应两种完全不同的动作:这就是「先看形状再动手」的全部含义。

它在六条暗线里站在哪

暗线这一章的回答
A 信息流动 数据形状:这一章几乎不改形状,改的是「每步用多少条数据」(batch)和「看几遍数据」(轮数)。 形状不变、损失曲线变,调参最好用的地方就在这:它不动结构,只动数
B 什么被牺牲了 验证集的独立性。反复按验证集调参,等于把验证集变成第二个训练集。 代价不是立刻出现的,它是「上线之后才知道差了几个点」
C 参数账本 调参的预算是实验次数 × 每次的算力,所以先用小配置粗筛是唯一划算的策略
D 跑在什么上 batch size 是硬件约束反射进算法的那一面镜子:能塞多大主要由显存决定, 而 batch 一定,学习率和 warmup 就得跟着变。最贵的动作是「跑一次完整训练」, 所以调参的胜负手是「每次实验能学到多少信息」
E 它假设了什么 假设验证集和上线分布同分布、而且验证集足够大到能反映真实表现。 第 8 节整节都在讲这个假设破了之后会发生什么
F 违背了哪个直觉 直觉说「模型越大越好」、「数据越多越好」。 偏差-方差的 U 形曲线说明:两者都存在一个最优位置,过了就变差, 而且「该加哪个」要看你现在在 U 的哪一侧
一句话带走调参方法

训练损失和验证损失这两条曲线,能告诉你现在缺的是轮数、数据、容量、还是正则, 用第 5 节那张表判断该往哪边加。
学习率是唯一值得反复调的那一个,而且它不单独存在:batch 翻倍、学习率要跟着变,但要用平方根而不是线性, 还必须配 warmup。
最后记住代价:每按验证集挑一次超参数,你就花掉一点它的可信度。
下一章换个材料:《CNN 卷积神经网络》拿块小印章盖满整张图。

11

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口, 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式