> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/llm_internals/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/llm_internals/di-er-bu-fen-xun-lian-pian/06_training_techniques/6.3_regularization.md).

# 6.3 正则化策略：防止过拟合的多重手段

尽管大语言模型通常训练在海量数据上（过拟合风险较低），适当的正则化仍然对训练稳定性和泛化性能至关重要。上一节的三条曲线规定的是每一步该走多远，却不对梯度本身的量级作任何假设——峰值学习率调得再准，一个异常放大的梯度照样能让单个批次毁掉整个训练过程。本节把这两侧一起管上：Dropout 与权重衰减冲着泛化去，梯度裁剪与对 attention logits 的动态约束则分别按住参数更新的幅度和前向计算中 logits 的量级。

## 6.3.1 Dropout

**Dropout** 在训练时随机将一定比例的神经元输出置零。原始 Transformer 在以下位置应用 Dropout（率 $$P\_{\text{drop}} = 0.1$$）：

* 自注意力的权重（注意力 Dropout）
* 每个子层的输出（残差 Dropout）
* 词嵌入与位置编码相加后

Dropout 的工作原理是**防止神经元之间的共适应**——当某些神经元随时可能被关闭时，模型被迫学习更鲁棒的、分布式的特征表示，而非依赖特定的少数神经元。

值得注意的是，现代超大规模语言模型预训练时通常**不使用 Dropout**——如 PaLM 论文明确说明训练未用 Dropout（仅微调时使用 0.1）。这是因为在极大规模数据上训练时，模型远未过拟合，Dropout 反而可能减慢收敛。

## 6.3.2 梯度裁剪与梯度爆炸防止

**梯度爆炸问题**：在深度网络中，梯度通过链式法则逐层反向传播。如果各层雅可比矩阵的奇异值（谱范数）普遍大于 1，多层连乘后梯度范数就会指数级放大。在极端情况下，梯度可能溢出为 NaN 或 Inf，完全破坏模型参数。这在长序列或深层网络中特别容易发生。

**梯度裁剪**（Gradient Clipping）是最直接的防护手段。常见的实现是按范数裁剪（L2 Clipping）：

$$\text{如果} |g| > \text{max\_norm}，\text{则} g \leftarrow g \cdot \frac{\text{max\_norm}}{|g|}$$

这确保了梯度的 L2 范数永不超过阈值，同时保留梯度的方向。在 PyTorch 中，这通过 `torch.nn.utils.clip_grad_norm_()` 实现。

常用的裁剪阈值为 1.0。在极端梯度（如遇到异常值或长序列的边界效应）出现时，梯度裁剪能防止单个批次完全摧毁训练过程。

**梯度裁剪对 Transformer 特别重要**的原因包括：

1. **注意力矩阵的尖锐性**：当 Softmax 的输入值变化剧烈时（如某些位置的值非常大），其导数可能接近零或变化剧烈，导致梯度波动
2. **长序列的累积效应**：长序列意味着梯度反向传播的路径更长，放大风险更高
3. **位置编码的相互作用**：位置编码与注意力权重的交互有时会产生数值不稳定

**梯度消失问题**虽然在现代 Transformer 中不如梯度爆炸严重（得益于残差连接和层归一化），但在极其深的模型中仍可能出现。梯度消失时，某些参数的梯度接近零，导致这些参数几乎停止更新。相比梯度爆炸的灾难性，梯度消失的危害是隐性的——模型可能能够训练，但某些早期层学习效率极低。

## 6.3.3 权重衰减

**权重衰减**（Weight Decay）在每次参数更新时对参数施加微小的收缩：

$$\theta \leftarrow (1 - \eta \lambda) \theta - \eta \nabla\_\theta \mathcal{L}$$

它倾向于使权重保持较小的值，防止模型过度依赖少数大权重特征。在与 AdamW 配合使用时，权重衰减通常设为 0.01\~0.1。

## 6.3.4 梯度累积

**梯度累积**（Gradient Accumulation）是在显存有限时扩大有效批次大小的重要技术。其基本思想是：不在每个小批次后更新参数，而是累积多个小批次的梯度后统一更新。

假设我们想要训练有效批次大小为 256，但 GPU 显存只能容纳 64 的批次，那么可以执行 4 个梯度累积步骤：

```python
accumulation_steps = 4
for i in range(accumulation_steps):
    batch = get_batch(size=64)
    loss = model(batch)
    (loss / accumulation_steps).backward()  # 梯度累积到各参数的 param.grad

optimizer.step()            # 每4个小批次后才更新一次参数
optimizer.zero_grad()
```

这里假设 `loss` 已经是每个小批次内按样本平均的标量；除以 `accumulation_steps` 后，累积梯度的尺度才与一次性使用有效批次大小 256 相匹配。对于语言模型等按 token 计算损失的任务，如果序列长度或 padding 数量不同，更稳妥的做法是对累积窗口内所有 non-padding token 的交叉熵求和，再除以该窗口的 non-padding token 总数，而不是简单平均多个小批次的 mean loss。

**梯度累积的关键性质**：

1. **等价于更大批次**：只要按上文方式正确归一化损失，累积 N 个梯度后的参数更新就在数学上严格等价于直接在大小为 N\*batch\_size 的批次上进行一步 SGD（若网络含 BatchNorm 等依赖批内统计量的层则不再严格等价；Transformer 使用逐样本归一化的 LayerNorm，不受此影响）
2. **有效批次 vs. 物理批次**：有效批次大小（完成梯度累积后的一次参数更新所使用的样本总数）与物理批次大小（GPU 一次加载的样本数）分离，提高了训练的灵活性
3. **对学习率的影响**：学习率通常按有效批次大小调整。批次越大，学习率通常应该越大——SGD 场景常用**线性缩放法则**（批次扩大 $$k$$ 倍，学习率乘 $$k$$，出自[大批次训练的经典实践](https://arxiv.org/abs/1706.02677)），Adam 等自适应优化器则常用更保守的**平方根法则**（乘 $$\sqrt{k}$$）

**在大规模训练中的应用**：现代 LLM 训练几乎总是使用梯度累积，因为：

* 有效批次大小（通常为百万级词元数）与物理批次大小（通常数百到数千）需要在显存约束下达成
* 梯度累积允许在分布式训练中保持一致的有效批次大小，而不同节点可以使用不同的物理批次大小
* 梯度累积与梯度裁剪配合时，需要注意裁剪应在累积完所有梯度后进行，而非在每个物理批次后进行

## 6.3.5 训练稳定性的实践建议

大规模 Transformer 训练中的稳定性问题是一个持续的工程挑战。常见的实践包括：

* **损失尖峰（Loss Spike）处理**：训练过程中偶尔出现的损失突然飙升，常见原因包括：
  * 梯度爆炸：通过梯度裁剪缓解
  * 学习率过大：降低学习率或检查学习率调度
  * 数据异常：过滤训练数据中的极端值
  * 可恢复性：从最近的检查点回滚，或跳过该批次（需谨慎，可能掩盖潜在问题）
* **参数初始化**：合理的初始化（如 Xavier、Kaiming 初始化）确保各层激活值与梯度处于合理范围。对于 Transformer，多数实现采用较小的初始化标准差（如 $$\mathcal{N}(0,,0.02^2)$$）；更关键的是 **GPT-2／Megatron 的残差缩放**——把每个子层中直接写入残差流的输出投影（注意力的 $$W\_O$$、FFN 的 $$W\_2$$）额外乘以 $$1/\sqrt{2L}$$（$$L$$ 为层数；GPT-2 原文记为 $$1/\sqrt{N}$$，$$N$$ 即残差子层总数 $$2L$$）。动机与 §3.5 的残差流一脉相承：每个子层输出都累加进同一条残差流，若不缩放，$$2L$$ 次累加会让激活方差随深度近似线性增长，在训练起点就把信号推入饱和区；乘以 $$1/\sqrt{2L}$$ 恰好抵消这种增长，使上百层的深网在初始化时仍保持稳定的激活尺度——这正是“残差让梯度流过百层”（§3.5）在前向方向上的对偶保障
* **监控训练指标**：定期记录：
  * 梯度的范数（检测梯度爆炸/消失）
  * 权重的平均大小和方差
  * 激活值的统计量
  * 学习率的当前值

## 6.3.6 注意力 logits 的动态约束：QK-Norm、z-loss 与 logit soft-cap

[2.2 节](/llm_internals/di-yi-bu-fen-ji-chu-pian/02_attention/2.2_scaled_dot_product.md)已经解释了为什么点积要除以 $$\sqrt{d\_k}$$：当 $$q$$ 与 $$k$$ 的分量独立同分布于 $$\mathcal{N}(0,1)$$ 时，点积方差等于 $$d\_k$$，除以 $$\sqrt{d\_k}$$ 把它拉回 1，Softmax 因而落在梯度活跃区。但这个论证有一个容易被忽略的前提：**那个分布假设只在初始化时刻成立**。训练一旦开始，$$W\_Q$$ 与 $$W\_K$$ 的范数会持续增长，而 $$\sqrt{d\_k}$$ 是个常数，它不会跟着一起长。于是 attention logits 可以在训练中途重新爆炸，缩放因子对此无能为力——$$\sqrt{d\_k}$$ **是静态修正，管不住动态漂移**。

这不是理论担忧。Google 把 ViT 扩到 220 亿参数时观察到：约 80 亿参数规模上训练损失会在几千步后发散，[原因是 attention logits 出现极大值](https://arxiv.org/abs/2302.05442)，导致注意力权重几乎变成 one-hot、熵接近零；论文附录给出的量级是——不加归一化时，attention logits 会迅速涨到 **50000 以上**。熵接近零意味着每个查询只盯住一个键，梯度几乎不再流向其他位置，模型实际上失去了继续学习注意力分布的能力。

那么范数为什么会一路涨？[Chameleon 报告给出的机制解释很有说服力](https://arxiv.org/abs/2405.09818)：它把发散的根源归到 Softmax 的**平移不变性**（$$\text{softmax}(z) = \text{softmax}(z+c)$$）——正因为整体抬高 logits 不改变输出，模型要让分布更尖锐时，除了拉开 logits 之间的相对差距，还可以简单地把整体范数抬上去，而后者**没有任何回压**。在多模态共享权重的设定下，各模态会靠略微抬高自身范数互相「竞争」，起初无害，直到超出 bf16 的有效表示范围就发散；单模态下的同一现象被称为 **logit drift**。

这份报告还顺带给出了一个比损失曲线更早的报警信号：训练发散**可能迟至训练进度的 20%\~30% 才显现**，但监控最后一层输出范数的失控增长，与预测未来的损失发散高度相关。这正好给 6.3.5 的监控清单补上一项。

三种手段被用来给 logits 加上动态约束，它们作用在**不同的位置**，理解这一点比记住名字更重要——Chameleon 报告的提法很清楚：Transformer 里的 Softmax 出现在两个地方，**注意力内部**和**最终输出**。

**QK-Norm**：在点积**之前**先归一化 $$Q$$ 与 $$K$$：

$$\text{softmax}\left(\frac{1}{\sqrt{d}},\text{LN}(XW^Q)\big(\text{LN}(XW^K)\big)^{\top}\right)$$

归一化把 $$q$$、$$k$$ 的范数钉在固定尺度上，logits 的量级于是不再随权重范数漂移——Chameleon 的措辞是它「直接控制送入 Softmax 的输入的范数增长」。它补上的正是 $$\sqrt{d\_k}$$ 缺的那一半：静态修正管初始化，QK-Norm 管整个训练过程。**但它只管注意力内部那个 Softmax**——同一份报告明确写道，QK-Norm 有助于 Transformer 内部的 Softmax，**并不解决最终 Softmax 上的 logit 漂移**。这就引出了第二种手段。

**z-loss**：约束的正是**最终 Softmax 的归一化项本身**。PaLM 在标准语言建模损失之外加了一项[辅助损失 $$z\_{\text{loss}} = 10^{-4}\cdot\log^2 Z$$](https://arxiv.org/abs/2204.02311)（$$Z=\sum\_i e^{x\_i}$$ 即 Softmax 的配分函数），把 $$\log Z$$ 往 0 上拉。它拴住的就是上面那个平移自由度：损失本身不管整行 logits 偏移多少，于是它可以自由漂到很大的绝对值上，而 $$e^{z}$$ 在低精度下会立刻失去精度。z-loss 的巧妙之处在于**只钉死平移这一个自由度，完全不干涉 logits 之间的相对差距**——而相对差距才是模型真正要学的东西。Chameleon 沿用了这一项但把系数取为 $$10^{-5}$$，并报告 7B 需要 dropout 与 z-loss 并用才稳定，34B 则只需 z-loss。MoE 的路由器有完全相同的问题，[ST-MoE 因此引入了 router z-loss](https://arxiv.org/abs/2202.08906)，并说明它是最终 Softmax 上那个 z-loss 的改编。

**logit soft-cap**：用 $$\tanh$$ 做软截断，把 logits 压进有界区间：

$$\text{logits} \leftarrow c\cdot\tanh(\text{logits}/c)$$

[Gemma 2 在每个注意力层和最后一层都做了这个截断](https://arxiv.org/abs/2408.00118)，注意力层取 $$c=50.0$$、最后一层取 $$c=30.0$$。它的代价在工程侧：截断插在 Softmax 之前，融合注意力核（FlashAttention 一类，见 [10.3 节](/llm_internals/di-san-bu-fen-tui-li-yu-bu-shu-pian/10_inference_optimization/10.3_flash_attention.md)）必须为它单独适配。这也是为什么下一代直接换了方案——Gemma 3 明确写道[「我们把 Gemma 2 的 soft-capping 换成了 QK-norm」](https://arxiv.org/abs/2503.19786)：稳定性收益相当，而 QK-Norm 对核实现友好得多。

**QK-clip：为什么还需要第四条路**。[6.1 节](/llm_internals/di-er-bu-fen-xun-lian-pian/06_training_techniques/6.1_loss_optimizer.md)提到 Kimi K2 用 MuonClip 训完 15.5 万亿词元零损失尖峰，其中的 Clip 指的就是 QK-clip。[K2 报告把选它的理由写得很直接](https://arxiv.org/abs/2507.20534)：他们发现 attention logits 爆炸在用 Muon 时比用 AdamW 更频繁，而现有手段都不够——**logit soft-cap 虽然直接裁剪 attention logits，但 Q 与 K 的点积在被裁剪之前就已经可以涨得过大**；而 **QK-Norm 不适用于 MLA，因为 MLA 的 Key 矩阵在推理时并不完整物化**（[10.2 节](/llm_internals/di-san-bu-fen-tui-li-yu-bu-shu-pian/10_inference_optimization/10.2_kv_cache.md)解释了 MLA 为何要把 K 留在压缩域）。QK-clip 的落点因此完全不同：不改前向结构、也不加损失项，而是在**优化器更新之后**按实测的逐头最大 logit 反过来缩放 $$W\_Q$$ 与 $$W\_K$$。这是一个很好的例子——**一个稳定性方案能不能用，取决于模型的注意力变体**，而不只是取决于它自身是否有效。

把这一小节接回 6.3.5 的清单：**梯度裁剪管的是参数更新的幅度，这几种手段管的是前向计算中 logits 的量级**。两者拦的是不同环节的失控，实践中通常同时开启；只有梯度裁剪而不管 logits，正是很多“裁剪已经开了、尖峰照来”的排查会卡住的地方。
