优化器的直觉——从 SGD 到 Adam,每一步为什么这样走

上一篇文章我们聊了损失函数的设计哲学——一个函数告诉你"模型现在有多差"。但知道有多差只是第一步,更关键的问题是:知道了差在哪,该怎么走?

这就是优化器的工作。

从最早的梯度下降(SGD),到带动量(Momentum),到自适应学习率的 AdaGrad/RMSProp,再到集大成者 Adam——这条进化路径不是零散的拼凑,而是人们在实践中一步步回答同一个问题的过程:

用梯度更新参数时,怎么才能又快又稳地走到最优解?

这篇博客会沿着这个进化路径,讲清楚每个优化器解决了什么问题、又是怎么解决的,以及它们各自在什么场景下好用。


1. SGD:最朴素的走法

$$
\theta_{t+1} = \theta_t - \eta \cdot g_t
$$

vanilla SGD 的逻辑最简单不过:看梯度往哪指,就往哪走一步,步长固定为 $\eta$。

好在哪里?直觉、简单、参数少。在凸优化理论上,SGD 有很好的收敛保证。

但实际训练中问题一堆:

  1. 步长难选。设大了震荡甚至发散,设小了龟速收敛。而且一个好的步长在训练初期和后期可能完全不同。
  2. 梯度方向嘈杂。mini-batch 的梯度只是真实梯度的有偏估计,在陡峭方向上震荡严重。想象你在山谷中下坡,每一步都在左右乱晃,实际路径像个锯齿。
  3. 在峡谷地形中效率极低。所谓峡谷(ravine)是指某个方向梯度极大而另一个方向梯度极小——比如损失函数在参数 $w_1$ 方向上非常陡,在 $w_2$ 方向上非常平缓。SGD 会在陡峭方向来回震荡,而在平缓方向几乎不动。形象地说,你每一步都踩在晃动的钢索上,但想去的地方其实在正前方

这些问题的核心是:梯度信息本身不够用——只有一阶信息,没有历史、没有曲率感知。

于是人们开始想办法给它"加点料"。


2. 动量(Momentum):给下山的路加个惯性

$$
\begin{aligned}
v_{t+1} &= \beta v_t + g_t \
\theta_{t+1} &= \theta_t - \eta v_{t+1}
\end{aligned}
$$

动量(Momentum)的思路简单又优雅:不要只看当前这一步的梯度,还要带着之前积累的"动量"走。

直觉可以这样理解:想象你从山顶推一个铁球下山。铁球不会每步都被小石子带偏,因为它有惯性——之前积累的速度让它在一段时间内保持方向一致。

动量在峡谷地形中尤其有用:

  • 在剧烈震荡的方向(陡峭方向),梯度正负交错,动量叠加后会相互抵消,震荡幅度大幅减少
  • 在缓慢变化的方向(平缓方向),梯度虽然小但方向恒定,动量会不断累积,加速前进

所以动量做了两件好事:抑制震荡 × 加速平缓方向。

折中的是 $\beta$ 这个超参数,通常取 0.9——意味着最新梯度权重为 1,前面 10 步左右累积的历史权重为 9,大约相当于用最近 $1/(1-\beta) \approx 10$ 步的平均梯度来更新。

Nesterov 加速梯度(NAG):往远处看一眼再走

NAG 在动量的基础上做了一处小改动:先按照动量方向往前探头,再计算那个位置的梯度。

$$
\begin{aligned}
v_{t+1} &= \beta v_t + \nabla f(\theta_t - \eta\beta v_t) \
\theta_{t+1} &= \theta_t - \eta v_{t+1}
\end{aligned}
$$

差别在哪?标准动量是在当前位置计算梯度,再沿着动量方向走。NAG 先沿着动量方向"预览"一下下一步的位置,在那个位置算梯度,然后综合两者。

这个改动带来的效果是:NAG 在接近最优点时有过冲预判能力。当动量带你冲向山谷底部时,NAG 会在到达之前就"看到"坡开始向上,提前刹车减速。而标准动量要等到越过谷底、梯度反向之后才开始减速,容易过冲和震荡。

很多研究中,NAG 比标准动量收敛更快、更稳定,这也是它在很多实践中被广泛使用的原因(比如 ResNet 的官方训练就用 NAG)。


3. AdaGrad:让每个参数有自己的步长

到现在为止,所有参数都共享同一个学习率 $\eta$。但这合理吗?

考虑一个稀疏特征场景:特征 $A$ 在所有样本中几乎都是 0,只有少数几个样本有非零值;特征 $B$ 几乎每个样本都有值。对特征 $A$ 来说,它只在少数样本中收到梯度信号,每个信号都非常宝贵,需要珍惜地用大一点步长去学。对特征 $B$ 来说,每个样本都有梯度,步长小一点也没关系,反复微调即可。

AdaGrad 的设计目标就是:让出现频率低的参数学习率大一些,出现频率高的参数学习率小一些。

它的做法是累积每个参数的历史梯度的平方和:

$$
\theta_{t+1,i} = \theta_{t,i} - \frac{\eta}{\sqrt{G_{t,ii} + \epsilon}} \cdot g_{t,i}
$$

其中 $G_{t,ii} = \sum_{\tau=1}^{t} g_{\tau,i}^2$ 是参数 $i$ 从开始到当前所有梯度的平方和。

核心直觉:如果一个参数历史上梯度一直很大,说明它频繁被更新,那它的学习率应该减小;如果一个参数罕见有梯度,它的累积平方和很小,学习率相对更大。

AdaGrad 在稀疏数据上效果非常好(比如 NLP 中的词嵌入训练),但它有一个致命问题:累计平方和 $G_t$ 随着训练步数单调递增,学习率不断衰减,最终趋近于零,模型彻底停止学习。 这在非凸的深度网络中尤其严重——学了一段时间后你就学不动了。


4. RMSProp:只记"近期"的梯度

AdaGrad 的"累积到死"问题让 Geoff Hinton 在课堂上提出了 RMSProp(虽然他没正式发论文)。思路很简单:

不要累积所有历史梯度,而是用指数移动平均(EMA)来维护梯度平方的估计。

$$
\begin{aligned}
v_t &= \beta v_{t-1} + (1-\beta) g_t^2 \
\theta_{t+1} &= \theta_t - \frac{\eta}{\sqrt{v_t + \epsilon}} \cdot g_t
\end{aligned}
$$

注意 $g_t^2$ 表示 element-wise 平方。

这个改动带来的变化是革命性的:

  • $v_t$ 不再单调递增,而是对近期梯度做加权平均
  • 如果当前区域梯度大,$v_t$ 上升,学习率自动减小
  • 如果当前区域梯度小,$v_t$ 下降,学习率自动增大
  • 学习率可以上下波动,而不是像 AdaGrad 那样只会一路下跌

在非凸优化(几乎所有深度网络都是)中,RMSProp 的表现远好于 AdaGrad,因为模型在不同区域需要的步长不同——陡峭区域要小步、平缓区域要大步。

这个"让学习率自适应梯度尺度"的想法,是 Adam 的核心组件之一。


5. 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 \
\hat{m}_t &= \frac{m_t}{1-\beta_1^t}, \quad \hat{v}t = \frac{v_t}{1-\beta_2^t} \
\theta
{t+1} &= \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_t
\end{aligned}
$$

Adam = Momentum + RMSProp,这个说法没错,但它不是简单的加法。Adam 的优雅在于它同时解决了三个问题:

问题 怎么解决的 对应组件
梯度方向嘈杂 一阶动量 EMA 平滑梯度方向 $m_t$
不同参数尺度不同 二阶动量 EMA 自适应调整步长 $v_t$
初始阶段偏差大 偏差校正(bias correction) $\hat{m}_t, \hat{v}_t$

第三个点是最容易被忽略的。在训练初期,$m_t$ 和 $v_t$ 都是从 $0$ 开始累加的,导致前几步的估计严重偏小。不做偏差校正的话,前几步的更新会特别大,然后迅速变小——这种"开头猛冲然后急刹"的行为对训练是不利的。

偏差校正让初始阶段更平稳。随着 $t$ 增大,$1-\beta^t$ 趋近于 1,校正的作用逐渐消失。

实际使用中,默认参数 $\beta_1=0.9$、$\beta_2=0.999$、$\epsilon=10^{-8}$ 在大多数场景下表现良好,这也是 Adam 被誉为"收件即用"优化器的原因。

Adam 的弱点与变体

Adam 也不是完美无缺。在有些任务中(尤其是 CV 中的大规模训练),Adam 的泛化能力不如带动量的 SGD。一个流行的解释是:Adam 的自适应步长在某些情况下会导致模型陷入损失平面的锐利极小值(sharp minima),而 SGD 更倾向于找到平坦极小值(flat minima),泛化更好。

于是有了 AdamW(Adam with decoupled weight decay),它将 SGD 中的 weight decay(L2 正则化)从 Adam 的自适应更新中解耦出来。在大多数现代 Transformer 训练中,AdamW 已经是事实上的标准。如果你在用 PyTorch,torch.optim.AdamW 现在比 torch.optim.Adam 更推荐。

另一个有意思的变体是 Nadam(Nesterov + Adam),加上 Nesterov 的"往前看一步"机制。


6. 实际选择建议

场景 推荐优化器 理由
CV 分类/检测(大batch) SGD + Momentum (NAG) 泛化好,易找 flat minima
NLP / Transformer AdamW 自适应步长适合不同词频,weight decay 解耦
强化学习(value/policy 网络) Adam 不需要最强泛化,要快速收敛
GAN 训练 Adam (常用) 两网络动态博弈,稳定比泛化重要
稀疏特征(推荐系统) Adam / AdaGrad 自适应步长对稀疏特征友好
资源受限 / 小模型 SGD 简单,少超参数,够用

需要强调的是:没有绝对最好的优化器,只有最适合当前任务的。 如果你在跑一个新项目,先用 Adam 快速迭代验证想法,最后换 SGD+Momentum 再跑一轮——这是很多研究者和工程师的实际工作流。


总结

从 SGD 到 Adam 的进化史,本质上是人们在回答三个问题:

  1. 如何抑制震荡、加速收敛? → Momentum / NAG
  2. 如何为每个参数自适应调整步长? → AdaGrad / RMSProp
  3. 如何把这俩结合起来并处理初始偏差? → Adam

每一步改进都来自一个具体的痛点:SGD 在峡谷中震荡 → 动量加惯性;固定学习率不适应不同参数 → AdaGrad 累积历史梯度;累积到死 → RMSProp 用指数动平均;缺少方向平滑 → Adam 融合一阶和二阶动量。

理解了这个演进脉络,你选优化器就不再是"大家都用 Adam 所以我也用",而是知道每个选择背后的 trade-off。


下篇预告:强化学习算法的设计美学——从 Bellman 方程到 PPO 的直觉路径。