第 13 章 训练与优化实务

第 12 章说明了梯度怎么算;本章回答梯度算出来之后怎么办:参数从什么尺度出发、每层的激活如何保持稳定、深层网络为什么需要残差、半精度为什么需要 loss scaling,以及 loss 不降时按什么顺序排查。

学习目标

读完本章,你应当能够:

  1. 从"前向激活方差与反向梯度方差守恒"推出 Xavier 与 He 初始化,并解释为什么 ReLU 必须配 He;
  2. 写出 BatchNorm 的训练与推理公式,说明 running statistics 的作用,并辨析 BN / LN / RMSNorm / GroupNorm;
  3. 分析 y = x + F(x) 的梯度流,解释残差为什么能缓解深层退化;
  4. 说明 FP16 与 BF16 的动态范围差异、loss scaling 的必要性,以及哪些算子必须保持高精度;
  5. 区分按值裁剪与按范数裁剪,并给出 warmup、裁剪、混合精度、梯度累积、权重衰减解耦各自解决的问题;
  6. 按"由简到繁"的顺序排查 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:

1
信号沿层传播的尺度满足   Var(z) = fan_in · Var(w) · Var(a)
  • 若 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
2
3
4
5
6
7
前向:z_i^(l) = Σ_{j=1}^{n_{l−1}} w_ij a_j^(l−1)
      Var(z^(l)) = n_{l−1} · Var(w) · Var(a^(l−1))
      要 Var(z^(l)) = Var(a^(l−1))   ⟹   Var(w) = 1/n_{l−1}      (n_{l−1} = fan_in)

反向:∂L/∂a^(l−1) = W^(l)ᵀ (∂L/∂z^(l))
      Var(∂L/∂a^(l−1)) = n_l · Var(w) · Var(∂L/∂z^(l))
      要反向同样守恒               ⟹   Var(w) = 1/n_l          (n_l = fan_out)

前向要求 1/fan_in、反向要求 1/fan_out,两者一般不能同时满足,于是取调和平均:

1
2
3
Xavier / Glorot:Var(w) = 2/(fan_in + fan_out)
  正态形式:W ~ N(0, 2/(fan_in + fan_out))
  均匀形式:W ~ U(−√(6/(fan_in + fan_out)), +√(6/(fan_in + fan_out)))   (均匀分布方差 = a²/3)

ReLU 破坏了"Var(a) = Var(z)“这个前提:它把约一半的神经元置零,于是 Var(a) ≈ ½·Var(z)。代回前向方差守恒条件:

1
2
Var(z) = fan_in · Var(w) · ½Var(z)   ⟹   Var(w) = 2/fan_in
He / Kaiming:W ~ N(0, 2/fan_in)      (标准差 σ_w = √(2/fan_in))

这就是"ReLU 配 He、Tanh/Sigmoid 配 Xavier"的定量原因:多出的因子 2 正是补偿被 ReLU 砍掉的那一半方差。

初始化方差标准差适配激活依据
全零00无对称性无法打破,等价于单个神经元
小常数固定固定无层数一多必然消失或爆炸
Xavier / Glorot2/(fan_in+fan_out)√(2/(fan_in+fan_out))Tanh、Sigmoid前向与反向方差折中守恒
He / Kaiming2/fan_in√(2/fan_in)ReLU 及其变体补偿 ReLU 置零的一半方差
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import numpy as np


def init_weights(shape, fan_in, fan_out, act):
    """按激活函数选择初始化:ReLU 族用 He,Tanh/Sigmoid 用 Xavier"""
    if act in ('relu', 'leaky_relu', 'gelu', 'silu'):
        std = np.sqrt(2.0 / fan_in)          # ReLU 把一半方差砍掉,故乘 2
    else:
        std = np.sqrt(2.0 / (fan_in + fan_out))   # 前向 1/fan_in 与反向 1/fan_out 的折中
    return np.random.randn(*shape) * std

提示

工程检查法:初始化后做一次前向,逐层打印激活的标准差。若它随层数单调衰减或增长(而不是在 1 附近波动),说明初始化尺度与激活函数不匹配——这比"训练不收敛再去猜"快得多。

13.2 归一化层

13.2.1 BatchNorm:公式与训练 / 推理的差异

BatchNorm(batch normalization)在一个 mini-batch 上统计均值与方差,再标准化并仿射还原:

1
2
3
4
μ_B = (1/B) Σ_{i=1}^{B} x_i
σ_B² = (1/B) Σ_{i=1}^{B} (x_i − μ_B)²
x̂_i = (x_i − μ_B)/√(σ_B² + ε)
y_i = γ·x̂_i + β                       γ、β 是可学习参数

关键在于训练与推理使用不同的统计量:

阶段均值 / 方差来源后果
训练当前 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 任务
1
2
LayerNorm:  x̂ = (x − mean(x))/√(var(x) + ε)    然后在特征维上仿射
RMSNorm:    x̂ = x/√(mean(x²) + ε)             只做缩放,不重新居中

RMSNorm 的动机是"尺度不变性才是关键、重新居中贡献很小”,于是砍掉均值项与偏置项,换来更少的归约与更简单的算子(第 19 章会看到它与残差相加的融合)。

为什么 NLP 用 LN 而不是 BN,有三条互相独立的原因:① 序列长度可变、batch 内 padding 位置的有效样本数不同,按位置统计的 μ_B、σ_B² 噪声极大;② 大模型训练时单卡 batch 很小甚至为 1,BN 的 batch 统计量已无意义;③ 自回归生成逐 token 推进,每一步的统计量必须只依赖当前样本,否则同一句话在不同上下文批量下会得到不同结果。

13.2.3 Pre-LN 与 Post-LN:位置决定训练稳定性

Transformer Block 有两种排布(详见第 15 章):

1
2
Post-LN(原始 Transformer):x_{l+1} = LN( x_l + F(x_l) )     ← Norm 在残差之后
Pre-LN :                    x_{l+1} = x_l + F( LN(x_l) )     ← Norm 在子层之前

差异出在残差主干上有没有算子阻挡。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),直接求导:

1
2
∂y/∂x = I + ∂F/∂x
∂L/∂x = (I + ∂F/∂x)ᵀ · ∂L/∂y = ∂L/∂y + (∂F/∂x)ᵀ ∂L/∂y

梯度被分成两项:第一项 ∂L/∂y 原样穿过(系数恒为 1),第二项才是经过子层变换的部分。把它沿 L 层展开:

1
2
3
4
∂L/∂x_l = (Π_{k=l}^{L−1} (I + J_k)) ∂L/∂x_L      J_k = ∂F_k/∂x_k
展开后含大量"全选 I"的项(例如 I·I·…·I)与混合项:
⟹ 从第 L 层到第 l 层始终存在系数为 1 的直接通路,梯度不会因连乘而指数衰减
⟹ 同时 J_k 较小时乘积不会爆炸,反向的谱半径被"1 + 小量"钉在 1 附近

这正好补上了第 12 章 12.7 节的漏洞:普通深层网络的梯度是若干雅可比的纯连乘,谱半径稍小于 1 就会消失;残差把"连乘"变成"连乘 + 恒等直达”,让梯度有了保底通道。

残差还解决"退化问题(degradation)”:实验发现 56 层普通网络比 20 层的训练误差更高——注意是训练误差,因此不是过拟合,而是优化困难:让一堆非线性层共同表示恒等映射非常难。加了残差后,网络"至少可以学成恒等"(把 F 训成 0 即可),更深的模型不应该比浅的更差。“残差 = 让子层只学残差(修正量)而不是完整映射”:目标映射若接近恒等,学 F ≈ 0 比学完整恒等映射容易得多。

重要

三个概念别混:残差连接改的是网络结构(提供梯度与信息通路);梯度裁剪改的是更新步长(13.5 节);归一化改的是激活尺度(13.2 节)。它们常一起用,但解决的是不同问题。

13.4 混合精度训练

13.4.1 FP16 与 BF16 的动态范围差异

格式符号 / 指数 / 尾数最小正规数最大有限值十进制有效位说明
FP321 / 8 / 23≈ 1.18×10⁻³⁸≈ 3.4×10³⁸约 7.2基准格式
TF321 / 8 / 10与 FP32 同与 FP32 同约 3~4Tensor Core 内部计算格式
FP161 / 5 / 10≈ 6.1×10⁻⁵65504约 3.3范围窄、精度较高,易上溢/下溢
BF161 / 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,参数完全学不动。解决办法是在损失层面整体放大:

1
2
3
前向:loss' = S · loss        (S 例如 2¹⁵)
反向:所有梯度被同比例放大 S 倍 ⟹ ∂L'/∂w = S · ∂L/∂w,落入 FP16 可表示范围
更新前:unscale   ∂L/∂w = (∂L'/∂w)/S     (或者先把 S 除掉再交给优化器)

动态 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
2
按值裁剪(clip by value):g_i ← clip(g_i, −c, +c)             每个分量独立截断
按范数裁剪(clip by norm): g ← g · min(1, c/‖g‖₂),   ‖g‖₂ = √(Σ_i g_i²)

按值裁剪会把向量的方向扭向坐标轴(大分量被压、小分量保留),而梯度的方向才是下降方向;按范数裁剪只做等比缩放,方向完全不变,只是把步长限制在阈值内——所以实践中默认用全局范数裁剪,阈值常取 1.0。

它解决的问题是偶发梯度尖峰:一个异常样本、一条超长序列或一次数值不稳,可以让梯度范数突然比平时大几个数量级,一步更新就把训练好的权重摧毁。裁剪给范数设上限,把"最坏一步"的破坏力封顶。与 warmup 的关系是互补而非替代:warmup 处理的是训练初期 Adam 二阶矩估计尚不可靠带来的系统性不稳,裁剪处理的是全程都可能出现的偶发尖峰。阈值不要拍脑袋定,先记录若干步的梯度范数曲线,再取略高于常见水平的取值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import torch


def clip_grad_norm(optimizer, max_norm=1.0, scaler=None):
    """全局范数裁剪:只缩放不转向;AMP 下必须先 unscale 再裁剪"""
    if scaler is not None:
        scaler.unscale_(optimizer)                # 把 loss scaling 除掉,否则阈值失去意义
    grads = [p.grad for group in optimizer.param_groups for p in group['params']
             if p.grad is not None]
    total = torch.sqrt(sum((g.detach() ** 2).sum() for g in grads))   # 全局 L2 范数
    if total > max_norm:
        scale = max_norm / (total + 1e-6)
        for g in grads:
            g.mul_(scale)                         # 等比缩放:方向不变,只限制步长
    return total.item()

顺序纪律:AMP unscale → 裁剪 → optimizer.step()。若在 unscale 之前裁剪,等于拿被 S 放大过的范数与阈值比较,阈值形同虚设(反向也说明为什么必须先 unscale 再判断 inf/nan)。

13.6 训练稳定性工程手段总览

手段解决什么问题机制代价与注意
学习率 warmup初期参数远离最优、Adam 二阶矩估计不可靠学习率从很小线性爬升,再转余弦/线性衰减前期进度慢;后期必须衰减
梯度裁剪偶发梯度尖峰摧毁权重全局范数 ‖g‖ ≤ c,等比缩放阈值过小会压低有效学习率
混合精度 FP16 / BF16显存与带宽瓶颈半精度计算 + FP32 累加;FP16 需 loss scalingBF16 精度低;关键算子须保 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)的排查顺序

按"由简到繁、由便宜到昂贵"的顺序排查,前四步就能覆盖绝大多数问题:

  1. 单 batch 过拟合测试:关掉数据增强与正则,取 1~2 个 batch 反复训练,能否把 loss 压到接近 0。若不能,问题一定在实现或数据,而不在超参。
  2. 查数据管线:输入与标签是否对齐(最常见的错位来源是 shuffle、过滤与缓存不同步);有没有全 0 输入、NaN、越界标签;padding 与 mask 是否一致。
  3. 查 loss 与输出层:交叉熵的输入应当是 logits 而不是 softmax 后的概率(框架内部会做 log-softmax,重复 softmax 等于把梯度压平);mask 位置是否正确排除。
  4. 查学习率:过大表现为 loss 剧烈震荡或直接 NaN,过小表现为几乎不动。用 13.7.3 的学习率范围测试确定数量级。
  5. 查梯度:逐层打印梯度范数,判断是消失(前几层接近 0)、爆炸还是恰好为 0(忘记 zero_grad、误用 detach、被 no_grad 包住)。
  6. 查初始化与归一化:逐层激活方差是否在 1 附近;BN 的 running stats 是否在更新;AMP 的 scale 是否为 inf。
  7. 最后才怀疑架构与超参:到这一步说明前面都没问题,此时改模型结构、加正则、调调度才有意义。

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 与余弦衰减)。

1
2
3
4
5
6
7
8
9
loss
 │●
 │ ●●
 │   ●●●
 │      ●●●●            ← 最陡下降段:取此处的 η,再除以 10
 │          ●●●●●
 │               ●●●●
 │                   ●●●●●●●   ← loss 开始上升:已过大
 └──────────────────────────────→ η(对数坐标)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def lr_range_test(model, train_loader, optimizer, criterion, start_lr=1e-7,
                  end_lr=1.0, steps=100):
    """乘性扫描学习率,返回 (lr, loss) 轨迹,用于挑初始学习率"""
    gamma = (end_lr / start_lr) ** (1.0 / steps)     # 每步的乘性增长因子
    lr, trace = start_lr, []
    for i, (x, y) in enumerate(train_loader):
        if i >= steps:
            break
        for group in optimizer.param_groups:
            group['lr'] = lr
        loss = criterion(model(x), y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        trace.append((lr, loss.item()))
        lr *= gamma
    return trace

本章小结

关键结论一句话表述常见错误
全零初始化对称性无法打破,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-LNNorm 放在子层之前,残差主干无阻挡,训练更稳把 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 算子融合的动机:避免残差结果写回 HBM13.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 —— 学习率范围测试的来源。