"惯性助你越过局部低谷,但也可能冲过全局最优"
认识优化器(Optimizer)
相识
想象我们训练模型是为了寻找某个能解释训练数据、推理预测数据的真相,那么损失函数则为我们铺好了通往真相的道路。而优化器,便是我们沿着道路不断逼近真相时,使用的通行工具。
不同的道路,需要不同的通行工具。几乎没有人会从欧洲骑着自行车去南极洲考察,也几乎没有人为了去近在300米处的学校上学而购买飞机票......
通行工具多种多样,但基本上不会偏离以下配件:
- 骨架(必备):产生基本的速度、改变行驶的方向,如自行车
- 加速器(可选):额外地提高或降低速度,如机动车
- 调节器(可选):改变行驶方式以适配不同的道路,如越野车
深度学习中的优化器亦是如此。
优化器的配件:
- 骨架(必备):梯度下降
- 加速器(可选):动量
- 调节器(可选):自适应
相知
梯度下降(Gradient Descent)——优化器的骨架
梯度下降的发展:
- 起源(19世纪)
梯度下降的数学思想可追溯至法国数学家Augustin-Louis Cauchy。他在1847年提出最速下降法(Method of Steepest Descent),用于求解无约束优化问题,核心是通过负梯度方向迭代逼近函数极小值点。 - 发展与理论完善(20世纪)
随着计算机的诞生,梯度下降在数值优化领域得到广泛应用。1951年,H. Robbins和S. Monro提出随机逼近算法,为随机梯度下降(SGD)奠定基础。20世纪80年代,反向传播算法(Backpropagation)与梯度下降结合,成为训练神经网络的基石。 - 深度学习的爆发(21世纪)
梯度下降的变体(如Adam、RMSProp)在深度学习中大放异彩,解决了高维非凸优化问题,支撑了ResNet、Transformer等复杂模型的训练。
梯度下降在反向传播负责计算梯度。其数学原理为:通过最小化损失函数,调整神经网络参数(权重和偏置)。
梯度下降的一次具体流程:
- 梯度计算:将真实值、模型的预测值代入损失函数,解得梯度
- 参数更新:将旧参数减去梯度与学习率的乘积,得到新的模型参数
以MSE作为损失函数的线性回归为例:
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)——优化器的加速器
动量的简历:
- 起源(20世纪60年代)
动量方法最早由苏联数学家Boris Polyak在1964年提出,用于加速传统优化算法的收敛速度。其核心思想是模拟物理中的动量,通过积累历史梯度方向来增强参数更新的稳定性。 - 理论发展与改进
- 1983年,Yurii Nesterov提出Nesterov Accelerated Gradient(NAG),通过提前计算“未来位置”的梯度,进一步优化了动量方向。
- 20世纪90年代,动量被引入神经网络训练,与反向传播结合,显著提升了收敛效率。
- 深度学习的核心组件
动量成为现代优化器(如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采用的版本)
版本二(Tensorflow、Pytorch、JAX等主流框架采用的优化版本)
版本二中移除(1-β)系数的主要原因:
超参数解耦
- 学习率 (α) 与动量系数 (β) 解耦:
若保留 (1−β),实际更新量隐含了 α×(1−β) 的联合缩放效应。这会导致:- 调整 β 时需同步调整 α 以维持等效步长。
- 超参数搜索空间复杂度增加。
- 版本二的设计将 α 和 β 解耦,使得:
- α 直接控制整体更新幅度。
- β 单纯控制历史梯度记忆强度,物理意义更清晰。
动量的价值:
- 核心作用
动量通过累积历史梯度信息,平滑参数更新方向,从而:- 加速收敛:在梯度方向一致的区域增大步长。
- 减少震荡:抑制梯度噪声或高曲率区域的振荡。
- 关键贡献
- 逃离局部极小值:动量提供的“惯性”有助于跳出局部最优。
- 平衡探索与利用:自适应调整更新幅度,适应不同区域的梯度特性。
需注意动量的超参数敏感
- β 过大(如0.99)会导致更新惯性过强,可能错过最优解。
- β 过小(如0.5)会减弱动量效果,接近普通梯度下降。
引入动量后梯度下降的一次具体流程:
- 梯度计算:将真实值、模型的预测值代入损失函数,解得梯度
- 动量加权:将历史梯度于本次解出的梯度进行加权和,解得新梯度
- 参数更新:将旧参数减去梯度与学习率的乘积,得到新的模型参数
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)。
可能包括但原因:
- 计算开销:动态调整阈值需要额外计算梯度范数或矩阵谱范数,增加训练时间。
- 超参数复杂性:自适应裁剪引入新超参数(如EMA系数 β),调参成本高。
- 替代方案成熟:通过权重初始化、归一化层(LayerNorm)和固定阈值裁剪,已能有效控制梯度爆炸。
可作为扩展了解:
优化器名称 | 自适应梯度裁剪机制 | 核心思想 | 特点与适用场景 |
---|---|---|---|
Clipped Adam | 基于梯度范数动态调整裁剪阈值 | 在Adam优化器中引入动态梯度裁剪,根据历史梯度幅值调整裁剪阈值。 | 防止梯度爆炸,适合训练不稳定的大模型。 |
GradNorm | 多任务学习中自适应调整梯度幅值 | 根据任务损失平衡各任务的梯度范数,动态缩放梯度。 | 多任务学习,避免某些任务主导训练。 |
AGCL | 基于梯度方向一致性的自适应裁剪 | 当梯度方向变化剧烈时增大裁剪阈值,方向稳定时减小阈值。 | 非平稳目标函数(如强化学习)。 |
AGC | 自适应梯度裁剪(Adaptive Gradient Clipping) | 结合权重矩阵的谱范数和梯度范数,分层调整裁剪阈值。 | 训练深度残差网络或Transformer。 |
SM3 | 通过矩阵缩放实现隐式梯度裁剪 | 在Adafactor优化器中引入结构化矩阵缩放,间接控制梯度幅值。 | 内存高效,适合大规模稀疏模型(如NLP)。 |
Comments NOTHING