9-优化器篇:优化器的认识和选择参考

发布于 2025-04-05  135 次阅读


"惯性助你越过局部低谷,但也可能冲过全局最优"

认识优化器(Optimizer)

相识

想象我们训练模型是为了寻找某个能解释训练数据、推理预测数据的真相,那么损失函数则为我们铺好了通往真相的道路。而优化器,便是我们沿着道路不断逼近真相时,使用的通行工具。

不同的道路,需要不同的通行工具。几乎没有人会从欧洲骑着自行车去南极洲考察,也几乎没有人为了去近在300米处的学校上学而购买飞机票......

通行工具多种多样,但基本上不会偏离以下配件:

  • 骨架(必备):产生基本的速度、改变行驶的方向,如自行车
  • 加速器(可选):额外地提高或降低速度,如机动车
  • 调节器(可选):改变行驶方式以适配不同的道路,如越野车

深度学习中的优化器亦是如此。

优化器的配件:

  • 骨架(必备):梯度下降
  • 加速器(可选):动量
  • 调节器(可选):自适应

相知

梯度下降(Gradient Descent)——优化器的骨架

梯度下降的发展:

  1. 起源(19世纪)
    梯度下降的数学思想可追溯至法国数学家Augustin-Louis Cauchy。他在1847年提出最速下降法(Method of Steepest Descent),用于求解无约束优化问题,核心是通过负梯度方向迭代逼近函数极小值点。
  2. 发展与理论完善(20世纪)
    随着计算机的诞生,梯度下降在数值优化领域得到广泛应用。1951年,H. Robbins和S. Monro提出随机逼近算法,为随机梯度下降(SGD)奠定基础。20世纪80年代,反向传播算法(Backpropagation)与梯度下降结合,成为训练神经网络的基石。
  3. 深度学习的爆发(21世纪)
    梯度下降的变体(如Adam、RMSProp)在深度学习中大放异彩,解决了高维非凸优化问题,支撑了ResNet、Transformer等复杂模型的训练。

梯度下降在反向传播负责计算梯度。其数学原理为:通过最小化损失函数,调整神经网络参数(权重和偏置)

梯度下降的一次具体流程:

  1. 梯度计算:将真实值、模型的预测值代入损失函数,解得梯度
  2. 参数更新:将旧参数减去梯度与学习率的乘积,得到新的模型参数

以MSE作为损失函数的线性回归为例:

$$ \begin{aligned} \text{MSE:} \quad J(w,b) &= \frac{1}{n} \sum_{i=1}^{n} (y_i - (w_i + b))^2 \\ \\ \text{where } & \begin{aligned} y_i &= \text{(真实值向量)} \\ \end{aligned} \\ \\ \\ \text{梯度计算:} \quad \frac{\partial J}{\partial w} &= -\frac{2}{n} \sum_{i=1}^{n} x_i(y_i - (w_i + b)) \\ \\ \frac{\partial J}{\partial b} &= -\frac{2}{n} \sum_{i=1}^{n} (y_i - (w_i + b)) \\ \\ \\ \text{参数更新:} \quad w_{new} &= w_{old} - α\frac{\partial J}{\partial w} \\ \\ b_{new} &= b_{old} - α\frac{\partial J}{\partial b} \\ \\ \text{where } & \begin{aligned} α &= \text{(学习率)} \\ \end{aligned} \end{aligned} $$

Python演示:

import numpy as np

# 数据
X = np.array([1, 2, 3, 4])
Y = np.array([2, 4, 6, 8])

# 初始化参数
w, b = 0, 0
alpha = 0.1
epochs = 5

for epoch in range(epochs):
    # 计算预测值和梯度
    Y_pred = w * X + b
    dw = -2 / len(X) * np.sum(X * (Y - Y_pred))
    db = -2 / len(X) * np.sum(Y - Y_pred)

    # 更新参数
    w = w - alpha * dw
    b = b - alpha * db

    # 计算损失
    loss = np.mean((Y - Y_pred) ** 2)
    print(f'Epoch {epoch + 1}: w={w:.2f}, b={b:.2f}, loss={loss:.2f}')
##
# output:
# Epoch 1: w=3.00, b=1.00, loss=30.00
# Epoch 2: w=1.00, b=0.30, loss=13.50
# Epoch 3: w=2.35, b=0.74, loss=6.09
# Epoch 4: w=1.45, b=0.42, loss=2.76
# Epoch 5: w=2.06, b=0.61, loss=1.27
# #

动量(Momentum)——优化器的加速器

动量的简历:

  1. 起源(20世纪60年代)
    动量方法最早由苏联数学家Boris Polyak在1964年提出,用于加速传统优化算法的收敛速度。其核心思想是模拟物理中的动量,通过积累历史梯度方向来增强参数更新的稳定性。
  2. 理论发展与改进
    • 1983年,Yurii Nesterov提出Nesterov Accelerated Gradient(NAG),通过提前计算“未来位置”的梯度,进一步优化了动量方向。
    • 20世纪90年代,动量被引入神经网络训练,与反向传播结合,显著提升了收敛效率。
  3. 深度学习的核心组件
    动量成为现代优化器(如Adam、RMSProp)的基础组件,解决了梯度下降中的震荡问题局部极小值陷阱,尤其在训练ResNet、GPT等复杂模型时表现突出。

动量的概念起源于物理学

在物理学中,动量可以理解为物体"运动的力量"。再次强调,是"运动的力量",而非“运动的能量”,后者一般称其为“动能”。

虽然二者密切相关,却有本质区别:

  • 动量(p = m·v):“运动的力量”(或理解为冲击力),非物体自身的能量,矢量
  • 动能(E = ½mv²):“运动的能量”,物体自身具有的能量,标量

动能接近的两个物体,动量差距可能巨大:

假设在某时刻,一个步枪发射的7.62×39mm子弹(弹头约8g),速度为700m/s,代入公式得

动能 E = 1960 (J);动量 p = 5.6 (kg·m/s)

同一时刻,一辆卡车(重10吨),速度为0.6m/s(约2.16km/h),代入公式得

动能 E = 1800 (J);动量 p = 6000 (kg·m/s)

二者动量差1000倍但动能差仅约10%

对于深度学习的优化器也类似,动量的引入让梯度的变化具有了“冲击力”,使得梯度更新时保留一定“惯性”

其数学原理为:引入一个速度变量v,记录历史梯度,在更新梯度时赋予其一定的权重β(动量系数)。

传统动量公式常有两个版本:

版本一(经典版本,Caffe采用的版本)

$$ \begin{aligned} \text{动量:} \quad v_t &= βv_{t-1} + (1-β)∇J(θ_t) \\ \\ θ_{t+1} &= θ_t - αv_t \\ \\ &β: \text{动量系数,通常取0.9} \\ &α: \text{学习率} \\ &θ: \text{参数向量} \\ \end{aligned} $$

版本二(Tensorflow、Pytorch、JAX等主流框架采用的优化版本)

$$ \begin{aligned} \text{动量:} \quad v_t &= βv_{t-1} + ∇J(θ_t) \\ \\ α_{TF/Pytorch} &= (1-β)*α \\ \\ θ_{t+1} &= θ_t - α_{TF/Pytorch}v_t \\ \\ &β: \text{动量系数,通常取0.9} \\ &α: \text{学习率} \\ &θ: \text{参数向量} \\ \end{aligned} $$

版本二中移除(1-β)系数的主要原因:

超参数解耦

  • 学习率 (α) 与动量系数 (β) 解耦
    若保留 (1−β),实际更新量隐含了 α×(1−β) 的联合缩放效应。这会导致:
    • 调整 β 时需同步调整 α 以维持等效步长。
    • 超参数搜索空间复杂度增加。
  • 版本二的设计将 α 和 β 解耦,使得:
    • α 直接控制整体更新幅度
    • β 单纯控制历史梯度记忆强度,物理意义更清晰。

动量的价值:

  1. 核心作用
    动量通过累积历史梯度信息,平滑参数更新方向,从而:
    • 加速收敛:在梯度方向一致的区域增大步长。
    • 减少震荡:抑制梯度噪声或高曲率区域的振荡。
  2. 关键贡献
    • 逃离局部极小值:动量提供的“惯性”有助于跳出局部最优。
    • 平衡探索与利用:自适应调整更新幅度,适应不同区域的梯度特性。

需注意动量的超参数敏感

  • β 过大(如0.99)会导致更新惯性过强,可能错过最优解。
  • β 过小(如0.5)会减弱动量效果,接近普通梯度下降。

引入动量后梯度下降的一次具体流程:

  1. 梯度计算:将真实值、模型的预测值代入损失函数,解得梯度
  2. 动量加权:将历史梯度于本次解出的梯度进行加权和,解得新梯度
  3. 参数更新:将旧参数减去梯度与学习率的乘积,得到新的模型参数

Python演示(可对比上一次无动量的运行效果):

import numpy as np

# 数据
X = np.array([1, 2, 3, 4])
Y = np.array([2, 4, 6, 8])

# 初始化参数和动量速度
w, b = 0, 0
v_w, v_b = 0, 0
beta = 0.9
alpha = 0.1 * (1-beta)
epochs = 5

for epoch in range(epochs):
    # 计算梯度
    Y_pred = w * X + b
    dw = -2 / len(X) * np.sum(X * (Y - Y_pred))
    db = -2 / len(X) * np.sum(Y - Y_pred)

    # 更新动量速度
    v_w = beta * v_w + dw
    v_b = beta * v_b + db

    # 更新参数
    w = w - alpha * v_w
    b = b - alpha * v_b

    # 计算损失
    loss = np.mean((Y - Y_pred) ** 2)
    print(f'Epoch {epoch + 1}: w={w:.2f}, b={b:.2f}, loss={loss:.2f}')

## output
# Epoch 1: w=0.30, b=0.10, loss=30.00
# Epoch 2: w=0.82, b=0.27, loss=20.84
# Epoch 3: w=1.45, b=0.48, loss=8.91
# Epoch 4: w=2.08, b=0.69, loss=1.17
# Epoch 5: w=2.60, b=0.86, loss=0.79
# #

自适应——优化器的调节器

在深度学习优化器中,“自适应”的核心目标是动态调整参数更新策略,以提升训练效率和模型性能。“自适应”的概念覆盖面较广,理论上,任何超参数或权重更新规则均可作为自适应的对象(但实际应用需综合考虑计算成本、稳定性和收益),只要有一项符合模型自身动态调参的优化器,都可以叫做自适应优化器。

截止2025年春,已实现有对学习率动量、梯度裁剪等核心参数的自适应的优化器(其中主要是针对学习率和动量的自适应)。随着技术的发展,很有可能会有更多的参数引入到优化器,并被设计为自动调参。

不同的优化器支持的自适应参数不尽相同,相应的数学原理也是百花齐放,篇幅限制原因,各自适应优化器的数学原理不在这篇文章中介绍。

下面的参考表中,将列举常用优化器的自适应参数,建议根据实际情况,有需要再深入研究其原理。


常见优化器参考

优化器名称梯度下降动量自适应自适应参数特点与注意事项
SGD✔️-基础优化器,需手动调整学习率;易陷入局部最优,适合简单任务。
Momentum✔️✔️-引入动量加速收敛,减少震荡;需调整动量系数(如0.9)。
Adagrad✔️✔️学习率累积梯度平方调整学习率,适合稀疏数据;后期学习率过小,需谨慎使用。
RMSProp✔️✔️学习率引入EMA平滑梯度平方,缓解Adagrad衰减问题;适合非平稳目标(如RNN)。
Adadelta✔️✔️学习率无需手动设置学习率,自适应更强;参数少但可能收敛慢,调参难度较高。
Adam✔️✔️✔️学习率、动量结合动量与自适应,通用性强;需调β₁、β₂,泛化性可能弱于SGD。
Nadam✔️✔️✔️学习率、动量(Nesterov)Adam + Nesterov动量,加速收敛;对高曲率区域更敏感,需适当降低学习率。
AMSGrad✔️✔️✔️学习率、动量修正Adam的二阶矩估计偏差,防止学习率过低;收敛更稳定,适合复杂非凸优化。
AdamW✔️✔️✔️学习率、动量、权重衰减解耦权重衰减与自适应学习率,提升模型泛化;推荐替代Adam用于Transformer等模型。
LAMB✔️✔️✔️学习率、动量(分层调整)结合Adam与LARS,支持大批次训练;需分层计算信任比率,适合大模型(如BERT)。

选择建议:

  • 简单任务:SGD或Momentum。
  • 稀疏数据:Adagrad或Adam。
  • RNN/非平稳目标:RMSProp或Adam。
  • 大规模训练:AdamW或LAMB。
  • 理论保障需求:AMSGrad或Nadam。


部分优化器正在实现或实现了梯度裁剪的自适应,但(截止2025年春)尚未内置主流框架(Tensorflow、Pytorch)。

可能包括但原因:

  1. 计算开销:动态调整阈值需要额外计算梯度范数或矩阵谱范数,增加训练时间。
  2. 超参数复杂性:自适应裁剪引入新超参数(如EMA系数 β),调参成本高。
  3. 替代方案成熟:通过权重初始化、归一化层(LayerNorm)和固定阈值裁剪,已能有效控制梯度爆炸。

可作为扩展了解:

优化器名称自适应梯度裁剪机制核心思想特点与适用场景
Clipped Adam基于梯度范数动态调整裁剪阈值在Adam优化器中引入动态梯度裁剪,根据历史梯度幅值调整裁剪阈值。防止梯度爆炸,适合训练不稳定的大模型。
GradNorm多任务学习中自适应调整梯度幅值根据任务损失平衡各任务的梯度范数,动态缩放梯度。多任务学习,避免某些任务主导训练。
AGCL基于梯度方向一致性的自适应裁剪当梯度方向变化剧烈时增大裁剪阈值,方向稳定时减小阈值。非平稳目标函数(如强化学习)。
AGC自适应梯度裁剪(Adaptive Gradient Clipping)结合权重矩阵的谱范数和梯度范数,分层调整裁剪阈值。训练深度残差网络或Transformer。
SM3通过矩阵缩放实现隐式梯度裁剪在Adafactor优化器中引入结构化矩阵缩放,间接控制梯度幅值。内存高效,适合大规模稀疏模型(如NLP)。