Momentum
为了抑制SGD的震荡,SGD-M认为梯度下降过程可以加入惯性。可以简单理解为:当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。SGD-M全称是SGD with momentum,在SGD基础上引入了一阶动量: v t = γ v t − 1 + η ∇ J ( θ ) v_t=\gamma v_{t-1}+\eta \nabla J(\theta) vt=γvt−1+η∇J(θ)SGD-M参数更新公式如下,其中 η η η 是学习率, ∇ J ( θ ) ∇J(θ) ∇J(θ) 是当前参数的梯度: θ = θ − v t \theta=\theta-v_t θ=θ−vt一阶动量是各个时刻梯度方向的指数移动平均值,也就是说,t时刻的下降方向,不仅由当前点的梯度方向决定,而且由此前累积的下降方向决定。 γ γ γ 的经验值为0.9,这就意味着下降方向主要是此前累积的下降方向,并略微偏向当前时刻的下降方向。想象高速公路上汽车转弯,在高速向前的同时略微偏向,急转弯可是要出事的。 SGD 震荡且缓慢的沿着沟壑的下坡方向朝着局部最优点前进,如下图:
SGD-M能够加速SGD方法,并且能够减少震荡,如下图:
SGD-M通过加入了动量因素,缓解了SGD在局部最优点梯度为0,无法持续更新的问题和振荡幅度过大的问题。但是当局部沟壑比较深,动量加持用完了,依然会困在局部最优里来回振荡。
NAG
SGD 有一个困在局部最优的沟壑里面震荡的问题。想象一下你走到一个盆地,四周都是略高的小山,你觉得没有下坡的方向,那就只能待在这里了。可是如果你爬上高地,就会发现外面的世界还很广阔。因此,我们不能停留在当前位置去观察未来的方向,而要向前一步、多看一步、看远一些。
NAG全称Nesterov Accelerated Gradient,是在SGD、SGD-M的基础上的进一步改进,我们知道在时刻t的主要下降方向是由累积动量决定的,自己的梯度方向说了也不算,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,NAG不计算当前位置的梯度方向,而是计算如果按照累积动量走了一步,那个时候的下降方向: v t = γ v t − 1 + η ∇ θ J ( θ − γ v t − 1 ) v_t=\gamma v_{t-1}+\eta \nabla_{\theta} J(\theta-\gamma v_{t-1}) vt=γvt−1+η∇θJ(θ−γvt−1)NAG参数更新公式如下,其中 η η η 是学习率, ∇ θ J ( θ − γ v t − 1 ) ∇_θJ(θ−γv_{t−1}) ∇θJ(θ−γvt−1) 是当前参数的梯度: θ = θ − v t \theta=\theta-v_t θ=θ−vt然后用下一个点的梯度方向,与历史累积动量相结合,计算当前时刻的累积动量。
如上图,动量法首先计算当前梯度(图中的小蓝色向量),然后在更新累积梯度(updated accumulated gradient)方向上大幅度的跳跃(图中的大蓝色向量)。与此不同的是,NAG 首先在先前的累积梯度(previous accumulated gradient)方向上进行大幅度的跳跃(图中的棕色向量),评估这个梯度并做一下修正(图中的红色向量),这就构成一次完整的 NAG 更新(图中的绿色向量)。这种预期更新防止我们进行的太快,也带来了更高的相应速度,这在一些任务中非常有效的提升了 RNN 的性能。
NAG虽然有利于跳出当前局部最优的沟壑,寻找新的最优值,但收敛速度慢。
AdaGrad
SGD 系列的都没有用到二阶动量。二阶动量的出现,才意味着“自适应学习率”优化算法时代的到来。SGD 及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。因此,Adagrad 非常适用于稀疏数据。
Dean 等人发现 Adagrad 能够大幅提高 SGD 的鲁棒性,并在 Google 用其训练大规模神经网络,这其中就包括 在 YouTube 中学习识别猫。除此之外,Pennington 等人用 Adagrad 来训练 GloVe 词嵌入,因为罕见的词汇需要比常见词更大的更新。
AdaGrad算法就是将每一个参数的每一次迭代的梯度取平方累加后在开方,用全局学习率除以这个数,作为学习率的动态更新。对于不同的参数动态的采取不同的学习率,让目标函数更快的收敛。为了简洁,我们用 g t g_t gt 来表示t时刻的梯度, g t , i g_{t,i} gt,i 就是目标函数的偏导数 g t , i = ∇ θ J ( θ t , i ) g_{t,i}=\nabla_\theta J(\theta_{t,i}) gt,i=∇θJ(θt,i)SGD在在每个时刻t对参数 θ i θ_i θi 的更新为: θ t + 1 , i = θ t , i − η ⋅ g t , i \theta_{t+1,i}=\theta_{t,i}-\eta \cdot g_{t,i} θt+1,i=θt,i−η⋅gt,iAdagrad修改了 t t t 时刻对于每个参数 θ i θ_i θi 的学习率 η η η: θ t + 1 , i = θ t , i − η G t , i + ϵ ⋅ g t , i \theta_{t+1,i}=\theta_{t,i}-\displaystyle \frac {\eta} {\sqrt {G_{t,i}+\epsilon}} \cdot g_{t,i} θt+1,i=θt,i−Gt,i+ϵη⋅gt,i其中 G t ∈ R d × d G_t∈R^{d×d} Gt∈Rd×d 是对角矩阵,其中每一个对角元素 i i i, i i i 是 θ i θ_i θi 在时刻 t t t 的梯度平方和,一般为了避免分母为0,会在分母上加一个小的平滑项,用符号 ϵ ϵ ϵ 表示,通常为 1 0 − 8 10^{−8} 10−8 左右。因此 G t + ϵ \sqrt {G_t+ϵ} Gt+ϵ 恒大于0,而且参数更新越频繁,二阶动量越大,学习率就越小。有趣的是,如果去掉开方操作,算法性能会大幅下降。
AdaGrad在稀疏数据场景下表现非常好,此前的SGD及其变体的优化器主要聚焦在优化梯度前进的方向上,而AdaGrad首次使用二阶动量来关注学习率(步长),开启了自适应学习率算法的里程。大多数实现使用一个默认值 0.01 。由于 G t + ϵ \sqrt {G_t+ϵ} Gt+ϵ是单调递增的,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法学到必要的知识。
AdaDelta
由于AdaGrad单调递减的学习率变化过于激进,考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。Adadelta是 Adagrad 的扩展,旨在帮助缓解后者学习率单调下降的问题。
指数移动平均值大约就是过去一段时间的平均值,因此我们用这一方法来计算二阶累积动量: E [ g 2 ] t = γ E [ g 2 ] t − 1 + ( 1 − γ ) g t 2 E[g^2]_t=\gamma E[g^2]_{t-1}+(1-\gamma)g^2_t E[g2]t=γE[g2]t−1+(1−γ)gt2其中 γ γ γ 类似于冲量,大约是0.9。现在将SGD更新的参数变化向量 Δ θ t Δθ_t Δθt: Δ θ t = − η ⋅ g t , i \Delta \theta_t=-\eta \cdot g_{t,i} Δθt=−η⋅gt,i θ t + 1 = θ t + Δ θ t \theta_{t+1}=\theta_t+\Delta \theta_t θt+1=θt+Δθt在Adagrad中, Δ θ t Δθ_t Δθt为: Δ θ t = − η G t + ϵ ⋅ g t , i \Delta \theta_t=-\displaystyle \frac {\eta} {\sqrt{G_t+\epsilon}} \cdot g_{t,i} Δθt=−Gt+ϵη⋅gt,i而在AdaDelta,用 E [ g 2 ] t E[g^2]_t E[g2]t简单代替原来的对角矩阵 G t G_t Gt: Δ θ t = − η E [ g 2 ] t + ϵ ⋅ g t , i \Delta \theta_t=-\displaystyle \frac {\eta} {\sqrt{E[g^2]_t+\epsilon}} \cdot g_{t,i} Δθt=−E[g2]t+ϵη⋅gt,i将分母简记为RMS,表示梯度的均方根误差: Δ θ t = − η R M S [ g ] t ⋅ g t , i \Delta \theta_t=-\displaystyle \frac {\eta} {RMS[g]_t} \cdot g_{t,i} Δθt=−RMS[g]tη⋅gt,i更新中,定义指数衰减均值,代替梯度平方: E [ Δ θ 2 ] t = γ E [ Δ θ 2 ] t − 1 + ( 1 − γ ) Δ θ t 2 E[\Delta \theta^2]_t=\gamma E[\Delta \theta^2]_{t-1} +(1-\gamma) \Delta \theta^2_t E[Δθ2]t=γE[Δθ2]t−1+(1−γ)Δθt2均方根误差变为: R M S [ Δ θ ] t = E [ Δ θ 2 ] t + ϵ RMS[\Delta \theta]_t=\sqrt {E[\Delta \theta^2]_t+\epsilon} RMS[Δθ]t=E[Δθ2]t+ϵ由于 R M S [ Δ θ ] t RMS[\Delta \theta]_t RMS[Δθ]t是未知的,我们近似用前一个时间步RMS值来估计: Δ θ t = − R M S [ Δ θ ] t − 1 R M S [ g ] t ⋅ g t \Delta \theta_t=-\displaystyle \frac {RMS[\Delta\theta]_{t-1}} {RMS[g]_t} \cdot g_t Δθt=−RMS[g]tRMS[Δθ]t−1⋅gt θ t + 1 = θ t − Δ θ t \theta_{t+1}=\theta_t-\Delta \theta_t θt+1=θt−ΔθtAdaDelta不用设置学习率,因为其更新规则已经把它消除了。AdaDelta避免了二阶动量持续累积、导致训练过程提前结束的问题了。
RMSProp
RMSProp 算法修改 AdaGrad 以在非凸情况下表现更好,它改变梯度累积为指数加权的移动平均值,从而丢弃距离较远的历史梯度信息。RMSProp 与 Adadelta 的移动均值更新方式十分相似: E [ g 2 ] t = 0.9 E [ g 2 ] t − 1 + 0.1 g t 2 E[g^2]_t=0.9E[g^2]_{t-1}+0.1g^2_t E[g2]t=0.9E[g2]t−1+0.1gt2RMSProp参数更新公式如下,其中 η η η 是学习率, g t g_t gt 是当前参数的梯度: θ t + 1 = θ t − η E [ g 2 ] t + ϵ ⋅ g t \theta_{t+1}=\theta_t-\displaystyle \frac {\eta} {\sqrt {E[g^2]_t+\epsilon}} \cdot g_t θt+1=θt−E[g2]t+ϵη⋅gtRMSprop将学习速率除以梯度平方的指数衰减平均值。Hinton建议γ设置为0.9,默认学习率η为0.001。
Adam
Adam最开始是由 OpenAI 的 Diederik Kingma 和多伦多大学的 Jimmy Ba提出的。Adam使用动量和自适应学习率来加快收敛速度。SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量(二阶矩估计)。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。
SGD的一阶矩的估计,即mean均值: m t = β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t m_t=\beta_1 \cdot m_{t-1}+(1-\beta_1) \cdot g_t mt=β1⋅mt−1+(1−β1)⋅gt加上AdaDelta的二阶动量,二阶距的估计,即variance,和方差类似,都是二阶距的一种: v t = β 2 ⋅ v t − 1 + ( 1 + β 2 ) ⋅ g t 2 v_t=\beta_2 \cdot v_{t-1} +(1+\beta_2)\cdot g_t^2 vt=β2⋅vt−1+(1+β2)⋅gt2对mean和var进行校正,因为mean和var的初始值为0,所以它们会向0偏置,这样处理后会减少这种偏置影响。 m ^ t = m t 1 − β 1 t \hat m_t= \displaystyle \frac {m_t} {1-\beta_1^t} m^t=1−β1tmt v ^ t = v t 1 − β 2 t \hat v_t=\displaystyle \frac {v_t} {1-\beta_2^t} v^t=1−β2tvtAdam参数更新公式如下: θ t + 1 = θ t − η ⋅ m ^ t / ( v ^ t + ϵ ) \theta_{t+1}=\theta_t-\eta \cdot \hat m_t /(\sqrt {\hat v_t}+\epsilon) θt+1=θt−η⋅m^t/(v^t+ϵ)其中 η η η 是学习率, g t g_t gt 是当前参数的梯度, β 1 β_1 β1 为一阶矩估计的指数衰减率(如 0.9), β 2 β_2 β2二阶矩估计的指数衰减率(如 0.999),前者控制一阶矩估计,后者控制二阶矩估计。该超参数在稀疏梯度(如在 NLP 或计算机视觉任务中)中应该设置为接近 1 的数, β 1 t β^t_1 β1t 和 β 2 t β^t_2 β2t 是 β 1 β_1 β1和 β 2 β_2 β2的 t t t 次方。
Adam通过一阶动量和二阶动量,有效控制学习率步长和梯度方向,防止梯度的振荡和在鞍点的静止。实现简单,计算高效,对内存需求少,参数的更新不受梯度的伸缩变换影响,超参数具有很好的解释性,且通常无需调整或仅需很少的微调。更新的步长能够被限制在大致的范围内(初始学习率),能自然地实现步长退火过程(自动调整学习率),很适合应用于大规模的数据及参数的场景,适用于不稳定目标函数,而且适用于梯度稀疏或梯度存在很大噪声的问题。Adam在很多情况下算作默认工作性能比较优秀的优化器。
但是由于二阶动量是固定时间窗口内的累积,随着时间窗口的变化,遇到的数据可能发生巨变,使得 v t v_t vt 可能会时大时小,不是单调变化。这就可能在训练后期引起学习率的震荡,导致模型无法收敛。 由于Adam中的学习率主要是由二阶动量控制的,为了保证算法的收敛,可以对二阶动量的变化进行控制,避免上下波动。 v t = max ( β 2 ⋅ v t − 1 + ( 1 − β 2 ) g t 2 , v t − 1 ) v_t=\max(\beta_2 \cdot v_{t-1}+(1-\beta_2)g^2_t,v_{t-1}) vt=max(β2⋅vt−1+(1−β2)gt2,vt−1)自适应学习率算法可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。后期Adam的学习率太低,影响了有效的收敛。
AdaMax
AdaMax是Adam的一种变体,此方法对学习率的上限提供了一个更简单的范围。公式上的变化如下: v t = β 1 ⋅ m t − 1 + ( 1 + β 1 ) ⋅ g t v_t=\beta_1 \cdot m_{t-1} +(1+\beta_1)\cdot g_t vt=β1⋅mt−1+(1+β1)⋅gt我们可以将此推广到 l p l_p lp范数。 v t = β 2 p v t − 1 + ( 1 − β 2 p ) ∣ g t ∣ p v_t=\beta^p_2v_{t-1}+(1-\beta^p_2)|g_t|^p vt=β2pvt−1+(1−β2p)∣gt∣p当 p p p 非常大的时候通常会导致数值上的不稳定,这也是实际中通常使用 l 1 l_1 l1 和 l 2 l_2 l2 的原因。然而, l ∞ l_∞ l∞ 通常也会比较稳定。因此,作者提出了 AdaMax,显示了结合了 v t v_t vt 和 l ∞ l_∞ l∞ 也能够收敛到下面的更稳定的值。为了避免与 Adam 混淆,我们使用 u t u_t ut 来表示无限范数约束的 v t v_t vt: u t = β 2 ∞ v t − 1 + ( 1 − β 2 ∞ ) ∣ g t ∣ ∞ = m a x ( β 2 ⋅ v t − 1 , ∣ g t ∣ ) u_t=\beta^\infin_2v_{t-1}+(1-\beta^\infin_2)|g_t|^\infin=max(\beta_2 \cdot v_{t-1},|g_t|) ut=β2∞vt−1+(1−β2∞)∣gt∣∞=max(β2⋅vt−1,∣gt∣)现在可以将此加进 Adam 的更新规则里,用 u t u_t ut 代替 v ^ t + ϵ \sqrt {\hat v_t}+ϵ v^t+ϵ,得到 AdaMax 的更新规则: θ t + 1 = θ t − η ⋅ m ^ t / u t \theta_{t+1}=\theta_t-\eta \cdot \hat m_t / u_t θt+1=θt−η⋅m^t/ut其中 u t u_t ut 依赖于 max 操作,这不像 Adam 中的 m t m_t mt 和 v t v_t vt 那样容易趋于0,这也是我们不需要为 u t u_t ut 计算偏差纠正的原因。建议的默认值是 η = 0.002 , β 1 = 0.9 η=0.002,β_1=0.9 η=0.002,β1=0.9 和 β 2 = 0.999 β_2=0.999 β2=0.999。
Nadam
Adam可以被看作是融合了RMSProp和momentum,RMSprop 贡献了历史平方梯度的指数衰减的平均值 v t v_t vt,而动量则负责历史梯度的指数衰减平均值 m t m_t mt,Nadam在Adam的基础上加入了一阶动量的累积,即Nesterov + Adam = Nadam,为了把NAG融入到Adam中,我们需要修改momentum的项 m t m_t mt:
momentum更新规则为: g t = ∇ θ t J ( θ t ) g_t=\nabla_{\theta_t} J(\theta_t) gt=∇θtJ(θt) m t = γ m t − 1 + η g t m_t=\gamma m_{t-1} +\eta g_t mt=γmt−1+ηgt θ t + 1 = θ t − m t \theta_{t+1}=\theta_t-m_t θt+1=θt−mt其中 γ γ γ 是动量的衰减项, η η η 是步长, J J J 是目标函数。将 m t m_t mt 代入上面的第三个式子展开得到:
θ t + 1 = θ t − ( γ m t − 1 + η g t ) \theta_{t+1}=\theta_t-(\gamma m_{t-1}+\eta g_t) θt+1=θt−(γmt−1+ηgt)
动量包括在前面的动量向量方向上的一步和在当前梯度方向上的一步。
NAG允许我们在计算梯度之前通过动量步长更新参数,从而在梯度方向上执行更精确的步长。然后我们只需要更新梯度 g t g_t gt 来达到 NAG: g t = ∇ θ t J ( θ t − γ m t − 1 ) g_t=\nabla_{\theta_t}J(\theta_t-\gamma m_{t-1}) gt=∇θtJ(θt−γmt−1) m t = γ m t − 1 + η g t m_t=\gamma m_{t-1} + \eta g_t mt=γmt−1+ηgt θ t + 1 = θ t − m t \theta_{t+1}=\theta_t-m_t θt+1=θt−mtDozat 提出按以下方式来修改 NAG :与应用动量步骤两次不同的是,一次用来更新梯度 g t g_t gt 和一次用来更新参数 θ t + 1 θ_{t+1} θt+1,直接对当前参数应用一个向前看的(look-ahead)动量向量: g t = ∇ θ t J ( θ t ) g_t=\nabla_{\theta_t}J(\theta_t) gt=∇θtJ(θt) m t = γ m t − 1 + η g t m_t=\gamma m_{t-1} + \eta g_t mt=γmt−1+ηgt θ t + 1 = θ t − ( γ m t + η g t ) \theta_{t+1}=\theta_t-(\gamma m_t+ \eta g_t) θt+1=θt−(γmt+ηgt)注意我们现在不再使用如上面展开的动量更新规则中的先前动量向量 m t − 1 m_{t−1} mt−1,而是使用当前动量向量 m t m_t mt 来向前看,为了把Netsterov Momentum融入到Adam,我们把旧的动量向量用新的动量向量代替,Adam的更新规则为(注意不用修改 v ^ t \hat v_t v^t): m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t=\beta_1m_{t-1}+(1-\beta_1)g_t mt=β1mt−1+(1−β1)gt m ^ t = m t 1 − β 1 t \hat m_t= \displaystyle \frac {m_t} {1-\beta_1^t} m^t=1−β1tmt θ t + 1 = θ t − η v ^ t + ϵ m ^ t \theta_{t+1}=\theta_t-\displaystyle \frac {\eta} {\sqrt {\hat v_t} +\epsilon}\hat m_t θt+1=θt−v^t+ϵηm^t上式子展开为: θ t + 1 = θ t − η v ^ t − 1 + ϵ ( β 1 m t − 1 1 − β 1 t + ( 1 − β 1 ) g t 1 − β 1 t ) \theta_{t+1}=\theta_t- \displaystyle \frac {\eta} {\sqrt {\hat v_{t-1}}+\epsilon}(\frac {\beta_1 m_{t-1}} {1-\beta^t_1}+ \frac {(1-\beta_1)g_t} {1-\beta_1^t}) θt+1=θt−v^t−1+ϵη(1−β1tβ1mt−1+1−β1t(1−β1)gt) θ t + 1 = θ t − η v ^ t − 1 + ϵ ( β 1 m ^ t − 1 + ( 1 − β 1 ) g t 1 − β 1 t ) \theta_{t+1}=\theta_t- \displaystyle \frac {\eta} {\sqrt {\hat v_{t-1}}+\epsilon}(\beta_1 \hat m_{t-1}+ \frac {(1-\beta_1)g_t} {1-\beta_1^t}) θt+1=θt−v^t−1+ϵη(β1m^t−1+1−β1t(1−β1)gt)
这个方程跟momentum的展开式类似,用 m ^ t − 1 \hat m_{t−1} m^t−1替换 m ^ t − 2 \hat m_{t−2} m^t−2,Nadam的更新规则为: θ t + 1 = θ t − η v ^ t + ϵ ( β 1 m ^ t + ( 1 − β 1 ) g t 1 − β 1 t ) \theta_{t+1}=\theta_t- \displaystyle \frac {\eta} {\sqrt {\hat v_{t}}+\epsilon}(\beta_1 \hat m_{t}+ \frac {(1-\beta_1)g_t} {1-\beta_1^t}) θt+1=θt−v^t+ϵη(β1m^t+1−β1t(1−β1)gt)
AMSGrad
AMSGrad在ICLR 2018年被提出来,并获得了最佳论文。AMSGrad是一个随机梯度下降优化方法,它试图解决基于Adam的优化器的收敛问题。AMSGrad使用最大化过去平方梯度 v t v_t vt 来更新参数,而不是使用指数平均,这样就降低了指数衰减平均,造成重要历史信息快速丢失的影响。 m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t=\beta_1 m_{t-1}+(1-\beta_1)g_t mt=β1mt−1+(1−β1)gt v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t=\beta_2 v_{t-1}+(1-\beta_2)g_t^2 vt=β2vt−1+(1−β2)gt2上面的两个公式跟Adam是一样的,求的是一阶矩和二阶矩, g t g_t gt 是当前参数的梯度, β 1 β_1 β1 为一阶矩估计的指数衰减率, β 2 β_2 β2 是二阶矩估计的指数衰减率,前者控制一阶矩估计,后者控制二阶矩估计。 v ^ t = m a x ( v ^ t − 1 , v t ) \hat v_t=max(\hat v_{t-1},v_t) v^t=max(v^t−1,vt)上式求过去最大的平方梯度 v ^ t \hat v_t v^t,参数的更新公式如下: θ t + 1 = θ t − η v ^ t + ϵ m t \theta_{t+1}=\theta_t - \displaystyle \frac {\eta} {\sqrt {\hat v_t} + \epsilon} m_t θt+1=θt−v^t+ϵηmt从上面的公式可以看出,参数更新公式与Adam没有啥区别,但是求 v ^ t \hat v_t v^t 有区别。AMSGRAD不增加步长,避免了ADAM和RMSPROP算法的缺陷。
AdaBound
AdaBound算法训练速度比肩Adam,性能媲美SGD。SGD现在后期调优时还是经常使用到,但SGD的问题是前期收敛速度慢。SGD前期收敛慢的原因: SGD在更新参数时对各个维度上梯度的放缩是一致的,并且在训练数据分布极不均很时训练效果很差。而因为收敛慢的问题应运而生的自适应优化算法Adam、AdaGrad、RMSprop 等,但这些自适应的优化算法虽然可以在训练早期展现出快速的收敛速度,但其在测试集上的表现却会很快陷入停滞,并最终被 SGD 超过。
具体做法是对学习率进行动态裁剪,在这一设置下,在训练早期由于上下界对学习率的影响很小,算法更加接近于 Adam;而随着时间增长裁减区间越来越收紧,模型的学习率逐渐趋于稳定,在末期更加贴近于 SGD。 m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t=\beta_1 m_{t-1}+(1-\beta_1)g_t mt=β1mt−1+(1−β1)gt v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t=\beta_2 v_{t-1}+(1-\beta_2)g_t^2 vt=β2vt−1+(1−β2)gt2上面的两个公式跟Adam是一样的,求的是一阶矩和二阶矩, g t g_t gt 是当前参数的梯度, β 1 β_1 β1 为一阶矩估计的指数衰减率, β 2 β_2 β2 是二阶矩估计的指数衰减率,前者控制一阶矩估计,后者控制二阶矩估计。 V t = d i a g ( v t ) V_t=diag(v_t) Vt=diag(vt) η ^ t = c l i p ( α / V t , η l ( t ) , η u ( t ) ) \hat \eta_t=clip(\alpha/\sqrt {V_t},\eta_l(t),\eta_u(t)) η^t=clip(α/Vt,ηl(t),ηu(t)) η t = η ^ t / t \eta_t=\hat \eta_t/\sqrt {t} ηt=η^t/t上述3个公式是把学习率限定在 [ η l , η u ] [η_l,η_u] [ηl,ηu] 之间,这个公式是对SGD+momentum和Adam的一般化,其中 η l = η u = α ∗ η_l=η_u=α^∗ ηl=ηu=α∗ 时,就变成了SGD+momentum的公式了,因为学习率固定了参数只与一阶动量有关;如果 η l = 0 ηl=0 ηl=0 和 η u = ∞ ηu=∞ ηu=∞ 整个公式就变成了Adam,因为Adam既与一阶矩有关也与二阶矩有关。其中 η l t η^t_l ηlt 是一个非递减函数,从0逐渐的收敛到 α α α,而 η u t η_u^t ηut 是一个非递增函数,从 ∞ ∞ ∞ 逐渐收敛到 α α α。 θ t + 1 = θ t − η t ⨀ m t \theta_{t+1}=\theta_t-\eta_t \bigodot m_t θt+1=θt−ηt⨀mt在这种设置下,AdaBound在最开始表现的像Adam,因为最开始学习率的边界对更新公式影响很小,渐渐的表现的像SGD+momentum,因为学习率逐渐被限制住了。
AdamW
L2 正则化是减少过拟合的经典方法,它会向损失函数添加由模型所有权重的平方和组成的惩罚项,并乘上特定的超参数以控制惩罚力度。加入L2正则以后,损失函数就变为: L l 2 ( θ ) = L ( θ ) + 1 / 2 γ ∣ ∣ θ ∣ ∣ 2 L_{l_2}(\theta)=L(\theta)+1/2 \gamma||\theta||^2 Ll2(θ)=L(θ)+1/2γ∣∣θ∣∣2SGD就变为: θ t = θ t − 1 − ∇ L l 2 ( θ t − 1 ) = θ t − 1 − ∇ L ( θ t − 1 ) − γ θ t − 1 \theta_t=\theta_{t-1}-\nabla L_{l_2}(\theta_{t-1})=\theta_{t-1}-\nabla L(\theta_{t-1})-\gamma \theta_{t-1} θt=θt−1−∇Ll2(θt−1)=θt−1−∇L(θt−1)−γθt−1SGD+momentum就变为: θ t = θ t − 1 − γ m t − 1 − η ( ∇ L ( θ t − 1 ) + γ θ t − 1 ) \theta_t=\theta_{t-1}-\gamma m_{t-1}- \eta(\nabla L(\theta_{t-1})+\gamma \theta_{t-1}) θt=θt−1−γmt−1−η(∇L(θt−1)+γθt−1) m t = γ m t − 1 + η ( ∇ L ( θ t − 1 ) + γ θ t − 1 ) m_t=\gamma m_{t-1} +\eta(\nabla L(\theta_{t-1})+\gamma \theta_{t-1}) mt=γmt−1+η(∇L(θt−1)+γθt−1) m t = γ m t − 1 + η ( ∇ L ( θ t − 1 ) ) m_t=\gamma m_{t-1} +\eta(\nabla L(\theta_{t-1})) mt=γmt−1+η(∇L(θt−1))最后一项是正则项产生。但是 m t m_t mt 的计算有上面两种,都可以。adamw的论文验证 m t = γ m t − 1 + η ( ∇ L ( θ t − 1 ) m_t=γm_{t−1}+η(∇L(θ_{t−1}) mt=γmt−1+η(∇L(θt−1) 效果好。
Adam 就变成: m t = γ m t − 1 + η ( ∇ L ( θ t − 1 ) ) m_t=\gamma m_{t-1} +\eta(\nabla L(\theta_{t-1})) mt=γmt−1+η(∇L(θt−1)) v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ L ( θ t − 1 ) + γ θ t − 1 ) v_t=\beta_2 v_{t-1}+(1-\beta_2)(\nabla L(\theta_{t-1})+\gamma \theta_{t-1}) vt=β2vt−1+(1−β2)(∇L(θt−1)+γθt−1)
AdamW最终的形式: m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ L ( θ t − 1 ) m_t=\beta_1 m_{t-1}+(1-\beta_1) \nabla L(\theta_{t-1}) mt=β1mt−1+(1−β1)∇L(θt−1) v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ L ( θ t − 1 ) ) 2 v_t=\beta_2 v_{t-1}+(1-\beta_2)(\nabla L(\theta_{t-1}))^2 vt=β2vt−1+(1−β2)(∇L(θt−1))2 θ t = θ t − 1 − η ( 1 v ^ t + ϵ m ^ t − γ θ t − 1 ) \theta_t=\theta_{t-1}-\eta(\displaystyle \frac {1} {\sqrt {\hat v_t}+\epsilon} \hat m_t-\gamma \theta_{t-1}) θt=θt−1−η(v^t+ϵ1m^t−γθt−1)从上面的公式可以看出,AdamW本质上就是在损失函数里面加入了L2正则项,然后计算梯度和更新参数的时候都需要考虑这个正则项。AdamW使用在hugging face版的transformer中,BERT、XLNET、ELECTRA等主流的NLP模型,都是用了AdamW优化器。
RAdam
RAdam(Rectified Adam)是Adam优化器的一个变体,它引入了一项来纠正自适应学习率的方差,试图解决Adam的收敛性差的问题。作者认为收敛性差的原因是自适应学习率在早期的模型训练的过程中有较大的方差,这是因为训练使用的数据有限。为了减小方差,在训练前几个epoch的时候使用一个较小的学习率,这证明了热身启发式(warmup)的合理性。warmup的方式启发了RAdam来纠正方差问题。 g t = ∇ θ f t ( θ t − 1 ) g_t= \nabla_\theta f_t(\theta_{t-1}) gt=∇θft(θt−1) v t = 1 β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t=\displaystyle \frac {1} {\beta_2} v_{t-1}+(1-\beta_2)g_t^2 vt=β21vt−1+(1−β2)gt2 m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t=\beta_1 m_{t-1} +(1-\beta_1)g_t mt=β1mt−1+(1−β1)gt m ^ t = m t 1 − β 1 t \hat m_t=\displaystyle \frac {m_t} {1-\beta^t_1} m^t=1−β1tmt ρ t = ρ ∞ − 2 t β 2 2 1 − β 2 t \rho_t=\rho_{\infin}-\displaystyle \frac {2t\beta^2_2} {1-\beta^t_2} ρt=ρ∞−1−β2t2tβ22其中 m t m_t mt 是一阶矩(动量), v t v_t vt 是二阶矩(自适应学习率), η η η 是学习率。
当 ρ t > 4 ρ_t>4 ρt>4 的时候,自适应的学习率的计算公式为: l t = ( 1 − β 2 t ) / v t l_t=\sqrt {(1-\beta^t_2)/v_t} lt=(1−β2t)/vt方差矫正项计算公式为: r t = ( ρ t − 4 ) ( ρ t − 2 ) ρ ∞ ( ρ ∞ − 4 ) ( ρ ∞ − 2 ) ρ t r_t=\sqrt {\displaystyle \frac {(\rho_t -4)(\rho_t-2)\rho_{\infin}} {(\rho_{\infin}-4)(\rho_\infin -2)\rho_t}} rt=(ρ∞−4)(ρ∞−2)ρt(ρt−4)(ρt−2)ρ∞我们使用自适应的momentum方法来更新参数: θ t = θ t − 1 − α t r t m ^ t l t \theta_t=\theta_{t-1}-\alpha_tr_t \hat m_t l_t θt=θt−1−αtrtm^tlt如果方差不容易得到(tractable),我们采用下面的公式: θ t = θ t − 1 − α t m ^ t \theta_t=\theta_{t-1}-\alpha_t \hat m_t θt=θt−1−αtm^t