从 SGD 到 Adam:用一个例子讲透深度学习五大优化器

从 SGD 到 Adam:用一个例子讲透深度学习五大优化器

深度学习里的优化器种类繁多,公式看着一个比一个复杂。但如果把它们放在同一个例子里逐步计算,你会发现它们其实是一条清晰的演进线:每一个新优化器,都是为了修复前一个的某个具体缺陷。

本文用同一个最简单的例子,把 SGD、Momentum、AdaGrad、RMSProp、Adam 五个优化器从头到尾走一遍,让你看清它们到底在解决什么问题、彼此有什么区别。

目录

统一的例子设定

梯度是怎么算出来的

[1. SGD:最朴素的梯度下降](#1. SGD:最朴素的梯度下降 "#1-sgd%E6%9C%80%E6%9C%B4%E7%B4%A0%E7%9A%84%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D")

[2. Momentum:给梯度下降加惯性](#2. Momentum:给梯度下降加惯性 "#2-momentum%E7%BB%99%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D%E5%8A%A0%E6%83%AF%E6%80%A7")

[3. AdaGrad:给每个参数配专属学习率](#3. AdaGrad:给每个参数配专属学习率 "#3-adagrad%E7%BB%99%E6%AF%8F%E4%B8%AA%E5%8F%82%E6%95%B0%E9%85%8D%E4%B8%93%E5%B1%9E%E5%AD%A6%E4%B9%A0%E7%8E%87")

[4. RMSProp:修好 AdaGrad 的学习率衰竭](#4. RMSProp:修好 AdaGrad 的学习率衰竭 "#4-rmsprop%E4%BF%AE%E5%A5%BD-adagrad-%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%8E%87%E8%A1%B0%E7%AB%AD")

[5. Adam:Momentum + RMSProp 合体](#5. Adam:Momentum + RMSProp 合体 "#5-adammomentum--rmsprop-%E5%90%88%E4%BD%93")

五大优化器全景对比

附:多层网络的梯度是怎么算的

统一的例子设定

为了让五个优化器可以直接对照,全文使用同一个极简任务。

任务 :学习一条过原点的直线 y = w·x,只有一个参数 w。

训练集 (真实规律是 y = 2x,我们希望模型学出 w ≈ 2):

样本

x

真实 y

1

1

2

2

2

4

3

3

6

损失函数:均方误差 MSE

L=1N ∑i=1N (wxi−yi)2 L = \frac{1}{N}\sum_{i=1}^{N}(w x_i - y_i)^2 L=N1∑i=1N(wxi−yi)2

初始值 :w = 0。学习率等超参数在每个优化器里单独给出。

梯度是怎么算出来的

在进入优化器之前,先把梯度公式推清楚,后面所有计算都要用到它。

对单个样本,损失是 Li=(wxi−yi)2 L_i = (w x_i - y_i)^2 Li=(wxi−yi)2。用链式法则 对 w 求导,设中间变量 u=wxi−yi u = w x_i - y_i u=wxi−yi:

∂Li ∂w = ∂Li ∂u ⏟ 2u ⋅ ∂u∂w ⏟ xi =2(wxi−yi) xi \frac{\partial L_i}{\partial w} = \underbrace{\frac{\partial L_i}{\partial u}}{2u} \cdot \underbrace{\frac{\partial u}{\partial w}}{x_i} = 2(w x_i - y_i)\,x_i ∂w∂Li=2u ∂u∂Li⋅xi ∂w∂u=2(wxi−yi)xi

直觉上这个式子拆成三部分:

∂Li ∂w =2⋅ (wxi−yi) ⏟ 误差 ⋅ xi⏟ 输入 \frac{\partial L_i}{\partial w} = 2 \cdot \underbrace{(w x_i - y_i)}{\text{误差}} \cdot \underbrace{x_i}{\text{输入}} ∂w∂Li=2⋅误差 (wxi−yi)⋅输入 xi

误差 (wxᵢ − yᵢ):预测减真实,误差越大梯度越大,参数要调得越猛;误差为 0 时梯度为 0。

乘输入 xᵢ :输入越大的样本,w 的微小变化对预测影响越大,对梯度的贡献也越大。

对全部样本求平均,得到本例的批量梯度公式(代入三个样本并化简):

g(w)=23∑i(wxi−yi)xi=283(w−2)≈9.33 (w−2)g(w) = \frac{2}{3}\sum_{i}(w x_i - y_i)x_i = \frac{28}{3}(w - 2) \approx 9.33\,(w - 2) g(w)=32∑i(wxi−yi)xi=328(w−2)≈9.33(w−2)

这个 g(w) = 9.33·(w − 2) 会贯穿全文:w 离目标 2 越远,梯度越大。

顺带厘清 BGD / SGD / Mini-batch :三者的唯一区别是每次更新用多少数据算梯度。BGD 用全部样本、一个 epoch 更新 1 次,最稳但最慢;SGD 每个样本更新一次、抖动大但能跳出局部最优;Mini-batch 是两者折中,也是实践主流。本文为聚焦优化器本身,统一用全部样本算梯度(BGD 方式)。

1. SGD:最朴素的梯度下降

逻辑

每一步只听当前梯度的,走完就忘:

w←w−lr⋅gtw \leftarrow w - lr \cdot g_t w←w−lr⋅gt

存在的问题

在"狭长山谷"型的损失曲面上(一个方向很陡、一个方向很平缓),SGD 会在陡的方向来回震荡,在平缓但正确的方向上磨蹭,收敛慢。这正是后续优化器要解决的核心痛点。

逐步计算(lr = 0.01)

w(更新前)

梯度 g

w(更新后)

1

0

−18.67

0.187

2

0.187

---

0.359

3

0.359

---

0.517

4

0.517

---

0.662

四步走到 0.662,平稳但慢。

PyTorch

python

复制代码

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

注意:PyTorch 的 torch.optim.SGD 实际是 mini-batch,batch 大小由 DataLoader 决定,叫 SGD 只是习惯。

2. Momentum:给梯度下降加惯性

逻辑

更新时不只看当前梯度,还累积之前几步的方向 ------像给下山的小球加了惯性。引入"速度"变量 v:

vt=β⋅ vt−1 +gtw←w−lr⋅vt v_t = \beta \cdot v_{t-1} + g_t \qquad w \leftarrow w - lr \cdot v_t vt=β⋅vt−1+gtw←w−lr⋅vt

其中 β 通常取 0.9:这次的速度 = 保留 90% 的上次速度 + 这次的新梯度。

关键直觉:一致则加速,震荡则抵消

方向一致的维度 :每步梯度同号,累加起来越滚越快,加速前进。

来回震荡的维度 :这步正、下步负,累加时正负抵消,抖动被抹平。

正好治了 SGD 的两个毛病。

逐步计算(lr = 0.01,β = 0.9,v₀ = 0)

w

梯度 g

速度 v

w(更新后)

1

0

−18.67

−18.67

0.187

2

0.187

−16.92

−33.72

0.524

3

0.524

−13.77

−44.12

0.965

4

0.965

−9.66

−49.37

1.459

注意第 2 步:速度 v₂ = −33.72 比当前梯度 −16.92 大了近一倍------因为方向一致,历史速度被累加进来了。

对比 SGD(同样 4 步)

SGD 的 w

Momentum 的 w

1

0.187

0.187

2

0.359

0.524

3

0.517

0.965

4

0.662

1.459

方向一致时,Momentum 明显冲得更快。

冲过头会自动纠正

Momentum 加速快,代价是可能冲过目标。一旦 w 越过 2、梯度变正号,速度 v 里存的负惯性会被新正梯度先抵消再拉回 ,表现为在目标附近摆动几下、摆幅递减、最后停在 2,像滚进碗底的球。

PyTorch

python

复制代码

optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

3. AdaGrad:给每个参数配专属学习率

逻辑

Momentum 管的是方向 ;AdaGrad 管的是步长 ------给每个参数配一个专属学习率,某参数历史梯度越大,学习率就自动调得越小。

累积历史梯度的平方:

Gt= Gt−1 +gt2w←w− lr Gt +ϵ ⋅gt G_t = G_{t-1} + g_t^2 \qquad w \leftarrow w - \frac{lr}{\sqrt{G_t} + \epsilon} \cdot g_t Gt=Gt−1+gt2w←w−Gt +ϵlr⋅gt

G 越大 → 分母越大 → 有效学习率越小。等于"更新得越多,越往后越谨慎"。

解决的问题

模型里有的参数频繁更新(梯度大)、有的很少更新(梯度小),用同一个学习率没法同时照顾。AdaGrad 让"动得多的减速、动得少的保持大步",尤其利于稀疏特征。

逐步计算(lr = 0.5,G₀ = 0)

w

梯度 g

G(累积)

有效学习率

w(更新后)

1

0

−18.67

348.5

0.0268

0.500

2

0.500

−14.00

544.5

0.0214

0.800

3

0.800

−11.20

669.9

0.0193

1.016

4

1.016

−9.18

754.2

0.0182

1.183

有效学习率单调递减(0.0268 → 0.0182)------这是 AdaGrad 最鲜明的特征。

致命缺点

G 只增不减、永不衰减,学习率会一路降到接近 0。在真实的长时间训练中,模型还没学好就"走不动了"。这就是下一个优化器要修的问题。

PyTorch

python

复制代码

optimizer = torch.optim.Adagrad(model.parameters(), lr=0.5)

4. RMSProp:修好 AdaGrad 的学习率衰竭

逻辑

AdaGrad 的病根是"无限累加"。RMSProp 的解法:别累加所有历史,只关注最近一段------旧的逐渐遗忘。 把累加换成指数移动平均(EMA):

Gt=γ⋅ Gt−1 +(1−γ)⋅gt2w←w− lr Gt +ϵ ⋅gt G_t = \gamma \cdot G_{t-1} + (1-\gamma) \cdot g_t^2 \qquad w \leftarrow w - \frac{lr}{\sqrt{G_t} + \epsilon} \cdot g_t Gt=γ⋅Gt−1+(1−γ)⋅gt2w←w−Gt +ϵlr⋅gt

γ 通常取 0.9 :保留 90% 旧累积(很久以前的按 0.9、0.81、0.729... 指数衰减被遗忘),掺入 10% 当前梯度平方。于是 G 反映"最近梯度大概多大",会随梯度变小而变小,不会无限膨胀。

逐步计算(lr = 0.5,γ = 0.9,G₀ = 0)

w

梯度 g

G(移动平均)

有效学习率

w(更新后)

1

0

−18.67

34.85

0.0847

1.581

2

1.581

−3.91

32.90

0.0872

1.922

3

1.922

−0.73

29.66

0.0918

1.989

4

1.989

−0.103

26.69

0.0968

1.999

精髓在第 2 步:梯度从 −18.67 掉到 −3.91(接近目标了),G 也跟着往下走(34.85 → 32.90),不像 AdaGrad 只增不减。

对比 AdaGrad

AdaGrad 有效学习率

RMSProp 有效学习率

AdaGrad 的 w

RMSProp 的 w

1

0.0268

0.0847

0.500

1.581

2

0.0214 ↓

0.0872

0.800

1.922

3

0.0193 ↓

0.0918

1.016

1.989

4

0.0182 ↓

0.0968

1.183

1.999

学习率走势:AdaGrad 单调下降(被 G 累加拖累);RMSProp 平稳甚至回升(G 能随梯度变小而回落)。

收敛速度:AdaGrad 4 步才到 1.183;RMSProp 4 步已到 1.999,几乎收敛。

PyTorch

python

复制代码

optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01, alpha=0.9) # alpha 即 γ

5. Adam:Momentum + RMSProp 合体

逻辑

Adam = Momentum(管方向)+ RMSProp(管步长),两套机制同时用。它同时维护两个累积量:

mt=β1 mt−1 +(1−β1)gt(一阶动量,方向,来自 Momentum) m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \qquad \text{(一阶动量,方向,来自 Momentum)} mt=β1mt−1+(1−β1)gt(一阶动量,方向,来自 Momentum)

vt=β2 vt−1 +(1−β2)gt2(二阶动量,步长,来自 RMSProp) v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \qquad \text{(二阶动量,步长,来自 RMSProp)} vt=β2vt−1+(1−β2)gt2(二阶动量,步长,来自 RMSProp)

默认 β₁=0.9,β₂=0.999。

偏差修正(Adam 特有的关键细节):

m^t = mt 1−β1t v^t = vt 1−β2t \hat{m}_t = \frac{m_t}{1-\beta_1^t} \qquad \hat{v}_t = \frac{v_t}{1-\beta_2^t} m^t=1−β1tmtv^t=1−β2tvt

更新:

w←w−lr⋅ m^t v^t +ϵ w \leftarrow w - lr \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} w←w−lr⋅v^t +ϵm^t

分子是 Momentum 的方向,分母是 RMSProp 的自适应缩放,合在一行就是 Adam。

为什么需要偏差修正

m₀、v₀ 都从 0 开始,第一步 m₁ = 0.1·g₁ 严重低估真实梯度(偏向 0)。偏差修正把它除以 (1−β₁ᵗ) 放大回来:第一步 1−0.9¹ = 0.1,于是 m̂₁ = m₁/0.1 = g₁,修正回真实梯度。随 t 增大修正因子趋于 1,只在训练早期起作用,专治冷启动低估。

逐步计算(lr = 0.5,β₁=0.9,β₂=0.999,m₀=v₀=0)

w

m̂(方向)

√v̂(缩放)

更新量

w(更新后)

1

0

−18.67

18.67

+0.500

0.500

2

0.500

−16.21

16.50

+0.491

0.991

3

0.991

−13.71

14.52

+0.472

1.463

4

1.463

−11.17

12.82

+0.436

1.899

每步更新量都很接近(0.5、0.491、0.472、0.436),非常平稳。因为分子 m̂ 和分母 √v̂ 随梯度同比例缩小,相除后步长被"归一化",不会梯度大就猛冲、小就磨蹭。这让 Adam 对学习率不敏感、训练稳定,是它成为默认选择的重要原因。

PyTorch

python

复制代码

optimizer = torch.optim.Adam(model.parameters(), lr=0.001,

betas=(0.9, 0.999), eps=1e-8)

lr=0.001 是经典默认值,很多任务开箱即用。训练大模型时的实际默认往往是 AdamW(Adam 的权重衰减改进版)。

五大优化器全景对比

用同一个例子,把整条演进线摆在一起(第 4 步的 w):

优化器

核心机制

解决了什么问题

第4步 w

SGD

直接用当前梯度

---(基线)

0.662

Momentum

累积梯度方向

SGD 的震荡与磨蹭

1.459

AdaGrad

累加梯度平方,自适应步长

各参数需要不同学习率

1.183

RMSProp

移动平均梯度平方

AdaGrad 学习率衰竭到 0

1.999

Adam

Momentum + RMSProp + 偏差修正

方向、步长同时自适应

1.899

关于数值 :具体数字和 lr 选择有关,这里重点看机制差异 而非绝对快慢。RMSProp 在这个特定例子里恰好收敛最快,但 Adam 的优势在于对各种问题的普适稳定性------真实任务里它通常是最省心的默认选择。

一句话串起整条线:

SGD 打基础 → Momentum 加方向惯性 → AdaGrad 引入自适应步长 → RMSProp 修好步长衰竭 → Adam 合二为一再加偏差修正。

理解了每一步"解决了上一个的什么问题",这五个优化器就串成了一个完整逻辑链,而不是五个孤立的公式。

相关科技文章

别再把ERP吹上天了,在中国根本没几家企业用得好!
365日博体育备用

别再把ERP吹上天了,在中国根本没几家企业用得好!

⌚ 08-21 👁️ 5137
裁员失业后的自救指南
365bet提现多久到账

裁员失业后的自救指南

⌚ 12-25 👁️ 2325
骰子游戏的百变魅力与欢乐时光,聚会家庭皆宜玩。
365日博体育备用

骰子游戏的百变魅力与欢乐时光,聚会家庭皆宜玩。

⌚ 11-03 👁️ 4363
峽字的意思、解释和含义以及拼音、笔画和笔顺
365体育投注注册

峽字的意思、解释和含义以及拼音、笔画和笔顺

⌚ 01-13 👁️ 6939
邓丽君墓地下葬,关于邓丽君墓地下葬有什么说法
365bet提现多久到账

邓丽君墓地下葬,关于邓丽君墓地下葬有什么说法

⌚ 07-20 👁️ 9770
ECShop常用函数详解与应用
365体育投注注册

ECShop常用函数详解与应用

⌚ 07-04 👁️ 6635

合作伙伴