梯度下降算法比较:SGD、Momentum、Adam在AI中的应用
关键词:梯度下降、随机梯度下降(SGD)、动量优化(Momentum)、Adam优化器、人工智能训练
摘要:在人工智能(尤其是深度学习)的世界里,“训练模型"就像教小机器人学习本领——需要不断调整它的"大脑参数”,让它越来越聪明。而调整参数的核心工具,就是今天要讲的"梯度下降家族"。本文将用通俗易懂的语言,结合生活案例、数学公式和代码实战,对比SGD(随机梯度下降)、Momentum(动量优化)、Adam(自适应矩估计)三种经典算法,帮你搞清楚它们的原理、优缺点和适用场景。
背景介绍:为什么需要梯度下降?
想象你在玩一个叫"参数调优"的游戏:你有一个小机器人(模型),它的"大脑"里有很多旋钮(参数),每个旋钮转一点,机器人的表现(比如识别图片的准确率)就会变化。你的目标是找到一组旋钮位置,让机器人表现最好。
但问题是:旋钮数量可能有成千上万个(比如深度学习模型的参数),直接暴力试所有组合是不可能的。这时候,梯度下降就像一个"智能指南针",能帮你快速找到最优的旋钮位置。
目的和范围
本文聚焦"梯度下降家族"中最常用的三种算法:SGD(基础款)、Momentum(带惯性的升级款)、Adam(带智能加速的旗舰款)。我们会从原理、数学公式、代码实现、实际效果四个维度对比,帮你在实际项目中选对工具。
预期读者
- 对AI/深度学习感兴趣的初学者(懂基础Python和导数即可)
- 正在训练模型但遇到"收敛慢""震荡"问题的开发者
- 想深入理解优化算法底层逻辑的进阶学习者
文档结构概述
本文将按照"故事引入→核心概念→数学原理→代码实战→应用场景"的顺序展开。你会先通过生活案例理解抽象概念,再通过公式和代码掌握细节,最后学会根据需求选择算法。
术语表(用小学生能懂的话)
- 梯度:可以想象成"下山的方向"。在数学里,梯度是函数上升最快的方向,取反就是下降最快的方向(我们要最小化损失函数,所以用负梯度)。
- 学习率(η):“每一步跨多大”。太大容易跨过山脚(震荡),太小会走得慢(收敛慢)。
- 损失函数(J):“机器人当前表现有多差”。我们的目标是让J尽可能小。
- 参数(θ):“机器人的旋钮”。比如线性回归的斜率和截距,神经网络的权重和偏置。
核心概念与联系:从"下山"到"智能导航"
故事引入:三个小朋友的下山比赛
假设有三个小朋友要从山顶(初始参数)下到山谷(最优参数),山谷里藏着宝藏(最小损失)。他们的下山方式不同,对应三种梯度下降算法:
- 小明(SGD):每走一步都要停下来,仔细看脚下的坡度(计算当前点的梯度),然后朝着最陡的下坡方向走一步(学习率大小)。
- 小红(Momentum):不仅看当前坡度,还会记住之前走的方向(动量),像滑滑梯一样带着惯性走——如果之前一直往右下方走,即使现在坡度稍微变平,也会继续往右下方多走一点。
- 小刚(Adam):不仅带惯性,还会用智能手表记录自己每一步的步长(二阶矩),自动调整下一步该跨多大——如果之前经常小步走(梯度变化小),就跨大一点;如果之前经常乱跳(梯度变化大),就跨小一点。
核心概念解释(像给小学生讲故事)
1. SGD(随机梯度下降):一步一探的"谨慎者"
SGD的全称是Stochastic Gradient Descent(随机梯度下降)。这里的"随机"不是随便走,而是"随机选一个数据点计算梯度"(传统梯度下降用所有数据计算梯度,SGD用单个数据,速度更快)。
生活类比:你在一个黑黢黢的山洞里下山(损失函数曲面复杂),手里只有一个手电筒(只能看到一个数据点的梯度)。每走一步,你用手电筒照一下脚下,确定下坡方向,然后走一步。这样虽然每次看得不全(可能走偏),但比用探照灯照整个山洞(全量数据)快得多。
2. Momentum(动量优化):带着惯性的"滑滑梯选手"
Momentum在SGD基础上加了一个"动量"(Momentum)项,相当于给梯度下降加了惯性。数学上,它会维护一个"速度"变量,这个速度不仅取决于当前梯度,还取决于之前的速度(乘以一个动量系数γ,通常设0.9)。
生活类比:你在玩滑滑梯,当滑梯变平缓时(梯度变小),你不会立刻停下来,而是因为惯性继续滑一段。Momentum的"动量"就像这种惯性,能帮你冲过局部小坑(平缓区域),更快接近山谷。
3. Adam(自适应矩估计):会"智能调速"的导航仪
Adam(Adaptive Moment Estimation)是目前深度学习最常用的优化器之一。它结合了Momentum的动量项(一阶矩估计)和RMSprop的自适应学习率(二阶矩估计),相当于同时维护"速度"和"步长调节器"。
生活类比:你开车下山,导航仪(Adam)会做两件事:
- 记住你之前的行驶方向(动量,避免突然转向);
- 记录你之前的车速变化(比如急刹车过,说明路况复杂),自动调整油门(学习率)——路况好(梯度稳定)就开快,路况差(梯度波动大)就开慢。
核心概念之间的关系:从基础到智能的进化链
三种算法的关系可以用"升级打怪"来理解:
- SGD是基础:解决了"如何根据当前信息调整方向"的问题,但容易走偏或卡住;
- Momentum是1.0升级:增加了"历史方向记忆",解决了SGD在平缓区域/局部最小值附近停滞的问题;
- Adam是2.0旗舰:同时记忆"历史方向"和"历史步长变化",解决了Momentum学习率固定、对不同参数调整不均衡的问题。
核心概念原理和架构的文本示意图
梯度下降家族进化链:
全量梯度下降(BGD) → SGD(随机选数据) → Momentum(加动量) → Adam(动量+自适应学习率)
Mermaid 流程图:参数更新的通用流程
graph TD
A[初始化参数θ] --> B[计算当前损失J(θ)]
B --> C[计算梯度∇J(θ)]
C --> D{选择优化器}
D -->|SGD| E[θ = θ - η∇J(θ)]
D -->|Momentum| F[计算速度v = γv_prev + η∇J(θ); θ = θ - v]
D -->|Adam| G[计算一阶矩m = β1*m_prev + (1-β1)∇J(θ); 计算二阶矩v = β2*v_prev + (1-β2)(∇J(θ))²; 修正m_hat = m/(1-β1^t); 修正v_hat = v/(1-β2^t); θ = θ - η*m_hat/(√v_hat + ε)]
E --> H[重复直到收敛]
F --> H
G --> H
核心算法原理 & 具体操作步骤
1. SGD:一步一探的数学表达
数学公式:
参数更新规则为:
θ
t
+
1
=
θ
t
−
η
⋅
∇
J
(
θ
t
)
\theta_{t+1} = \theta_t - \eta \cdot \nabla J(\theta_t)
θt+1=θt−η⋅∇J(θt)
其中:
- θ t \theta_t θt:第t步的参数;
- η \eta η:学习率(步长);
- ∇ J ( θ t ) \nabla J(\theta_t) ∇J(θt):损失函数在 θ t \theta_t θt处的梯度(下山方向)。
关键特点:
- 每次只用1个样本计算梯度(随机选),计算快,但梯度噪声大(像用手电筒照路,可能看错方向);
- 学习率固定,可能在接近山谷时仍大步走,导致震荡。
2. Momentum:带着惯性的数学表达
数学公式:
引入速度变量
v
t
v_t
vt(类比物理中的动量),更新规则为:
v
t
+
1
=
γ
⋅
v
t
+
η
⋅
∇
J
(
θ
t
)
v_{t+1} = \gamma \cdot v_t + \eta \cdot \nabla J(\theta_t)
vt+1=γ⋅vt+η⋅∇J(θt)
θ
t
+
1
=
θ
t
−
v
t
+
1
\theta_{t+1} = \theta_t - v_{t+1}
θt+1=θt−vt+1
其中:
- γ \gamma γ:动量系数(通常设0.9,代表保留90%的历史速度);
- v t v_t vt:第t步的速度(历史方向的累积)。
关键特点:
- 梯度噪声会被动量"平滑"(因为历史速度是多个梯度的加权平均);
- 能加速通过平缓区域(像滑滑梯的惯性),避免SGD在局部最小值附近来回震荡。
3. Adam:智能调速的数学表达
数学公式:
同时维护一阶矩(动量,方向)和二阶矩(梯度平方的期望,步长调节器),更新规则为:
m
t
=
β
1
⋅
m
t
−
1
+
(
1
−
β
1
)
⋅
∇
J
(
θ
t
)
(一阶矩,方向记忆)
m_t = \beta_1 \cdot m_{t-1} + (1-\beta_1) \cdot \nabla J(\theta_t) \quad \text{(一阶矩,方向记忆)}
mt=β1⋅mt−1+(1−β1)⋅∇J(θt)(一阶矩,方向记忆)
v
t
=
β
2
⋅
v
t
−
1
+
(
1
−
β
2
)
⋅
(
∇
J
(
θ
t
)
)
2
(二阶矩,步长记忆)
v_t = \beta_2 \cdot v_{t-1} + (1-\beta_2) \cdot (\nabla J(\theta_t))^2 \quad \text{(二阶矩,步长记忆)}
vt=β2⋅vt−1+(1−β2)⋅(∇J(θt))2(二阶矩,步长记忆)
m
^
t
=
m
t
1
−
β
1
t
(偏差修正,初始阶段矩估计有偏)
\hat{m}_t = \frac{m_t}{1 - \beta_1^t} \quad \text{(偏差修正,初始阶段矩估计有偏)}
m^t=1−β1tmt(偏差修正,初始阶段矩估计有偏)
v
^
t
=
v
t
1
−
β
2
t
(偏差修正)
\hat{v}_t = \frac{v_t}{1 - \beta_2^t} \quad \text{(偏差修正)}
v^t=1−β2tvt(偏差修正)
θ
t
+
1
=
θ
t
−
η
⋅
m
^
t
v
^
t
+
ϵ
(参数更新)
\theta_{t+1} = \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} \quad \text{(参数更新)}
θt+1=θt−η⋅v^t+ϵm^t(参数更新)
其中:
- β 1 \beta_1 β1、 β 2 \beta_2 β2:分别控制一阶矩和二阶矩的衰减率(通常设0.9和0.999);
- ϵ \epsilon ϵ:防止除零的小常数(如 1 e − 8 1e-8 1e−8)。
关键特点:
- 自适应调整每个参数的学习率( m ^ t v ^ t \frac{\hat{m}_t}{\sqrt{\hat{v}_t}} v^tm^t相当于为每个参数定制步长);
- 结合了Momentum的方向记忆和RMSprop的自适应步长,对稀疏数据(如自然语言处理中的词频)表现尤其好。
数学模型和公式 & 详细讲解 & 举例说明
用"下山"案例理解公式差异
假设我们要最小化一个简单的二次函数: J ( θ ) = θ 2 J(\theta) = \theta^2 J(θ)=θ2(山谷在 θ = 0 \theta=0 θ=0)。初始位置 θ 0 = 5 \theta_0=5 θ0=5,学习率 η = 0.1 \eta=0.1 η=0.1。
SGD的表现:
梯度
∇
J
(
θ
t
)
=
2
θ
t
\nabla J(\theta_t) = 2\theta_t
∇J(θt)=2θt,更新规则:
θ
t
+
1
=
θ
t
−
0.1
⋅
2
θ
t
=
0.8
θ
t
\theta_{t+1} = \theta_t - 0.1 \cdot 2\theta_t = 0.8\theta_t
θt+1=θt−0.1⋅2θt=0.8θt
- 第1步: θ 1 = 0.8 × 5 = 4 \theta_1 = 0.8 \times 5 = 4 θ1=0.8×5=4
- 第2步: θ 2 = 0.8 × 4 = 3.2 \theta_2 = 0.8 \times 4 = 3.2 θ2=0.8×4=3.2
- … 逐步逼近0,像小步走楼梯。
Momentum的表现(γ=0.9):
速度 v t = 0.9 v t − 1 + 0.1 ⋅ 2 θ t v_t = 0.9v_{t-1} + 0.1 \cdot 2\theta_t vt=0.9vt−1+0.1⋅2θt,初始 v 0 = 0 v_0=0 v0=0
- 第1步: v 1 = 0.9 × 0 + 0.1 × 10 = 1 v_1 = 0.9 \times 0 + 0.1 \times 10 = 1 v1=0.9×0+0.1×10=1 → θ 1 = 5 − 1 = 4 \theta_1 = 5 - 1 = 4 θ1=5−1=4
- 第2步: v 2 = 0.9 × 1 + 0.1 × 8 = 0.9 + 0.8 = 1.7 v_2 = 0.9 \times 1 + 0.1 \times 8 = 0.9 + 0.8 = 1.7 v2=0.9×1+0.1×8=0.9+0.8=1.7 → θ 2 = 4 − 1.7 = 2.3 \theta_2 = 4 - 1.7 = 2.3 θ2=4−1.7=2.3
- 第3步:
v
3
=
0.9
×
1.7
+
0.1
×
4.6
=
1.53
+
0.46
=
1.99
v_3 = 0.9 \times 1.7 + 0.1 \times 4.6 = 1.53 + 0.46 = 1.99
v3=0.9×1.7+0.1×4.6=1.53+0.46=1.99 →
θ
3
=
2.3
−
1.99
≈
0.31
\theta_3 = 2.3 - 1.99 ≈ 0.31
θ3=2.3−1.99≈0.31
可以看到,Momentum的速度逐渐累积,后期下降更快(像滑滑梯越来越快)。
Adam的表现(β1=0.9, β2=0.999, η=0.1):
初始 m 0 = 0 m_0=0 m0=0, v 0 = 0 v_0=0 v0=0, t = 1 t=1 t=1
- 第1步:
m 1 = 0.9 × 0 + 0.1 × 10 = 1 m_1 = 0.9 \times 0 + 0.1 \times 10 = 1 m1=0.9×0+0.1×10=1
v 1 = 0.999 × 0 + 0.001 × 100 = 0.1 v_1 = 0.999 \times 0 + 0.001 \times 100 = 0.1 v1=0.999×0+0.001×100=0.1
m ^ 1 = 1 / ( 1 − 0.9 1 ) = 10 \hat{m}_1 = 1/(1 - 0.9^1) = 10 m^1=1/(1−0.91)=10
v ^ 1 = 0.1 / ( 1 − 0.999 1 ) ≈ 100 \hat{v}_1 = 0.1/(1 - 0.999^1) ≈ 100 v^1=0.1/(1−0.9991)≈100
θ 1 = 5 − 0.1 × ( 10 / 100 + 1 e − 8 ) ≈ 5 − 0.1 × 1 = 4.9 \theta_1 = 5 - 0.1 \times (10/\sqrt{100} + 1e-8) ≈ 5 - 0.1 \times 1 = 4.9 θ1=5−0.1×(10/100+1e−8)≈5−0.1×1=4.9 - 第2步(t=2):
m 2 = 0.9 × 1 + 0.1 × 9.8 = 0.9 + 0.98 = 1.88 m_2 = 0.9 \times 1 + 0.1 \times 9.8 = 0.9 + 0.98 = 1.88 m2=0.9×1+0.1×9.8=0.9+0.98=1.88
v 2 = 0.999 × 0.1 + 0.001 × 96.04 ≈ 0.0999 + 0.096 = 0.1959 v_2 = 0.999 \times 0.1 + 0.001 \times 96.04 ≈ 0.0999 + 0.096 = 0.1959 v2=0.999×0.1+0.001×96.04≈0.0999+0.096=0.1959
m ^ 2 = 1.88 / ( 1 − 0.9 2 ) ≈ 1.88 / 0.19 ≈ 9.89 \hat{m}_2 = 1.88/(1 - 0.9^2) ≈ 1.88/0.19 ≈ 9.89 m^2=1.88/(1−0.92)≈1.88/0.19≈9.89
v ^ 2 = 0.1959 / ( 1 − 0.999 2 ) ≈ 0.1959 / 0.001999 ≈ 97.99 \hat{v}_2 = 0.1959/(1 - 0.999^2) ≈ 0.1959/0.001999 ≈ 97.99 v^2=0.1959/(1−0.9992)≈0.1959/0.001999≈97.99
θ 2 = 4.9 − 0.1 × ( 9.89 / 97.99 ) ≈ 4.9 − 0.1 × 1 ≈ 4.8 \theta_2 = 4.9 - 0.1 \times (9.89/\sqrt{97.99}) ≈ 4.9 - 0.1 \times 1 ≈ 4.8 θ2=4.9−0.1×(9.89/97.99)≈4.9−0.1×1≈4.8
早期Adam调整较保守(因为偏差修正项大),但后期会根据梯度变化自动加速。
项目实战:用Python代码对比三种算法效果
开发环境搭建
- 安装Python 3.8+、numpy、matplotlib
- 代码示例在Jupyter Notebook中运行更直观
源代码详细实现和代码解读
我们以拟合一条直线(线性回归)为例,比较三种优化器的训练效果。假设真实数据满足 y = 2 x + 1 + ϵ y = 2x + 1 + \epsilon y=2x+1+ϵ( ϵ \epsilon ϵ是噪声),我们需要用模型 y = w x + b y = wx + b y=wx+b拟合,损失函数用均方误差(MSE)。
import numpy as np
import matplotlib.pyplot as plt
# 生成模拟数据
np.random.seed(42)
x = np.random.rand(100, 1) * 10 # 100个0-10的随机数
y = 2 * x + 1 + np.random.randn(100, 1) * 1 # 真实值=2x+1+噪声
# 初始化参数
w = np.random.randn(1) # 斜率
b = np.random.randn(1) # 截距
lr = 0.01 # 学习率(Adam可能需要更大,比如0.001)
epochs = 200 # 训练轮数
# 定义损失函数(MSE)
def compute_loss(y_true, y_pred):
return np.mean((y_true - y_pred) ** 2)
# 定义梯度计算函数
def compute_gradients(x, y_true, w, b):
y_pred = w * x + b
dw = -2 * np.mean(x * (y_true - y_pred)) # dL/dw
db = -2 * np.mean(y_true - y_pred) # dL/db
return dw, db
# 定义三种优化器的参数更新函数
def sgd_update(w, b, dw, db, lr):
new_w = w - lr * dw
new_b = b - lr * db
return new_w, new_b
def momentum_update(w, b, dw, db, lr, v_w, v_b, gamma=0.9):
v_w = gamma * v_w + lr * dw
v_b = gamma * v_b + lr * db
new_w = w - v_w
new_b = b - v_b
return new_w, new_b, v_w, v_b
def adam_update(w, b, dw, db, lr, m_w, m_b, v_w, v_b, t, beta1=0.9, beta2=0.999, eps=1e-8):
# 计算一阶矩(动量)
m_w = beta1 * m_w + (1 - beta1) * dw
m_b = beta1 * m_b + (1 - beta1) * db
# 计算二阶矩(梯度平方的期望)
v_w = beta2 * v_w + (1 - beta2) * (dw ** 2)
v_b = beta2 * v_b + (1 - beta2) * (db ** 2)
# 偏差修正
m_w_hat = m_w / (1 - beta1 ** t)
m_b_hat = m_b / (1 - beta1 ** t)
v_w_hat = v_w / (1 - beta2 ** t)
v_b_hat = v_b / (1 - beta2 ** t)
# 参数更新
new_w = w - lr * m_w_hat / (np.sqrt(v_w_hat) + eps)
new_b = b - lr * m_b_hat / (np.sqrt(v_b_hat) + eps)
return new_w, new_b, m_w, m_b, v_w, v_b
代码解读与分析
- SGD:每次直接用当前梯度乘以学习率更新参数(
new_w = w - lr * dw
)。 - Momentum:维护速度变量
v_w
和v_b
,每次更新时结合历史速度(v_w = gamma * v_w + lr * dw
)。 - Adam:同时维护一阶矩(
m_w
)和二阶矩(v_w
),并通过偏差修正(m_w_hat
和v_w_hat
)解决初始阶段矩估计的偏差问题。
训练过程对比(画图展示)
# 训练SGD
w_sgd, b_sgd = np.random.randn(1), np.random.randn(1)
losses_sgd = []
for epoch in range(epochs):
dw, db = compute_gradients(x, y, w_sgd, b_sgd)
w_sgd, b_sgd = sgd_update(w_sgd, b_sgd, dw, db, lr=0.001) # SGD需要更小学习率防震荡
loss = compute_loss(y, w_sgd * x + b_sgd)
losses_sgd.append(loss)
# 训练Momentum
w_mom, b_mom = np.random.randn(1), np.random.randn(1)
v_w, v_b = 0, 0
losses_mom = []
for epoch in range(epochs):
dw, db = compute_gradients(x, y, w_mom, b_mom)
w_mom, b_mom, v_w, v_b = momentum_update(w_mom, b_mom, dw, db, lr=0.01, v_w=v_w, v_b=v_b)
loss = compute_loss(y, w_mom * x + b_mom)
losses_mom.append(loss)
# 训练Adam
w_adam, b_adam = np.random.randn(1), np.random.randn(1)
m_w, m_b, v_w, v_b = 0, 0, 0, 0
losses_adam = []
for t in range(1, epochs+1): # t从1开始计算偏差修正
dw, db = compute_gradients(x, y, w_adam, b_adam)
w_adam, b_adam, m_w, m_b, v_w, v_b = adam_update(w_adam, b_adam, dw, db, lr=0.001,
m_w=m_w, m_b=m_b, v_w=v_w, v_b=v_b, t=t)
loss = compute_loss(y, w_adam * x + b_adam)
losses_adam.append(loss)
# 画图对比损失下降曲线
plt.figure(figsize=(12, 6))
plt.plot(losses_sgd, label='SGD')
plt.plot(losses_mom, label='Momentum')
plt.plot(losses_adam, label='Adam')
plt.xlabel('Epochs')
plt.ylabel('Loss (MSE)')
plt.title('Loss Convergence Comparison')
plt.legend()
plt.show()
实验结果分析:
- SGD的损失下降最慢,且后期有小幅震荡(因为梯度噪声大);
- Momentum的损失下降更快,震荡更小(动量平滑了噪声);
- Adam的损失下降最快,早期就接近最小值(自适应学习率和动量的双重优势)。
实际应用场景
1. SGD:适合小数据/简单模型
- 场景:数据量小(如几百条样本)、模型简单(如逻辑回归)。
- 原因:SGD计算简单,内存占用小(每次只用1个样本),但需要仔细调学习率。
- 案例:学生用小数据集做课程作业时,SGD足够且容易实现。
2. Momentum:适合梯度有噪声的场景
- 场景:数据有噪声(如传感器采集的时序数据)、损失函数曲面不平滑(有局部最小值)。
- 原因:动量能平滑梯度噪声,帮助模型冲过局部最小值。
- 案例:训练时间序列预测模型(如LSTM预测股价),Momentum比SGD更稳定。
3. Adam:适合大数据/复杂模型
- 场景:数据量大(如百万级图像)、模型复杂(如ResNet、Transformer)。
- 原因:Adam自适应调整学习率,对稀疏特征(如自然语言中的低频词)友好,无需手动调参。
- 案例:训练BERT模型做文本分类时,Adam是默认优化器(PyTorch的
torch.optim.Adam
)。
工具和资源推荐
- PyTorch优化器:直接调用
torch.optim.SGD
、torch.optim.SGD(momentum=0.9)
、torch.optim.Adam
。 - TensorFlow优化器:
tf.keras.optimizers.SGD
、tf.keras.optimizers.Momentum
、tf.keras.optimizers.Adam
。 - 可视化工具:用
matplotlib
画损失曲线,或用tensorboard
实时监控训练过程。 - 论文推荐:
- SGD原始论文:《Large-Scale Machine Learning with Stochastic Gradient Descent》
- Momentum原始论文:《A Method for Solving the Convex Programming Problem with Convergence Rate O(1/k^2)》
- Adam原始论文:《Adam: A Method for Stochastic Optimization》
未来发展趋势与挑战
趋势1:自适应优化器的改进
Adam虽然好用,但在某些场景(如GAN训练)中可能出现"过冲"问题。近年研究提出了AdamW(加入权重衰减)、RAdam(动态调整二阶矩修正)等改进版本,进一步提升稳定性。
趋势2:与二阶方法结合
梯度下降是一阶方法(只用一阶导数),而牛顿法等二阶方法(用二阶导数)收敛更快,但计算复杂。未来可能出现"一阶+二阶"混合优化器,平衡速度和计算量。
挑战:超参数调优
即使是Adam,也需要调学习率( η \eta η)、动量系数( β 1 \beta_1 β1)等超参数。如何自动调参(如贝叶斯优化)是工业界的重要问题。
总结:学到了什么?
核心概念回顾
- SGD:基础梯度下降,用单个样本计算梯度,速度快但噪声大;
- Momentum:加动量项,利用历史梯度方向,平滑噪声、加速收敛;
- Adam:结合动量和自适应学习率,为每个参数定制步长,适合复杂模型。
概念关系回顾
三种算法是"基础→改进→智能"的进化链:
SGD解决了"如何用少量数据更新参数"的问题;
Momentum解决了"SGD在平缓区域停滞"的问题;
Adam解决了"Momentum学习率固定、参数调整不均衡"的问题。
思考题:动动小脑筋
- 为什么SGD需要更小的学习率?如果学习率太大,会发生什么?(提示:梯度噪声大,大步走容易跨过最小值)
- Momentum的动量系数 γ \gamma γ设为0.9和0.5,效果会有什么不同?(提示: γ \gamma γ越大,越依赖历史速度)
- 如果你训练一个图像分类模型(如ResNet),数据量很大(百万张图),应该选SGD、Momentum还是Adam?为什么?
附录:常见问题与解答
Q:SGD是"随机"的,那它和"全量梯度下降(BGD)"有什么区别?
A:BGD用所有样本计算梯度(准确但慢),SGD用单个样本(快但噪声大)。还有"小批量梯度下降(Mini-batch GD)",用一批样本(如32个),是SGD的改进版(PyTorch的SGD
优化器默认是Mini-batch)。
Q:Adam的学习率为什么通常设0.001,而SGD设0.01?
A:Adam的自适应学习率会自动缩放梯度,所以需要更小的初始学习率防止过冲;SGD的学习率直接控制步长,需要更大才能有效下降。
Q:训练时loss突然上升,可能是优化器的问题吗?
A:可能!如果学习率太大(SGD/Momentum),或二阶矩估计不稳定(Adam早期),都可能导致参数更新过大,loss上升。可以尝试降低学习率,或检查数据是否有异常。
扩展阅读 & 参考资料
- 《深度学习》(花书)第8章:优化算法
- 李宏毅机器学习课程:《Optimization for Deep Learning》
- 论文链接:
- SGD:https://www.cs.utoronto.edu/~gdahl/papers/sgdTR.pdf
- Momentum:https://www.math.ucla.edu/~wotaoyin/momentum.pdf
- Adam:https://arxiv.org/abs/1412.6980