前言

在人工智能和深度学习领域,优化器(Optimizer)是驱动神经网络不断学习和收敛的核心引擎。它的核心任务是通过更新网络的参数 θ \theta θ,来最小化目标函数(Loss Function) J ( θ ) J(\theta) J(θ)。理解优化器的演进过程,本质上是理解数学上如何更高效、更稳定地在复杂的高维空间中寻找全局最优解(或足够好的局部最优解)。

一.传统梯度下降

  1. 前置符号定义(业界标准)在推导公式前,我们先统一业界最通用的数学符号:
    θ \theta θ:模型的参数向量(包含所有的权重 weights 和偏置 biases)。
    m m m:训练集中的样本总数。 ( x ( i ) , y ( i ) ) (x^{(i)}, y^{(i)}) (x(i),y(i)):第 i i i 个训练样本,其中 x ( i ) x^{(i)} x(i) 是输入特征, y ( i ) y^{(i)} y(i) 是真实标签。
    f ( x ( i ) ; θ ) f(x^{(i)}; \theta) f(x(i);θ):模型在当前参数 θ \theta θ 下,对输入 x ( i ) x^{(i)} x(i) 的预测输出。
    L ( y ^ , y ) L(\hat{y}, y) L(y^,y):单个样本的损失函数(Loss Function),衡量预测值 y ^ \hat{y} y^ 与真实值 y y y 之间的差异。
    J ( θ ) J(\theta) J(θ):目标函数或代价函数(Cost Function),通常是所有训练样本损失的平均值。
    η \eta η:学习率(Learning Rate,有时也用 α \alpha α 表示),控制每次参数更新的步长大小。
    ∇ θ J ( θ ) \nabla_\theta J(\theta) θJ(θ):代价函数 J J J 关于参数 θ \theta θ 的梯度(偏导数向量)。

在整个梯度下降过程 θ n e w = θ o l d − η ⋅ ∇ θ J ( θ ) \theta_{new} = \theta_{old} - \eta \cdot \nabla_{\theta} J(\theta) θnew=θoldηθJ(θ) 中:我们是在拿一个向量/矩阵 ( θ \theta θ),减去一个标量 ( η \eta η) 乘以另一个同维度的向量/矩阵 ( ∇ θ J ( θ ) \nabla_{\theta} J(\theta) θJ(θ))。一切都是为了让最终的那个标量分数 ( J ( θ ) J(\theta) J(θ)) 趋近于 0。

1.1 批量梯度下降 (Batch Gradient Descent, BGD)

它的核心思想是:每次更新参数时,都要把整个训练集的所有样本都看一遍,计算出一个绝对精确的梯度方向。
首先,定义在整个训练集上的代价函数 J ( θ ) J(\theta) J(θ) J ( θ ) = 1 m ∑ i = 1 m L ( f ( x ( i ) ; θ ) , y ( i ) ) J(\theta) = \frac{1}{m} \sum_{i=1}^{m} L(f(x^{(i)}; \theta), y^{(i)}) J(θ)=m1i=1mL(f(x(i);θ),y(i))接下来,我们需要计算 J ( θ ) J(\theta) J(θ) 对参数 θ \theta θ 的梯度: ∇ θ J ( θ ) = 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) ) \nabla_\theta J(\theta) = \frac{1}{m} \sum_{i=1}^{m} \nabla_\theta L(f(x^{(i)}; \theta), y^{(i)}) θJ(θ)=m1i=1mθL(f(x(i);θ),y(i))最后,参数 θ \theta θ 沿着梯度的反方向(即下降最快的方向)进行更新: θ t + 1 = θ t − η ∇ θ J ( θ t ) \theta_{t+1} = \theta_t - \eta \nabla_\theta J(\theta_t) θt+1=θtηθJ(θt)
上面是宏观视角,为了方便读者理解,我们将上面矩阵展开,来看看内部视角,这样可以促进直观形象理解。
假设我们现在有一个 Batch,里面包含了 m m m 个训练样本。对于这 m m m 个样本中的第 k k k 个样本( k = 1 , 2 , . . . , m k = 1, 2, ..., m k=1,2,...,m),它在前向传播时产生的误差我们记作 L ( k ) L^{(k)} L(k)。在 BGD 中,总体代价函数 J J J 对矩阵 W W W 的梯度,等于每一个样本的梯度矩阵相加,然后再除以 m m m ∂ J ∂ W = 1 m ∑ k = 1 m ( ∂ L ( k ) ∂ W ) \frac{\partial J}{\partial W} = \frac{1}{m} \sum_{k=1}^{m} \left( \frac{\partial L^{(k)}}{\partial W} \right) WJ=m1k=1m(WL(k))我们把上面求和符号里的 m m m 个梯度矩阵全部写出来,暴露它在真实计算时的样子: ∂ J ∂ W = 1 m ( [ ∂ L ( 1 ) ∂ w 11 ∂ L ( 1 ) ∂ w 12 ∂ L ( 1 ) ∂ w 13 ∂ L ( 1 ) ∂ w 21 ∂ L ( 1 ) ∂ w 22 ∂ L ( 1 ) ∂ w 23 ∂ L ( 1 ) ∂ w 31 ∂ L ( 1 ) ∂ w 32 ∂ L ( 1 ) ∂ w 33 ] + [ ∂ L ( 2 ) ∂ w 11 ∂ L ( 2 ) ∂ w 12 ∂ L ( 2 ) ∂ w 13 ∂ L ( 2 ) ∂ w 21 ∂ L ( 2 ) ∂ w 22 ∂ L ( 2 ) ∂ w 23 ∂ L ( 2 ) ∂ w 31 ∂ L ( 2 ) ∂ w 32 ∂ L ( 2 ) ∂ w 33 ] + ⋯ + [ ∂ L ( m ) ∂ w 11 ∂ L ( m ) ∂ w 12 ∂ L ( m ) ∂ w 13 ∂ L ( m ) ∂ w 21 ∂ L ( m ) ∂ w 22 ∂ L ( m ) ∂ w 23 ∂ L ( m ) ∂ w 31 ∂ L ( m ) ∂ w 32 ∂ L ( m ) ∂ w 33 ] ) \frac{\partial J}{\partial W} = \frac{1}{m} \left( \begin{bmatrix} \frac{\partial L^{(1)}}{\partial w_{11}} & \frac{\partial L^{(1)}}{\partial w_{12}} & \frac{\partial L^{(1)}}{\partial w_{13}} \\ \frac{\partial L^{(1)}}{\partial w_{21}} & \frac{\partial L^{(1)}}{\partial w_{22}} & \frac{\partial L^{(1)}}{\partial w_{23}} \\ \frac{\partial L^{(1)}}{\partial w_{31}} & \frac{\partial L^{(1)}}{\partial w_{32}} & \frac{\partial L^{(1)}}{\partial w_{33}} \end{bmatrix} + \begin{bmatrix} \frac{\partial L^{(2)}}{\partial w_{11}} & \frac{\partial L^{(2)}}{\partial w_{12}} & \frac{\partial L^{(2)}}{\partial w_{13}} \\ \frac{\partial L^{(2)}}{\partial w_{21}} & \frac{\partial L^{(2)}}{\partial w_{22}} & \frac{\partial L^{(2)}}{\partial w_{23}} \\ \frac{\partial L^{(2)}}{\partial w_{31}} & \frac{\partial L^{(2)}}{\partial w_{32}} & \frac{\partial L^{(2)}}{\partial w_{33}} \end{bmatrix} + \dots + \begin{bmatrix} \frac{\partial L^{(m)}}{\partial w_{11}} & \frac{\partial L^{(m)}}{\partial w_{12}} & \frac{\partial L^{(m)}}{\partial w_{13}} \\ \frac{\partial L^{(m)}}{\partial w_{21}} & \frac{\partial L^{(m)}}{\partial w_{22}} & \frac{\partial L^{(m)}}{\partial w_{23}} \\ \frac{\partial L^{(m)}}{\partial w_{31}} & \frac{\partial L^{(m)}}{\partial w_{32}} & \frac{\partial L^{(m)}}{\partial w_{33}} \end{bmatrix} \right) WJ=m1 w11L(1)w21L(1)w31L(1)w12L(1)w22L(1)w32L(1)w13L(1)w23L(1)w33L(1) + w11L(2)w21L(2)w31L(2)w12L(2)w22L(2)w32L(2)w13L(2)w23L(2)w33L(2) ++ w11L(m)w21L(m)w31L(m)w12L(m)w22L(m)w32L(m)w13L(m)w23L(m)w33L(m)
根据矩阵加法的规则,形状相同的矩阵相加,就是相同位置的元素自己加自己。加完之后,外面还有一个乘数 1 m \frac{1}{m} m1,乘进去就变成了对每个位置求平均。最终,这 m m m 个矩阵融合成了一个唯一的、崭新的 3 × 3 3 \times 3 3×3 平均梯度矩阵: ∂ J ∂ W = [ 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 11 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 12 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 13 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 21 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 22 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 23 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 31 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 32 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 33 ] \frac{\partial J}{\partial W} = \begin{bmatrix} \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{11}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{12}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{13}} \\ \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{21}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{22}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{23}} \\ \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{31}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{32}} & \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{33}} \end{bmatrix} WJ= m1k=1mw11L(k)m1k=1mw21L(k)m1k=1mw31L(k)m1k=1mw12L(k)m1k=1mw22L(k)m1k=1mw32L(k)m1k=1mw13L(k)m1k=1mw23L(k)m1k=1mw33L(k) 比如它左上角的第一个元素 1 m ∑ k = 1 m ∂ L ( k ) ∂ w 11 \frac{1}{m}\sum_{k=1}^m \frac{\partial L^{(k)}}{\partial w_{11}} m1k=1mw11L(k),其实就是一个标量(纯数字)。它的物理意义是:“综合了全部 m m m 个样本的意见后,我们认为参数 w 11 w_{11} w11 应该朝着这个平均数值的方向去调整。”
绝对优势:因为每次更新都使用了全部 m m m 个样本,所以计算出的梯度方向是非常准确的。对于凸优化问题,它能保证收敛到全局最优点;对于非凸问题,能保证收敛到局部最优点。它的优化轨迹是一条平滑下降的曲线致命缺陷:当数据集非常大(比如 m m m 是一百万或一千万)时,计算一次 ∇ θ J ( θ ) \nabla_\theta J(\theta) θJ(θ) 需要对整个数据集求和。这意味着每走一小步,就要耗费巨大的计算资源和时间,并且内存可能根本装不下这么多数据。在现代深度学习中,纯粹的 BGD 几乎不被使用。

1.2 随机梯度下降 (Stochastic Gradient Descent, SGD)

为了解决 BGD 计算太慢的问题,随机梯度下降走向了另一个极端:每次参数更新,我只随机抽取“1 个”样本来计算梯度。数学推导在 SGD 中,我们不再计算全部样本的平均损失,而是用第 i i i 个样本的单体损失来近似代表整体的代价函数: J S G D ( θ ) ≈ L ( f ( x ( i ) ; θ ) , y ( i ) ) J_{SGD}(\theta) \approx L(f(x^{(i)}; \theta), y^{(i)}) JSGD(θ)L(f(x(i);θ),y(i))因此,梯度的计算变得极其简单,只求单个样本的梯度: g t = ∇ θ L ( f ( x ( i ) ; θ t ) , y ( i ) ) g_t = \nabla_\theta L(f(x^{(i)}; \theta_t), y^{(i)}) gt=θL(f(x(i);θt),y(i))参数更新公式变为: θ t + 1 = θ t − η ∇ θ L ( f ( x ( i ) ; θ t ) , y ( i ) ) \theta_{t+1} = \theta_t - \eta \nabla_\theta L(f(x^{(i)}; \theta_t), y^{(i)}) θt+1=θtηθL(f(x(i);θt),y(i))绝对优势:计算速度极快!因为每次只用一个样本,模型参数可以瞬间完成更新。
致命缺陷:因为单个样本存在极大的随机性和噪声,它计算出的梯度方向常常偏离整体的最优方向。这导致 SGD 的下降轨迹极其曲折(呈现出剧烈的锯齿状震荡)。虽然大方向是朝着谷底走的,但它很难精确收敛到最小值点,往往会在最小值附近不断游荡。

1.3 小批量梯度下降 (Mini-batch Gradient Descent, MBGD)

今天我们在各种深度学习框架(如 PyTorch, TensorFlow)中调用的 optim.SGD,实际上绝大多数情况下跑的是“小批量梯度下降”。它结合了 BGD 的稳定性和 SGD 的高效性:每次更新抽取一小批(Mini-batch)样本,数量为 n n n。 ( n n n 通常是 32, 64, 128, 256 等 2 的幂次方)。数学推导代价函数变为在这 n n n 个样本上的平均: J M B G D ( θ ) = 1 n ∑ i = 1 n L ( f ( x ( i ) ; θ ) , y ( i ) ) J_{MBGD}(\theta) = \frac{1}{n} \sum_{i=1}^{n} L(f(x^{(i)}; \theta), y^{(i)}) JMBGD(θ)=n1i=1nL(f(x(i);θ),y(i))梯度计算和参数更新: θ t + 1 = θ t − η ( 1 n ∑ i = 1 n ∇ θ L ( f ( x ( i ) ; θ t ) , y ( i ) ) ) \theta_{t+1} = \theta_t - \eta \left( \frac{1}{n} \sum_{i=1}^{n} \nabla_\theta L(f(x^{(i)}; \theta_t), y^{(i)}) \right) θt+1=θtη(n1i=1nθL(f(x(i);θt),y(i)))为什么这是现代标准?降低方差:使用 n n n 个样本求平均梯度,大大降低了单样本 SGD 带来的剧烈震荡,更新方向更准确。矩阵运算加速:现代 GPU 极其擅长并行计算。一次性计算 n n n 个样本的前向和反向传播时间,与计算 1 个样本的时间几乎一样长。这使得 MBGD 在计算效率和收敛稳定性上达到了完美的平衡。

二. 标准动量法 (Standard Momentum)

物理直觉很简单:想象一个小球从山坡上滚下。它不仅仅受当前位置的坡度(梯度)影响,它自身还带有向前的惯性(速度)。如果它一直在往一个方向滚,它会越滚越快;如果它遇到了阻力或地形崎岖,之前的惯性能帮它冲过去。数学推导与公式我们需要引入两个新的变量和超参数: v t v_t vt:速度(Velocity),表示当前积累的动量向量。它的维度和参数 θ \theta θ 完全一样。初始化时 v 0 = 0 v_0 = 0 v0=0 γ \gamma γ:动量衰减系数(Momentum factor),通常设为 0.9 或 0.99。你可以把它理解为物理环境中的“摩擦力”或“空气阻力”,防止小球速度无限增大。标准动量的核心更新公式分为两步(业界最通用的表达):
第一步:更新速度 v t = β v t − 1 + ( 1 − β ) ∇ θ J ( θ t ) v_t = \beta v_{t-1} + (1 - \beta) \nabla_\theta J(\theta_t) vt=βvt1+(1β)θJ(θt)第二步:更新参数 θ t + 1 = θ t − η v t \theta_{t+1} = \theta_t - \eta v_t θt+1=θtηvt为什么公式能抑制震荡?
让我们把速度公式 v t v_t vt 像剥洋葱一样展开。假设我们在第 t t t 步: v t = ( 1 − β ) ∇ θ J ( θ t ) + β v t − 1 v_t = (1 - \beta) \nabla_\theta J(\theta_t) + \beta v_{t-1} vt=(1β)θJ(θt)+βvt1 v t = ( 1 − β ) ∇ θ J ( θ t ) + β ( ( 1 − β ) ∇ θ J ( θ t − 1 ) + β v t − 2 ) v_t = (1 - \beta) \nabla_\theta J(\theta_t) + \beta \left( (1 - \beta) \nabla_\theta J(\theta_{t-1}) + \beta v_{t-2} \right) vt=(1β)θJ(θt)+β((1β)θJ(θt1)+βvt2) v t = ( 1 − β ) ∇ θ J ( θ t ) + β ( 1 − β ) ∇ θ J ( θ t − 1 ) + β 2 ( 1 − β ) ∇ θ J ( θ t − 2 ) + ⋯ + β t ( 1 − β ) ∇ θ J ( θ 0 ) v_t = (1 - \beta) \nabla_\theta J(\theta_t) + \beta (1 - \beta) \nabla_\theta J(\theta_{t-1}) + \beta^2 (1 - \beta) \nabla_\theta J(\theta_{t-2}) + \dots + \beta^t (1 - \beta) \nabla_\theta J(\theta_0) vt=(1β)θJ(θt)+β(1β)θJ(θt1)+β2(1β)θJ(θt2)++βt(1β)θJ(θ0)提取公因式 ( 1 − β ) (1 - \beta) (1β),我们可以得到一个极其优美的级数表达式: v t = ( 1 − β ) [ ∇ θ J ( θ t ) + β ∇ θ J ( θ t − 1 ) + β 2 ∇ θ J ( θ t − 2 ) + ⋯ + β t ∇ θ J ( θ 0 ) ] v_t = (1 - \beta) \left[ \nabla_\theta J(\theta_t) + \beta \nabla_\theta J(\theta_{t-1}) + \beta^2 \nabla_\theta J(\theta_{t-2}) + \dots + \beta^t \nabla_\theta J(\theta_0) \right] vt=(1β)[θJ(θt)+βθJ(θt1)+β2θJ(θt2)++βtθJ(θ0)]
所有历史梯度的权重加起来,刚好约等于 1。
(因为当 t → ∞ t \to \infty t 时,等比数列求和 1 + β + β 2 + ⋯ = 1 1 − β 1 + \beta + \beta^2 + \dots = \frac{1}{1-\beta} 1+β+β2+=1β1,再乘以前面的 ( 1 − β ) (1-\beta) (1β),结果正好是 1)。这意味着, v t v_t vt 不再是一个随着时间可能无限膨胀的“累加和”,而是一个真正意义上、量纲完全对齐的“平均梯度”!***

从这个最终的展开式中,我们可以极其清晰地看到动量抑制震荡的数学本质:
1.在陡峭的震荡方向上(峡谷两侧):梯度的符号是正负交替的。比如 ∇ θ J ( θ t ) \nabla_\theta J(\theta_t) θJ(θt) 是正向拉扯, ∇ θ J ( θ t − 1 ) \nabla_\theta J(\theta_{t-1}) θJ(θt1) 是反向拉扯。在括号内的求和过程中,这些方向相反的梯度会相互抵消。最终乘上学习率 η \eta η 后,在这个方向上的实际移动步长会变得非常小,从而大幅削弱了“之”字形的震荡。
2.在平缓的推进方向上(指向谷底):梯度的符号始终是一致的。括号内的历史梯度 ∇ θ J ( θ k ) \nabla_\theta J(\theta_k) θJ(θk) 全是指向同一个方向。在求和时,它们会不断累加,使得括号内的总值变大。最终乘上学习率 η \eta η 后,在这个方向上的实际移动步长被显著放大,从而加速了模型向最优解的冲刺。
在这里插入图片描述
抵消震荡(做减法): 在走弯路的方向上,前后梯度一上一下,正负相反。累加后互相抵消,抹平了无用的反复折返。
加速冲刺(做加法): 在走向终点的正路上,前后梯度始终一致。同向叠加后数值越来越大,像踩油门一样加速到底。
一句话总结:把来回横跳浪费的力气全省下来,全部加在往前冲的方向上

三. 自适应学习率优化器

为什么需要“自适应”?
在前文的 SGD 和 Momentum 中,有一个隐藏的致命假设:所有的参数都共享同一个全局学习率 η \eta η。在现实的高维数据(尤其是稀疏数据)中,这是一个巨大的缺陷。想象一下模型中有两类特征(对应两类参数):高频特征(常见特征):在几乎每个样本中都出现,它们的参数每次都在更新。低频特征(罕见特征):只在极少数样本中出现,它们的参数很久才更新一次。如果用同一个学习率 η \eta η:如果 η \eta η 设得大,高频特征的参数会剧烈震荡,无法收敛到极小值。如果 η \eta η 设得小,低频特征的参数好不容易遇到一次更新机会,却只往前挪了一小步,导致学习极其缓慢。我们的终极目标是:给每个参数定制专属的学习率。 更新频繁的参数,步子迈小点(精细微调);更新罕见的参数,步子迈大点(加速学习)。

3.1 Adagrad (Adaptive Gradient)

Adagrad 是第一个真正意义上解决这个问题的算法。它的核心思想极其聪明:用历史梯度的平方和,来衡量一个参数更新的频繁程度
假设 g t g_t gt 是当前第 t t t 步的梯度向量,即 g t = ∇ θ J ( θ t ) g_t = \nabla_\theta J(\theta_t) gt=θJ(θt)。第一步:累加历史梯度的平方 G t = G t − 1 + g t ⊙ g t G_t = G_{t-1} + g_t \odot g_t Gt=Gt1+gtgt(注: ⊙ \odot 表示逐元素相乘。也就是把梯度向量的每个元素自己乘自己。 G t G_t Gt 是一个与参数维度相同的向量,记录了每个参数从训练开始到现在的梯度平方总和。)第二步:更新参数 θ t + 1 = θ t − η G t + ϵ ⊙ g t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} \odot g_t θt+1=θtGt+ϵ ηgt(注: ϵ \epsilon ϵ 是一个极小的常数,通常设为 10 − 8 10^{-8} 108,仅仅是为了防止分母为零出现数学错误。)
它是如何实现“自适应”的?
观察第二步的公式,真正的专属学习率变成了 η G t + ϵ \frac{\eta}{\sqrt{G_t + \epsilon}} Gt+ϵ η。对于高频更新的参数:它每次的梯度 g t g_t gt 都不为零,累加起来的 G t G_t Gt 会非常大。代入公式,分母很大,导致它实际的学习率变得非常小。对于低频更新的参数:它大部分时间的梯度为零, G t G_t Gt 累加得很慢,值很小。代入公式,分母很小,它实际的学习率就能保持在较大的水平。
但是,成也萧何,败也萧何。Adagrad 的数学设计导致了一个无法挽回的悲剧:仔细看第一步的公式 G t = G t − 1 + g t ⊙ g t G_t = G_{t-1} + g_t \odot g_t Gt=Gt1+gtgt。因为每次累加的都是平方项(一定大于等于零),所以 G t G_t Gt 是一定单调递增的,永远只会变大,不会变小。这意味着,随着训练轮数的增加,分母 G t + ϵ \sqrt{G_t + \epsilon} Gt+ϵ 会不可控地膨胀到极大值。结果就是,在训练的中后期,所有参数的学习率都会不可避免地衰减到接近于零,导致模型直接停止学习,提前“饿死”在半路上,根本到不了全局最优解。下面则是针对这个缺陷的改进算法。

3.2 RMSprop (Root Mean Square Propagation)

为了区分动量中的速度 v t v_t vt,这里我们用 s t s_t st(表示 squared gradients)来记录梯度平方的移动平均。同时引入一个新的衰减系数 β \beta β(通常设为 0.9 或 0.99)。第一步:计算梯度平方的指数加权移动平均 s t = β s t − 1 + ( 1 − β ) ( g t ⊙ g t ) s_t = \beta s_{t-1} + (1 - \beta) (g_t \odot g_t) st=βst1+(1β)(gtgt)第二步:更新参数 θ t + 1 = θ t − η s t + ϵ ⊙ g t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{s_t} + \epsilon} \odot g_t θt+1=θtst +ϵηgt让我们剥开 s t s_t st 的洋葱皮,看看 指数加权移动平均(EMA) 是怎么解决无穷大问题的: s t = ( 1 − β ) ( g t ⊙ g t ) + β ( 1 − β ) ( g t − 1 ⊙ g t − 1 ) + β 2 ( 1 − β ) ( g t − 2 ⊙ g t − 2 ) + … s_t = (1 - \beta)(g_t \odot g_t) + \beta(1 - \beta)(g_{t-1} \odot g_{t-1}) + \beta^2(1 - \beta)(g_{t-2} \odot g_{t-2}) + \dots st=(1β)(gtgt)+β(1β)(gt1gt1)+β2(1β)(gt2gt2)+因为所有历史权重的和刚好趋近于 1(即 ( 1 − β ) ∑ β i ≈ 1 (1-\beta) \sum \beta^i \approx 1 (1β)βi1, 其实前面标准动量法已经证明过了),所以 s t s_t st 在数学上并不是一个不断变大的“累加和”,而是一个真实的**“局部加权平均值”。
这意味着,即使你训练了一万步、一百万步,只要最近的梯度波动在正常范围内, s t s_t st 的值就会一直稳定在这个波动量级上,绝对不会爆炸!真实学习率也就永远不会衰减到零**。

3.2.1 小探究:动态学习率真的是根据不同的参数“自定义”的吗?

我们需要把矩阵展开才能把这件事情讲明白。
视角的转换:从整体矩阵到独立元素。假设有一个神经网络的一层,它的参数矩阵是 W W W。为了方便展示,我们假设它极小,只有 2 × 2 2 \times 2 2×2 W t = [ w 11 w 12 w 21 w 22 ] W_t = \begin{bmatrix} w_{11} & w_{12} \\ w_{21} & w_{22} \end{bmatrix} Wt=[w11w21w12w22]当你进行一次反向传播时,计算出的梯度矩阵 g t g_t gt(即 ∂ L ∂ W \frac{\partial L}{\partial W} WL)必然有着完全相同的形状: g t = [ g 11 g 12 g 21 g 22 ] g_t = \begin{bmatrix} g_{11} & g_{12} \\ g_{21} & g_{22} \end{bmatrix} gt=[g11g21g12g22]在最原始的基础梯度下降(SGD)中,更新公式是标量乘矩阵: W t + 1 = W t − η ⋅ g t = [ w 11 − η g 11 w 12 − η g 12 w 21 − η g 21 w 22 − η g 22 ] W_{t+1} = W_t - \eta \cdot g_t = \begin{bmatrix} w_{11} - \eta g_{11} & w_{12} - \eta g_{12} \\ w_{21} - \eta g_{21} & w_{22} - \eta g_{22} \end{bmatrix} Wt+1=Wtηgt=[w11ηg11w21ηg21w12ηg12w22ηg22]在这里,矩阵中的每一个参数( w 11 , w 12 … w_{11}, w_{12} \dots w11,w12)确实都在共享同一个步长倍数 η \eta η
破局点:RMSprop 的“逐元素”魔法。现在,我们把目光切回到 RMSprop 的核心公式。请特别注意里面的运算符号:第一步:计算二阶矩 s t s_t st s t = β s t − 1 + ( 1 − β ) ( g t ⊙ g t ) s_t = \beta s_{t-1} + (1 - \beta) (g_t \odot g_t) st=βst1+(1β)(gtgt)第二步:更新参数 W t + 1 = W t − η s t + ϵ ⊙ g t W_{t+1} = W_t - \frac{\eta}{\sqrt{s_t + \epsilon}} \odot g_t Wt+1=Wtst+ϵ ηgt这里的 ⊙ \odot 是 Hadamard 乘积(逐元素相乘),而公式里的除法也是逐元素除法。这意味着, s t s_t st 本身也是一个 2 × 2 2 \times 2 2×2 的矩阵! 它记录了矩阵中每一个独立参数的梯度平方历史。 s t = [ s 11 s 12 s 21 s 22 ] s_t = \begin{bmatrix} s_{11} & s_{12} \\ s_{21} & s_{22} \end{bmatrix} st=[s11s21s12s22]当我们计算 η s t + ϵ \frac{\eta}{\sqrt{s_t + \epsilon}} st+ϵ η 时,我们实际上生成了一个全新的、同样大小的“动态学习率矩阵”: 动态学习率矩阵 = [ η s 11 + ϵ η s 12 + ϵ η s 21 + ϵ η s 22 + ϵ ] 动态学习率矩阵 = \begin{bmatrix} \frac{\eta}{\sqrt{s_{11} + \epsilon}} & \frac{\eta}{\sqrt{s_{12} + \epsilon}} \\ \frac{\eta}{\sqrt{s_{21} + \epsilon}} & \frac{\eta}{\sqrt{s_{22} + \epsilon}} \end{bmatrix} 动态学习率矩阵=[s11+ϵ ηs21+ϵ ηs12+ϵ ηs22+ϵ η]最后,把这个“动态学习率矩阵”与梯度矩阵 g t g_t gt 逐元素相乘,真正的更新过程在底层是这样独立进行的: w 11 ( t + 1 ) = w 11 ( t ) − ( η s 11 + ϵ ) g 11 w_{11}^{(t+1)} = w_{11}^{(t)} - \left( \frac{\eta}{\sqrt{s_{11} + \epsilon}} \right) g_{11} w11(t+1)=w11(t)(s11+ϵ η)g11 w 12 ( t + 1 ) = w 12 ( t ) − ( η s 12 + ϵ ) g 12 w_{12}^{(t+1)} = w_{12}^{(t)} - \left( \frac{\eta}{\sqrt{s_{12} + \epsilon}} \right) g_{12} w12(t+1)=w12(t)(s12+ϵ η)g12 . . . ... ...
所以参数矩阵 W W W 中的每一个元素 w i j w_{ij} wij 都在独立计算自己的分母 s i j s_{ij} sij
高频特征(如 w 11 w_{11} w11 对应常见词“的”):每次训练几乎都会遇到这个词,所以它的梯度 g 11 g_{11} g11 经常有值。随着时间的推移,它的局部历史平方和 s 11 s_{11} s11 累加得很大。结果:它的专属学习率 η s 11 + ϵ \frac{\eta}{\sqrt{s_{11} + \epsilon}} s11+ϵ η 变得很小,更新变得平稳。低频特征(如 w 22 w_{22} w22 对应罕见专业词汇“涌现能力”):这个词可能几百个 batch 才出现一次,平时大部分时间 g 22 g_{22} g22 都是 0。所以它的历史平方和 s 22 s_{22} s22 极小。结果:它的专属学习率 η s 22 + ϵ \frac{\eta}{\sqrt{s_{22} + \epsilon}} s22+ϵ η 保持在极大的状态。当它终于在某个样本中出现时,系统会给它一个巨大的步长,让它迅速学习到位。

3.2.2 平方小细节

动量公式里的叫 v t v_t vt(一阶矩 / 速度),它没有平方。RMSprop 公式里的叫 s t s_t st(二阶矩 / 梯度平方和),它必须有平方。简单来说:动量不平方,是为了保留正负号,让反向的梯度互相抵消(找准方向);RMSprop 加平方,是为了抹除正负号,单纯衡量梯度波动的“剧烈程度”(计算步长)

场景一:管方向的动量(Momentum)绝不能平方核心目的:找准真实的推进方向。极端例子:连续四个梯度为 +10, -10, +10, -10,直接相加平均为 0。物理本质:保留正负号能让相反的梯度互相抵消。系统算出总速度为 0,精准识别出该方向在“原地打转”,从而停止无效发力。若强行平方,梯度全变正数,系统会致命误判为“一直在单向加速”而导致彻底失控。
场景二:RMSprop 若不平方的“毁灭性灾难”核心目的:计算专属学习率的分母,根据波动幅度惩罚震荡(震荡越大,学习率越小)。极端例子:若将上述未平方的平均梯度 0 直接塞进分母,公式变为: η 0 + ϵ \frac{\eta}{\sqrt{0 + \epsilon}} 0+ϵ η物理本质:出现致命误判!系统看到分母趋近 0,误以为“该参数毫无动静”,直接给出一脚趋于无穷大的猛油门。但实际上参数正以 10 的幅度剧烈震荡,这脚油门会让参数瞬间起飞,模型当场崩溃(Loss 变成 NaN)。
场景三:RMSprop 平方的“完美救场”核心目的:抹除方向干扰,只暴露波动的绝对幅度。极端例子:+10, -10, +10, -10 经过平方变为 100, 100, 100, 100。平均值为 100,开根号后为 10。真实学习率变为: η 10 \frac{\eta}{10} 10η物理本质:平方操作完美剥离了正负号的伪装。系统看到分母算出来是 10,立刻清醒:“该参数波动极剧烈!”从而果断将其专属学习率缩小 10 倍,强行让参数“冷静”,稳稳压制住了震荡。

四.Adam (Adaptive Moment Estimation,自适应矩估计)

动量法像是装了导航,为我们指明方向,抵消了震荡,带来了惯性。RMSprop像一个自适应的智能底盘,根据路况的颠簸程度自动的调节轮子的速度。这个时候,一个极其自然的想法就诞生了:“我全都要!”
在这里插入图片描述
为了符合学术界和 PyTorch 等框架的最标准源码规范,我们在这里统一一下符号:把前面动量里的速度 v t v_t vt 改叫 m t m_t mt (一阶矩,Mean)。把前面 RMSprop 里的均方根 s t s_t st 改叫 v t v_t vt (二阶矩,Variance/Uncentered Variance)。
假设当前在第 t t t 步,计算出的当前梯度为 g t = ∇ θ J ( θ t ) g_t = \nabla_\theta J(\theta_t) gt=θJ(θt)。超参数通常默认设置为: β 1 = 0.9 \beta_1 = 0.9 β1=0.9, β 2 = 0.999 \beta_2 = 0.999 β2=0.999, ϵ = 10 − 8 \epsilon = 10^{-8} ϵ=108
第一步:计算一阶矩估计 m t m_t mt(也就是动量 Momentum)完全照搬我们之前推导的带 ( 1 − β ) (1-\beta) (1β) 的 EMA 公式,它负责找准平滑的下降方向。 m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt1+(1β1)gt第二步:计算二阶矩估计 v t v_t vt(也就是 RMSprop)完全照搬我们刚刚推导的带平方的 EMA 公式,它负责衡量梯度的剧烈程度。 v t = β 2 v t − 1 + ( 1 − β 2 ) ( g t ⊙ g t ) v_t = \beta_2 v_{t-1} + (1 - \beta_2) (g_t \odot g_t) vt=β2vt1+(1β2)(gtgt)第三步:偏差校正(Bias Correction)—— Adam 的独门绝技由于 m 0 m_0 m0 v 0 v_0 v0 在最开始都被初始化为 0 0 0 向量,在训练的最初几步,计算出来的 m t m_t mt v t v_t vt 会严重偏向于 0 0 0(我们称之为“冷启动”问题)。为了修正这个偏差,Adam 引入了对时间步 t t t 的指数缩放: m ^ t = m t 1 − β 1 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} m^t=1β1tmt v ^ t = v t 1 − β 2 t \hat{v}_t = \frac{v_t}{1 - \beta_2^t} v^t=1β2tvt(注:这里的 β 1 t \beta_1^t β1t 表示 β 1 \beta_1 β1 t t t 次方。)
第四步:最终的参数更新将校正后的一阶矩(方向)和二阶矩(步长缩放)结合起来,乘上全局学习率 η \eta η θ t + 1 = θ t − η v ^ t + ϵ ⊙ m ^ t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \odot \hat{m}_t θt+1=θtv^t +ϵηm^t前两步和第四步我们已经非常熟悉了,分子 m ^ t \hat{m}_t m^t 提供稳定的方向,分母 v ^ t \sqrt{\hat{v}_t} v^t 提供自适应的刹车/油门。

4.1 为什么需要偏差校正?

假设我们刚开始训练,是第 1 1 1 步( t = 1 t=1 t=1)。初始状态 m 0 = 0 m_0 = 0 m0=0。第一步算出来的真实梯度是 g 1 = 10 g_1 = 10 g1=10。超参数 β 1 = 0.9 \beta_1 = 0.9 β1=0.9。我们代入第一步的公式算一下 m 1 m_1 m1 m 1 = 0.9 × 0 + ( 1 − 0.9 ) × 10 = 0.1 × 10 = 1 m_1 = 0.9 \times 0 + (1 - 0.9) \times 10 = 0.1 \times 10 = 1 m1=0.9×0+(10.9)×10=0.1×10=1真实的梯度明明是 10 10 10,但因为我们把历史记录初始化为了 0 0 0 并且强行分配了 0.9 0.9 0.9 的权重,导致算出来的平均值 m 1 m_1 m1 只有 1 1 1
这在数学上被严重“稀释”了。同理, v t v_t vt 初始化为 0 0 0 β 2 = 0.999 \beta_2 = 0.999 β2=0.999,第一步算出来的 v 1 v_1 v1 更是会被稀释到极其可怜的 0.001 0.001 0.001 倍。为了解决这个问题,引入了除以 ( 1 − β t ) (1 - \beta^t) (1βt) 的操作。还是刚才的例子,当前步数 t = 1 t=1 t=1,算出来的 m 1 = 1 m_1 = 1 m1=1。代入偏差校正公式: m ^ 1 = m 1 1 − β 1 1 = 1 1 − 0.9 = 1 0.1 = 10 \hat{m}_1 = \frac{m_1}{1 - \beta_1^1} = \frac{1}{1 - 0.9} = \frac{1}{0.1} = 10 m^1=1β11m1=10.91=0.11=10随着训练步数 t t t 的不断增加(比如 t = 1000 t=1000 t=1000): β 1 1000 \beta_1^{1000} β11000(也就是 0.9 0.9 0.9 1000 1000 1000 次方)会极其趋近于 0 0 0。此时的分母 ( 1 − β 1 1000 ) (1 - \beta_1^{1000}) (1β11000) 极其趋近于 1 1 1。这意味着:在训练初期,偏差校正会强力拉平被稀释的数值;而在训练中后期,随着历史数据真正积累起来,这个校正项会自动“隐身”(除以 1 等于没除),平滑地过渡到真实的移动平均值。

4.2 优雅美丽的初始化小巧思

既然是为了计算梯度的移动平均,为什么 Adam 优化器不直接拿第一步的真实梯度作为初始值,非要‘绕远路’从 0 开始,然后再费力搞一个复杂的‘偏差校正’?(究极神级细节!!!)
答案隐藏在深度学习极其特殊的物理环境里:在神经网络中,第一步的梯度( g 1 g_1 g1)往往是质量最差、最不能信任的“垃圾数据”。
如果强行令 v 1 = g 1 2 v_1 = g_1^2 v1=g12(这里我们以影响最致命的二阶矩 v t v_t vt 为例,假设 β 2 = 0.999 \beta_2 = 0.999 β2=0.999),
t = 1 t=1 t=1: v 1 = g 1 2 v_1 = g_1^2 v1=g12
t = 2 t=2 t=2: v 2 = 0.999 × v 1 + ( 1 − 0.999 ) × g 2 2 v_2 = 0.999 \times v_1 + (1 - 0.999) \times g_2^2 v2=0.999×v1+(10.999)×g22,也就是: v 2 = 0.999 g 1 2 + 0.001 g 2 2 v_2 = 0.999 g_1^2 + 0.001 g_2^2 v2=0.999g12+0.001g22
t = 3 t=3 t=3: v 3 = 0.999 × v 2 + 0.001 × g 3 2 v_3 = 0.999 \times v_2 + 0.001 \times g_3^2 v3=0.999×v2+0.001×g32,也就是: v 3 = 0.999 2 g 1 2 + 0.999 × 0.001 g 2 2 + 0.001 g 3 2 v_3 = 0.999^2 g_1^2 + 0.999 \times 0.001 g_2^2 + 0.001 g_3^2 v3=0.9992g12+0.999×0.001g22+0.001g32
第一步的垃圾梯度 g 1 2 g_1^2 g12 占据了绝对的统治地位!到了第 2 步,它占了 99.9% 的权重;即使到了第 100 步,由于 0.999 100 ≈ 0.904 0.999^{100} \approx 0.904 0.9991000.904,这第一步的随机噪声依然死死占据着高达 90.4% 的权重!如果 g 1 g_1 g1 碰巧非常大,你的分母就会在接下来的几百步里极大,导致真实学习率极小,模型直接“原地停摆”。如果 g 1 g_1 g1 碰巧非常小,分母极小,模型在接下来的几百步里会像疯了一样迈着巨大的步子,直接飞出峡谷(Loss 爆炸)。
现在,我们来看看 Adam 从 0 开始并加入偏差校正,在数学上产生了极其优美的什么化学反应。
t = 1 t=1 t=1:
v 1 = 0 + 0.001 g 1 2 v_1 = 0 + 0.001 g_1^2 v1=0+0.001g12
偏差校正: v ^ 1 = 0.001 g 1 2 1 − 0.999 1 = 0.001 g 1 2 0.001 = g 1 2 \hat{v}_1 = \frac{0.001 g_1^2}{1 - 0.999^1} = \frac{0.001 g_1^2}{0.001} = g_1^2 v^1=10.99910.001g12=0.0010.001g12=g12
t = 2 t=2 t=2:
v 2 = 0.999 × ( 0.001 g 1 2 ) + 0.001 g 2 2 = 0.001 ( 0.999 g 1 2 + g 2 2 ) v_2 = 0.999 \times (0.001 g_1^2) + 0.001 g_2^2 = 0.001 (0.999 g_1^2 + g_2^2) v2=0.999×(0.001g12)+0.001g22=0.001(0.999g12+g22)
现在,见证奇迹的偏差校正: v ^ 2 = v 2 1 − 0.999 2 = 0.001 ( 0.999 g 1 2 + g 2 2 ) 1 − 0.998001 = 0.001 ( 0.999 g 1 2 + g 2 2 ) 0.001999 \hat{v}_2 = \frac{v_2}{1 - 0.999^2} = \frac{0.001 (0.999 g_1^2 + g_2^2)}{1 - 0.998001} = \frac{0.001 (0.999 g_1^2 + g_2^2)}{0.001999} v^2=10.9992v2=10.9980010.001(0.999g12+g22)=0.0019990.001(0.999g12+g22)我们把分子分母约一下: v ^ 2 ≈ 0.999 g 1 2 + g 2 2 1.999 ≈ 1 2 g 1 2 + 1 2 g 2 2 \hat{v}_2 \approx \frac{0.999 g_1^2 + g_2^2}{1.999} \approx \frac{1}{2} g_1^2 + \frac{1}{2} g_2^2 v^21.9990.999g12+g2221g12+21g22 t = 3 t=3 t=3:
如果我们继续推导 v ^ 3 \hat{v}_3 v^3,会惊讶地发现,它约等于: v ^ 3 ≈ 1 3 g 1 2 + 1 3 g 2 2 + 1 3 g 3 2 \hat{v}_3 \approx \frac{1}{3} g_1^2 + \frac{1}{3} g_2^2 + \frac{1}{3} g_3^2 v^331g12+31g22+31g32偏差校正的真实身份是“动态平滑器”
直接赋值法:在初期,把 99.9% 的信任全部押注在第一步的随机噪声 g 1 g_1 g1 上,导致系统极度脆弱。
Adam 的偏差校正法:在最开始的前几步,它巧妙地在数学上退化成了“简单算术平均(Arithmetic Mean)”!
第 1 步:100% 相信 g 1 g_1 g1
第 2 步:50% 相信 g 1 g_1 g1,50% 相信 g 2 g_2 g2
第 3 步:均分前 3 步的权重……
直到步数 t t t 慢慢变大,它才平滑地过渡回标准的指数加权移动平均(给最近的梯度更多权重)。
它完美化解了神经网络初始梯度极度不可靠的危机。它不盲目迷信第一步的垃圾数据,而是让前几步的梯度“平起平坐”,通过算术平均迅速中和掉初始的随机噪声,然后再稳稳地交接给指数加权系统。

五. 总结

优化器的演进是一场不断“对症下药”的数学优化之旅:从 MBGD 寻找算力与稳定性的平衡,到 Momentum 利用历史惯性优化方向,再到 RMSprop 为矩阵中的每一个参数量身定制动态步长,最后由 Adam 融合所有前辈的优势并优雅地修补了初始冷启动漏洞,最终打造出了驱动现代神经网络高效收敛的最强引擎。理解这些内部机制,不仅能帮助我们看透繁杂的数学公式,更能指导我们在实际模型训练中更敏锐地选择和调优优化器。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐