第 13 章 训练与优化实务
第 13 章 训练与优化实务
第 12 章说明了梯度怎么算;本章回答梯度算出来之后怎么办:参数从什么尺度出发、每层的激活如何保持稳定、深层网络为什么需要残差、半精度为什么需要 loss scaling,以及 loss 不降时按什么顺序排查。
学习目标
读完本章,你应当能够:
- 从"前向激活方差与反向梯度方差守恒"推出 Xavier 与 He 初始化,并解释为什么 ReLU 必须配 He;
- 写出 BatchNorm 的训练与推理公式,说明 running statistics 的作用,并辨析 BN / LN / RMSNorm / GroupNorm;
- 分析
y = x + F(x)的梯度流,解释残差为什么能缓解深层退化; - 说明 FP16 与 BF16 的动态范围差异、loss scaling 的必要性,以及哪些算子必须保持高精度;
- 区分按值裁剪与按范数裁剪,并给出 warmup、裁剪、混合精度、梯度累积、权重衰减解耦各自解决的问题;
- 按"由简到繁"的顺序排查 loss 不降与 NaN,并会做学习率范围测试。
前置知识检查
| 你需要知道 | 在哪一节补齐 |
|---|---|
| 前向与反向传播、梯度的形状规则 | 第 12 章 12.6 节,本章 13.1、13.3 节直接使用 |
| 期望、方差、独立随机变量方差可加 | 第 4 章;13.1 节的推导只用到 Var(Σ) = Σ Var 与 Var(cX) = c²Var(X) |
| 学习率、动量、Adam 的更新式 | 第 6 章;13.5、13.6 节用到"二阶矩估计"与权重衰减 |
| 交叉熵与 Softmax 的数值稳定性 | 第 5 章与第 12 章 12.6.2 节 |
| L2 正则与早停 | 第 8 章;13.2、13.6、13.7 节反复引用 |
13.1 参数初始化
13.1.1 全零初始化的对称性
若把同一层所有神经元的权重与偏置都初始化为 0,则每个隐单元的前向输出完全相同(都是同一个常数),反向收到的梯度也完全相同,更新后依然完全相同。结论:宽度 m 的网络在功能上等价于 1 个神经元,容量被完全浪费。对称性必须由权重的随机性打破(偏置置零是安全的:只要权重不同,各单元的加权输入就不同)。
反过来,把权重初始化得过大或过小同样致命。设某层的输入维为 fan_in:
| |
- 若
Var(w) ≫ 1/fan_in:每层激活方差被放大fan_in·Var(w)倍,深层指数爆炸(并可能让 Sigmoid/Tanh 立即饱和); - 若
Var(w) ≪ 1/fan_in:深层激活迅速趋近 0,反向梯度同样衰减为 0。
所以初始化不是"随便给个小数",而是要让尺度跨层守恒。
13.1.2 Xavier / Glorot 与 He 初始化的推导
设权重与输入独立、均值为 0、同分布,且暂时忽略激活函数的非线性(即认为 a ≈ z,这对 Tanh/Sigmoid 的线性区成立):
| |
前向要求 1/fan_in、反向要求 1/fan_out,两者一般不能同时满足,于是取调和平均:
| |
ReLU 破坏了"Var(a) = Var(z)“这个前提:它把约一半的神经元置零,于是 Var(a) ≈ ½·Var(z)。代回前向方差守恒条件:
| |
这就是"ReLU 配 He、Tanh/Sigmoid 配 Xavier"的定量原因:多出的因子 2 正是补偿被 ReLU 砍掉的那一半方差。
| 初始化 | 方差 | 标准差 | 适配激活 | 依据 |
|---|---|---|---|---|
| 全零 | 0 | 0 | 无 | 对称性无法打破,等价于单个神经元 |
| 小常数 | 固定 | 固定 | 无 | 层数一多必然消失或爆炸 |
| Xavier / Glorot | 2/(fan_in+fan_out) | √(2/(fan_in+fan_out)) | Tanh、Sigmoid | 前向与反向方差折中守恒 |
| He / Kaiming | 2/fan_in | √(2/fan_in) | ReLU 及其变体 | 补偿 ReLU 置零的一半方差 |
| |
提示
工程检查法:初始化后做一次前向,逐层打印激活的标准差。若它随层数单调衰减或增长(而不是在 1 附近波动),说明初始化尺度与激活函数不匹配——这比"训练不收敛再去猜"快得多。
13.2 归一化层
13.2.1 BatchNorm:公式与训练 / 推理的差异
BatchNorm(batch normalization)在一个 mini-batch 上统计均值与方差,再标准化并仿射还原:
| |
关键在于训练与推理使用不同的统计量:
| 阶段 | 均值 / 方差来源 | 后果 |
|---|---|---|
| 训练 | 当前 mini-batch 的 μ_B、σ_B² | 输出依赖同 batch 的其他样本;batch 越大越稳 |
| 推理 | 训练期累积的 running statistics(滑动平均)μ_run ← (1−m)μ_run + m·μ_B,σ_run² 同理 | 输出只依赖自身,逐样本确定 |
必须这样做的原因是:推理时 batch 可能只有 1 个样本(在线服务、自回归生成),此时 σ_B² = 0;即使 batch 大于 1,用当前 batch 的统计量也会让同一个输入的输出随同批其他样本变化,预测不可复现。直接用训练 batch 统计量则会造成训练/推理行为不一致的系统偏差(典型 bug 是忘记切换到 eval 模式)。
13.2.2 LN、RMSNorm、GroupNorm 与 BN 的对比
| 归一化 | 归一化维度 | 依赖 batch? | 训练/推理差异 | 额外开销 | 典型场景 |
|---|---|---|---|---|---|
| BN | 同一通道内跨样本、跨空间位置 | 是 | 有(running stats) | 需同步跨卡统计 | CNN、大 batch 视觉任务 |
| LN | 单个样本的特征维 | 否 | 无 | 两次归约 | Transformer、RNN |
| RMSNorm | 单个样本特征维,不减均值、无偏置 | 否 | 无 | 一次归约(比 LN 省一次减法与一次归约) | LLaMA 等大模型 |
| GroupNorm | 单个样本内按通道分组 | 否 | 无 | 中等 | 检测/分割等小 batch 任务 |
| |
RMSNorm 的动机是"尺度不变性才是关键、重新居中贡献很小”,于是砍掉均值项与偏置项,换来更少的归约与更简单的算子(第 19 章会看到它与残差相加的融合)。
为什么 NLP 用 LN 而不是 BN,有三条互相独立的原因:① 序列长度可变、batch 内 padding 位置的有效样本数不同,按位置统计的 μ_B、σ_B² 噪声极大;② 大模型训练时单卡 batch 很小甚至为 1,BN 的 batch 统计量已无意义;③ 自回归生成逐 token 推进,每一步的统计量必须只依赖当前样本,否则同一句话在不同上下文批量下会得到不同结果。
13.2.3 Pre-LN 与 Post-LN:位置决定训练稳定性
Transformer Block 有两种排布(详见第 15 章):
| |
差异出在残差主干上有没有算子阻挡。Pre-LN 中 x_{l+1} = x_l + F(LN(x_l)),从输出到输入存在一条未经任何变换的恒等通路,梯度可以沿它直接回传(见 13.3 节的推导);Post-LN 中主干每层都要穿过一个 LN,梯度尺度在输出附近被显著放大,深层网络必须靠 warmup 才能稳住。因此 GPT 一类现代模型普遍采用 Pre-LN(代价是最终质量有时略逊于调好的 Post-LN,后者有 DeepNorm 等改良方案)。
13.2.4 与推理优化的接口:ResidualAdd + RMSNorm 的融合
归一化与残差相加都是逐元素、访存受限(memory-bound)的算子:几乎不耗算力,耗的是显存带宽。不融合时残差结果要写回 HBM,再被 Norm 读回来,白白多一轮 O(N·d) 的读写;融合成一个 kernel 后,相加结果留在寄存器或 SRAM 里就地归一化。
对应 【2026-09-02 选择 Q4】,正确答案是"避免残差结果写回 HBM,直接在片上完成归一化"。干扰项中"硬件不支持加法"“残差无法计算"与事实相悖,“为了增加 FLOPs"方向搞反——融合的目的是减少访存,计算量增加只会更慢。完整的推理侧分析(存储层级、算术强度、算子融合家族)见第 19 章。
13.3 残差连接
把子层输出写成 y = x + F(x),直接求导:
| |
梯度被分成两项:第一项 ∂L/∂y 原样穿过(系数恒为 1),第二项才是经过子层变换的部分。把它沿 L 层展开:
| |
这正好补上了第 12 章 12.7 节的漏洞:普通深层网络的梯度是若干雅可比的纯连乘,谱半径稍小于 1 就会消失;残差把"连乘"变成"连乘 + 恒等直达”,让梯度有了保底通道。
残差还解决"退化问题(degradation)”:实验发现 56 层普通网络比 20 层的训练误差更高——注意是训练误差,因此不是过拟合,而是优化困难:让一堆非线性层共同表示恒等映射非常难。加了残差后,网络"至少可以学成恒等"(把 F 训成 0 即可),更深的模型不应该比浅的更差。“残差 = 让子层只学残差(修正量)而不是完整映射”:目标映射若接近恒等,学 F ≈ 0 比学完整恒等映射容易得多。
重要
三个概念别混:残差连接改的是网络结构(提供梯度与信息通路);梯度裁剪改的是更新步长(13.5 节);归一化改的是激活尺度(13.2 节)。它们常一起用,但解决的是不同问题。
13.4 混合精度训练
13.4.1 FP16 与 BF16 的动态范围差异
| 格式 | 符号 / 指数 / 尾数 | 最小正规数 | 最大有限值 | 十进制有效位 | 说明 |
|---|---|---|---|---|---|
| FP32 | 1 / 8 / 23 | ≈ 1.18×10⁻³⁸ | ≈ 3.4×10³⁸ | 约 7.2 | 基准格式 |
| TF32 | 1 / 8 / 10 | 与 FP32 同 | 与 FP32 同 | 约 3~4 | Tensor Core 内部计算格式 |
| FP16 | 1 / 5 / 10 | ≈ 6.1×10⁻⁵ | 65504 | 约 3.3 | 范围窄、精度较高,易上溢/下溢 |
| BF16 | 1 / 8 / 7 | ≈ 1.18×10⁻³⁸ | ≈ 3.4×10³⁸ | 约 2.4 | 范围与 FP32 相同,精度低 |
关键对比:FP16 与 BF16 都是 16 位,但位宽分配不同。FP16 指数只有 5 位,动态范围窄,e^100 这种量级直接上溢、1e−8 这种梯度直接下溢为 0;BF16 用 8 位指数换来与 FP32 相同的动态范围,代价是尾数只有 7 位(精度低)。大模型训练更偏 BF16,正是因为它几乎不会因为范围问题溢出,而且通常不需要 loss scaling。
13.4.2 loss scaling 为什么必要
FP16 下梯度的典型量级是 1e−5 ~ 1e−8,而 FP16 的最小正规数只有 6.1×10⁻⁵——梯度会整体下溢为 0,参数完全学不动。解决办法是在损失层面整体放大:
| |
动态 loss scaling 的流程是:若干步没有出现 inf/nan 就把 S 翻倍;一旦发现 inf/nan(说明缩放后仍上溢),就跳过这一步更新并把 S 减半。BF16 通常不需要这套机制,但仍需 FP32 累加。
13.4.3 哪些算子必须保持高精度
| 算子 / 环节 | 低精度下会发生什么 | 处理方式 |
|---|---|---|
Softmax(含 exp、减最大值) | 指数项上溢或大数相消,输出全 0/全 1 | 在 FP32 中计算,或减最大值后仍用 FP32 累加 |
| 归一化的均值 / 方差统计 | Σx² 与 (Σx)² 相减造成灾难性抵消,方差可为负 | 统计量用 FP32 累加,ε 不能省 |
| GEMM / 卷积的累加 | 上千项的 FP16 累加把有效位数吃光 | 用 FP32 累加器(Tensor Core 原生支持) |
| 损失计算 | 小概率项的对数放大误差 | loss 用 FP32 |
| 参数主副本与更新 | w ← w − η·g 中 η·g 远小于 w,低精度下被舍入吞掉 | 保留 FP32 master weights,更新后再转半精度 |
除以小量(如 1/√(σ²+ε)) | 分母误差被放大 | 分母在 FP32 中计算 |
警告
混合精度训练出现 NaN 时,第一嫌疑是 loss scale 太大导致梯度上溢,而不是模型结构错误。处理顺序:先看 scale 是否已到 inf(多数框架会自动跳过该步并减半),再确认 Softmax/归一化/累加是否都在 FP32,最后才检查数据里有没有 NaN。
13.5 梯度裁剪
两种裁剪方式在几何上完全不同:
| |
按值裁剪会把向量的方向扭向坐标轴(大分量被压、小分量保留),而梯度的方向才是下降方向;按范数裁剪只做等比缩放,方向完全不变,只是把步长限制在阈值内——所以实践中默认用全局范数裁剪,阈值常取 1.0。
它解决的问题是偶发梯度尖峰:一个异常样本、一条超长序列或一次数值不稳,可以让梯度范数突然比平时大几个数量级,一步更新就把训练好的权重摧毁。裁剪给范数设上限,把"最坏一步"的破坏力封顶。与 warmup 的关系是互补而非替代:warmup 处理的是训练初期 Adam 二阶矩估计尚不可靠带来的系统性不稳,裁剪处理的是全程都可能出现的偶发尖峰。阈值不要拍脑袋定,先记录若干步的梯度范数曲线,再取略高于常见水平的取值。
| |
顺序纪律:AMP unscale → 裁剪 → optimizer.step()。若在 unscale 之前裁剪,等于拿被 S 放大过的范数与阈值比较,阈值形同虚设(反向也说明为什么必须先 unscale 再判断 inf/nan)。
13.6 训练稳定性工程手段总览
| 手段 | 解决什么问题 | 机制 | 代价与注意 |
|---|---|---|---|
| 学习率 warmup | 初期参数远离最优、Adam 二阶矩估计不可靠 | 学习率从很小线性爬升,再转余弦/线性衰减 | 前期进度慢;后期必须衰减 |
| 梯度裁剪 | 偶发梯度尖峰摧毁权重 | 全局范数 ‖g‖ ≤ c,等比缩放 | 阈值过小会压低有效学习率 |
| 混合精度 FP16 / BF16 | 显存与带宽瓶颈 | 半精度计算 + FP32 累加;FP16 需 loss scaling | BF16 精度低;关键算子须保 FP32 |
| 梯度累积 | 显存装不下目标 batch | 多个 micro-batch 累加梯度后再更新 | 与 BN 的 batch 统计不等价(LN 无此问题) |
| 权重衰减解耦(AdamW) | L2 与 Adam 的自适应缩放耦合,正则效果被扭曲 | 直接从参数中减去 η·λ·w,不进梯度 | 需重新调 λ 与学习率 |
| 归一化层(LN / RMSNorm) | 激活分布漂移、尺度累积 | 每层把激活重新标准化 | 小 batch 下 BN 不稳(改用 LN/GN) |
| 残差连接 | 深层梯度衰减与退化 | 提供恒等通路(13.3 节) | 几乎无代价 |
| 合理初始化 | 前向/反向尺度失控与 Dead ReLU | 方差守恒(13.1 节) | 必须与激活函数匹配 |
对应 【2026-08-05 选择 Q20】(多选):A(学习率 warmup)、C(梯度裁剪)、D(混合精度训练)正确;B(“完全不使用任何学习率调度、固定学习率从头到尾”)错误——主流做法是 warmup 之后接余弦或线性衰减,全程固定学习率会让后期在最优解附近持续震荡、难以收敛。
注意
这张表可以当作"训练不稳时的手段清单"来用:先判断症状属于哪一类(初期发散 → warmup;偶发爆掉 → 裁剪;OOM/太慢 → 混合精度与梯度累积;泛化变差 → 权重衰减解耦与归一化),再对症下药,而不是把所有手段一次性全加上——超参越多,越难定位真正起作用的那一个。
13.7 调试实践
13.7.1 loss 不降(或直接 NaN)的排查顺序
按"由简到繁、由便宜到昂贵"的顺序排查,前四步就能覆盖绝大多数问题:
- 单 batch 过拟合测试:关掉数据增强与正则,取 1~2 个 batch 反复训练,能否把 loss 压到接近 0。若不能,问题一定在实现或数据,而不在超参。
- 查数据管线:输入与标签是否对齐(最常见的错位来源是 shuffle、过滤与缓存不同步);有没有全 0 输入、NaN、越界标签;padding 与 mask 是否一致。
- 查 loss 与输出层:交叉熵的输入应当是 logits 而不是 softmax 后的概率(框架内部会做 log-softmax,重复 softmax 等于把梯度压平);mask 位置是否正确排除。
- 查学习率:过大表现为 loss 剧烈震荡或直接 NaN,过小表现为几乎不动。用 13.7.3 的学习率范围测试确定数量级。
- 查梯度:逐层打印梯度范数,判断是消失(前几层接近 0)、爆炸还是恰好为 0(忘记
zero_grad、误用detach、被no_grad包住)。 - 查初始化与归一化:逐层激活方差是否在 1 附近;BN 的 running stats 是否在更新;AMP 的 scale 是否为
inf。 - 最后才怀疑架构与超参:到这一步说明前面都没问题,此时改模型结构、加正则、调调度才有意义。
13.7.2 过拟合与欠拟合
| 训练 loss | 验证 loss | 判断 | 首选手段 |
|---|---|---|---|
| 高 | 高 | 欠拟合 | 增大模型容量/训练更久、降低正则强度、检查特征与标签质量 |
| 低 | 先降后升 | 过拟合 | 早停、增大权重衰减、Dropout、数据增强、增加数据(第 8 章) |
| 低 | 低且接近 | 正常 | 关注训练效率与推理成本(第 19、20 章) |
| 高 | 低 | 数据划分或正则过强 | 检查验证集是否与训练集同分布、正则是否过强 |
这张表要与 【2026-09-02 选择 Q3】 的判据一致:训练精度持续升、验证精度回落是过拟合,其应对是早停、增大权重衰减等;若答成"欠拟合、需要增大容量",方向就完全反了。
13.7.3 学习率范围测试
学习率是唯一"错一个数量级就全盘皆输"的超参。学习率范围测试(LR range test)用一个 epoch 的成本给出可靠区间:让学习率从 1e−7 开始乘性增长(每步乘 1.05~1.1)到 1,记录每步的 loss,画出 loss–学习率曲线,取下降最陡处对应的学习率再除以 10 作为初始值(配合 warmup 与余弦衰减)。
| |
| |
本章小结
| 关键结论 | 一句话表述 | 常见错误 |
|---|---|---|
| 全零初始化 | 对称性无法打破,m 宽的网络退化成 1 个神经元 | 认为"偏置也必须随机"(偏置置零是安全的) |
| Xavier / He | 保持前向激活与反向梯度方差守恒;2/(fan_in+fan_out) 与 2/fan_in | 深层网络用 ReLU 却配 Xavier |
| BN 的推理 | 用训练期累积的 running statistics,不用当前 batch | 忘记切 eval 模式,导致预测不可复现 |
| LN / RMSNorm | 统计量只依赖单样本;RMSNorm 不减均值、无偏置 | 在变长序列与小 batch 上用 BN |
| Pre-LN vs Post-LN | Norm 放在子层之前,残差主干无阻挡,训练更稳 | 把 Post-LN 训练不稳归因于学习率没调好 |
| 残差 | ∂y/∂x = I + ∂F/∂x,恒等项提供系数为 1 的梯度通路 | 把"退化"当成过拟合 |
| FP16 / BF16 | 指数位 5 vs 8:FP16 范围窄易溢出,BF16 范围同 FP32 | 认为两者差别只在"精度高低" |
| loss scaling | 防 FP16 梯度下溢;unscale 后才能裁剪与更新 | 在 unscale 之前裁剪梯度 |
| 梯度裁剪 | 按范数裁剪只缩放不转向;按值裁剪会扭曲方向 | 裁剪阈值过小,等于偷偷降低学习率 |
| 稳定性手段 | warmup、裁剪、混合精度、梯度累积、权重衰减解耦各解决不同问题 | 认为"固定学习率最省事"(【2026-08-05 选择 Q20】 的 B 项) |
| 调试顺序 | 先做单 batch 过拟合测试与数据检查,最后才动架构 | 一上来就换模型、加正则 |
自测题
Q1(计算)某层输入维 fan_in = 512、输出维 fan_out = 128,激活为 ReLU。求 He 初始化的标准差;若同一个网络改用 Tanh,Xavier 的标准差是多少?两者差多少倍?
答案与解析
He:σ = √(2/fan_in) = √(2/512) = √0.00390625 = 0.0625。
Xavier:σ = √(2/(fan_in+fan_out)) = √(2/640) = √0.003125 ≈ 0.0559。
比值 0.0625/0.0559 ≈ 1.12。注意这个差距随 fan_out 变小而变大:当 fan_out ≪ fan_in 时 2/(fan_in+fan_out) → 2/fan_in;差距最大出现在两个维度相近时。真正的要点不是这个比值,而是因子 2 的来源:ReLU 把一半激活置零,使 Var(a) ≈ ½Var(z),所以前向方差守恒要求 Var(w) = 2/fan_in。用错初始化会让激活方差随深度单调衰减或增长(13.1.2 节),表现为深层网络训不动或一开训就爆。
Q2(辨析)① 把所有权重和偏置都初始化为 0 会怎样?② 只把偏置初始化为 0、权重随机可以吗?③ 为什么初始化方差必须与 fan_in 成反比?
答案与解析
① 同一层每个神经元的前向输出完全相同,反向收到的梯度也完全相同,更新后依然完全相同:宽度为 m 的网络在功能上等价于 1 个神经元,容量被浪费,对称性永远无法打破。② 可以。打破对称性靠的是权重的随机性;只要各神经元的加权输入不同,偏置是否为零不影响它们的分化。③ 因为 Var(z) = fan_in · Var(w) · Var(a):fan_in 越大,求和项越多,方差被放大的倍数越大。要让它跨层守恒,必须让 Var(w) 正比于 1/fan_in(前向视角);反向还有对称的 1/fan_out 要求,Xavier 取两者折中,He 再补上 ReLU 的一半因子。
Q3(辨析)BatchNorm 在训练与推理时分别用什么统计量?为什么推理不能直接用当前 batch 的统计量?为什么 NLP 模型普遍用 LayerNorm 而不是 BatchNorm?
答案与解析
训练用当前 mini-batch 的 μ_B、σ_B²,并把它们以滑动平均写入 running statistics;推理固定使用 running statistics。
推理不能用当前 batch 统计量的原因:在线服务与自回归生成时 batch 常常只有 1 个样本,此时 σ_B² = 0,标准化直接失效;即使 batch 更大,“同一输入的输出会随同批其他样本变化"也违反预测必须确定、可复现的要求。
NLP 用 LN 的三条独立原因:① 序列长度可变、padding 使各位置有效样本数不同,按位置统计的 batch 统计量噪声极大;② 大模型单卡 batch 很小甚至为 1,BN 已无意义;③ 自回归逐 token 生成要求统计量只依赖当前样本,BN 的跨样本依赖会破坏这一点。此外 LN/RMSNorm 没有训练/推理差异,省掉了 running statistics 这套状态与相应 bug(典型如忘记切 eval)。
Q4(推导)写出 y = x + F(x) 的 ∂y/∂x,并说明它为什么能缓解梯度消失;再解释残差与"退化问题"的关系。
答案与解析
∂y/∂x = I + ∂F/∂x,于是 ∂L/∂x = (I + (∂F/∂x)ᵀ)·∂L/∂y = ∂L/∂y + (∂F/∂x)ᵀ∂L/∂y。
沿 L 层展开是 ∂L/∂x_l = (Π_{k=l}^{L−1}(I + J_k))·∂L/∂x_L。相比普通网络的纯连乘 Π J_k(谱半径稍小于 1 就指数消失),这里每个因子含恒等项:展开后存在大量"全选 I“的路径,其系数恒为 1,因此从输出到任意浅层始终有一条不被衰减的梯度通路;同时 J_k 较小时乘积被钉在 1 附近,也不容易爆炸。
退化问题指实验现象:56 层普通网络的训练误差反而高于 20 层。这是优化困难而非过拟合。加残差后,“学成恒等映射"变成把 F 训成 0 这样一件容易的事,所以更深的结构至少不会比浅的更差;换句话说,残差让子层只需要学残差(修正量),而不是从头学完整映射。
Q5(场景 + 计算)某模型用 FP16 混合精度训练,第 300 步 loss 突然变成 NaN。给出排查顺序;并说明为什么梯度在 FP16 下会下溢、loss scaling 与 unscale 各自的作用。若某层梯度范数平时在 0.3 附近,某步突然达到 1.2×10³,全局范数裁剪(c = 1.0)后该步的实际更新被缩放到原来的几分之一?
答案与解析
排查顺序:① 先看 AMP 的 scale 是否已冲到 inf——多数框架检出 inf/nan 会跳过该步并把 scale 减半,先确认这条机制在生效;② 确认 Softmax、归一化统计量、GEMM 累加、loss 与参数更新都在 FP32(13.4.3 节的表);③ 检查数据里有没有 NaN/异常大值;④ 最后才怀疑结构。
下溢原因:FP16 的最小正规数是 6.1×10⁻⁵,而梯度典型量级 1e−5 ~ 1e−8,低于它就变成 0,参数完全学不动。loss scaling 把 loss 乘以 S,反传时所有梯度同比例放大 S 倍进入可表示范围;unscale 是在更新前把 S 除掉,恢复真实梯度——顺序上必须先 unscale 再裁剪、再 optimizer.step(),否则阈值的量级失去意义。
裁剪计算:‖g‖ = 1.2×10³ > c = 1.0,缩放因子 c/‖g‖ = 1/(1.2×10³) ≈ 8.3×10⁻⁴,即更新步长被压到原来的约 1/1200。这也解释了为什么裁剪能"救回"一步坏更新:没有它,这一步的有效步长会是平时的约 4000 倍(1200/0.3),足以摧毁已训练的权重。
与真题的关联
| 真题 | 考查角度 | 对应小节 |
|---|---|---|
【2026-08-05 选择 Q20】 | 训练稳定性手段多选:warmup、梯度裁剪、混合精度正确;“全程固定学习率"错误 | 13.4、13.5、13.6 |
【2026-09-02 选择 Q4】 | ResidualAdd + RMSNorm 算子融合的动机:避免残差结果写回 HBM | 13.2.4、13.3 |
【2026-09-02 选择 Q3】 | 过拟合的判据与应对(早停、增大权重衰减),与"欠拟合"的区分 | 13.7.2 |
【2026-08-05 选择 Q13】(见第 12 章) | 推理峰值显存构成,与训练显存(激活值)的对照 | 13.2.1 与第 12 章 12.8 节 |
延伸阅读
- Glorot & Bengio (2010):Understanding the difficulty of training deep feedforward neural networks —— Xavier 初始化。
- He 等 (2015):Delving deep into rectifiers —— He 初始化;Ba 等 (2016):LayerNorm;Zhang & Sennrich (2019):RMSNorm。
- Xiong 等 (2020):On layer normalization in the transformer architecture —— Pre-LN / Post-LN 的梯度分析。
- Micikevicius 等 (2018):Mixed precision training —— loss scaling;Loshchilov & Hutter (2019):Decoupled weight decay regularization(AdamW)。
- Smith (2017):Cyclical learning rates for training neural networks —— 学习率范围测试的来源。