基础梯度下降算法Gradient Descent, GD)的核心思想:

通过计算损失函数 $J(\theta)$ 关于模型参数 $\theta$ 的梯度 $\nabla_{\theta} J(\theta)$,并沿着梯度的反方向更新参数 $\theta \leftarrow \theta - \eta \cdot \nabla_{\theta} J(\theta)$(其中 $\eta$ 是学习率),以逐步逼近损失函数的最小值点,从而优化模型性能。

然而,在实际应用中,基础的梯度下降算法(尤其是批量梯度下降)常常面临收敛速度慢、易陷入局部极小点、对学习率选择敏感等问题,尤其是在处理大规模数据集或复杂模型时。

为了克服这些挑战,研究者们提出了多种改进的梯度下降算法,统称为梯度下降优化算法,常见的梯度下降优化算法有Momentum、Adagrad、RMSprop、Adam等方法。

下面选择用同一个一维二次函数(如下)作为损失函数,来对比 不同梯度下降优化算法对参数更新的影响

$$ L(w) = \frac{1}{2}w^2 $$

  • 梯度 = w

  • 当前 w 越大,梯度越大

一、Momentum(动量法):优化“梯度方向”

将 Momentum 之前,先介绍一下SGD(随机梯度下降)。

SGD解决的是最基础的优化问题:

沿着损失函数下降最快的方向,不断更新参数

SGD 的核心思想是每一步都只做一件事:

$$ w_{t+1} = w_t - \eta \nabla L(w_t) $$

  • 当前梯度指向哪里,就往哪里走

  • 学习率 $\eta$ 决定步子有多大

SGD 优缺点

优点

  • 思想极其简单

  • 计算开销小,易于分析

缺点

  • 在“峡谷型”或“鞍点”区域容易左右震荡

  • 收敛速度慢

  • 对学习率非常敏感

SGD 可以直观类比为蒙着眼睛下山:只看脚下坡度,每一步都重新判断,没有“惯性”。

为了解决SGD带来的“梯度方向来回变化”、“参数更新不够平滑”等问题,引入了Momentum。

Momentum 核心思想

不仅看当前梯度,还考虑历史梯度的“惯性”

引入一个“速度”变量:

$$
\begin{aligned}
v_t &= \beta v_{t-1} + \nabla L(w_t) \\
w_{t+1} &= w_t - \eta v_t
\end{aligned}
$$

Momentum 优缺点

优点

  • 减少震荡

  • 在一致方向上加速收敛

  • 对鞍点更友好

缺点

  • 学习率仍然是固定的

  • 超参数(momentum)需要调

Momentum 直观类比推着一个有惯性的球下山:坡度方向一致时,球会越滚越快;小的反向坡度,不容易立刻改变方向。

def momentum_demo():
    # 初始化权重值
    w = torch.tensor([1.0],requires_grad=True,dtype=torch.float32)

    # 自定义损失函数
    criterion = (w**2)/2

    # 创建优化器对象,作用:1- 计算梯度;2- 自动更新权重
    # 创建随机梯度下降优化器,用于更新模型参数
    # 参数说明:
    #   [w] - 需要优化的参数列表,这里是对变量w进行优化
    #   lr=0.1 - 学习率,控制参数更新的步长大小
    #   momentum=0.9 - 动量参数,用于加速收敛并减少震荡
    optimizer = torch.optim.SGD([w],lr=0.1,momentum=0.9)


    # 反向传播计算梯度
    # 梯度清零
    optimizer.zero_grad()
    # 反向传播
    criterion.sum().backward()
    # 更新权重
    optimizer.step()
    print(f"第1次,梯度的结果{w.grad},更新后的w权重值{w.detach()}")
    print(f"第一次criterion值为{criterion.sum().detach()}")

    # 第二次
    criterion = (w ** 2) / 2
    # 反向传播计算梯度
    # 梯度清零
    optimizer.zero_grad()
    # 反向传播
    criterion.sum().backward()
    # 更新权重
    optimizer.step()
    print(f"第2次,梯度的结果{w.grad},更新后的w权重值{w.detach()}")
    print(f"第二次criterion值为{criterion.sum().detach()}")

对应优化器为:

optimizer = torch.optim.SGD([w], lr=0.1, momentum=0.9)

1) [w]:要被优化/更新的参数列表

  • 这里传的是一个 list:[w]

  • 含义:告诉优化器“我只更新这个张量 w”

  • 重要前提:w 必须 requires_grad=True,这样 backward() 才会把梯度写到 w.grad 里,然后 optimizer.step() 才能更新它。

在这个 demo 里只有一个标量参数w,所以用 [w] 很直观;真实模型里通常model.parameters()

2) lr=0.1:学习率(步长)

3) momentum=0.9:动量系数(“惯性”有多强)

   动量系数就是上面公式里的$\beta$

输出如下:

第1次,梯度的结果tensor([1.]),更新后的w权重值tensor([0.9000])
第一次criterion值为0.5
第2次,梯度的结果tensor([0.9000]),更新后的w权重值tensor([0.7200])
第二次criterion值为0.4049999713897705

二、Adagrad:只优化学习率

Adagrad 核心思想

对“频繁出现的大梯度”自动降低学习率

它会给每个参数一个自己的有效学习率(会随训练变化):

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

其中 $$G_{t, i} = \sum_{k=1}^{t} g_{k, i}^2$$

关键点:

  • Adagrad 不改变梯度 $$g_t$$ 本身(不像 Momentum 会引入速度 vvv 去平滑方向)

  • 它改变的是前面的系数:η/G+ϵ\eta / \sqrt{G+\epsilon}η/G+ϵ​
    也就是“走多大步”这件事(学习率/步长)

所以说“只优化学习率”更准确的表述是:

只在“步长策略”上做自适应,不在“方向/动量”上做改造。

Adagrad 优缺点

优点

  • 自动调节学习率

  • 对稀疏特征非常友好

  • 前期下降很快

缺点

  • 梯度平方是“无限累加”的

  • 学习率后期可能小到几乎不动

Adagrad 可直观类比为常走的路越走越慢,生路走得更快:出现频率高的方向会被“重点刹车”。

对应优化器为:

optimizer = torch.optim.Adagrad([w],lr=0.1)

小总结

Adagrad 的核心贡献是“按参数自适应学习率”,但它的致命问题是学习率会单调衰减,后期可能衰减到几乎停止学习;RMSprop/Adam 的改进就是想办法避免这种‘越学越慢’。

三、RMSprop

由前面可知Adagrad 的致命问题是:

学习率衰减过头,训练提前停滞

RMSprop 就是解决这个问题的。

RMSprop 核心思想

不累加所有历史梯度,而是关注“最近一段时间”

使用梯度平方的指数移动平均:

$$ E[g^2]_t = \alpha E[g^2]_{t-1} + (1 - \alpha)g_t^2 $$

RMSprop 优缺点

优点

  • 学习率不会无限减小

  • 对非平稳目标函数更稳定

  • 实践效果好

缺点

  • 仍然没有利用梯度方向的“动量”

RMSprop 可以直观类比为 只记住最近的路况,而不是一辈子的历史

对应优化器为:

optimizer = torch.optim.RMSprop([w],lr=0.1,alpha=0.9)

四、Adam(Adaptive Moment Estimation)

为了能够同时解决震荡问题和学习率自适应问题,Adam 应运而生。

Adam 核心思想:

Adam = Momentum + RMSprop

它在每一步都会维护两个统计量:

  • 一阶矩 $m_t$:梯度的平均(方向)

  • 二阶矩 $v_t$:梯度平方的平均(尺度)

Adam 内部的计算过程:

首先计算当前梯度:

$$ g_t = \nabla_{\theta} J(\theta_t) $$

接着,Adam 会对梯度做指数移动平均:

$$ m_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t $$

  • $m_t$​:梯度的“动量方向”

  • $\beta_1$​:控制历史梯度保留比例,一般取 0.9

✅ 这一步就是动量法:

梯度方向不会剧烈波动,而是更平滑。

同时,Adam 也会对梯度平方做指数平均:

$$ v_t = \beta_2 v_{t-1} + (1 - \beta_2)g_t^2 $$

  • $v_t$:梯度尺度的估计

  • $\beta_2$​:通常取 0.999 或 0.99

✅ 这一步就是 RMSprop:

梯度大 ⇒ 学习率自动变小
梯度小 ⇒ 学习率相对变大

由于初始时:

$$m_0=0$$

$$v_0=0$$

所以刚开始 $m_t,v_t$ 会偏小。

Adam 进行偏置修正:

$$
\begin{aligned}
\hat{m}_t &= \frac{m_t}{1 - \beta_1^t} \\
\hat{v}_t &= \frac{v_t}{1 - \beta_2^t}
\end{aligned}
$$

偏置修正,让前期更稳定。

Adam 最终的更新是:

$$ \theta_{t+1} = \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} $$

其中:

  • $\eta$:学习率

  • $\epsilon$:防止除零(通常 $10^{-8}$ )

Adam 优缺点

优点

  • 收敛快

  • 对超参数不敏感

  • 工程中表现稳定

  • 适合大多数深度学习任务

缺点

  • 理论收敛性曾有争议

  • 有时泛化不如 SGD + Momentum

Adam 可以直观类比为 又有方向感,又能自动调节步长的智能下山者

对应优化器为:

optimizer = torch.optim.Adam([w],lr=0.1,betas=(0.9,0.99))

五、总结

优化器 优化对象 核心优势 主要问题
SGD 梯度 简单直观 收敛慢、震荡
Momentum 梯度方向 加速、平滑 学习率固定
Adagrad 学习率 稀疏友好 学习率衰减过快
RMSprop 学习率 更稳定 无动量
Adam 梯度 + 学习率 快且稳 理论收敛性争议

优化器的本质不是“谁更高级”,而是它们在不同场景下,对梯度信息的使用方式不同。

更多推荐