优化器深度解析:SGD / Momentum / Adam 为什么是这样
你以为梯度下降只是"沿梯度反方向走"——其实有 10 多种花式走法。Adam 凭什么成为深度学习默认款?
L1-03 我们讲了梯度下降的基本公式:
但这只是最朴素的版本——叫 vanilla SGD。
实际训练 ChatGPT 这样的模型,从来没人用纯 SGD——大家都在用 Adam。 为什么?这一篇我们彻底搞懂”优化器家族”。
🎮 强烈建议:先去 梯度下降登山者可视化 玩 5 分钟,你会有直观的感受。本文是它的”伴读”。
第一站:vanilla SGD
最朴素的梯度下降:
for batch in data:
grad = compute_gradient(batch)
theta -= lr * grad
它的问题:
问题 1:在”狭长山谷”里疯狂震荡
想象一个椭圆形损失曲面:x 方向缓、y 方向陡。
SGD 沿梯度最陡方向走——它会在窄方向反复横跳,慢慢往最优点挪。走的”路径”特别浪费。
问题 2:被”鞍点”卡住
鞍点(saddle point):梯度 = 0 但不是最优。在高维空间里到处都是鞍点——研究表明,几十亿参数的网络损失曲面上,鞍点比局部最优多得多。
SGD 一到鞍点附近,梯度 ≈ 0,就基本不动了。
问题 3:所有参数共享同一个学习率
但不同参数其实需要不同步长——有的参数应该走大步,有的应该小心翼翼。
第二站:Momentum(动量)
灵感:滚雪球。
如果一个球已经在朝某个方向滚——它继续滚比临时改方向更省力。
v = 0 # 速度
for batch in data:
grad = compute_gradient(batch)
v = beta * v - lr * grad # 累积速度
theta = theta + v # 沿速度方向走
其中 是经验值——意思是”90% 保留旧速度 + 10% 加入新梯度”。
Momentum 解决了什么
- 狭长山谷震荡:窄方向上的震荡互相抵消,宽方向上的”前进”累加——速度越来越快
- 鞍点:靠累积的”惯性”冲过梯度为 0 的区域
- 训练加速:通常比 SGD 快 2-3 倍收敛
但它仍有问题
- 仍然全参数一个学习率
- 在最优点附近会”过冲”——靠惯性冲过去再回来
- 这个超参数需要手动调
第三站:AdaGrad
灵感:让经常更新的参数学小步,让很少更新的参数学大步。
G = 0 # 历史梯度平方累积
for batch in data:
grad = compute_gradient(batch)
G = G + grad ** 2
theta = theta - lr * grad / (sqrt(G) + eps)
注意: 是按参数维度独立累积的。梯度大的参数,分母大,实际步长小;梯度小的参数,分母小,实际步长大。
AdaGrad 解决了什么
- 稀疏数据:自然语言处理中,词频分布极不均匀——常见词高频更新、罕见词低频。AdaGrad 给罕见词更大步长,让它们也能被学好。
但它有致命问题
- 是单调累积的——它只增不减
- 训练越久,分母越大——学习率最终趋于 0
- 训练后期,模型几乎”停学”
第四站:RMSProp
Hinton 在 Coursera 课程上提出的”修复”。
把”累积所有历史梯度”改成”指数加权平均”:
v = 0
for batch in data:
grad = compute_gradient(batch)
v = beta * v + (1 - beta) * grad ** 2 # 只看最近
theta = theta - lr * grad / (sqrt(v) + eps)
是常见值——意思是”99% 旧梯度方差 + 1% 新的”。只看最近的梯度,老旧的会被遗忘。
RMSProp 解决了什么
- 修复了 AdaGrad “学习率趋零”的问题
- 仍然保留了”自适应学习率”的好处
但它没融入 Momentum
我们已经知道 Momentum 有用——RMSProp 没用它。
第五站:Adam(Momentum + RMSProp 的合体)
Adam 全称 Adaptive Moment Estimation,2014 年提出。它把上面所有想法揉在一起:
m = 0 # 一阶矩(动量)
v = 0 # 二阶矩(梯度平方)
for batch in data:
t = t + 1
grad = compute_gradient(batch)
# 1. 累积动量(像 Momentum)
m = beta1 * m + (1 - beta1) * grad
# 2. 累积梯度平方(像 RMSProp)
v = beta2 * v + (1 - beta2) * grad ** 2
# 3. 偏置修正(小技巧,让早期估计更准)
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
# 4. 更新参数
theta = theta - lr * m_hat / (sqrt(v_hat) + eps)
默认超参数:
- (动量衰减)
- (方差衰减)
- (数值稳定)
- (学习率)
Adam 解决了所有上面提到的问题
| 问题 | SGD | Momentum | RMSProp | Adam |
|---|---|---|---|---|
| 山谷震荡 | ❌ | ✅ | ✅ | ✅ |
| 鞍点卡住 | ❌ | ✅ | 部分 | ✅ |
| 自适应步长 | ❌ | ❌ | ✅ | ✅ |
| 训练后期不停学 | — | — | ✅ | ✅ |
这就是为什么 Adam 是 PyTorch 默认优化器。
偏置修正(bias correction)是 Adam 的精髓之一。早期 和 都从 0 开始累积,会”偏小”——除以 修正这个偏差。等 大了,修正系数趋近 1,影响消失。
AdamW:解决 Adam 的一个隐藏 bug
后来研究者发现 Adam 跟 weight decay(权重衰减,一种正则化)配合不好。
传统做法:在损失里加 项
AdamW:直接在更新公式里减一项
这个小修改让 Adam 在 Transformer 上的表现显著提升——所以今天训 LLM 几乎都用 AdamW,不是裸 Adam。
一些”花哨”的变种
| 优化器 | 特点 |
|---|---|
| NAdam | Adam + Nesterov momentum(“先看一眼再决定怎么走”) |
| AdaBelief | 优化 Adam 的方差估计 |
| Lion | 2023 年 Google 提出,只用符号信息,省内存 |
| Shampoo | 用全矩阵预条件,效果好但贵 |
| AdamW + Schedule-free | 2024 年提出,不用 LR schedule |
真实情况:Adam / AdamW 覆盖了 95% 的深度学习场景。其它优化器都在跟它比性能或省成本。
学习率调度
光选 Adam 不够,学习率怎么变也很关键。
常见策略:
1. Constant(不变)
最简单但通常次优。
2. Step Decay
每 N 个 epoch 把 lr 减半。简单粗暴。
3. Cosine Annealing
lr 按余弦曲线降到 0,效果稳定。LLM 训练常用。
4. Warmup + Cosine
前 N 步 lr 从 0 升到目标值(warmup),然后余弦下降。几乎所有 Transformer 都这么做。
import torch.optim as optim
from torch.optim.lr_scheduler import CosineAnnealingLR
optimizer = optim.AdamW(model.parameters(), lr=1e-3)
scheduler = CosineAnnealingLR(optimizer, T_max=100)
for epoch in range(100):
train_one_epoch()
scheduler.step()
怎么挑
| 场景 | 推荐 |
|---|---|
| 标准深度学习 | AdamW + Cosine schedule |
| 训练 LLM | AdamW + Warmup + Cosine |
| 训练 CNN(视觉) | SGD with Momentum(出乎意料!) |
| 内存极度紧张 | Lion 或 8-bit AdamW |
| 研究新场景 | 先试 AdamW,再考虑别的 |
一个反常识:在视觉任务(CNN)上,SGD + Momentum 经常打败 Adam。原因不明,可能与损失曲面性质有关。所以 CV 论文里 SGD 仍然常见。
用 PyTorch 切换很简单
# 切换优化器只是一行
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
# 或
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
# 训练循环不变
for batch in dataloader:
optimizer.zero_grad()
loss = compute_loss(batch)
loss.backward()
optimizer.step()
一句话总结
SGD 是步行,Momentum 是骑滑板,AdaGrad 是助力车,Adam 是混动车。
你训 ChatGPT 不会用步行——所以默认用 AdamW。
但每种工具都有它的场景,别迷信任何一个。
想”看见”它
👀 梯度下降登山者可视化 —— 在四种地形上比较 SGD / Momentum / Adam 的轨迹差异。这次读完文章再玩一遍,你会理解为什么它们走得不同。
你已经懂了:损失函数(L1-04)+ 梯度下降(L1-03)+ 优化器(本篇)。 下一步推荐到 L3 路径——把这些数学武器组合起来,理解 Transformer 等真实神经网络。
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。