Pytorch常用的函数(八)常见优化器SGD,Adagrad,RMSprop,Adam,AdamW总结
-
在深度学习中,优化器的目标是通过调整模型的参数,最小化(或最大化)一个损失函数。
-
优化器使用梯度下降等迭代方法来更新模型的参数,以使损失函数达到最优或接近最优。
- 如下图,优化算法可分为一阶算法和二阶算法,常用的是一阶算法,今天主要介绍下一阶优化相关的优化器。
1 SGD优化算法
1.1 梯度下降法概述
1.1.1 梯度下降法概念
梯度下降法(Gradient Descent)是一种常用的优化算法,用于最小化(或最大化)一个函数。在机器学习和深度学习中,梯度下降法被广泛应用于训练模型,通过调整模型的参数来最小化损失函数。
θ = θ − α ∗ Δ J ( θ ) θ = θ - α * \Delta J(θ) θ=θ−α∗ΔJ(θ)
其中:
- θ表示要更新的参数向量或矩阵。
- α是学习率(learning rate),控制参数更新的步长。
- ∇J(θ)是损失函数J关于参数θ的梯度向量。
1.2 梯度下降法三个变种
1.2.1 批量梯度下降BGD
BGD是批量梯度下降(Batch Gradient Descent)的缩写,是一种基本的梯度下降优化算法。在批量梯度下降中,每次参数更新时使用整个训练数据集的梯度.
计算公式
图示
特点
- 全局最优解:由于BGD使用整个训练数据集的梯度,它有潜力收敛到全局最优解(如果存在)。
- 低效性:BGD的计算开销较大,因为在每次参数更新步骤中需要计算整个数据集的梯度。对于大规模系统和大型数据集,这可能会导致训练时间较长。
- 稳定性:由于使用整个数据集的梯度,BGD的参数更新相对稳定,不容易受到单个样本或噪声的影响。
- 需要注意的是,尽管BGD可能收敛到全局最优解,但它也可能陷入糟糕的局部最小值中。
1.2.2 随机梯度下降SGD
随机梯度下降(Stochastic Gradient Descent,SGD)是一种基于随机采样的梯度下降优化算法。与批量梯度下降(BGD)每次都使用整个训练数据集的梯度相比,SGD每次仅使用单个样本或一小批样本的梯度进行参数更新。
计算公式
图示
特点
- 更快的更新速度:由于每次更新只使用一个样本或一小批样本的梯度,SGD的参数更新速度比BGD更快。这使得SGD在大规模数据集上具有优势,特别是在迭代次数较少的情况下。
- 可能陷入局部最小值:由于使用随机采样的梯度,SGD的参数更新在每次迭代中都具有一定的随机性。这可能导致SGD在搜索空间中陷入局部最小值,而无法达到全局最优解。
- 然而,这种随机性也有助于SGD跳出局部最小值并继续搜索更好的解。
- 由于其随机性采样和快速更新的特点,SGD能够在多个局部最小值之间进行搜索,有助于找到更好的局部最小值或接近全局最优解。
1.2.3 小批量梯度下降Mini-BGD
Mini-Batch Gradient Descent(小批量梯度下降)是介于批量梯度下降(BGD)和随机梯度下降(SGD)之间的一种梯度下降优化算法。它在每次参数更新时使用一小批次的样本来计算梯度和更新参数.
计算公式
图示
特点
- 折中的更新速度:相比于BGD,Mini-Batch Gradient Descent的参数更新速度更快,因为每次使用的样本数量较少。这使得Mini-Batch Gradient Descent在大规模数据集上具有一定的优势。与SGD相比,Mini-Batch Gradient Descent的参数更新速度较慢,但相对更稳定。
- 梯度估计的抖动减少:与SGD相比,Mini-Batch Gradient Descent的梯度估计具有更小的抖动。由于使用的是一小批次的样本,梯度计算的结果更加平滑,减少了随机性带来的波动。
- 内存效率:相对于BGD需要存储整个训练数据集的梯度以及SGD需要存储单个样本的梯度,Mini-Batch Gradient Descent在内存使用方面更有效率。它只需存储每个小批量样本的梯度,使得在处理大型数据集时更加可行。
- 可调节的更新步长:Mini-Batch Gradient Descent的学习率可以根据需要进行调整,以控制参数更新的步长。这使得算法能够更好地平衡快速收敛和避免震荡之间的权衡。
注意:下文SGD一般为小批量梯度下降Mini-BGD
1.2 带动量的SGD(常用)
虽然随机梯度下降仍然是非常受欢迎的优化方法,但其学习过程有时会很慢。动量方法 (Polyak, 1964) 旨在加速学习, 特别是处理高曲率、小但一致的梯度, 或是带噪声的梯度。动量算法积累了之前梯度指数级衰减的移动平均, 并且继续沿该方向移动。
1.2.1 算法过程
实践中, α 的一般取值为0.5,0.9 和0.99。
1.2.2 算法图示
动态效果展示
1.2.3 特点
- 动量的积累:带动量的随机梯度下降利用一个动量变量来积累梯度的历史信息。在每次参数更新时,动量项会考虑前一次更新的方向和幅度。
- 加速参数更新:由于动量的引入,带动量的随机梯度下降可以加速参数更新的速度。当梯度在相同方向上持续增加时,动量项会逐渐增大,从而加速参数更新。这有助于在梯度方向上形成更大的动量,更快地接近最优解。
- 减少参数更新方向的震荡:动量项可以减少参数更新方向的震荡,特别是在存在噪声或不稳定梯度的情况下。通过考虑历史梯度的平均方向,动量可以平滑参数更新的路径,减少震荡现象。
1.2.4 作用
- 加速收敛:带动量的随机梯度下降可以加速模型的收敛速度。通过积累历史梯度信息,它能够更快地朝着梯度下降的方向移动,从而加速参数的更新过程。
- 跳出局部最小值:由于动量的引入,带动量的随机梯度下降能够在搜索空间中跳出局部最小值并继续寻找更好的解。通过考虑历史梯度的方向和幅度,动量项可以帮助算法在平坦区域上获得更大的动量,并有助于跳过局部极小点。
- 平滑参数更新路径:动量项可以减少参数更新方向的震荡。通过考虑历史梯度的平均方向,带动量的随机梯度下降可以平滑参数更新的路径,使得参数更新更加稳定。
1.3 NAG
1.3.1 算法原理
等价于 SGD with Nesterov Momentum,利用当前位置处先前的梯度值先做一个参数更新,然后在更新后的位置再求梯度,将此部分梯度然后跟之前累积下来的梯度值矢量相加,简单的说就是先根据之前累积的梯度方向模拟下一步参数更新后的值,然后将模拟后的位置处梯度替换动量方法中的当前位置梯度。
现在有一个预测后一步位置梯度的步骤,所以当在山谷附近时,预测到会跨过山谷时(跨过山谷后梯度方向会发生变化),该项梯度就会对之前梯度有个修正,相当于阻止了其跨度太大。
1.3.2 算法原理图
SGD with Momentum
NAG
1.3.3 算法详述
动量的方法,我们发现参数更新是基于两部分组成,一部分为当前位置的梯度,另一部分为前面累计下来的梯度值,参数更新方向就是将两者矢量相加的方向,但是我们会发现一个问题,当刚好下降到山谷附近时,如果这个时候继续以这样的方式更新参数,我们会有一个较大的幅度越过山谷,即:模型遇到山谷不会自动减弱更新的幅度。
1.4 Pytorch 中SGD的实现
python
torch.optim.SGD(params, # 优化器要优化的参数
lr=0.001, # 初始学习率
momentum=0, # 动量,一般设置为0.9
dampening=0,
weight_decay=0, # 权值衰减系数,也就是L2正则项的系数
nesterov=False, # 是否启用NAG
*,
maximize=False, # 默认是最小化loss
foreach=None,
differentiable=False
)
- SGD开启动量梯度时,额外的内存/显存消耗只有momentum_buffer,是1倍的模型参数量;
- 不开启动量梯度则没有额外内存/显存消耗。
2 AdaGrad优化算法
2.1 自适应学习率
- Adagrad(Adaptive Gradient)的核心思想是,深度模型带来的稀疏性,导致模型中一些参数可能频繁获得较大梯度,
另一些参数偶尔获得较大梯度,若采用统一学习率导致后者的更新会非常缓慢
。基于此,可以调节模型中不同参数的学习率,而不是用统一的学习率。 - 如果一个参数的历史累计梯度更新量大,则降低该参数的学习率;如果一个参数的历史累计梯度更新量小,则增大该参数的学习率。
2.2 AdaGrad 算法
- 因为梯度有正有负,所以对梯度的平方进行累计,然后再开根号。
- 为了避免分母为0,加了一项随机扰动 δ \delta δ。
- 可以看成对每个参数分别调节学习率,也可以看成调节每个参数的梯度。
2.3 AdaGrad优缺点
优点:
- 前期较小的时候, 分母较小,能够放大梯度;
- 后期较大的时候, 分母较大,能够约束梯度;
- 无需手动调整梯度;
- 这一方法在稀疏数据场景下表现非常好。
缺点:
- 仍依赖于人工设置一个全局学习率,一般采用默认值0.01;
- 中后期,
分母上梯度平方的累加将会越来越大
,分母会不断积累使 Δ θ t Δθ_t Δθt趋近于0,学习率就会收缩并最终会变得非常小使得训练提前结束
。
2.6 pytorch 中AdaGrad的实现
python
torch.optim.Adagrad(params,
lr=0.01, # 学习率
lr_decay=0, # 学习率衰减系数
weight_decay=0, # 权重衰减系数
initial_accumulator_value=0,
eps=1e-10, # 添加到分母以提高数值稳定性
foreach=None,
*,
maximize=False, # 默认最小化loss
differentiable=False
)
- Adagrad优化器实现中加入了学习率衰减机制、权重衰减机制;
- 该算法需要给每一个待更新的模型参数设置一个累计统计量 s t a t e _ s u m t − 1 state\sum{t-1} state_sumt−1 ,所以额外的内存/显存消耗是1倍的模型参数量。
- 论文:Adaptive Subgradient Methods for Online Learning and Stochastic Optimization
3 RMSProp优化算法
3.1 概述
- RMSProp(root mean square propagation)优化算法是Hinton提出的,也属于自适应梯度范畴。
- 指数移动平均EMA(Exponential Moving Average),是一种给予近期数据更高权重的平均方法。
区别于Adagrad之处是它采用了EMA方式来统计每个参数的最近的累计梯度量,所以多次迭代后不会导致模型参数更新缓慢
。- RMSProp适合处理非平稳目标 - 对于RNN效果很好。
- 不过,RMSProp依然依赖于全局学习率。
3.2 算法流程
- RMSProp算法是AdaGrad算法的改进,修改AdaGrad以在非凸条件下效果更好,解决了AdaGrad所面临的问题。
- RMSProp主要思想:使用指数加权移动平均的方法计算累积梯度,以丢弃遥远的梯度历史信息。
- 不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。
RMSProp 的标准形式
带Nesterov 动量的形式
- Hinton 建议设定 γ 为 0.9, 学习率 η 为 0.001。
3.3 pytorch 实现
python
torch.optim.RMSprop(params,
lr=0.01, # 学习率
alpha=0.99, # 平滑常数
eps=1e-08, # 添加到分母以提高数值稳定性
weight_decay=0, # 权重衰减
momentum=0, # 动量因子
centered=False, # 如果为True,则计算中心化的RMSProp,梯度将通过其方差的估计进行归一化
foreach=None,
maximize=False,
differentiable=False
)
- RMSProp优化器实现中还加入了权重衰减机制、动量梯度机制;
- centered参数,其主要是通过估计方差来对梯度进行归一化,主要操作就是让二阶动量去减去平均梯度的平方,这样会使得结果更加平稳;
- 该算法需要给每一个待更新的模型参数设置一个累计梯度统计量 v t v_t vt 、一个自适应梯度的动量 b t b_t bt ,所以额外的内存/显存消耗是2倍的模型参数量。
4 Adadelta
4.1 概述及算法流程
-
从时间线上来看,AdaGrad算法是由John Duchi等人在2011年提出的。RMSProp算法是由Hinton在2012年提出的,Adadelta算法由Matthew D. Zeiler同样在2012年提出。
-
我们已经知道,RMSProp是在AdaGrad的基础上引入了衰减量,从而解决不断地累加二阶动量导致最终学习率会接近于零以及训练提前终止的问题。
-
RMSProp优化器虽然可以对不同的权重参数自适应的改变学习率,但仍依赖于全局学习率。AdaDelta优化器对RMSProp算法进一步优化:AdaDelta算法额外维护一个状态变量 Δ x t Δx_t Δxt,并使用 R M S [ Δ x ] t RMS[Δx]_t RMS[Δx]t代替 RMSProp 中的学习率参数,
使AdaDelta优化器不需要指定学习率这个超参数
。 -
Adadelta在训练初中期,加速效果不错,很快;但是训练后期,反复在局部最小值附近抖动
算法流程如下:
4.2 pytorch 实现
- 可以看到与RMSProp的区别就是加入了 u t u_t ut
4.3 不同优化算法效果对比
- 从上图展现了不同算法在鞍点处的表现。
- 这里,SGD、SGD-M、NAG 都受到了鞍点的严重影响,尽管后两者最终还是逃离了鞍点;
- 而 Adagrad、RMSprop、Adadelta 都很快找到了正确的方向。
- 从上图中可以看到不同算法在损失面等高线图中的学习过程,它们均同同一点出发,但沿着不同路径达到最小值点。
- 其中 Adagrad、Adadelta、RMSprop 从最开始就找到了正确的方向并快速收敛;
- SGD 找到了正确方向但收敛速度很慢;
- SGD-M 和 NAG 最初都偏离了航道,但也能最终纠正到正确方向,SGD-M 偏离的惯性比 NAG 更大。
5 Adam 优化器(常用)
5.1 概述及算法实现流程
-
Adam是前述方法的集大成者。SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了。
-
Adam 算法即自适应矩估计(Adaptive Moment Estimation)方法。Adam算法的本质:其实就是Momentum+RMSProp的结合 ,然后再修正其偏差。
-
Adam通常被认为对超参数的选择相当鲁棒,同时相比于AdaGrad,不用存储全局所有的梯度,适合处理大规模数据。
-
特点:
- 结合了AdaGrad善于处理稀疏梯度和RMSProp善于处理非平稳目标的优点
- 对内存需求较小
- 为不同的参数计算不同的自适应学习率
- 也适用于大多非凸优化
- 适用于大数据集和高维空间
5.2 pytorch 实现
python
torch.optim.Adam(params,
lr=0.001, # 学习率
betas=(0.9, 0.999), # 平滑常数
eps=1e-08, # 添加到分母以提高数值稳定性
weight_decay=0, # 权重衰减,注意:这里加到梯度上
amsgrad=False, # 如果amsgrad为True,保留历史最大的,记为v_{max},每次计算都是用最大的v_{max},否则是用当前v_t
*,
foreach=None,
maximize=False,
capturable=False,
differentiable=False,
fused=None
)
5.3 效果展示
6 AdamW(常用)
6.1 算法原理
-
我们已经知道Adam是集成一阶动量和二阶动量的优化器。AdamW是在Adam的基础上加入了weight decay正则化,但是Adam中已经有正则化,那么两者有什么区别呢?
-
其实AdamW和Adam唯一的区别,就是weight decay的加入方式
。- 在Adam当中,weight decay是直接加入到梯度当中(如下图紫色部分);
- AdamW直接在权重上进行衰减(如下图绿色)。
-
人们发现,理论上更优的Adam算法,有时表现并不如SGD momentum好,尤其是在模型泛化性上。
-
我们知道,L2范数(也叫权重衰减,weight decay)有助于提高模型的泛化性能。
-
但是AdamW的作者证明,Adam算法弱化了L2范数的作用,所以导致了用Adam算法训练出来的模型泛化能力较弱。
-
具体来说,在Adam中,权重衰减的梯度是直接加在 g t g_t gt 上的,这就导致权重衰减的梯度也会随着 g t g_t gt 去除以分母。当梯度的平方和累积过大时,权重衰减的作用就会被大大削弱。
-
6.2 pytorch实现
python
torch.optim.AdamW(
params,
lr=0.001,
betas=(0.9, 0.999),
eps=1e-08,
weight_decay=0.01, # 这里直接在权重上进行衰减
amsgrad=False,
*,
maximize=False,
foreach=None,
capturable=False,
differentiable=False,
fused=None
)
6.3 Optimizer 收敛趋势对比图
参考: