> **本章对应课件**：《2.1 自然语言处理》中的优化器部分。核心内容：SGD、Momentum、Adam、AdamW——如何更好地利用梯度来更新参数。

## 2.1 为什么需要优化器

:::unfold 先懂直觉
最朴素的更新是「沿梯度反方向走一步」。但真实损失面是崎岖的：有的方向陡、有的方向平、还有噪声。优化器就是「怎么走得更快更稳」的策略集合。
:::

第 1 章学过最朴素的更新公式：

$$
w_{t+1} = w_t - \eta \frac{\partial L}{\partial w}
$$

但真实训练中，梯度并不总是这么「听话」：

- 损失面可能是狭长的峡谷：陡峭方向来回震荡，平坦方向前进缓慢；
- mini-batch 梯度有噪声，方向抖动；
- 不同参数的梯度尺度差异巨大。

优化器就是**如何更好地利用梯度来更新参数**的工程方案。

## 2.2 SGD（随机梯度下降）

$$
W_t = W_{t-1} - \eta\, g_t
$$

其中 $g_t$ 是当前 batch 算出的梯度。

**特点**：只看当前梯度，实现简单、显存开销小。

**缺点**：

- 梯度方向有噪声，可能在峡谷形损失面上**来回震荡**；
- 在平坦方向前进很慢，在陡峭方向又冲得太猛；
- 对学习率非常敏感。

:::note 三种 SGD 变体
| 变体 | 每步用的数据 | 特点 |
| --- | --- | --- |
| BGD（全批量） | 全部训练集 | 方向准，极慢 |
| SGD（单样本） | 1 条 | 快、噪声大 |
| Mini-batch SGD | 32~4096 条 | 实际使用的版本 |

深度学习里说的「SGD」通常指 mini-batch 版本。
:::

## 2.3 Momentum：给优化加一点「惯性」

:::unfold 先懂直觉
像球下山：一直朝一个方向滚就积累速度；左右摇摆的方向会互相抵消。这样既抑制震荡，又加速稳定方向。
:::

引入速度变量 $v_t$：

$$
v_t = \gamma\, v_{t-1} + \eta\, g_t
$$

$$
W_t = W_{t-1} - v_t
$$

直觉像一个球下山：前几步一直往一个方向走，就积累速度。

因此能：

- **减少左右震荡**（方向相反的梯度互相抵消）；
- **加速稳定方向**（持续同向的梯度被累积放大）。

其中 $\gamma$ 通常取 0.9 左右，表示「保留多少历史速度」。

:::math 为什么能加速（以峡谷为例）
在陡峭方向，梯度正负交替，历史速度互相抵消 → 有效步长变小 → 不震荡；
在平坦方向，梯度方向一致，速度不断累积 → 有效步长变大 → 加速前进。

等效学习率约为 $\frac{\eta}{1-\gamma}$（γ=0.9 时约 10 倍），但只在方向一致时生效。
:::

## 2.4 Adam：Momentum + 自适应学习率 ★

:::unfold 先懂直觉
Adam = 动量（记住方向）+ 自适应（每个参数自己调步长）。梯度大的参数步子缩小，梯度小的参数步子放大，所以对学习率没那么敏感。
:::

Adam 同时维护两个统计量。

**一阶矩**（梯度的指数移动平均，类似 Momentum）：

$$
m_t = \beta_1 m_{t-1} + (1 - \beta_1)\, g_t
$$

**二阶矩**（梯度平方的指数移动平均，衡量梯度尺度）：

$$
v_t = \beta_2 v_{t-1} + (1 - \beta_2)\, g_t^2
$$

**偏差修正**（因为 $m_0 = v_0 = 0$，训练初期估计偏小）：

$$
\hat m_t = \frac{m_t}{1 - \beta_1^t}, \qquad \hat v_t = \frac{v_t}{1 - \beta_2^t}
$$

**最终更新**：

$$
W \leftarrow W - \eta \frac{\hat m_t}{\sqrt{\hat v_t} + \epsilon}
$$

**直觉**：

- 分子 $\hat m_t$：往哪走（方向，带惯性）；
- 分母 $\sqrt{\hat v_t}$：梯度大的参数步子缩小，梯度小的参数步子放大——**自适应学习率**；
- $\epsilon$：防止除零的小常数（通常 $10^{-8}$）。

:::math 为什么需要偏差修正
训练开始时 $m_0 = 0$，第一步 $m_1 = 0.1 g_1$（β₁=0.9 时），只有真实梯度的 1/10。如果不修正，前几十步的更新会严重偏小。

除以 $1-\beta_1^t$：$t=1$ 时 $1-0.9 = 0.1$，$m_1/0.1 = g_1$——修正回真实尺度。随着 $t$ 增大，$1-\beta_1^t \to 1$，修正自然消失。
:::

:::example 具体数字算一遍（单参数）
设 $\eta = 0.001$，$\beta_1 = 0.9$，$\beta_2 = 0.999$，梯度序列 $g_1 = 1.0$，$g_2 = 1.0$：

**第 1 步**：
- $m_1 = 0.1 \times 1.0 = 0.1$，$\hat m_1 = 0.1/0.1 = 1.0$
- $v_1 = 0.001 \times 1.0 = 0.001$，$\hat v_1 = 0.001/0.001 = 1.0$
- 更新量 $= 0.001 \times \frac{1.0}{\sqrt{1.0} + 10^{-8}} \approx 0.001$

**第 2 步**：
- $m_2 = 0.9 \times 0.1 + 0.1 \times 1.0 = 0.19$，$\hat m_2 = 0.19/(1-0.81) \approx 1.0$
- $v_2 = 0.999 \times 0.001 + 0.001 = 0.001999$，$\hat v_2 = 0.001999/(1-0.998001) \approx 1.0$
- 更新量 $\approx 0.001$

可以看到：梯度持续为 1 时，自适应分母让每步更新稳定在 $\eta$ 附近。
:::

## 2.5 常用超参数

| 参数 | 典型值 | 含义 |
| --- | --- | --- |
| $\eta$ | 1e-3 ~ 3e-4 | 学习率（大模型常用更小） |
| $\beta_1$ | 0.9 | 一阶矩衰减率 |
| $\beta_2$ | 0.95 ~ 0.999 | 二阶矩衰减率（LLM 常用 0.95） |
| $\epsilon$ | 1e-8 | 数值稳定项 |

:::demo optimizer-race 交互：三种优化器赛跑
同一起点、同一学习率，SGD（红）、Momentum（橙）、Adam（绿）在带波浪的非凸函数上赛跑。观察谁更快到达谷底、谁的轨迹更抖。
:::

:::fold 工程里怎么用（PyTorch 优化器配置）
```python
# SGD + Momentum
opt = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)

# Adam（大模型训练默认用 AdamW）
opt = torch.optim.AdamW(
    model.parameters(),
    lr=3e-4,              # 大模型常用 1e-4 ~ 3e-4
    betas=(0.9, 0.95),    # LLM 常用 β₂=0.95
    weight_decay=0.1,     # 解耦的 weight decay
    eps=1e-8,
)
```
:::

## 2.6 为什么大模型常见 AdamW

AdamW 是 Adam 的改进版：把 **weight decay（L2 正则）** 从梯度更新中**解耦**出来，直接作用于参数：

$$
W \leftarrow W - \eta \frac{\hat m_t}{\sqrt{\hat v_t} + \epsilon} - \eta\lambda W
$$

:::math 为什么要「解耦」
在原始 Adam 中把 L2 项 $\lambda W$ 加进梯度：$g_t \leftarrow g_t + \lambda W$。

但 Adam 会用 $\sqrt{\hat v_t}$ 归一化梯度——梯度大的参数，其 L2 项也被同比例缩小，导致**正则化效果被自适应学习率扭曲**：大梯度参数几乎不受正则约束。

AdamW 把 decay 从梯度里拿出来，直接乘在参数上：无论梯度大小，正则强度一致。
:::

这也是为什么训练 Transformer、大模型时你经常会看到 **AdamW**（如 `torch.optim.AdamW`）。

## 2.7 三者对比

| 优化器 | 核心思想 | 优点 | 缺点 |
| --- | --- | --- | --- |
| SGD | 只用当前梯度 | 简单、省显存、泛化有时更好 | 震荡、收敛慢、对 lr 敏感 |
| Momentum | 累积速度 | 抑制震荡、加速 | 仍需手动调学习率 |
| Adam / AdamW | 一阶矩 + 二阶矩 + 偏差修正 | 自适应、收敛快、对超参不敏感 | 显存多（存 m、v）、有时泛化略差 |

:::interview 面试常问
**Q1：Adam 的显存开销为什么这么大？**

:::answer
除了权重本身，还要保存一阶矩 m 和二阶矩 v（各同参数量大小）。混合精度训练还要 FP32 master weights 和 FP32 梯度。合计约 18 bytes/参数（见第 13 章）。这是分布式训练需要 ZeRO/FSDP 分片优化器状态的原因。
:::

**Q2：Adam 和 AdamW 的区别？**

:::answer
Adam 把 L2 正则加进梯度，会被自适应学习率缩放，正则效果不稳定；AdamW 把 weight decay 解耦，直接作用于参数，正则强度与梯度大小无关。现代 Transformer 训练默认 AdamW。
:::

**Q3：为什么大模型训练常用 β₂ = 0.95 而不是 0.999？**

:::answer
β₂ 越大，二阶矩估计越平滑但越滞后。大模型训练梯度噪声大、训练步数少，0.95 让估计更快跟上梯度变化，训练更稳定（LLaMA 等模型的常见配置）。
:::
:::

:::key 本节必须记住
| 优化器 | 一句话 |
| --- | --- |
| SGD | $W \leftarrow W - \eta g$，简单但震荡 |
| Momentum | 累积速度 $v_t = \gamma v_{t-1} + \eta g_t$，抑制震荡 |
| Adam | 一阶矩 + 二阶矩 + 偏差修正，自适应步长 |
| AdamW | 解耦 weight decay，LLM 训练默认 |
| 显存 | Adam 额外存 m、v，共约 18 bytes/参数（混合精度） |
:::

:::quiz
Adam 的偏差修正（除以 1−β^t）解决什么问题？

A. 梯度爆炸
B. 训练初期 m、v 从 0 开始导致估计偏小
C. 学习率过大
D. 过拟合

答案: B
解析: m₀ = v₀ = 0，初期移动平均只有真实值的 (1−β) 比例。偏差修正把估计恢复到真实尺度，且随 t 增大自动消失。
:::

:::quiz
Momentum 能抑制震荡的原因是？

A. 减小学习率
B. 方向相反的梯度在速度变量中互相抵消
C. 增加 batch size
D. 使用二阶矩

答案: B
解析: 在峡谷的陡峭方向，梯度正负交替，历史速度相互抵消；平坦方向梯度一致，速度累积加速。这就是「惯性」的效果。
:::

:::quiz
AdamW 与 Adam 的关键区别是？

A. AdamW 不需要二阶矩
B. AdamW 把 weight decay 从梯度更新中解耦，直接作用于参数
C. AdamW 学习率更大
D. AdamW 不支持 Transformer

答案: B
解析: 原始 Adam 把 L2 加进梯度，会被自适应学习率缩放而失效；AdamW 解耦后正则强度与梯度大小无关，行为更一致，是 LLM 训练默认选择。
:::

:::related
依赖 | 梯度下降, 反向传播, L2 正则
用于 | Transformer 训练, 大模型预训练, 微调
:::
