梯度下降与优化器实战:SGD、Momentum、Adam 的原理与选择

梯度下降与深度学习优化器实战:梯度下降的直觉与收敛、学习率的角色、SGD 与批大小、Momentum 指数加权、AdaGrad/RMSProp 自适应、Adam 与 AdamW、学习率调度、优化器选择矩阵、优化器的工程实践与调试。

引言

训练模型本质上是「把损失函数压到最低」——而压到最低的引擎就是梯度下降。理解优化器,是理解训练一切模型的起点:为什么学习率一大就震荡、Adam 为什么总比 SGD 收敛快、为什么 2018 年后大家默认用 AdamW。本文从梯度下降的直觉出发,讲清 SGD、Momentum、AdaGrad、RMSProp、Adam、AdamW 六代优化器的演进逻辑,给出超参建议与选型矩阵,并附 PyTorch 实战对照。

前置:/ml-neural-networks-basics/(张量与自动求导)、/ml-model-evaluation/(验证集与调参)、/ml-deep-learning-advanced/(训练调参全景)。


目录


1. 梯度下降:把损失压到最低的引擎

1.1 直觉:下坡找最低点

想象把损失函数想成一片起伏的山地,参数是位置,目标是走到谷底。梯度就是当前点「最陡的上坡方向」,梯度下降则沿相反方向(最陡的下坡)迈步:

# 更新规则:θ ← θ - lr · ∇L(θ)
theta = theta - lr * grad

其中 lr(学习率)是步长,grad 是损失对参数的梯度。

1.2 一个超简线性回归对照

import numpy as np

# y = 2x + 1 + noise,用梯度下降拟合
X = np.random.rand(100, 1) * 10
y = 2 * X + 1 + np.random.randn(100, 1) * 0.5

w, b, lr = 0.0, 0.0, 0.01
for _ in range(500):
    pred = w * X + b
    loss = ((pred - y) ** 2).mean()
    dw = (2 * (pred - y) * X).mean()   # ∂L/∂w
    db = (2 * (pred - y)).mean()       # ∂L/∂b
    w -= lr * dw
    b -= lr * db
print(w, b)  # ≈ 2.0, 1.0

记忆:梯度下降 = 沿最陡下坡方向迈步;θ ← θ - lr·∇L;学习率是步长,梯度是方向。


2. 学习率:最敏感的超参

2.1 学习率三态

学习率表现诊断
太小收敛极慢,loss 平缓下降训练半天 loss 才动
合适loss 平滑下降、收敛到好解理想状态
太大loss 震荡甚至发散训练曲线锯齿状

2.2 实践建议

# 从默认值开始:SGD 0.01 / Adam 0.001
# 观察前几百步:loss 若震荡 → 减半;若几乎不动 → 翻倍
# 用对数尺度搜:1e-1 / 1e-2 / 1e-3 / 1e-4,各跑短训练比较

记忆:学习率三态「太小慢 / 合适稳 / 太大炸」;从默认值起步、按对数尺度搜索、看前几百步 loss 反应。


3. 批大小与随机性:全量、随机与小批量

3.1 三种梯度

模式每次用多少样本特点
全量(Batch GD)全部方向准但慢、易陷入局部最优
随机(SGD)1 个更新快、噪声大、loss 抖动
小批量(Mini-batch)如 32/64折中、GPU 并行友好

3.2 为什么噪声反而是好事

小批量引入的随机噪声让参数「能跳出」尖锐的局部极小值——这是 SGD 家族相对全量梯度下降的隐性优势。实践中 Mini-batch + 合适学习率是默认选择,batch size 常用 16/32/64,随 GPU 显存调大。

记忆:默认用小批量(32/64)——既有并行效率又有跳出局部极小的噪声;batch 太大要相应调大学习率。


4. Momentum:让下坡更稳

4.1 问题:SGD 在山谷里震荡

损失曲面常像狭长山谷:垂直于谷底的方向陡、沿谷底的方向缓。SGD 会沿陡的方向来回震荡,沿缓的方向爬得慢。

4.2 Momentum 的解法:累计方向

Momentum 用「指数加权移动平均」累计历史梯度,让陡方向的震荡相互抵消、缓方向持续加速:

v = 0.9 * v - lr * grad   # v 是速度,0.9 是动量系数
theta += v
  • 震荡方向:正负梯度平均 → 趋于 0 → 不来回跳
  • 一致方向:连续同向 → 加速 → 更快到达谷底

4.3 参数

动量系数 momentum 常用 0.9(PyTorch 的 SGD(momentum=0.9)),0.99 更稳但更慢。

记忆:Momentum 用历史梯度的加权平均当速度——抵消震荡、加速一致方向;动量系数默认 0.9。


5. AdaGrad 与 RMSProp:逐参数自适应

5.1 问题:所有参数该用同一个学习率吗

稀疏特征(如「用户 ID」独热)梯度稀疏但偶尔很大,密集特征梯度频繁但较小——用同一学习率很难兼顾。

5.2 AdaGrad:按历史平方根缩放

AdaGrad 给每个参数累计「历史梯度平方」,学习率除以它的平方根——被更新多的参数步长自动变小:

G += grad ** 2
theta -= lr * grad / (sqrt(G) + 1e-8)

问题:G 只增不减,学习率会单调衰减到零——后期几乎停学。

5.3 RMSProp:衰减的历史平方

RMSProp 给 G 加指数衰减(类似 Momentum 的加权平均),让学习率能「恢复」:

G = 0.9 * G + 0.1 * grad ** 2
theta -= lr * grad / (sqrt(G) + 1e-8)

记忆:自适应优化器按「该参数历史梯度」逐参数缩放学习率——AdaGrad 让学习率单调衰减到停学,RMSProp 加指数衰减让它可恢复。


6. Adam:一阶动量二阶动量结合

6.1 Adam = Momentum + RMSProp

Adam 同时维护一阶动量(方向,像 Momentum)与二阶动量(自适应缩放,像 RMSProp),并加偏差校正抵消初始阶段动量偏小:

# Adam 更新核心(PyTorch 内部)
m = 0.9 * m + 0.1 * grad          # 一阶动量(带偏差校正)
v = 0.999 * v + 0.001 * grad**2   # 二阶动量(带偏差校正)
theta -= lr * m_hat / (sqrt(v_hat) + 1e-8)

6.2 为什么 Adam 好用

  • 对学习率不敏感:默认 lr=0.001 在绝大多数任务都能跑出不错结果
  • 快速起步:自适应步长让它在早期收敛很快
  • 适合稀疏/非平稳梯度:NLP、Embedding、GAN 等场景首选

6.3 使用姿势

import torch.optim as optim

opt = optim.Adam(model.parameters(), lr=1e-3)   # 默认即可
opt = optim.Adam(model.parameters(), lr=1e-3, betas=(0.9, 0.999), weight_decay=1e-5)

记忆:Adam = Momentum(一阶动量)+ RMSProp(二阶动量)+ 偏差校正——对学习率不敏感、起步快,是「不知道选什么时」的默认解。


7. AdamW:解耦权重衰减

7.1 L2 正则与权重衰减的微妙差异

传统实现里「权重衰减」被混进梯度(等价于 L2 正则),但 Adam 的自适应步长会扭曲这种等价关系——L2 大项被小步长吸收,衰减失效。

7.2 AdamW:衰减直接在参数上做

AdamW 把权重衰减与梯度更新解耦,衰减项绕过二阶动量直接作用在参数上:

# 经典 Adam 中:grad += weight_decay * theta(进自适应)
# AdamW 中:theta -= lr * weight_decay * theta(独立、不解耦自适应)

实践表明 AdamW 在 Transformer/预训练模型上泛化更好——PyTorch 的 optim.AdamW 已是事实标准。

记忆:AdamW 把权重衰减从自适应梯度里解耦出来直接作用于参数——Transformer 时代的事实标准,泛化更好。


8. 学习率调度:从常数到 warmup

8.1 调度的两种动机

  • 前期想快:大步走快速到达好区域
  • 后期想稳:小步走精修到谷底

8.2 常用调度器

调度器行为适用
StepLR每 N 步乘 γ简单、经典
CosineAnnealing余弦从高到 0训练后期平滑收敛
ReduceLROnPlateauloss 平台期才降自动、省心
Warmup + Decay先线性升再降Transformer/大模型标配

8.3 warmup 为什么必要

大学习率 + 大模型早期梯度不可靠(参数随机、统计不稳),先小步预热几百步再放开,能避免早期发散。PyTorch 用 torch.optim.lr_scheduler 组合实现:

sched = optim.lr_scheduler.CosineAnnealingLR(opt, T_max=50)
for epoch in range(50):
    train_one_epoch()
    sched.step()

记忆:学习率调度 = 前期大步、后期小步;CosineAnnealing 平滑收敛、ReduceLROnPlateau 平台期自动降;大模型标配 warmup 防早期发散。


9. 优化器选择与超参矩阵

9.1 选型矩阵

场景优化器理由
简单回归/CNN 入门SGD + Momentum收敛解质量高、可解释
一般深度学习Adam / AdamW默认、省心、对学习率不敏感
Transformer/NLPAdamW + warmup泛化最好、事实标准
稀疏特征(推荐/Embedding)Adam二阶动量适配稀疏梯度
小数据要稳SGD + 小学习率噪声下更稳

9.2 超参默认值与调节

# Adam: lr=1e-3, betas=(0.9, 0.999), eps=1e-8
# AdamW: lr=1e-3~3e-4(越大模型越小), weight_decay=0.01
# SGD+Momentum: lr=0.01~0.1, momentum=0.9
# 学习率调度:Cosine 或 ReduceLROnPlateau,大模型加 warmup
# 排查顺序:先固定优化器,只调 lr;再试调度器;最后调 weight_decay

记忆:选型看场景——一般任务 AdamW、NLP 必 AdamW+warmup、追求可解释与高泛化用 SGD+Momentum;超参先锁优化器只动学习率。


10. 速查表与一句话记忆

优化器核心机制一句话
SGD直接沿梯度简单、解质量高、要自己调 lr
SGD+Momentum累计方向抵消震荡、加速一致方向
AdaGrad平方根缩放学习率单调衰减到停学
RMSProp衰减平方根学习率可恢复
Adam一阶+二阶动量默认解、对 lr 不敏感
AdamW解耦权重衰减Transformer 标配、泛化好

一句话记忆:优化器演进是「补丁」的叠加——Momentum 补方向震荡、RMSProp 补逐参数步长、Adam 两者合并、AdamW 再解耦权重衰减;工程上一般任务默认 AdamW(lr=1e-3、weight_decay=0.01),追求高泛化/可解释用 SGD+Momentum(lr=0.01~0.1、momentum=0.9),Transformer 必加 warmup + Cosine 调度——「先锁优化器、只调学习率、最后动衰减」是调参的稳妥顺序。


延伸阅读

  • /ml-neural-networks-basics/ — 张量、自动求导与训练循环
  • /ml-deep-learning-advanced/ — 优化器演进、Dropout 与训练调参
  • /ml-model-evaluation/ — 验证集、学习曲线与过拟合
  • /ml-automl-hpo/ — 学习率等超参的自动搜索
  • [[ai-ml]] — 训练稳定性与大规模分布式训练
  • PyTorch Optimizers 文档
  • AdamW 原论文

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 模型压缩与推理优化实战:量化、剪枝与知识蒸馏
  2. 强化学习入门实战:MDP、Q-learning 与 DQN 概览
  3. 多分类与多标签学习实战:OvR、softmax 与多输出评估