共计 1797 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点:为什么需要 Adam?
传统随机梯度下降(SGD)在非凸优化问题中(如神经网络训练)存在两个典型问题:
- 学习率敏感:固定学习率在平坦区域下降过慢,在陡峭区域容易震荡
- 梯度稀疏性:某些特征对应的梯度更新频率极低(如 NLP 中的低频词向量)
$$
\theta_{t+1} = \theta_t – \eta \cdot \nabla_\theta J(\theta_t)
$$
这种一刀切的学习率策略,导致模型在参数空间不同维度上无法实现差异化的更新步长。2015 年提出的 Adam 算法通过 自适应动量估计,实现了:
- 历史梯度的一阶矩估计(动量方向)
- 历史梯度平方的二阶矩估计(自适应学习率)
数学原理:Adam 如何工作?
Adam 的核心在于对每个参数维护两个状态变量:
$$
\begin{aligned}
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
\end{aligned}
$$
其中 $\beta_1$(默认 0.9)控制动量衰减率,$\beta_2$(默认 0.999)控制二阶矩衰减率。为解决初始阶段的偏差问题,需要进行修正:
$$
\begin{aligned}
\hat{m}_t &= \frac{m_t}{1-\beta_1^t} \
\hat{v}_t &= \frac{v_t}{1-\beta_2^t}
\end{aligned}
$$
最终参数更新规则为:
$$
\theta_t = \theta_{t-1} – \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}
$$
PyTorch 实战实现
import torch
import torch.nn as nn
# 模型定义
model = nn.Sequential(nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10)
)
# Adam 优化器初始化
optimizer = torch.optim.Adam(model.parameters(),
lr=1e-3,
betas=(0.9, 0.999), # β1, β2
eps=1e-8,
weight_decay=0 # L2 正则化
)
# 带梯度裁剪的训练步骤
def train_step(x, y):
optimizer.zero_grad()
output = model(x)
loss = nn.CrossEntropyLoss()(output, y)
loss.backward()
# 梯度裁剪(防止爆炸)torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
关键参数说明:
betas:动量衰减系数,建议保持默认eps:数值稳定项,防止除以零weight_decay:实际实现的是 L2 正则化,不是真正的权重衰减
对比实验:MNIST 上的表现
我们对比三种优化器在 MNIST 数据集上的收敛速度:
- SGD:学习率 0.1,动量 0.9
- RMSprop:学习率 1e-3,alpha=0.99
- Adam:默认参数

可以看到:
- Adam 初期收敛速度最快
- SGD 后期在测试集上可能找到更优解
- RMSprop 对学习率更敏感
生产环境建议
1. 初始学习率选择
- CV 任务:通常 1e- 3 到 1e-4
- NLP 任务:建议 3e- 5 到 5e-5(因文本数据更稀疏)
- 配合学习率预热(Warmup)效果更好
2. amsgrad 的适用场景
# 启用 amsgrad 模式
optimizer = torch.optim.Adam(model.parameters(), amsgrad=True)
- 原始 Adam 的 $v_t$ 估计可能过于激进
- amsgrad 保证 $\hat{v}_t$ 单调不减
- 适合梯度分布变化剧烈的任务(如 GAN)
3. 与 BatchNorm 的配合
- BatchNorm 层依赖 batch 统计量,与 Adam 的自适应特性可能冲突
- 解决方案:
- 使用更大的 batch size(>64)
- 降低 BatchNorm 层的动量系数(默认 0.1)
- 考虑 LayerNorm 替代
总结
Adam 通过自适应学习率机制,在大多数深度学习任务中都能取得不错的效果。但需要注意:
- 超参数虽少但对最终性能影响大
- 可能找到尖锐的最小值(泛化性差)
- 与某些网络结构(如 BatchNorm)需要特别调参
建议在小数据集上先用 Adam 快速验证 idea,再考虑用 SGD 进行精细调优。
