本文是《深度学习基础:从反向传播到残差》系列的第 4 篇(共六篇)。上一篇:优化器——从 SGD 到 AdamW 与学习率调度;下一篇:CNN——从 LeNet 到 ResNet,再到 ViT。
L2 导读讲过经典机器学习的第一定律:模型容量超过数据量就会过拟合。一个 160 万参数的网络在 4000 个样本上训练,按这条定律应该背下每一个样本、在测试集上一败涂地。第九章的实验里它做到了前半句——训练错误率 0.0%——却没有做到后半句:测试错误率 14.5%,是所有宽度里最好的。而一个参数只有样本两倍的小网络,测试错误率 43%。
这个现象——参数越多泛化越好,越过某个点之后——在 2017 年前后被系统地记录下来(Zhang 等 2017;Belkin 等 2019),它推翻的不是”过拟合”这个概念,而是”容量”这个度量。本篇讲三件事:为什么深网络在参数远多于样本时不过拟合(隐式正则化与 double descent);显式正则化手段各自在做什么(dropout、weight decay、早停、label smoothing);以及什么时候过拟合会回来——因为它一定会回来,SFT 训几个 epoch 之后模型开始逐字背答案,就是它。全篇的核心问题是:
一个参数量是样本量几百倍的网络为什么不过拟合?同一个网络在什么数据规模、多少个 epoch 之后会开始过拟合?怎么提前看到?
一、总览:两个体制
1. 参数 / 数据比决定你在哪个体制
泛化问题的第一步是算一个数:参数量除以训练 token(或样本)数。它把深度学习分成两个体制:
| 场景 | 参数 \(N\) | 数据 \(D\) | \(N / D\) | 体制 |
|---|---|---|---|---|
| 本文实验:MNIST 子集,宽 2048 | 1.6M | 4000 样本 | 407 | 极度过参数化——但不过拟合 |
| Llama-3-8B 预训练 | 8.0B | 15T token | 0.0005 | 数据远多于参数,每个 token 只见一次 |
| Chinchilla 最优 | \(N\) | \(20N\) | 0.05 | 数据仍多于参数 |
| SFT:8B 模型,1 万条指令 | 8.0B | 约 5M token | 1600 | 极度过参数化——会过拟合 |
| 奖励模型:8B,10 万偏好对 | 8.0B | 约 50M token | 160 | 极度过参数化——reward hacking 的来源 |
LLM 预训练在第二行:每个参数只”分到”两千个 token,模型远远背不下数据,过拟合不是主要风险,所以几乎不用 dropout、只训一个 epoch(第六章)。SFT 与奖励模型在第四、五行:与本文实验同一个体制,参数是数据的几百倍。同一个体制里本文实验不过拟合、SFT 却会——差别在于训练了多久、任务是什么、数据有没有噪声。这就是本文要讲清楚的事。
2. 本文的章节安排
| 章 | 主题 | 内容 |
|---|---|---|
| 二 | 经典视角与它失效的地方 | 容量、偏差-方差、过拟合;参数远多于样本时经典理论的预言与现实 |
| 三 | Double descent | 实测 11 个宽度的 U 形 + 第二次下降;插值阈值处的尖峰;为什么越过之后再变好 |
| 四 | 隐式正则化 | SGD 偏好平坦解、最小范数解;随机标签也能背下来说明容量不是度量 |
| 五 | 显式正则化 | weight decay 作为先验、dropout 的期望等价与集成视角、早停、label smoothing;实测四种配置 |
| 六 | 为什么 LLM 预训练不用 dropout、只训一个 epoch | 数据体制;多 epoch 的收益递减(Muennighoff 等 2023) |
| 七 | 过拟合回来的时候 | 小语言模型三种数据量下的 train / held-out 曲线与逐字记忆率;SFT 与奖励模型 |
| 八 | 诊断 | 三个信号:gap、held-out 拐点、记忆探针;loss 涨而准确率不变的含义 |
| 九 | 实验 | 代码与完整结果 |
| 十 | 本文小结 |
二、经典视角与它失效的地方
1. 经典的预言
L2 导读的偏差 - 方差分解说:模型太简单,偏差大(欠拟合);模型太复杂,方差大(过拟合);测试误差随容量呈 U 形,最优点在中间。经典理论里”容量”由参数量(或 VC 维一类的度量)决定,所以规则是:参数量不要超过样本量太多,超过了就加正则化。
2. 现实
第九章的实验:4000 个 MNIST 样本,标签有 20% 被随机替换(制造噪声,让”背下训练集”与”学到规律”能区分),两层 MLP 宽度从 2 扫到 2048,Adam 训 6000 步到收敛:
| 宽度 | 参数量 | 参数 / 样本 | 训练错误率 | 测试错误率 | 测试 loss |
|---|---|---|---|---|---|
| 2 | 1.6K | 0.4 | 54.5% | 60.3% | 1.95 |
| 4 | 3.2K | 0.8 | 26.0% | 42.6% | 2.07 |
| 8 | 6.4K | 1.6 | 3.4% | 43.4% | 5.48 |
| 16 | 12.7K | 3.2 | 0.0% | 35.4% | 4.44 |
| 32 | 25K | 6.4 | 0.0% | 27.3% | 2.55 |
| 64 | 51K | 12.7 | 0.0% | 22.4% | 1.82 |
| 128 | 102K | 25 | 0.0% | 19.3% | 1.35 |
| 256 | 204K | 51 | 0.0% | 16.8% | 1.14 |
| 512 | 407K | 102 | 0.0% | 16.0% | 1.08 |
| 1024 | 814K | 204 | 0.0% | 15.4% | 1.01 |
| 2048 | 1.63M | 407 | 0.0% | 14.5% | 0.95 |
从宽度 16 起训练错误率就是 0——网络把 20% 的错误标签也全部背下来了。经典理论说此后测试误差应该越来越差。实际上:宽度 8 到 16 附近确实有一个尖峰(测试 loss 从 2.07 冲到 5.48),然后一路变好,到 2048 时是全表最好。参数是样本 407 倍的网络,泛化最好。
经典理论没有错,错的是用参数量当容量。深网络的”有效容量”由训练过程决定——优化器会在所有能拟合训练集的解里挑一个特定的,而这个解的复杂度远低于参数量所允许的。这就是隐式正则化(第四章)。在讲它之前,先看清那个尖峰。
三、Double descent
1. 曲线的三段
上面的表画出来是 Belkin 等 2019 命名的 double descent:
测试误差
│
│ ╲ ╱╲
│ ╲ ╱ ╲
│ ╲ ╱ ╲
│ ╲ ╱ ╲
│ ╲____________╱ ╲___________
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾
│ 欠拟合 │ 插值阈值 │ 过参数化:第二次下降
└──────────────────┴─────────┴──────────────────────────▶ 模型容量
训练误差刚到 0 的地方
第一段是经典的 U 形(宽度 2 → 4:偏差下降);尖峰在插值阈值——模型刚好能把训练集(含噪声标签)拟合到零误差的地方(宽度 8 → 16);越过阈值后是第二次下降,越宽越好。Nakkiran 等 2019 在 ResNet、Transformer 上都观察到同样的形状,并发现它也随训练时长出现(epoch-wise double descent):训练更久有时先变差再变好。
2. 尖峰为什么出现
在插值阈值上,能拟合全部训练样本的解几乎唯一——参数刚够用,网络没有选择,只能扭曲自己去穿过每一个点,包括 20% 的错误标签。穿过错误标签需要在那些点附近做出剧烈的弯曲,弯曲波及周围的测试样本,测试误差爆炸;测试 loss 比错误率更敏感(5.48),因为那些被扭曲的区域给出了自信而错误的预测。
越过阈值后,能拟合训练集的解变成一个高维的集合,优化器可以从中挑最”平缓”的那个:穿过错误标签的点,但用最小的弯曲,对周围的影响最小。宽度越大,这个集合越大,能找到的解越平缓。这就是第二次下降。
3. 对实践的含义
两条。第一,不要停在插值阈值附近:一个”刚刚好能拟合”的模型往往是最差的选择,要么明显小(接受欠拟合),要么明显大(进入第二次下降)。第二,加数据可能让同一个模型变差:数据量增加会把插值阈值往右推,一个原本在第二次下降区间的模型可能被推回尖峰上(Nakkiran 等的 “more data hurts”)。这两条在小数据微调时都会遇到。
四、隐式正则化
1. 优化器挑了哪个解
过参数化时训练集对解的约束不够,剩下的自由度由优化器填上。SGD(以及 Adam)不是任意地填,它有系统性的偏好:
最小范数解。 对线性模型(以及某些条件下的宽网络),从零初始化出发的梯度下降收敛到所有插值解中范数最小的那个——梯度始终落在数据张成的子空间里,垂直方向的分量从不被更新。范数小意味着函数平缓。
平坦极小值。 SGD 的噪声(上一篇第二章,尺度 \(\eta / \sqrt{B}\))让参数在尖锐的极小值里待不住——一点扰动就跳出去——而在平坦的极小值里能停下。平坦的极小值对参数扰动不敏感,通常也对输入扰动不敏感,泛化更好。这解释了一个反复被观察到的现象:大 batch(噪声小)训出来的模型泛化略差,即使训练 loss 相同;也解释了为什么学习率衰减要留到最后——早期的大噪声是在挑盆地。
先学简单的。 网络在训练早期先拟合低频、简单的模式,晚期才去拟合高频、复杂的(包括噪声标签)。这就是早停有效的原因:在网络开始拟合噪声之前停下。
2. 随机标签也能背下来
Zhang 等 2017 做了一个干净的实验:把 CIFAR-10 的标签全部打乱成随机的,标准的 CNN 照样能把训练集拟合到零误差。结论是网络的容量足以记住任何数据,所以容量不是它在真实数据上泛化好的原因。泛化好,是因为真实数据里有结构、优化器先学结构、结构比噪声容易拟合。
这个实验也给出了本篇最有用的一个诊断工具:记忆探针——如果模型能逐字复现训练样本,它在背,不是在学(第七、八章)。
五、显式正则化
隐式正则化是免费的;显式正则化是额外加的约束,每种都可以理解为”把模型拉向某种简单解的先验”。
1. Weight decay:高斯先验
给参数一个均值 0、方差 \(\sigma^2\) 的高斯先验 \(p(\theta) \propto \exp(-\|\theta\|^2 / 2\sigma^2)\),最大后验估计(L0 导读第三章的 MAP)在负对数似然上多出一项 \(\frac{1}{2\sigma^2}\|\theta\|^2\)——这就是 \(L_2\) 正则,\(\lambda = 1/\sigma^2\)。它说的先验是”参数应该小”,也就是”函数应该平缓”。上一篇第五章讨论了它在 Adam 下的正确形式(AdamW)与它在 LLM 里作为”范数平衡点”的含义;这里补一个数字:第九章的实验里,1000 个样本、AdamW \(\lambda = 0.5\),300 个 epoch 后测试 loss 是 0.42,不加是 0.55——weight decay 把训练后期测试 loss 的上升压平了大半。
2. Dropout:期望等价与集成
Dropout(Srivastava 等 2014)训练时以概率 \(p\) 把每个激活置零,并把剩下的乘 \(1 / (1 - p)\)(inverted dropout,让期望不变:\(\mathbb{E}[\tilde a] = (1 - p) \cdot a / (1 - p) = a\));推理时什么都不做。两种理解:
集成视角。 每个 batch 用的是一个随机子网络,\(n\) 个神经元有 \(2^n\) 个子网络,训练在同时训所有子网络的一个加权平均;推理时用完整网络,近似于这 \(2^n\) 个子网络的集成(L2 导读第二章:集成降方差)。
噪声视角。 Dropout 给激活注入乘性噪声,逼迫每个神经元不依赖特定的其他神经元(防止”共适应”),学到的特征更冗余、更稳健。
代价:训练时有效容量变小(每步只有 \((1 - p)\) 的神经元在工作),收敛更慢;对已经有足够数据的场景收益小甚至为负——第六章讲 LLM 为什么不用它。第九章的实验里 \(p = 0.5\) 在前 10 个 epoch 与 weight decay 效果相当(测试 loss 0.39),但训到 300 个 epoch 后反而是最差的(0.62):dropout 拖慢了过拟合的速度,没有改变它的终点。
3. 早停
在验证 loss 开始上升时停止训练。对线性模型可以证明它等价于 \(L_2\) 正则,正则强度反比于训练步数 × 学习率(\(\lambda \approx 1 / (\eta t)\));对深网络它是第四章”先学简单的”的直接利用。它几乎没有代价,只要求留出验证集并定期评估——这是横切”实验方法论”里最便宜的一条规则。第九章:不加正则化时最优停止点在第 10 个 epoch(测试 loss 0.40),之后一路涨到 0.55。
4. Label smoothing 与数据增强
Label smoothing(Szegedy 等 2016)把 one-hot 标签换成 \((1 - \epsilon)\) 在正确类、\(\epsilon / (C - 1)\) 均分给其他类,\(\epsilon\) 常取 0.1。它阻止 logits 无限拉大(one-hot 下 loss 只有在 logit 差趋于无穷时才到零),改善校准;代价是模型的置信度被人为压低,对需要校准概率的下游(蒸馏、奖励模型)要小心。数据增强(图像的裁剪翻转、文本的回译与同义替换)是给数据加先验:”这些变换不改变标签”;它是 CV 里最强的正则化,在 LLM 预训练里几乎不用——数据够多,且语言的”标签不变的变换”难以定义。
5. 实测:四种配置
1000 个训练样本、宽 512、AdamW \(\eta = 10^{-3}\)、300 个 epoch:
| 配置 | 第 10 epoch 测试 loss | 第 300 epoch 测试 loss | 第 300 epoch 训练 loss | 最优早停点 | 准确率(300 ep) |
|---|---|---|---|---|---|
| 无正则化 | 0.402 | 0.548 | 0.000 | ep 10 | 89.1% |
| weight decay 0.5 | 0.392 | 0.422 | 0.000 | ep 40 | 89.6% |
| dropout 0.5 | 0.391 | 0.617 | 0.000 | ep 10 | 89.5% |
| 两者都加 | 0.386 | 0.421 | 0.000 | ep 10 | 89.8% |
四种配置的准确率都在 89% 上下,几乎没有差别;差别全在测试 loss——它随训练持续上升,而准确率不动。这是过拟合在深网络上最常见的形态:模型没有把更多测试样本判错,而是对判错的那些越来越自信(logits 越来越大)。第八章会把它作为一个诊断信号。
六、为什么 LLM 预训练不用 dropout、只训一个 epoch
1. 数据体制
回到第一章的表:Llama-3-8B 每个参数分到约 1900 个 token。这个体制里模型是欠拟合的——训练 loss 与验证 loss 几乎重合(差在 0.01 nat 量级),加更多数据、更多算力 loss 还会降(这正是 scaling law 描述的)。过拟合不是风险,容量才是瓶颈。任何减少有效容量的正则化(dropout 最典型)都在浪费算力。GPT-2 还用 dropout 0.1;GPT-3 之后的大模型几乎全部去掉;Llama、Qwen、DeepSeek 的 config 里没有 dropout。
Weight decay 保留了下来,但如上一篇所述,它在这里的角色主要是控制参数范数与有效学习率,不是防过拟合。
2. 一个 epoch
数据多到训不完一遍时,多 epoch 没有意义——每个 token 只见一次,也就不存在”背下来”。当数据受限、必须重复时,Muennighoff 等 2023 的实验给出了量化结论:重复到 4 个 epoch 以内,loss 与用同样多的全新数据几乎没有差别;之后收益迅速递减,到几十个 epoch 时重复数据的价值趋近于零。他们据此修正了 scaling law,给出了数据受限下参数与 epoch 的最优分配。当前实践:高质量数据(代码、数学、教科书)重复 2–4 个 epoch 是常见做法,网页数据基本只过一遍。
3. 与本文实验的关系
第一章的表说清了为什么第二章的 MLP 与 LLM 预训练看起来矛盾却不矛盾:前者在过参数化体制,靠隐式正则化不过拟合,但如果训得足够久、数据有噪声,测试 loss 仍会缓慢上升(第五章表里的”无正则化”一行);后者在数据主导体制,根本没到能过拟合的地方。SFT 把一个 8B 的模型放回第一种体制——下一章。
七、过拟合回来的时候
1. 小语言模型上的三种数据量
第九章训了一个字符级的 MLP 语言模型(上下文 8 个字符,约 12 万参数),语料是 Python 标准库的源码,训练数据分别取 2 万、20 万、200 万字符,每个 epoch 后测 held-out loss,并做一个记忆探针:随机取训练文本里的 200 个位置,给模型 8 个字符的前缀,贪心生成 32 个字符,统计与原文逐字相同的比例。
| 训练字符数 | 参数 / 字符 | held-out loss 最低点 | 最低值 | 此后 | 逐字记忆率 |
|---|---|---|---|---|---|
| 20K | 5.8 | 第 16 epoch | 2.41 | 涨到 2.98(40 ep);训练 loss 降到 0.44 | 2.5%(8 ep)→ 5.5%(16 ep)→ 10%(40 ep) |
| 200K | 0.58 | 第 8 epoch | 1.61 | 微涨到 1.62(10 ep);gap 从 −0.14 涨到 +0.51 | ≤ 1% |
| 2M | 0.06 | 第 10 epoch 仍在降 | 1.60 | — | 0% |
三行是同一个模型、同一份代码,只有数据量不同:2 万字符时第 16 个 epoch 起 held-out loss 掉头上升、训练 loss 继续降、模型开始逐字背源码;20 万字符时拐点推到第 8 个 epoch、记忆率几乎为零;200 万字符时 10 个 epoch 还没到拐点。数据每多 10 倍,能安全训练的 epoch 数就多几个,而”背下来”只在参数明显多于数据时发生。
2. SFT
SFT 就是第一行的体制:8B 模型、几千到几万条指令(几百万 token),参数是 token 的上千倍。典型现象:训练 loss 平滑下降,验证 loss 在第 2–3 个 epoch 后回升,模型对训练集里的问题逐字复述回答、对稍微改写的问题失去泛化。当前实践因此把 SFT 控制在 2–3 个 epoch;数据量大(几十万条以上)时 1–2 个;学习率比预训练小一个量级(上一篇第六章)也是同一个考虑——不要把预训练学到的表示冲掉。检测手段就是上面的记忆探针:拿训练集的 prompt 让模型生成,与训练集的回答比对。
3. 奖励模型
奖励模型是第一章表的最后一行,也是 L2 导读讨论过的 reward hacking 的来源:几万到几十万偏好对训一个 8B 模型,1 个 epoch 之后验证准确率就不再涨、2 个 epoch 后开始降,是常见的经验。它比 SFT 更敏感,因为它的输出是一个标量,”背下来”的方式是记住表面特征(长度、格式、语气)——这些特征在训练集上与偏好相关,被 RL 优化后放大到荒谬。经典解法都适用:更多更多样的数据、早停、集成、正则化(KL 惩罚)。
八、诊断
| 信号 | 看什么 | 含义 |
|---|---|---|
| 训练 / 验证 gap | 两条 loss 曲线的差 | gap 持续扩大是过拟合的定义;gap 为负(第七章 200K 那行的前两个 epoch)说明验证集比训练集”容易”,正常 |
| 验证 loss 的拐点 | held-out loss 从降到升的那个 epoch | 早停的位置;数据越少拐点越早 |
| 测试 loss 涨而准确率不变 | 第五章的表 | 模型在变得过度自信,不是在判错更多样本;校准变差;对需要概率的下游(judge、RM)有害 |
| 记忆探针 | 给训练样本的前缀,看模型是否逐字复现 | 直接检测”背下来”;SFT 与预训练的污染检测都用它 |
| 参数 / 数据比 | 第一章的表 | 训练开始前就能算出来:比值大于 1 就要准备早停与验证集,大于 100 就要预期几个 epoch 内过拟合 |
最后一条最便宜、也最常被忽略:训练之前先算参数 / 数据比,就知道该不该担心过拟合。预训练不必担心,SFT 与 RM 一定要。
九、实验
1. 代码
在系列基座上加两个层:Dropout(inverted,训练 / 推理两种模式)与 Embedding(查表;反向用 np.add.at 把梯度散射回对应的行)。三组实验:
# double descent:4000 样本、20% 标签噪声、宽度扫描、Adam 全 batch 6000 步
for w in [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]:
net = Net([Linear(784, w), ReLU(), Linear(w, 10)])
... # 训到收敛,记录训练 / 测试错误率与测试 loss
# 正则化:1000 样本、宽 512、四种 (wd, dropout) 组合、300 epoch,记录 train / test loss 曲线
# 小语言模型:Embedding(V, 16) → Linear(8*16, 512) → ReLU → Linear(512, V)
# 数据 2 万 / 20 万 / 200 万字符,每 epoch 测 held-out loss 与逐字记忆率
2. 结果
Double descent 与正则化的完整表在第二、五章。小语言模型 2 万字符一组的逐 epoch 数字:
| epoch | 训练 loss | held-out loss | 逐字记忆率(32 字符) |
|---|---|---|---|
| 1 | 3.166 | 3.091 | 0.0% |
| 4 | 2.353 | 2.596 | 0.5% |
| 8 | 1.790 | 2.431 | 2.5% |
| 16 | 1.213 | 2.408 | 5.5% |
| 24 | 0.850 | 2.527 | 5.5% |
| 32 | 0.606 | 2.734 | 7.0% |
| 40 | 0.438 | 2.975 | 10.0% |
三组实验在笔记本 CPU 上共约 6 分钟(double descent 一组最慢,宽度 2048 的全 batch 训练占了大半)。
3. 值得自己动手的扩展
- 把标签噪声从 20% 改成 0% 重跑 double descent,看尖峰是否消失(没有噪声时插值阈值上的解不需要扭曲);
- 固定宽度 64,扫训练步数 100 到 20000,看 epoch-wise double descent;
- 在小语言模型上把参数量放大 10 倍(宽 5120),看 200K 字符那一行的拐点提前到哪里、记忆率升到多少。
十、本文小结
- 先算参数 / 数据比:LLM 预训练是 0.0005(数据主导,欠拟合体制),SFT 与奖励模型是几百到上千(过参数化体制)。两个体制的规则不同。
- 过参数化体制里深网络不过拟合,是因为优化器挑解:最小范数、平坦极小值、先学简单的——隐式正则化。容量不是度量,网络能背下随机标签(Zhang 等 2017)。
- Double descent:测试误差随容量先降、在插值阈值处尖峰(实测宽度 8 处测试 loss 5.48)、再一路下降(宽度 2048 最好,参数是样本的 407 倍)。不要停在阈值附近;加数据可能让同一模型变差。
- 显式正则化各是一种先验:weight decay 是高斯先验(实测把测试 loss 的上升从 0.55 压到 0.42);dropout 是集成 / 噪声(拖慢过拟合,不改变终点);早停利用”先学简单的”(最便宜);label smoothing 改善校准但压低置信度。
- LLM 预训练不用 dropout、只训一个 epoch,因为在数据主导体制里容量是瓶颈;数据受限时重复到 4 个 epoch 以内几乎无损,之后收益递减(Muennighoff 等 2023)。
- 过拟合在 SFT 与奖励模型上回来:小语言模型实验里 2 万字符时第 16 个 epoch 起 held-out loss 回升、逐字记忆率升到 10%;20 万字符拐点在第 8 个 epoch;200 万字符 10 个 epoch 未到拐点。
- 诊断:训练 / 验证 gap、held-out 拐点、测试 loss 涨而准确率不变 = 过度自信、记忆探针。
- 下一篇回到结构史:卷积网络解决了什么、ResNet 留下了什么、ViT 怎么把图切成 token。
配套代码:deep-learning-foundations/04_regularization.py——dd / reg / lm 三个子实验,完整跑约 15 分钟,--quick 两分钟;字符级语料用的是 Python 自带的标准库源码,不需要下载。
下一篇
本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/regularization-and-generalization.html)的前提下,欢迎各种形式的转载、翻译或商业引用。
COMMENTS
评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。