一个超大的梯度会把模型甩飞吗?
本课一问:学习率的时间表定好了。可训练几万亿 token,不可能每一批都正常;只要有一批让梯度一下子大了几十倍,这一步会把模型甩飞吗?
这一课先不碰:开着裁剪为什么大模型还会出 loss spike、以及怎么按住注意力 logit(C8 兑现);FP16 下梯度溢出成 inf(C9)。
从已有出发
正常的一批——比如一批「小明把苹果给了小王」这样的句子——给出的梯度,长度 ‖g‖ 通常在一个稳定的范围里。异常的一批会让 ‖g‖ 突然大几十倍。按前几课的规则,这一步会照单全收。这一课第一次碰训练循环里「裁剪」那一格:
大模型里的异常批次没法在页面上复现,所以用浏览器里的小模型做一个受控实验:第 150 步那一批的输入 embedding 整体放大 k 倍(模拟一次数值异常),其余一切照常,三个 run 共用同一个初始化和取批顺序。这个「放大」是我们人为注入的,不是小模型自己出的事——注入之后发生的一切则都是真算出来的。
先自己设计:不管它
最省事的是不做任何处理:梯度多大,就按多大去更新。
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
数字来源:浏览器实时训练(纯 JS,Float64)。模型、语料、种子写在上方;同一组对照里所有 run 共用初始化和取批顺序,只改被比较的那一项。换种子重跑可以看到噪声有多大——差异小于种子间波动时不下结论。
试三件事:① 默认设置读第一行:‖g‖ = 49.9、裁剪系数 c/‖g‖ = 0.0401;第二行:全局裁剪的方向余弦是 1,而「逐元素截断到 ±c/√P」的方向余弦只有 0.584——那种做法把方向也改了。② 把注入倍数切到 ×100:不裁剪的验证 loss 冲到约 5.26,第 300 步还有 3.94;裁剪的那条线纹丝不动。③ 把阈值切到 1:第三行显示 300 步里每一步都被裁了——这个小模型平时的 ‖g‖ 约 1.2–1.4,阈值 1 让裁剪从「拦异常」变成「每步把梯度长度固定成 1」,等于换了一种优化器。阈值要看自己模型的典型范数。
为什么会甩飞:它不止影响一步
动量 SGD 的位移是 η × v,而 v 会把这次的超大梯度存下来:β = 0.9 时,它在之后每一步里还剩 0.9、0.81、0.73……倍。一次异常,推着参数连走好几步,验证 loss 在后面几十步里一直偏高。
裁剪把这一步的 g 缩到长度 c,方向不变 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条g·min(1, c/‖g‖) 是 g 乘一个正数:方向不变,长度 ≤ c 出处待补 ;v 里存进去的也就是一个正常大小的梯度。阈值取在典型范数之上,平时几乎不触发,只拦异常——这是 Google 调参手册给的思路:阈值略高于「典型」梯度范数 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条经验规则;组件里用第 100–149 步的 90% 分位作参照 出处Deep Learning Tuning Playbook ↗需联网 。
Adam 自带一层保护,但有代价
把优化器切到 AdamW 再跑一遍(组件里「优化器」选 AdamW):同样注入 ×30,不裁剪的验证 loss 只到约 3.78。原因在分母:
- 这一步最多走多远:尖峰梯度 g 进来,m ≈ (1 − β₁)g,v ≈ (1 − β₂)g²,位移约 η·(1 − β₁)/√(1 − β₂)。β₂ = 0.95 时是 0.447η——比平时还小 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条设尖峰远大于历史梯度,代入 Adam 更新式即得;β₂ = 0.999 时是 3.16η 出处待补 。
- 之后很多步都变小:v 被这一下放大了。设平时 v ≈ gtyp²、尖峰是 k 倍,那么 v 变成约 (β₂ + (1 − β₂)k²) 倍。k = 100、β₂ = 0.95 时是约 501 倍,之后的步长缩小约 22.4 倍,要约 121 步才衰减回来;β₂ = 0.999 时放大约 11.0 倍、步长缩小约 3.3 倍,但要约 2,397 步才恢复 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条稳态近似 v ≈ g_typ² 下的推导;真实训练中 v 本身随时间变化,只看量级 出处待补 。
所以对 Adam 来说,裁剪的作用是让尖峰根本进不了 m 和 v,而不是救这一步。
真实配置:GPT-3 把全局梯度范数裁剪在 1.0;DeepSeek-V3 的 gradient clipping norm 是 1.0 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条两份报告原文;PaLM 也是 1.0(R4 摘录) 出处DeepSeek-V3 §4.2;GPT-3 附录 B ↗ 。但 PaLM 开着裁剪,训练中仍出现了约 20 次 loss spike(R4 从原文摘录)——裁剪只拦得住「单步梯度太长」,拦不住「模型内部某个量在慢慢失控」。
项目里的同一个实验(第 200 步注入异常 batch,开 / 关裁剪,AdamW 与动量 SGD 各一对;项目代码已有 inject_step、inject_kind、inject_loss_scale 三个配置项):
读这张表要分两对看(数字来自 c05-optimization.json,2026-09-26 导出):
- AdamW 这一对是干净的对照:不注入时开 / 关裁剪 4.626 / 4.661;注入后开裁剪 4.624,关裁剪 4.681。注入那一步的梯度范数是 131.7,裁剪把它压住,最终几乎不留痕迹。
- 动量 SGD(学习率 0.1)这一对不能这么读:关掉裁剪后,不注入也停在 6.684(开着裁剪是 5.405)。所以「注入 + 关裁剪」的 6.654 不能算在异常 batch 头上——在这个学习率下,是裁剪本身在让 SGD 训得动。要单看异常 batch 的影响,得先把学习率降到不开裁剪也稳的范围。
⚠ 常见误解
「裁剪会改变梯度的方向。」全局范数裁剪不会:所有分量乘同一个正数。会改方向的是「逐元素截断」(每个数单独夹到 ±τ),组件里它的方向余弦只剩 0.584。
「开了梯度裁剪,就不会有 loss spike。」PaLM 开着 c = 1.0 的裁剪,仍然出现了约 20 次 spike。裁剪管的是单步的梯度长度;spike 的其他来源(注意力 logit 失控等)要靠别的手段,C8 专门讲。
「阈值 1.0 是标准答案。」1.0 是 GPT-3、DeepSeek-V3 的选择,不是物理常数。我们的小模型平时 ‖g‖ 就在 1.2–1.4,阈值 1 会让每一步都被裁。先看自己模型的典型范数(项目日志里的 grad_norm),再定阈值。
在演进地图上的位置
- 全量梯度下降
- 小批量随机梯度下降 SGD
- 动量(重球法)
- Adam:每个参数一把尺子
- AdamW:解耦权重衰减
- 学习率 warmup
- 学习率衰减:cosine → WSD
- batch 大小:渐增,还是一开始就大
- 全局梯度范数裁剪
- Muon:按矩阵的奇异方向更新
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 偶发的超大梯度一步把参数推远,还通过动量(或 Adam 的 v)影响之后很多步 |
| ② 数学上为什么可能有效? | 裁剪后长度 ≤ c、方向不变 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条乘一个正的标量 出处待补 ;动量 SGD 与 Adam 对单次尖峰的反应可以直接代入更新式推出 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条见上文两条推导(Adam 那条用了稳态近似) 出处待补 |
| ③ 实验上为什么最后选了它? | RNN 上的梯度爆炸(Pascanu 2012);此后几乎所有 LLM 预训练都开着裁剪(GPT-3、PaLM、DeepSeek-V3 都是 1.0) 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条「开着就好」是经验共识;阈值的选择没有理论 出处Pascanu et al. 2012;GPT-3;DeepSeek-V3 §4.2 ↗需联网 |
一句话
裁剪只缩长度、不改方向:把一次离谱的梯度变成一次普通的步子。
先停在这里:记住「阈值略高于典型范数,平时不触发」,进下一课。
再深一层:在组件里把优化器切到 AdamW,对照上面 v 被放大的推导,看注入之后的几十步里 loss 曲线是不是下降得更慢。
下一个缺口
到这里,每一个参数都被照顾到了:有自己的尺子(Adam)、有干净的衰减(AdamW)、开局有 warmup、后期会降、异常会被裁。可第 2 课的旋转峡谷留了一个问题:Adam 只认坐标轴。一个权重矩阵,被 Adam 当成一堆互不相干的数——浪费了什么? → 第 7 课:Muon
回扣先记下:PaLM「开着裁剪仍有约 20 次 spike」会在 C8(稳定性)兑现。
你可能会问
这一课还没有收到真实提问;你的提问会记到 05-问答记录/C5-L6.md。
🔍 除了裁剪,真实训练里遇到 spike 还怎么办?
常见的事后处理是「回滚到 spike 之前的 checkpoint,跳过出事的那几百个 batch 再继续」——PaLM 报告里就是这么做的:从 spike 前约 100 步的 checkpoint 重启,跳过 200–500 个 batch。报告还发现,把同一批数据喂给更早的 checkpoint 并不会 spike——spike 是「特定数据 × 特定参数状态」一起触发的,不是单纯的坏数据(R4 从 PaLM §5.1 摘录)。这属于稳定性工程,C8 展开;本课只讲「每一步」层面的裁剪。