深度学习中的优化算法串讲

本文默认您已了解深度学习中的梯度下降算法的基本原理,在对深度学习有基本认识的基础上。如果尚未了解,可以查看我另外的文章《李宏毅机器学习2021》,《Python神经网络编程》。
博客地址:https://tianjuewudi.gitee.io/

基本框架

首先优化算法的基本框架是:

定义当前待优化的参数为 θ t ∈ R d \theta_t \in R^d θtRd,损失函数为J(θ),学习率为 η \eta η,参数更新的框架为:

  1. 计算损失函数关于当前参数的梯度:
    g t = ∇ J ( θ t ) g_t = \nabla J(\theta_t) gt=J(θt)

  2. 根据历史梯度计算一阶和二阶动量:
    m t = ϕ ( g 1 , g 2 , . . . , g t ) V t = ψ ( g 1 , g 2 , . . . , g t ) m_t = \phi(g_1,g_2,...,g_t) \\ V_t = \psi(g_1,g_2,...,g_t) mt=ϕ(g1,g2,...,gt)Vt=ψ(g1,g2,...,gt)
    这里一阶动量是关于历史梯度的一阶函数,二阶动量是关于历史梯度的二阶函数。

  3. 计算当前时刻的下降梯度:
    Δ θ t = − η m t V t \Delta \theta_t = -\eta \frac{m_t}{\sqrt{V_t}} Δθt=ηVt mt
    所有的优化算法都是基于这个公式,只不过是一阶动量和二阶动量的计算方式不同。

  4. 根据下降梯度更新参数:
    θ t + 1 = θ t + Δ θ t \theta_{t+1} = \theta_t + \Delta \theta_t θt+1=θt+Δθt

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

随机梯度下降算法没有动量的概念,也没有考虑历史梯度,所以一阶动量等于当前时刻梯度 m t = g t m_t = g_t mt=gt,且二阶动量 V t = 1 V_t = 1 Vt=1。所以更新公式为:
Δ θ t = − η m t V t = − η g t \Delta \theta_t = -\eta \frac{m_t}{\sqrt{V_t}} = -\eta g_t Δθt=ηVt mt=ηgt
还有一个比较能让人看懂的等价的公式,这是我们平常的表达形式:
v n + 1 = − η d L d W n v_{n+1} = - \eta \frac{dL}{dW_n} vn+1=ηdWndL

W n + 1 = W n + v n + 1 W_{n+1} = W_n + v_{n+1} Wn+1=Wn+vn+1

这里说一下标准梯度下降和随机梯度下降的区别,标准梯度下降是把数据全部读取一遍然后全部计算Loss,然后进行的梯度更新,缺点是容易陷入鞍点,且一次计算量很大,数据有溢出的风险。随机梯度下降是读取每一个样本计算Loss后都进行一次更新,但是由于是分开训练更新的波动会较大,且一个epoch训练次数过多,会导致训练时间过长。小批量随机梯度下降是把50~256个样本为一批数据,一批批进行训练,这样就能很好平衡两者,一般都是采用这种方法。

这个算法的缺点是容易陷入局部最优。

Momentum

为了解决这个问题,我们引入一个惯性,使得这一时刻的梯度和以前时刻的梯度有关。
v n + 1 = a v n − η d L d W n v_{n+1} = av_{n} - \eta \frac{dL}{dW_n} vn+1=avnηdWndL

W n + 1 = W n + v n + 1 W_{n+1} = W_n + v_{n+1} Wn+1=Wn+vn+1

其中a是大于0小于1的值,这个方法在权重变换上加上了上一次的变化,可以让权重越过一些局部最小或者变化较为平稳的点,到达全局最优点。这个权重更新时是一定会冲过Loss的最小值到达另一边的,而且梯度要大一些后才往回运动。这样波动会比没有动量会小一些。

AdaGrad

学习率过大,在算法前期会加速学习,容易接近最优解,但是在后期会有较大波动,甚至出现损失函数的值围绕最小值徘徊,始终难以达到最优,因此我们的方法是在前期采用较大学习率,随着迭代次数的增加,学习率要逐渐衰减。这个时候AdaGrad算法的优势就出来了:
W n + 1 = W n − η 1 h n d L d W n W_{n+1} = W_n- \eta \frac{1}{\sqrt{h_n}} \frac{dL}{dW_n} Wn+1=Wnηhn 1dWndL

h n = h n − 1 + d L d W n ∗ d L d W n h_n = h_{n-1} + \frac{dL}{dW_n} * \frac{dL}{dW_n} hn=hn1+dWndLdWndL

这里面的权重的变化多除了一个 h \sqrt{h} h ,这就是用来改变学习率的。其中h为以前所有梯度的平方和,随着迭代次数的增加,h会越来越大,因此学习率会越来越小。如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率讲下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较少,那么该元素的学习率将下降较慢。侧面说明了二阶动量就是控制步长(学习率的)。但AdaGrad算法有个明显的缺点,由于 一直在累加按元素平方的梯度,学习率在迭代过程中一直在下降(或不变),所以即使后面的梯度突然变大了,但由于AdaGrad算法在迭代后期由于学习率过小,较难找到一个有用解。

Nesterov accelerated gradient (NAG)

带冲量的随机梯度下降算法的缺点是不能在Loss的最低点停住,一定会冲过头,必须经过多次反复才有可能找到最优点。那么我们可以提前预测是否要到达最优点。
W ~ = W n − 1 − η v n − 1 \tilde W = W_{n-1} - \eta v_{n-1} W~=Wn1ηvn1
这里对权重作了一个预测,用上一次的梯度对下一步到达的位置作了一个预测。
v n = α v n − 1 + η d L ( W ~ n ) d W ~ n v_n = \alpha v_{n-1} + \eta \frac{dL(\tilde W_n)}{d\tilde W_n} vn=αvn1+ηdW~ndL(W~n)

W n = W n − 1 − v n W_n = W_{n-1} - v_n Wn=Wn1vn

和Momentum类似,不过是用预测的梯度对现在的梯度进行更新,而不是取决于现在的梯度。如果预测的下一步已经在最低点另一侧了,那么符号相反,那么速度减小,结合设置的参数,能达到较好的效果。

RMSProp

为了解决AdaGrad到迭代后期梯度消失的问题,同样是这条公式:
W n + 1 = W n − η 1 h n d L d W n W_{n+1} = W_n- \eta \frac{1}{\sqrt{h_n}} \frac{dL}{dW_n} Wn+1=Wnηhn 1dWndL
现在我们对h进行修改,不是用以前所有梯度的平方和,而是用历史梯度的移动平均:
h n = β h n − 1 + ( 1 − β ) d L d W n ∗ d L d W n h_n = \beta h_{n-1} + (1 - \beta)\frac{dL}{dW_n} * \frac{dL}{dW_n} hn=βhn1+(1β)dWndLdWndL
其中β是大于0小于1的数,一般大概在0.9左右。当现在所处的梯度较小时,h不会增大而是朝着现在的方向慢慢减小,学习率增大,这样有利于冲出局部最优点和鞍点,并且能够在找到最优点后迅速收敛。当所处梯度较大时,慢慢增大,学习率减小,这样又不会使其冲过头,使得无法收敛。h初始通常加上一个小常数防止零除。但是在梯度较小时h过小会导致学习率过大而在最低点附近震荡,最优点附近坡度越陡表现越好。在 L = x 2 L = x^2 L=x2这样的函数中由于底部过于平坦,h下降速度过快,大于梯度的下降速度的时候,x下降到一定程度会越振荡越大,但不会无限增大,当 h = g t \sqrt{h} = g_t h =gt时,振荡幅度不变,振荡范围取决于学习率,如果学习率为0.01,则x在$\pm 0.005 之 间 振 荡 。 如 果 L o s s 函 数 底 部 比 较 陡 峭 , 如 0.005之间振荡。如果Loss函数底部比较陡峭,如 0.005LossL = x^3 则 则 g_t$下降的速度大于h下降的速度,学习率会一直减小到0,实现完全收敛。

AdaGrad AdaDelta

AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 。有意思的是,AdaDelta算法没有学习率这一超参数。
x t = x t − 1 + Δ x \boldsymbol{x}_t = \boldsymbol{x}_{t-1} + \Delta\boldsymbol{x} xt=xt1+Δx

Δ x = − E ( Δ x 2 ) t + ϵ E ( g 2 ) t + ϵ g t \Delta\boldsymbol{x} = - \sqrt{\frac{ E(\Delta\boldsymbol{x}^2)_t + \epsilon}{E(g^2)_t + \epsilon}} g_t Δx=E(g2)t+ϵE(Δx2)t+ϵ gt

E ( g 2 ) t = ρ E ( g 2 ) t − 1 + ( 1 − ρ ) g t ⊙ g t E(g^2)_t =\rho E({g}^2)_{t-1} + (1 - \rho) \boldsymbol{g}_t \odot \boldsymbol{g}_t E(g2)t=ρE(g2)t1+(1ρ)gtgt

E ( Δ x 2 ) t = ρ E ( Δ x 2 ) t − 1 + ( 1 − ρ ) Δ x t ⊙ Δ x t E(\Delta\boldsymbol{x}^2)_t = \rho E(\Delta\boldsymbol{x}^2)_{t-1} + (1 - \rho) \boldsymbol \Delta{x}_t \odot \boldsymbol \Delta{x}_t E(Δx2)t=ρE(Δx2)t1+(1ρ)ΔxtΔxt

g t g_t gt即这一时刻的梯度,其中的点乘是矩阵各个元素分别乘积,和我们理解的数与数相乘一致。 ϵ \epsilon ϵ是一个较小的常数,用来防止零除,或者在其他值为0时整体也不为0。其中: v 0 = 0 \boldsymbol{v}_0 = 0 v0=0 E ( g 2 ) t = 0 E(g^2)_t = 0 E(g2)t=0。这个算法和RMSProp英雄所见略同,但是学习率变成了动态调整的了,省去了人工调节超参数的麻烦,在x的变化较小的情况下,自动把学习率降低,有利于减小振荡,达到最优点。

Adam(Adaptive Moment Estimation)

Adam算法相当于AdaDelta和Momentum的合体。把上面一阶动量和二阶动量的计算方法都加进来。
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 = β 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

m t ^ = m t 1 − β 1 t \hat{m_t} = \frac{m_t}{1- \beta_1^t} mt^=1β1tmt

v t ^ = v t 1 − β 2 t \hat{v_t} = \frac{v_t}{1- \beta_2^t} vt^=1β2tvt

x t + 1 = x t − η v ^ t + ϵ m t ^ \boldsymbol{x}_{t+1} = x_t - \frac{\eta}{\sqrt{\hat v_t + \epsilon}} \hat{m_t} xt+1=xtv^t+ϵ ηmt^

β 1 \beta_1 β1 β 2 \beta_2 β2是梯度一阶矩估计值和二阶矩估计值的指数衰减速率,在0到1之间。每一步的有效学习率在一定范围变动。这个算法适用于大多数非凸环境的优化,是目前最流行的算法。之所以还要除以一项计算估计值是因为这样可以保证里面每一个梯度或者梯度的平方前面的参数之和为1!越往前的梯度前面的参数就越小,呈指数衰减。这就是一个指数加权移动平均的概念。这样随着梯度的降低,越来越解决最优点的同时,分子在不断减小,学习率降低,并保留一部分之前的冲量,让其能冲出鞍点。同时保留了分母中RMSProp的优点。

Adamax

Adamax是Adam的一种变体,此方法对学习率的上限提供了一个更简单的范围。公式上的变化如下:
n t = m a x ( β n t − 1 , ∣ g t ∣ ) n_t = max(\beta n_{t-1},|g_t|) nt=max(βnt1,gt)

Δ x = − m ^ t n t + ϵ ∗ η \Delta x = -\frac{\hat m_t}{n_t + \epsilon} * \eta Δx=nt+ϵm^tη

其中 n 0 = 0 n_0 = 0 n0=0,这样当梯度变化较为缓的情况下,我们取后面的梯度,当梯度变化较为剧烈的情况下,我们取较大的梯度。这样就能实现前期变化幅度大,后期变化幅度小。

Nadam

Nadam是Adam和NAG的合体,只需要把Adam的分子部分的梯度换成预测的梯度,把往往比Adam能产生更好的效果。其实也不一定换梯度,只需要在公式中的一个因素换成未来的因素即可。

深度学习中的优化算法串讲_第1张图片

你可能感兴趣的