一句话定义:优化器是神经网络训练时,决定「每一步把模型参数往哪个方向调、调多少」的算法。
把它放进 AI 体系里看:训练一个神经网络要做三件事——前向算出预测、用损失函数衡量预测差多少、再让优化器根据差距反向调整参数。损失函数和反向传播负责告诉模型「这里错了」,优化器负责决定「具体怎么改」。它和损失函数、学习率是搭档关系,本概念紧挨着「梯度下降」这个更基础的概念,可以理解为「梯度下降的进阶升级版家族」。
一个贯穿全文的主类比:蒙眼下山
想象你被蒙着眼放在一座山上,目标是走到最低的山谷。你看不到全貌,只能用脚感受脚下哪边更陡(这就是梯度),然后朝下坡方向迈一步。优化器就是「你的下山策略」——同样都在感受坡度,不同策略走出来的路线天差地别:
- SGD(随机梯度下降):每一步只看脚下当前的坡度,立刻迈出去。
- Momentum(动量法):记住自己前几步的方向,惯性帮助你维持大方向、不被小坑洼带偏。
- Adam(自适应矩估计):在 Momentum 的基础上,再给每个参数单独配一个步长,陡的维度走稳、缓的维度走快。
下面逐个拆开看,重点讲清它们为什么层层递进。
图 1:从 SGD 到 Momentum 再到 Adam,每代都在前一代基础上「多解决一个问题」,并非推倒重来。
第一代:SGD——纯粹「看脚下」
SGD 的更新规则极其简单——用当前梯度乘上学习率,直接从参数里减掉:
符号逐项解读:
:当前这一步的模型参数(一个数或一个矩阵,看你调的是哪个权重)。 :当前算出来的梯度,指「朝哪个方向走,损失上升最快」;我们朝相反方向走,就能让损失下降。 (读作「伊塔」):学习率,一个手动设的小数(如 0.01),决定每步迈多大。 :更新后的新参数。
直觉:每一步都把当前看到的坡度当成全部信息,立刻照做。简单、可靠、好实现,是深度学习最早的默认选择。
问题:在「峡谷」地形(一个方向陡、另一个方向缓)里,SGD 会在陡的方向上来回横跳——因为每步只看脚下,看到左陡就往右迈,下一步又看到右陡往左迈,明明大方向是顺着峡谷往下走,却一直在峡谷两壁之间反复震荡,走得又慢又乱。
图 2:SGD 在峡谷两壁间来回横跳、前进缓慢;Momentum 靠惯性抵消横跳,贴着谷底快速前进。
第二代:Momentum——记住方向,少走弯路
Momentum(Polyak, 1964)的核心改动是引入一个「速度」变量
符号逐项解读:
:当前这一步的「累积速度」,相当于把过去几步的梯度做了一个加权平均(叫指数加权移动平均)。 :动量系数(常用 0.9),决定历史信息的「记忆长度」——越大越重视过去方向。 :当前梯度的权重,确保新旧信息此消彼长。
直觉:像一个重球滚下山,球有惯性——即使当前这一步脚下提示「往左」,但球之前一直在朝前滚,惯性会带着它继续往前,只是稍微偏左一点。在峡谷里:陡方向上的梯度左右摇摆,正负抵消,累积速度被压小;缓方向上的梯度始终同向,累积速度越来越大。结果就是「震荡方向被压、有用方向被放大」,球更快地朝山谷前进。
小例题(自检):设
- 算累积速度:
。 - 更新参数:
。
对比纯 SGD 同样条件:
注意:PyTorch 的
torch.optim.SGD(momentum=0.9)内部公式是(没有 这一项),和我们这里写的「指数加权」形式相差一个常数倍,本质行为一致。Adam 部分用「指数加权」写法更连贯,所以本文统一采用这一形式。
第三代:Adam——每个参数有自己的步长
Adam(Kingma & Ba, 2014)在 Momentum 的基础上再加一个关键思想:自适应学习率。它同时维护两个滑动平均:
符号逐项解读:
:梯度本身的滑动平均(沿用 Momentum 的思想,决定方向)。 :梯度平方的滑动平均(衡量这个参数历史上梯度有多大)。 :两个记忆系数,常用 、 。 :偏差修正。因为 ,刚开始几步估计严重偏小,除以 (一个接近 0 的小数)把它放大回来。 (如 ):防止分母为 0 的小常数。 - 步长
:方向由 定,但每个参数的实际步长被 缩放。
直觉(这是 Adam 的精髓):梯度一直很大的参数 →
小例题(自检):设
- 算一阶矩:
。 - 算二阶矩:
。 - 偏差修正:
, 。 - 更新:
。
可以看到:偏差修正后,第一步的有效梯度基本等于真实梯度
图 3:Adam 给每个参数配专属步长——历史梯度大的被压小(走稳),历史梯度小的被放大(走快)。
三者递进一张表
| 优化器 | 核心机制 | 解决了什么 | 代价 |
|---|---|---|---|
| SGD | 当前梯度 × 学习率 | 提供最基础的下山规则 | 峡谷里来回震荡、收敛慢 |
| Momentum | 累积历史梯度的速度 | 抑制震荡方向、放大有用方向 | 多一个动量超参要调 |
| Adam | 一阶矩 + 二阶矩 + 偏差修正 | 每个参数自适应步长、稀疏梯度友好 | 多几个超参(但默认值通常够用) |
实践经验:Adam 因为几乎「开箱即用」(默认
完整代码
下面这份代码可以复制即跑。用假数据演示如何用三种优化器训练同一个最小神经网络,重点看三种 optimizer 实例化方式,以及一个标准训练步的五个动作。
import torch
import torch.nn as nn
# === 准备阶段 ===
# 1. 固定随机种子——让 torch 生成随机数的结果可复现
# 新手常困惑:为什么同样的代码两次跑结果不一样?多半是随机种子没固定
torch.manual_seed(42)
# 2. 造一份假数据:100 个样本,每个样本 4 个特征
# torch.randn(100, 4) 返回 100 行 4 列、服从标准正态分布的张量,模拟「输入特征矩阵」
X = torch.randn(100, 4)
# 3. 造对应的假标签:100 个 0 或 1 的整数,模拟「二分类的真值」
# torch.randint(0, 2, (100,)) 从 [0, 2) 区间随机采 100 个整数
y = torch.randint(0, 2, (100,))
# === 定义模型 ===
# 自定义神经网络:必须继承 nn.Module(PyTorch 所有神经网络模型的基类)
class TinyNet(nn.Module):
def __init__(self):
# super().__init__() 调用父类 nn.Module 的初始化,注册内部状态(必须写,否则参数不被追踪)
super().__init__()
# nn.Linear(4, 2):全连接层(线性层),输入 4 维、输出 2 维
# 内部自动创建权重 W(形状 2×4)和偏置 b(形状 2),并标记为「可学习参数」
self.fc = nn.Linear(4, 2)
def forward(self, x):
# 前向传播:定义数据怎么从输入流到输出
# 这一层就一步:y_pred = x @ W.T + b(@ 是矩阵乘法的运算符简写)
return self.fc(x)
# 实例化模型——此时 self.fc 里的 W 和 b 已被随机初始化
model = TinyNet()
# === 损失函数 + 三种优化器对比 ===
# 交叉熵损失:分类任务的标准损失,内部会自动对 logits 做 softmax
criterion = nn.CrossEntropyLoss()
# 【SGD】最基础的优化器——所有参数共用同一个学习率 0.01
optimizer_sgd = torch.optim.SGD(model.parameters(), lr=0.01)
# 【Momentum】在 SGD 上加 momentum=0.9,相当于「重球」惯性
optimizer_momentum = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# 【Adam】自适应学习率;lr=0.001 是 Adam 的常用默认值(比 SGD 小一个数量级)
optimizer_adam = torch.optim.Adam(model.parameters(), lr=0.001)
# model.parameters() 把模型里所有「可学习参数」(W、b 等)打包传给优化器,让它知道要调谁
# 本例选 Adam 做一次完整的训练步(想换 SGD / Momentum,把下面这行改一下即可)
optimizer = optimizer_adam
# === 一个标准训练步的五个动作(背下来,几乎每次都这样写) ===
# 动作 1:清空上一步残留的梯度
# PyTorch 默认「梯度累加」:不清零的话,本次梯度会叠到上次梯度之上
optimizer.zero_grad()
# 动作 2:前向传播——把数据喂给模型,得到预测值(这里是 100×2 的 logits)
outputs = model(X)
# 动作 3:算损失——衡量「预测值」和「真实标签」的差距
loss = criterion(outputs, y)
# 动作 4:反向传播——PyTorch 自动算出每个参数对损失的梯度(这一步三种优化器完全一样)
loss.backward()
# 动作 5:更新参数——优化器根据梯度调整 W 和 b(这一步三种优化器差别最大!)
optimizer.step()
print(f"训练一步后的损失: {loss.item():.4f}")跑完会打印一行类似 训练一步后的损失: 0.7321 的数值。把 optimizer 换成 optimizer_sgd 或 optimizer_momentum 再跑一次,对比损失下降的速度——你会直观感受到 Adam 在训练前期往往掉得最快。
小结
一句话浓缩全文:优化器 = 用梯度信息更新参数的策略。SGD 只看当前脚下;Momentum 加惯性、压震荡;Adam 再叠一层「每个参数自适应步长」。三者层层递进,工程上的默认选择几乎都是 Adam,但 SGD + Momentum 在「卷精度」的场景依然有它的位置。理解了这条演进线,你也就理解了深度学习训练里最关键的一个旋钮。
参考资料
- Adam: A Method for Stochastic Optimization - Diederik P. Kingma & Jimmy Ba (2014/2015, ICLR) https://arxiv.org/abs/1412.6980
- An overview of gradient descent optimization algorithms - Sebastian Ruder https://ruder.io/optimizing-gradient-descent/
- 《动手学深度学习》——优化算法章节 - 李沐等 https://zh.d2l.ai/chapter_optimization/index.html