深度学习常用优化器
参考:
- https://cloud.tencent.com/developer/article/1872633
- https://www.cnblogs.com/guoyaohua/p/8542554.html
- https://zhuanlan.zhihu.com/p/377141061
- 论文
前置知识:梯度计算、指数加权平均
1. 背景
为深度学习模型选择合适的 optimizer 不是一项容易的任务。Pytorch、Tensorflow、Paddle 等深度学习库都提供了多种优化器,它们各有优缺点,选择合适的优化器能够提高训练效果、收敛速度等。因此,optimizer 是搭建、优化和部署深度学习模型过程中的关键一环。
2. 常用优化器
接下来,介绍几种常见的优化器。用 w w w 代表权重参数, g g g 代表梯度, η \eta η 为学习率。
2.1 基础
2.1.1 BGD (Batch Gradient Descent)
规则:计算整个数据集后执行一次梯度更新
θ = θ − η ⋅ ∇ θ J ( θ ) \theta=\theta-\eta \cdot \nabla_{\theta} J(\theta) θ=θ−η⋅∇θJ(θ)
缺点:训练非常慢。对于很大的数据集来说,可能会有相似的样本,BGD 在计算梯度时会出现冗余。此外还不能加入新数据实时更新模型。
2.1.2 SGD (Stochastic Gradient Descent)
规则:对每个样本进行一次梯度更新
θ = θ − η ⋅ ∇ θ J ( θ ; x ( i ) ; y ( i ) ) \theta=\theta-\eta \cdot \nabla_{\theta} J\left(\theta ; x^{(i)} ; y^{(i)}\right) θ=θ−η⋅∇θJ(θ;x(i);y(i))
优点:和 BGD 相比,能够解决冗余问题,训练速度比较快,并且可以新增样本。
缺点:SGD 的噪音较 BGD 要多,使得迭代过程中会出现频繁震荡。所以虽然训练速度加快了,但是稳定性也下降了。
2.1.3 MBGD (Mini-Batch Gradient Descent)
规则: 每次利用一小批样本进行梯度更新
θ = θ − η ⋅ ∇ θ J ( θ ; x ( i : i + n ) ; y ( i : i + n ) ) \theta=\theta-\eta \cdot \nabla_{\theta} J\left(\theta ; x^{(i: i+n)} ; y^{(i: i+n)}\right) θ=θ−η⋅∇θJ(θ;x(i:i+n);y(i:i+n))
优点:可以降低参数更新时的方差,收敛更稳定。并且,深度学习库对矩阵运算进行了高度优化,可以非常高效计算梯度。
(之后提及的 SGD 多指 MBGD 优化器)
2.1.4 局限性
a. 在最小化非凸损失函数时,陷入局部最小值,或者停留在鞍点。因为该点处的梯度接近于0,以上三种梯度更新方法都很难逃脱。有时候可以修改学习率来减缓这种情况的发生,但是选择合适的学习率十分困难。学习率太小会导致收敛时间过长,而学习率过大又会阻碍收敛导致损失函数在最小值附近波动甚至发散。先前的方法是根据预定义的时间表或者目标函数变化调整学习率。但是,这些计划和阈值必须预先定义,因此无法适应数据集的特征。
b. 采用相同学习率更新所有参数。如果数据集稀疏,可能并不想将所有模型特征更新到相同的程度,且对很少出现的特性执行较大的更新。
2.2 进阶
针对局限性 a,这里介绍了 Momentum 和 Nesterov Accelerated Gradient,对于局限性 b,介绍了 AdaGrad、RMSprop 和 Adam。
2.1.1 Momentum
假如我们最小化目标函数的任务,是从坡顶到坡底的过程,如下图所示。SGD 难以在沟壑中有效移动,会频繁震荡,沿着底部最优的方向缓慢下降,如下左图所示。当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,就会变小。
规则:在 SGD 中引入了 Momentum
如上右图所示,加入的这一项,**可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。**用如下公式计算,
m t = γ m t − 1 + ( 1 − γ ) g t m_{t}=\gamma m_{t-1}+(1-\gamma)g_t mt=γmt−1+(1−γ)gt
$ θ = θ − η ⋅ m t \theta=\theta-\eta\cdot m_{t} θ=θ−η⋅mt
其中,超参数 γ \gamma γ 一般取 0.9。
2.1.2 NAG (Nesterov Accelerated Gradient)
然而,从山上滚下来的球盲目地跟随斜坡是不尽如人意的。我们希望有一个更聪明的球,快要上坡时,就知道需要减速了,适应性会更好。
NAG 是一种能够给 Momentum 带来上述能力的方法,将 θ − γ v t − 1 \theta-\gamma v_{t-1} θ−γvt−1 近似为下一步参数会变成的值,计算该位置的梯度,用如下式子描述,
m t = γ m t − 1 + ( 1 − γ ) ∇ θ J ( θ − γ ν t − 1 ) m_{t}=\gamma m_{t-1}+(1-\gamma) \nabla_{\theta} J\left(\theta-\gamma \nu_{t-1}\right) mt=γmt−1+(1−γ)∇θJ(θ−γνt−1)
θ = θ − η v t \theta=\theta-\eta v_{t} θ=θ−ηvt
同样,超参数 γ \gamma γ 取 0.9。
Momentum 和 NAG 的区别,如下图所示,
-
蓝色向量是 Momentum。首先计算当前的梯度值 η ∇ θ J ( θ ) \eta\nabla_{\theta} J(\theta) η∇θJ(θ),然后再计算累计的动量 γ v t − 1 \gamma v_{t-1} γvt−1,因此会有一个大跳跃,
-
其余向量都是描述 NAG。首先在先前累积梯度(棕色向量) γ v t − 1 \gamma v_{t-1} γvt−1 方向上前进一大步,再计算当前位置的梯度值 η ∇ θ J ( θ − γ ν t − 1 ) \eta \nabla_{\theta} J\left(\theta-\gamma \nu_{t-1}\right) η∇θJ(θ−γνt−1) 做一个修正(红色向量),从而完成 NAG 更新(绿色向量)。
这个具有预见性的更新参数方法,防止梯度前进得太快,同时增强了算法的适应能力,对于 RNN 的性能提升有着重要的意义。
目前为止,可以做到在更新模型权重时顺应损失函数的梯度进行调整,使 SGD 减弱震荡,加快收敛。接下来,就是希望根据每个参数的重要性,对不同的参数进行不同程度的更新。
2.1.3 AdaGrad (Adaptive Gradient Algorithm)
之前提到的优化器均是以相同的学习率去更新权重参数,而深度学习模型中往往涉及大量的参数,不同参数的更新频率也有所区别。因此, A d a G r a d AdaGrad AdaGrad 采用了自适应的学习率,可以对低频的参数做较大的更新,对高频的做较小的更新。这样,对于稀疏数据收敛更快,很好地提高了 S G D SGD SGD 的鲁棒性,如下所示为计算每个参数更新,
θ t + 1 , i = θ t , i − η G t , i i + ϵ ⋅ g t , i \theta_{t+1, i}=\theta_{t, i}-\frac{\eta}{\sqrt{G_{t, i i}+\epsilon}} \cdot g_{t, i} θt+1,i=θt,i−Gt,ii+ϵη⋅gt,i
其中, g t , i g_{t,i} gt,i 为 t t t 时刻参数 θ t , i \theta_{t,i} θt,i 的梯度,这里的学习率随着时间 t t t 和参数 i i i 发生变化。 G t , i i G_{t,ii} Gt,ii 是到 t t t 时刻为止,梯度 g i g_i gi 的平方和。
推广到 m m m 维,得到如下式子
[ θ t + 1 , 1 θ t + 1 , 2 ⋮ θ t + 1 , m ] = [ θ t , 1 θ t , 2 ⋮ θ t , m ] − η ( [ ε 0 ⋯ 0 0 ε ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ ε ] + [ G t , 11 0 ⋯ 0 0 G t , 22 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ G t , m m ] ) − 1 / 2 [ g t , 1 g t , 2 ⋮ g t , m ] \left[\begin{array}{c} \theta_{t+1,1} \\ \theta_{t+1,2} \\ \vdots \\ \theta_{t+1,m} \end{array}\right]=\left[\begin{array}{c} \theta_{t,1} \\ \theta_{t,2} \\ \vdots \\ \theta_{t,m} \end{array}\right]-\eta\left(\left[\begin{array}{cccc} \varepsilon & 0 & \cdots & 0 \\ 0 & \varepsilon & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & \varepsilon \end{array}\right]+\left[\begin{array}{cccc} G_{t,11} & 0 & \cdots & 0 \\ 0 & G_{t,22} & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & G_{t,mm} \end{array}\right]\right)^{-1 / 2}\left[\begin{array}{c} g_{t,1} \\ g_{t,2} \\ \vdots \\ g_{t,m} \end{array}\right] ⎣⎢⎢⎢⎡θt+1,1θt+1,2⋮θt+1,m⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡θt,1θt,2⋮θt,m⎦⎥⎥⎥⎤−η⎝⎜⎜⎜⎛⎣⎢⎢⎢⎡ε0⋮00ε⋮0⋯⋯⋱⋯00⋮ε⎦⎥⎥⎥⎤+⎣⎢⎢⎢⎡Gt,110⋮00Gt,22⋮0⋯⋯⋱⋯00⋮Gt,mm⎦⎥⎥⎥⎤⎠⎟⎟⎟⎞−1/2⎣⎢⎢⎢⎡gt,1gt,2⋮gt,m⎦⎥⎥⎥⎤
简记为, θ t + 1 = θ t − η G t + ϵ g t \theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{G_{t}+\epsilon}}g_{t} θt+1=θt−Gt+ϵηgt
优点:无需手动调整学习率。
缺点:梯度平方的累加和会越来越大,导致学习率变小,模型也就提前更新不动了。
接下来的算法就是为了解决这个问题。
2.1.4 AdaDelta
和 AdaGrad 相比,不在计算某个参数历史所有梯度的平方和,而是计算梯度平方的指数加权平均,梯度缩放不太积极了(缩放系数增大较缓)。如下所示,在 t t t 时刻的均值 E [ g 2 ] t E\left[g^{2}\right]_{t} E[g2]t 取决于先前计算好的均值和当前的梯度平方,
E [ g 2 ] t = γ E [ g 2 ] t − 1 + ( 1 − γ ) g t 2 E\left[g^{2}\right]_{t}=\gamma E\left[g^{2}\right]_{t-1}+(1-\gamma) g_{t}^{2} E[g2]t=γE[g2]t−1+(1−γ)gt2
一般情况下,超参数 γ \gamma γ 取 0.9。
那么,就可以得到如下的更新公式,(也就是接下来要讲的 R M S p r o p RMSprop RMSprop)
θ t + 1 = θ t − η E [ g 2 ] t + ϵ g t \theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{E\left[g^{2}\right]_{t}+\epsilon}} g_{t} θt+1=θt−E[g2]t+ϵηgt
分母是梯度的均方根 R M S RMS RMS,每次梯度的更新量可以简记为,
Δ θ t = − η R M S [ g ] t g t \Delta \theta_{t}=-\frac{\eta}{R M S[g]_{t}} g_{t} Δθt=−RMS[g]tηgt
但作者指出,以上谈到的所有优化器存在单位不匹配的问题,因此定义了另一个指数衰减均值,由参数变化量的平方计算:
E [ Δ θ 2 ] t = γ E [ Δ θ 2 ] t − 1 + ( 1 − γ ) Δ θ t 2 E\left[\Delta \theta^{2}\right]_{t}=\gamma E\left[\Delta \theta^{2}\right]_{t-1}+(1-\gamma) \Delta \theta_{t}^{2} E[Δθ2]t=γE[Δθ2]t−1+(1−γ)Δθt2
参数变化量的均方根为,
R M S [ Δ θ ] t = E [ Δ θ 2 ] t + ϵ R M S[\Delta \theta]_{t}=\sqrt{E\left[\Delta \theta^{2}\right]_{t}+\epsilon} RMS[Δθ]t=E[Δθ2]t+ϵ
利用 R M S [ Δ θ ] t − 1 R M S[\Delta \theta]_{t-1} RMS[Δθ]t−1 替换先前的更新中的学习率 η \eta η,得到 AdaDelta 的更新规则,
θ t + 1 = θ t − R M S [ Δ θ ] t − 1 R M S [ g ] t g t \theta_{t+1} = \theta_{t}-\frac{R M S[\Delta \theta]_{t-1}}{R M S[g]_{t}} g_{t} θt+1=θt−RMS[g]tRMS[Δθ]t−1gt
因此不需要设置学习率了。
2.1.5 RMSprop
R M S p r o p RMSprop RMSprop 和 A d a D e l t a AdaDelta AdaDelta 都是为了解决 A d a G r a d AdaGrad AdaGrad 学习率急剧下降问题的。与 A d a D e l t a AdaDelta AdaDelta 的第一种形式相同,某一维度的梯度比较大,则其均方根 R M S RMS RMS 就大(学习率越小),某一维度的梯度比较小,则其均方根 R M S RMS RMS 就小,这样就解决了学习率急剧下降的问题,同时也保证了各维度梯度都在一个量级。允许使用一个更大的学习率,用如下式子表示,
E [ g 2 ] t = γ E [ g 2 ] t − 1 + ( 1 − γ ) g t 2 E\left[g^{2}\right]_{t}=\gamma E\left[g^{2}\right]_{t-1}+(1-\gamma) g_{t}^{2} E[g2]t=γE[g2]t−1+(1−γ)gt2
θ t + 1 = θ t − η E [ g 2 ] t + ϵ g t \theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{E\left[g^{2}\right]_{t}+\epsilon}} g_{t} θt+1=θt−E[g2]t+ϵηgt
一般情况下,超参数 γ \gamma γ 取 0.9。
2.1.6 Adam
这个算法相当于 R M S p r o p RMSprop RMSprop + M o m e n t u m Momentum Momentum
除了像 A d a D e l t a AdaDelta AdaDelta 和 R M S p r o p RMSprop RMSprop 一样采用过去梯度平方的指数加权平均 ,也像 M o m e n t u m Momentum Momentum 一样保持了过去梯度的指数加权平均:
m t = β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t m_{t}=\beta_{1} \cdot m_{t-1}+\left(1-\beta_{1}\right) \cdot g_{t} mt=β1⋅mt−1+(1−β1)⋅gt
$v_{t}=\beta_{2} \cdot v_{t-1}+\left(1-\beta_{2}\right) \cdot g_{t}^{2} $
如果 m t m_t mt 和 v t v_t vt 被初始化为 0 向量,那它们就会偏向于 0 ,需要做偏差校正,来抵消这些偏差,
$\hat{m}{t}=\frac{m{t}}{1-\beta_{1}^{t}} $
v ^ t = v t 1 − β 2 t \hat{v}_{t}=\frac{v_{t}}{1-\beta_{2}^{t}} v^t=1−β2tvt
所以,最后的参数更新计算如下所示,
θ t + 1 = θ t − η v ^ t + ϵ m ^ t \theta_{t+1}=\theta_{t}-\frac{\eta}{\sqrt{\hat{v}_{t}+\epsilon}} \hat{m}_{t} θt+1=θt−v^t+ϵηm^t
一般情况下,超参数 β 1 \beta_1 β1 和 β 2 \beta_2 β2 分别取 0.9 和 0.999。
A d a m Adam Adam 将 A d a G r a d AdaGrad AdaGrad、 R M S p r o p RMSprop RMSprop 和 M o m e n t u m Momentum Momentum 方法结合到一起。每一步的方向由梯度的指数加权平均决定,步长由梯度平方的指数加权平均和超参数 η \eta η (学习率)决定。此外,类似于 R M S p r o p RMSprop RMSprop, A d a m Adam Adam 对梯度的每个维度进行重新缩放。 A d a m Adam Adam 和 R M S p r o p RMSprop RMSprop(或 A d a G r a d AdaGrad AdaGrad)之间一个主要区别是对 m t m_t mt 和 v t v_t vt 的零偏差进行了矫正。
A d a m Adam Adam 以少量超参数微调就能获得良好的性能著称,之后多数论文都采用了该优化器。