L2 经典机器学习系列讲过经典机器学习的第一定律:模型容量超过数据量就会过拟合。一个 160 万参数的网络在 4000 个样本上训练,按这条定律应该背下每一个样本、在测试集上一败涂地。第九章的实验里它做到了前半句——训练错误率 0.0%——却没有做到后半句:测试错误率 14.5%,是所有宽度里最好的。而一个参数只有样本两倍的小网络,测试错误率 43%。

这个现象——参数越多泛化越好,越过某个点之后——在 2017 年前后被系统地记录下来(Zhang 等 2017;Belkin 等 2019),它推翻的不是”过拟合”这个概念,而是”容量”这个度量。本篇讲三件事:为什么深网络在参数远多于样本时不过拟合(隐式正则化与 double descent);显式正则化手段各自在做什么(dropout、weight decay、早停、label smoothing);以及什么时候过拟合会回来——因为它一定会回来,SFT 训几个 epoch 之后模型开始逐字背答案,就是它。全篇的核心问题是:

一个参数量是样本量几百倍的网络为什么不过拟合?1 同一个网络在什么数据规模、多少个 epoch 之后会开始过拟合?2 怎么提前看到?3

双下降曲线:测试误差在插值阈值处达到峰值,之后随参数量继续增大而再次下降

一、总览:两个区间

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 的来源

把五行放到一根对数刻度的数轴上:

五个场景的参数 / 数据比放到对数刻度数轴上:Llama-3 预训练 0.0005、Chinchilla 最优 0.05、参数 = 数据 1 在左侧蓝色的数据主导区间(背不下数据,过拟合不是主要风险),奖励模型 160、本文实验 407、SFT 1600 在右侧橙色的过参数化区间(能背下数据,会不会过拟合看训多久、有没有噪声)

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,000 张图的过拟合与解药
  • 训练 loss 归零、测试 loss 掉头的曲线
  • weight decay / dropout / 早停各怎么改变它
  • 宽度扫描画出 double descent
  • 小语言模型的逐字记忆
十 本文小结  
十一 自测 5 道题

3. 来龙去脉:一个理论被现实推翻的过程

正则化与泛化理论的来历
年 谁 当时的问题 怎么解决 效果 引入或遗留问题
1943 / 1970
  • Tikhonov
  • Hoerl & Kennard
病态的方程组解乱跳 给解加一个”小一点”的偏好 正则化这个词与 weight decay 的源头(第五章第 1 节,《LLM 时代的经典机器学习(03):逻辑回归与奖励模型——每个分类头的原型》的 Ridge) 为什么”小一点”的解泛化更好,当时没有理论
1971–1995 Vapnik & Chervonenkis 什么决定一个模型在新数据上的表现 统计学习理论:用容量(VC 维)刻画泛化界,容量越大、样本越少,泛化越差 第二章的经典预言:模型不能比数据大——统治了几十年的泛化观 深网络参数远超样本却泛化得很好,预言在那里失效
2012 / 2014
  • Hinton 等
  • Srivastava 等
AlexNet 6000 万参数、120 万张图,全连接层严重过拟合 Dropout:训练时随机丢一半神经元,强迫表示冗余(第五章第 2 节) 2012–2017 年几乎每个网络都用 LLM 预训练一个 epoch 背不下数据,反而用不上它(第六章)
2017 Zhang 等,《Understanding deep learning requires rethinking generalization》 深网络参数远多于样本,按 VC 理论应该毫无泛化能力,却泛化得很好 用实验证明同一个网络能把随机标签背到 100%(第四章) 容量不是度量,经典理论对深网络失效——泛化理论被公开动摇 那泛化到底随模型大小怎么变,没有解释
2018–2019
  • Belkin 等
  • Nakkiran 等
那泛化到底随模型大小怎么变 double descent:画出测试误差先降、在插值阈值升到尖峰、再降的曲线(第三章) “越大越好”在阈值右边成立——泛化随容量的新图景 解释的是现象不是机制:SGD 为什么恰好落在泛化好的解上仍无理论
2020 GPT-3 1750 亿参数、3000 亿 token、一个 epoch 不靠显式正则化:参数 / 数据比大到根本背不下数据 预训练不用 dropout、只训一遍也能泛化(第六章) 高质量数据总有一天用完
2023 Muennighoff 等,《Scaling data-constrained LMs》 高质量数据用完了怎么办 实测数据重复的边际收益 4 个 epoch 以内几乎无损,之后递减(第六章)
  • 过拟合在 LLM 里以”多 epoch”的形式回来
  • 小数据微调仍需要正则化

这张表讲的是一个理论被现实推翻、再被新现象重建的过程:VC 理论说参数不能比样本多,2017 年的实验说深网络就是这么干的还泛化得很好,2019 年的 double descent 说清了”什么时候会出事”(插值阈值附近)、”什么时候不会”(远超过之后)。今天的结论分两个区间(第一节的数轴):数据主导区间里正则化几乎不需要(LLM 预训练),过参数化区间里需要(SFT、奖励模型、小数据微调)——而后者正是大多数人实际做的事。

二、经典视角与它失效的地方

1. 经典的预言

L2 经典机器学习系列的偏差 - 方差分解说:模型太简单,偏差大(欠拟合);模型太复杂,方差大(过拟合);测试误差随容量呈 U 形,最优点在中间。经典理论里”容量”由参数量(或 VC 维一类的度量)决定,所以规则是:参数量不要超过样本量太多,超过了就加正则化。

2. 现实

第九章的实验:4000 个 MNIST 样本,标签有 20% 被随机替换(制造噪声,让”背下训练集”与”学到规律”能区分),两层 MLP 宽度从 2 扫到 2048,Adam 训 6000 步到收敛:

Double descent:宽度扫描下的训练与测试错误率
宽度 参数量 参数 / 样本 训练错误率 测试错误率 测试 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 倍的网络,泛化最好。

经典理论没有错,错的是用参数量当容量。”容量”指的是模型能表示多复杂的函数;经典理论假设参数越多能表示的函数越多、就越容易学到花里胡哨的解。但参数量只说了”能表示什么”,没说”训练会挑到哪一个”。一个例子:宽度 2048 的网络能拟合训练集的参数组合有无穷多种,其中有的把 20% 错误标签用极其扭曲的函数硬记下来,有的用一个几乎平滑的函数顺带记住——两者训练误差都是 0,但测试表现天差地别。梯度下降从小的随机初始化出发、一步步走,总是先找到那些平滑的、简单的解,扭曲的解它根本走不到。所以深网络的”有效容量”由训练过程决定:优化器会在所有能拟合训练集的解里挑一个特定的,而这个解的复杂度远低于参数量所允许的。这就是隐式正则化(第四章会展开:为什么梯度下降偏爱简单解、这和 weight decay 有什么区别)。在讲它之前,先看清那个尖峰。

三、Double descent

1. 曲线的三段

上面的表画出来是 Belkin 等 2019 命名的 double descent:

double descent 示意曲线:测试误差随模型容量先降出经典 U 形,在插值阈值(训练误差刚到 0 的地方)冲出尖峰,之后在过参数化区间第二次下降;训练误差单调降到 0

第九章的宽度扫描画出来:

4,000 张、20% 错标签的 MNIST,隐藏层宽度从 2 到 2048:测试错误率从 60% 降到 43%、在宽度 8–16(训练错误率刚到 0)处测试 loss 冲到 5.5,之后一路降到 14.5%

Nakkiran 等 2019 在 CIFAR-10 上的 ResNet-18 画出了同一个形状——下图是他们论文的图 1,横轴是网络宽度,测试误差在”插值阈值”处凸起再下降,而且训练更久(右图)尖峰更明显:

Nakkiran 等 2019《Deep Double Descent》图 1:CIFAR-10 上 ResNet-18 的测试误差随宽度先降、在插值阈值升起、再降;训练误差单调降到 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)有系统性的偏好:

最小范数解。 对线性模型(以及某些条件下的宽网络),从零初始化出发的梯度下降收敛到所有插值解中范数最小的那个——梯度始终落在数据张成的子空间里,垂直方向的分量从不被更新。范数小意味着函数平缓。

一个能算到底的例子:单样本 \(x = (1, 2)\)、\(y = 5\) 的线性回归,解集是直线 \(w_1 + 2 w_2 = 5\) 上的所有点。平方损失上从零初始化出发的梯度下降,轨迹一直停在 \(x\) 张成的方向上,收敛到 \(w^* = (1, 2)\)——直线上范数最小的点,\(\|w^*\| = \sqrt{5} \approx 2.236\);同样插值的解 \((5, 0)\) 范数是 \(5\)。优化器没有“选择”去最小范数点,是初始化加上梯度方向把它钉在了那里。

最小范数解:解集 w1 + 2 w2 = 5 是一条直线;从原点出发的梯度下降轨迹收敛到直线上离原点最近的点 w* = (1,2)(范数 √5 ≈ 2.236),而不是同样插值但范数 5 的解 (5,0)

平坦极小值。 SGD 的噪声(《深度学习基础(06):优化器——从 SGD 到 AdamW 与学习率调度》「SGD 与它的噪声」中的尺度 \(\eta / \sqrt{B}\))让参数在尖锐的极小值里待不住——一点扰动就跳出去——而在平坦的极小值里能停下。平坦的极小值对参数扰动不敏感,通常也对输入扰动不敏感,泛化更好。这解释了一个反复被观察到的现象:大 batch(噪声小)训出来的模型泛化略差,即使训练 loss 相同;也解释了为什么学习率衰减要留到最后——早期的大噪声是在挑盆地。

先学简单的。 网络在训练早期先拟合低频、简单的模式,晚期才去拟合高频、复杂的(包括噪声标签)。这就是早停有效的原因:在网络开始拟合噪声之前停下。

效果:第二章的表里,参数是样本 407 倍的网络测试错误率 14.5%、全表最好——优化器挑出来的解远比参数量允许的简单。隐式正则化不收任何费用:不用改 loss、不用加约束,它是训练过程自带的。

遗留问题:这些偏好不可控——它们是优化器与超参(学习率、batch、初始化)的性质,换一个优化器、换一组超参,挑到的解就不同;也没有任何机制保证它挑的解“简单”——下面 Zhang 等 2017 的实验说明,只要数据里没有结构可学,网络照样子背下随机标签。

2. 随机标签也能背下来

Zhang 等 2017 做了一个干净的实验:把 CIFAR-10 的标签全部打乱成随机的,标准的 CNN 照样能把训练集拟合到零误差。结论是网络的容量足以记住任何数据,所以容量不是它在真实数据上泛化好的原因。泛化好,是因为真实数据里有结构、优化器先学结构、结构比噪声容易拟合。

这个实验也给出了本篇最有用的一个诊断工具:记忆探针——如果模型能逐字复现训练样本,它在背,不是在学(第七、八章)。

五、显式正则化

隐式正则化是免费的;显式正则化是额外加的约束,每种都可以理解为”把模型拉向某种简单解的先验”。五种常用手段放在一起:

五种显式正则化手段总览
方案 针对的问题 怎么解决 效果(第九章实测) 引入或遗留问题
Weight decay 参数无约束地涨、函数越走越扭曲 给参数一个高斯先验:loss 加 \(\lambda|\theta|^2\),或 AdamW 里解耦衰减 测试 loss @300 epoch 从 0.548 压到 0.422(\(\lambda = 0.5\)),改变过拟合的终点
  • \(\lambda\) 要调
  • 在 AdamW 里更多是范数平衡点而非防过拟合(第六章)
Dropout 神经元共适应、有效容量太大 训练时以概率 \(p\) 置零并乘 \(1/(1-p)\) 保持期望 \(p = 0.5\) 前 10 epoch 与 weight decay 相当(0.39),300 epoch 后反而最差(0.62)
  • 只拖慢过拟合的速度、不改变终点
  • 伤容量,LLM 不用
早停 训练后期网络开始拟合噪声 验证 loss 的拐点处停止 无正则时最优点在第 10 epoch(测试 loss 0.40),之后涨到 0.55
  • 要留验证集
  • 只决定何时停,不改变解的性质
Label smoothing one-hot 逼着 logit 差走向无穷、模型过度自信 正确类目标换成 \(1-\epsilon\),其余类均分 \(\epsilon\) 改善校准(本章用一条曲线说明机制) 置信度被人为压低,对蒸馏、奖励模型等下游要小心
数据增强 数据量不足、多样性不够 给数据加”这些变换不改变标签”的先验 CV 里最强的正则化 LLM 预训练几乎不用:数据够多,语言的标签不变变换难定义

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\)。它说的先验是”参数应该小”,也就是”函数应该平缓”。《深度学习基础(06):优化器——从 SGD 到 AdamW 与学习率调度》的「五、AdamW 与 L2 正则的区别」讨论了它在 Adam 下的正确形式与它在 LLM 里作为”范数平衡点”的含义。

一个数字感受它的力度:AdamW 的解耦衰减是每步把参数乘 \(1 - \eta\lambda\)。\(\eta = 10^{-3}\)、\(\lambda = 0.5\) 时这个因子是 \(0.9995\);如果梯度是零,一个权重 1000 步后收缩到 \(0.9995^{1000} \approx 0.606\)——衰减不依赖 loss,它一直把参数往零点拉。

效果:第九章的实验里,1000 个样本、AdamW \(\lambda = 0.5\),300 个 epoch 后测试 loss 是 0.42,不加是 0.55——weight decay 把训练后期测试 loss 的上升压平了大半。

引入或遗留问题:\(\lambda\) 要自己调;而且在 AdamW 语义下它控制的是参数范数的平衡点,”防过拟合”只是小数据场景下顺带的效果(第六章)。

2. Dropout:期望等价与集成

问题:神经元相互依赖地共同适应——一个特征靠另一个特征在场才有用,换一个数据分布就散架;同时有效容量太大,足以背下训练集。

怎么解决:Dropout(Srivastava 等 2014)训练时以概率 \(p\) 把每个激活置零,并把剩下的乘 \(1 / (1 - p)\)(inverted dropout,让期望不变:\(\mathbb{E}[\tilde a] = (1 - p) \cdot a / (1 - p) = a\));推理时什么都不做。

一个具体例子看乘性缩放:激活 \(h = [2, 4, 6, 8]\)、\(p = 0.5\)、这一步的 mask 是 \([1, 0, 1, 0]\),那么训练时的输出是 \([2, 0, 6, 0] \times 2 = [4, 0, 12, 0]\)——被保留的单元放大一倍,被丢掉的输出零;每个单元的期望不变,推理时既不做 mask 也不做缩放。两种理解:

Srivastava 等 2014《Dropout》图 1:左边是一个两层隐藏层的标准网络,右边是训练时随机丢掉一部分神经元(画 ×)之后的"瘦网络"——每个 batch 用一个不同的瘦网络。图片版权归原作者,此处为教学评述引用

集成视角。 每个 batch 用的是一个随机子网络,\(n\) 个神经元有 \(2^n\) 个子网络,训练在同时训所有子网络的一个加权平均;推理时用完整网络,近似于这 \(2^n\) 个子网络的集成(L2 经典机器学习系列第一篇:集成降方差)。

噪声视角。 Dropout 给激活注入乘性噪声,逼迫每个神经元不依赖特定的其他神经元(防止”共适应”),学到的特征更冗余、更稳健。做过分布式系统的读者会认出这个思路:它和混沌工程随机杀掉节点是同一件事——训练时随机让一部分神经元”宕机”,剩下的必须能独立把任务做完,于是没有任何一个特征能成为单点。区别在于混沌工程验证的是既有系统的冗余,dropout 是在训练过程中逼出冗余;推理时所有神经元都在,相当于一个从未同时演练过的满配集群,inverted dropout 的 \(1/(1-p)\) 缩放就是为了让满配时的输出尺度与演练时一致。

效果:第九章的实验里 \(p = 0.5\) 在前 10 个 epoch 与 weight decay 效果相当(测试 loss 0.39),但训到 300 个 epoch 后反而是最差的(0.62):dropout 拖慢了过拟合的速度,没有改变它的终点。

引入或遗留问题:训练时有效容量变小(每步只有 \((1 - p)\) 的神经元在工作),收敛更慢;对已经有足够数据的场景收益小甚至为负——第六章讲 LLM 为什么不用它。

3. 早停

问题:训练后期网络开始拟合噪声——held-out loss 掉头向上时,训练 loss 还在往下走。

怎么解决:在验证 loss 开始上升时停止训练。对线性模型可以证明它等价于 \(L_2\) 正则,正则强度反比于训练步数 × 学习率(\(\lambda \approx 1 / (\eta t)\));对深网络它是第四章”先学简单的”的直接利用。

效果:第九章里不加正则化时最优停止点在第 10 个 epoch(测试 loss 0.40),之后一路涨到 0.55——停在那里等于白捡一个正则化。

引入或遗留问题:它几乎没有代价,只要求留出验证集并定期评估——这是横切”实验方法论”里最便宜的一条规则;但它只决定何时停,模型本身学到的还是不加约束的解。

4. Label smoothing 与数据增强

Label smoothing(Szegedy 等 2016):

问题:one-hot 标签下交叉熵只有在 logit 差趋于无穷时才到零——训练会一直推着正确类的 logit 往上走,模型对一切判断都过度自信,校准很差。

怎么解决:把 one-hot 换成 \((1 - \epsilon)\) 在正确类、\(\epsilon / (C - 1)\) 均分给其他类,\(\epsilon\) 常取 0.1。算一条曲线看效果:\(C = 10\)、\(\epsilon = 0.1\) 时正确类目标是 \(0.9\)、其余每类 \(0.1 / 9 \approx 0.0111\);设正确类与错误类的 logit 差为 \(g\)(logits 取 \((g, 0, \dots, 0)\)),one-hot 的交叉熵随 \(g \to \infty\) 单调趋 0,而平滑后的 loss 在 \(g^* = \ln(0.9 \cdot 9 / 0.1) = \ln 81 \approx 4.394\) 处取最小值——logit 差再大 loss 反而上升,模型被推回有界的自信。

label smoothing 的机制:横轴是正确类与错误类的 logit 差 g,one-hot 目标的交叉熵随 g 增大一直下降到 0,平滑目标(ε = 0.1)的交叉熵在 g* = ln 81 ≈ 4.394 处到最小点后回升

效果:改善校准——输出的概率更接近真实正确率。

引入或遗留问题:模型的置信度被人为压低,对需要校准概率的下游(蒸馏、奖励模型)要小心。

数据增强(图像的裁剪翻转、文本的回译与同义替换):

问题:数据量不足、多样性不够。

怎么解决:给数据加先验:”这些变换不改变标签”。

效果:CV 里最强的正则化。

引入或遗留问题:在 LLM 预训练里几乎不用——数据够多,且语言的”标签不变的变换”难以定义。

5. 实测:四种配置

1000 个训练样本、宽 512、AdamW \(\eta = 10^{-3}\)、300 个 epoch:

四种正则化配置的测试 loss 与早停点
配置 第 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%

三幅子图:左,四种配置 300 个 epoch 的训练 loss 全部归零;中,测试 loss——无正则从第 10 个 epoch 起一路升到 0.55,dropout 升到 0.62,加了 weight decay 的两条几乎水平,每条线上的圆点标出各自的最优早停点;右,测试准确率四条线都在 89%–90% 之间几乎不动

四种配置的准确率都在 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 保留了下来,但如《深度学习基础(06):优化器——从 SGD 到 AdamW 与学习率调度》所述,它在这里的角色主要是控制参数范数与有效学习率,不是防过拟合。

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 还没到拐点。把第一行的逐 epoch 数字画出来:

小语言模型 2 万字符:左,训练 loss 与 held-out loss 逐 epoch 曲线——held-out 在第 16 epoch 到最低点 2.408(圆点标注)后升到 2.975,训练 loss 继续降到 0.438;右,同一时刻逐字记忆率一路上升到 10%

数据每多 10 倍,能安全训练的 epoch 数就多几个,而”背下来”只在参数明显多于数据时发生。

2. SFT

SFT 就在第一行那个区间:8B 模型、几千到几万条指令(几百万 token),参数是 token 的上千倍。典型现象:训练 loss 平滑下降,验证 loss 在第 2–3 个 epoch 后回升,模型对训练集里的问题逐字复述回答、对稍微改写的问题失去泛化。当前实践因此把 SFT 控制在 2–3 个 epoch;数据量大(几十万条以上)时 1–2 个;学习率比预训练小一个量级(《深度学习基础(06):优化器——从 SGD 到 AdamW 与学习率调度》的「峰值学习率的量级」)也是同一个考虑——不要把预训练学到的表示冲掉。检测手段就是上面的记忆探针:拿训练集的 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,000 张图的过拟合与解药

问题:前面几章的数字来自三组实验,这一章把它们放在一起说清楚各自在回答什么。三个问题各一个实验:参数比数据多很多倍时会怎样(1,000 张训练图、40 万参数、训 300 个 epoch——第五章的四种配置);模型大小怎么影响泛化(4,000 张、20% 错标签、宽度从 2 扫到 2048——第三章的 double descent);过拟合在语言模型上长什么样(字符级小语言模型、三种数据量——第七章的逐字记忆)。曲线在第三、五章,这里是代码、完整数字和第三组实验。

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 数字:

小语言模型 2 万字符逐 epoch 的 loss 与记忆率
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 涨而准确率不变 = 过度自信、记忆探针。
  • 下一篇进入结构史:《深度学习基础(08):CNN——从 LeNet 到 ResNet,再到 ViT》,看卷积网络解决了什么、ResNet 留下了什么、ViT 怎么把图切成 token。

配套代码:deep-learning-foundations/04_regularization.py——dd / reg / lm 三个子实验,完整跑约 15 分钟,--quick 两分钟;字符级语料用的是 Python 自带的标准库源码,不需要下载。第三、五章的曲线由 case_04_regularization_plots.py 画出,第一、三、四、五、七章的概念图与工作实例由 case_04_concept_figures.py 画出(不需要训练)。

十一、自测

  1. SFT 一个 8B 模型,1 万条指令约 500 万 token;预训练 Llama-3-8B 用 15T token。两者的参数 / 数据比各是多少、在哪个区间?

    答案

    SFT:\(8 \times 10^9 / 5 \times 10^6 = 1600\),过参数化区间,会过拟合;预训练:\(8 \times 10^9 / 1.5 \times 10^{13} \approx 0.0005\),数据主导,每个 token 只见一次,过拟合不是主要风险。

  2. double descent 曲线的尖峰出现在哪里?为什么在那里加数据可能让同一个模型变差?

    答案

    在插值阈值——模型刚好能把训练集(含噪声标签)拟合到零误差的容量处。加数据把阈值往右推,一个原本在阈值右侧(已进入第二次下降)的模型可能正好落到新的阈值上。

  3. dropout \(p = 0.5\) 训练时把激活乘什么?推理时做什么?为什么 LLM 预训练不用它?

    答案

    训练时以 0.5 概率置零、剩下的乘 \(1/(1-p) = 2\) 保持期望;推理时什么都不做。预训练在数据主导区间里容量是瓶颈,dropout 减少有效容量、拖慢收敛,而过拟合本来就不是风险。

  4. weight decay 对应什么先验?实验里它把测试 loss 的上升从多少压到多少?它改变了过拟合的终点还是速度?

    答案

    参数上的高斯先验(MAP 视角);从 0.55 压到 0.42;改变了终点——它限制了参数范数,模型能背下的东西变少。相比之下 dropout 只拖慢速度、不改变终点。

  5. 验证集上 loss 在涨、准确率却不动——发生了什么?该看哪个探针确认?

    答案

    过度自信:模型对答对的题给出越来越极端的概率,错的题也越来越自信,交叉熵被少数极端错误拉高而 argmax 不变。看校准(预测概率 vs 实际正确率)与记忆探针(逐字复现训练样本的比例)。

  1. 过参数化时能拟合训练集的解有无数个,优化器(SGD / Adam 从小初始化出发)挑的是最小范数、平坦的、「先学简单的」那一个——隐式正则化;容量不是度量,同一个网络能背下随机标签。实测宽度 2048(参数是样本的 407 倍)的测试 loss 反而最好——double descent 的第二次下降。详见第二至四章。 ↩

  2. 参数 / 数据比进入几百到上千的区间、且训练足够多个 epoch。小语言模型实验:2 万字符时第 16 个 epoch 起 held-out loss 回升、逐字记忆率升到 10%;20 万字符拐点在第 8 个 epoch;200 万字符 10 个 epoch 未到拐点。LLM 预训练 \(N/D = 0.0005\) 只训一个 epoch,不在这个区间里;SFT 与奖励模型在。详见第六章、第七章。 ↩

  3. 四条曲线:held-out loss 的拐点、训练 / 验证 gap、测试 loss 涨而准确率不变(过度自信)、记忆探针。早停是最便宜的对策。详见第八章。 ↩

这篇对你有用?

本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/regularization-and-generalization.html)的前提下,欢迎各种形式的转载、翻译或商业引用。


COMMENTS

评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。

×