优化器学习笔记
最近在学习模型的过程中,发现同一个模型,我使用了SGD进行优化,别人使用了Adam进行优化,所以认真学习了一下这些优化器的原理。
优化器不像损失函数,对不同问题有不同的函数可供选择。不同的优化器更像是在深度学习技术进步的过程中对前种优化器的不断改进。大致的演进过程可分为SGD-Momentum-NAG-AdaGrad-RMSProp-Adam-AdamW。
梯度下降
深度学习中,学习的核心就是计算参数对损失函数的梯度,让参数沿梯度的反方向进行更新,最终在一轮轮的测试中,网络的参数能够拟合输入与输出间的函数关系。梯度下降的公式为:
θ
t
+
1
=
θ
t
−
γ
g
t
\theta_{t+1}=\theta_t-\gamma g_t
θt+1=θt−γgt
其中t表示训练的轮数,
θ
\theta
θ表示参数,
γ
\gamma
γ表示学习率,
g
g
g表示梯度。
原始的梯度下降会把所有的输入数据一并丢入模型中,计算出平均损失,使用平均损失进行梯度下降工作。但是传统的梯度下降方法一旦遇到大量数据,计算量也会随之增大,这使得网络不易规模化。
SGD
SGD ( Stochastic Gradient Descent,随机梯度下降法)被提出就是为了解决这个问题。SGD会在原始数据中随机挑选一组,用这一组数据来近似代表全部数据计算损失值来优化神经网络的参数。根据证明,这种方法可以近似的等价于原始的梯度下降法。采用SGD的好处就是可以在保证精度的同时降低计算量,让神经网络可以应用于更大规模的数据上。
Momentum
在使用SGD的过程中,我们总是一个固定的速度沿梯度的反方向前进。但是如果梯度的反方向指向的是最优点,我们就应该加快前进的脚步。但如果梯度的方向突然改变,可能是因为我们在某个方向上已经跨越了最优点。此时我们不应该朝着梯度的反方向大踏步,反而应该走一下步,尝试更加靠近最优点。
如图所示,我们应当加快参数在横向上的前进速度,削减参数在纵向上的震荡。为了达成目标,Momentum引入了动量。添加了动量之后的公式如下:
θ
t
+
1
=
θ
t
−
γ
v
t
v
t
=
μ
v
t
−
1
+
g
t
\theta_{t+1}=\theta_t-\gamma v_t \\ v_t = \mu v_{t-1}+g_t
θt+1=θt−γvtvt=μvt−1+gt
其中,
g
g
g表示梯度,
v
v
v表示参数的动量。每次在计算动量时,值都会收到上轮动量值的影响,使方向相同的速度增加,方向相反的速度减少。
NAG
上述Momentum算法也有自己的问题。当参数接近最优点时,附加的动量会使参数直接冲过最优值点,模型需要额外数轮训练才能让参数停止振荡,停在最优点,整个过程可以看作是小球停留在U型容器底部的过程。
NAG(Nesterov’s Accelerated Gradient)算法就是为了解决这个问题而提出的,它的基本原理是在梯度更新之前往前看一步,让优化器可以预知未来的情况,从而对现在的梯度进行调整。它的公式为:
θ
t
+
1
=
θ
t
−
γ
v
t
v
t
=
μ
v
t
−
1
+
g
t
(
θ
t
−
γ
μ
v
t
−
1
)
\theta_{t+1}=\theta_t-\gamma v_t \\ v_t = \mu v_{t-1}+g_t(\theta_t-\gamma\mu v_{t-1})
θt+1=θt−γvtvt=μvt−1+gt(θt−γμvt−1)
其中,在梯度上增加的系数
θ
t
−
γ
μ
v
t
−
1
\theta_t-\gamma\mu v_{t-1}
θt−γμvt−1可以看作是当前参数按照上轮动量前进一步的结果。如果前进一步时参数冲过了头,这个系数就会把梯度往回拉一点,提前踩下了刹车,减少了振荡。
AdaGrad
AdaGrad(Adaptive Gradient)则是为了解决另一类问题,不同的参数最适合的学习率是不同的,假设有两个参数,一个参数梯度较大,另一个梯度较小。采用同样的学习率会导致梯度较大的参数出现震荡,梯度较小的参数前进幅度又太小,同时对所有参数来说,我们希望最开始训练时步子迈大些,训练一段时间,参数已经接近最优点,我们又希望前进的步子能小些。AdaGrad为每个参数新增了特征,特征中保留了参数的历史梯度。它的公式是:
θ
t
+
1
=
θ
t
−
γ
s
t
+
ϵ
g
t
s
t
=
s
t
−
1
+
g
t
2
\theta_{t+1}=\theta_t-\frac{\gamma}{\sqrt{s_t}+\epsilon} g_t \\ s_t=s_{t-1}+g_t^2
θt+1=θt−st+ϵγgtst=st−1+gt2
其中,
s
t
s_t
st是每个参数的历史梯度的平方和,实现历史梯度小的快些走,历史梯度大的走慢点。同时所有参数最后都能缓慢接近最优点,避免震荡。而这也正是它的缺点,在模型到达最优点之前,特征就可能到了一个极大的值,导致梯度消失,模型不再进一步优化。
RMSProp
RMSProp解决了前者的问题,它对特征进行加权处理,公式如下:
s
t
=
α
s
t
−
1
+
(
1
−
α
)
g
t
2
s_t=\alpha s_{t-1}+(1-\alpha)g_t^2
st=αst−1+(1−α)gt2
特征减少了来自历史梯度的影响,防止特征过早的极大化。
Adam
回顾全文,我们发现SGD随机抽取了部分数据作为整体数据的代表,减少了运算量,Momentum则加快了梯度下降的速度,减少了震荡,RMSProp作为AdaGrad的升级,优化了学习率。Adam则吸收了这些优化器的想法,进行了整合,公式如下:
θ
t
+
1
=
θ
t
−
γ
s
t
+
ϵ
v
t
s
t
=
β
2
s
t
−
1
+
(
1
−
β
2
)
g
t
2
v
t
=
β
1
v
t
−
1
+
(
1
−
β
1
)
g
t
\theta_{t+1}=\theta_t-\frac{\gamma}{\sqrt{s_t}+\epsilon}v_t \\ s_t = \beta_2s_{t-1}+(1-\beta_2)g_t^2 \\ v_t = \beta_1v_{t-1}+(1-\beta_1)g_t
θt+1=θt−st+ϵγvtst=β2st−1+(1−β2)gt2vt=β1vt−1+(1−β1)gt
AdamW
在PyTorch对优化器的实现中,作者对所有优化器的权重都进行了预处理——权重衰减(Weight Decay)。关于权重衰减的详情信息后面会写成学习笔记。对于Adam的实现,权重衰减系数在一开始就被加入到了梯度中,但梯度又会经历特征和动量的处理,这一处理过程大幅削减了权重衰减的作用。所以AdamW的作者将权重衰减放到了最后,直到梯度被用来处理参数时,权重衰减系数才会被加入。