在深度学习里,目标函数通常是训练数据集中有关各个样本的损失函数的平均。设 f i ( x ) f_{i}(x) fi(x) 是有关索引为 i i i 的训练数据样本的损失函数, n n n 是训练数据样本数, x x x 是模型的参数向量,那么目标函数定义为
f ( x ) = 1 n ∑ i = 1 n f i ( x ) f(\boldsymbol{x})=\frac{1}{n} \sum_{i=1}^{n} f_{i}(\boldsymbol{x}) f(x)=n1i=1∑nfi(x)
目标函数在 x x x 处的梯度计算为
∇ f ( x ) = 1 n ∑ i = 1 n ∇ f i ( x ) \nabla f(\boldsymbol{x})=\frac{1}{n} \sum_{i=1}^{n} \nabla f_{i}(\boldsymbol{x}) ∇f(x)=n1i=1∑n∇fi(x)
梯度下降公式为:
x ← x − η ∇ f ( x ) \boldsymbol{x} \leftarrow \boldsymbol{x}-\eta \nabla f(\boldsymbol{x}) x←x−η∇f(x)
如果使⽤用梯度下降,每次要计算所有样本的损失平均,每次自变量迭代的计算开销为 O ( n ) O(n) O(n),它随着 n n n 线性增长。因此,当训练数据样本数很大时,梯度下降每次迭代的计算开销很高
每次选择一个样本来计算梯度,并更新
x ← x − η ∇ f i ( x ) \boldsymbol{x} \leftarrow \boldsymbol{x}-\eta \nabla f_{i}(\boldsymbol{x}) x←x−η∇fi(x)
小批量随机梯度下降通过重复采样或者不重复采样 B \mathcal{B} B 个样本得到一个小批量样本集。
g t ← ∇ f B t ( x t − 1 ) = 1 ∣ B ∣ ∑ i ∈ B t ∇ f i ( x t − 1 ) \boldsymbol{g}_{t} \leftarrow \nabla f_{\mathcal{B}_{t}}\left(\boldsymbol{x}_{t-1}\right)=\frac{1}{|\mathcal{B}|} \sum_{i \in \mathcal{B}_{t}} \nabla f_{i}\left(\boldsymbol{x}_{t-1}\right) gt←∇fBt(xt−1)=∣B∣1i∈Bt∑∇fi(xt−1)
x t ← x t − 1 − η t g t \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\eta_{t} \boldsymbol{g}_{t} xt←xt−1−ηtgt
小批量随机梯度在每个迭代周期的耗时介于梯度下降和随机梯度下降的耗时之间。
小结
梯度下降法相关的优化方法容易产生震荡,且容易被困在鞍点,迟迟不能到达全局最优值
考虑目标函数为 f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(x) = 0.1 x_{1}^{2} + 2x_2^2 f(x)=0.1x12+2x22,这里 x 1 x_1 x1的系数和 x 2 x_2 x2的系数不在同一个数量级,使用学习率为 0.4 时自变量的迭代轨迹如下:
可以看到,同一位置上,目标函数在竖直方向( x 2 x_2 x2 轴⽅方向)比在水平方向( x 1 x_1 x1 轴方向)的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢
我们试着将学习率调得稍大一点,此时自变量在竖直方向不断越过最优解并逐渐发散
v t ← γ v t − 1 + η t g t x t ← x t − 1 − v t \begin{array}{l} \boldsymbol{v}_{t} \leftarrow \gamma \boldsymbol{v}_{t-1}+\eta_{t} \boldsymbol{g}_{t} \\ \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\boldsymbol{v}_{t} \end{array} vt←γvt−1+ηtgtxt←xt−1−vt
其中, g t g_t gt 表示一个batch的样本的梯度,在时间步0动量法创建速度 v 0 = 0 v_0=0 v0=0, η t \eta_t ηt是学习率,动量超参数 γ \gamma γ 满足 0 ≤ γ < 1 0 \leq\gamma<1 0≤γ<1
动量法原理
我们知道 y t = γ y t − 1 + ( 1 − γ ) x t y_{t}=\gamma y_{t-1}+(1-\gamma) x_{t} yt=γyt−1+(1−γ)xt 代表 y t y_t yt 是 x t x_t xt 的指数加权平均,我们常常将 y t y_t yt 看作是对最近 1 / ( 1 − γ ) 1/(1-\gamma) 1/(1−γ) 个时间步的 x t x_t xt 值的加权平均。例如,当 γ = 0.95 \gamma=0.95 γ=0.95 时, y t y_t yt 可以被看作对最近20个时间步的 x t x_t xt 值的加权平均;
我们对动量法的速度变量做变形
v t ← γ v t − 1 + ( 1 − γ ) ( η t 1 − γ g t ) \boldsymbol{v}_{t} \leftarrow \gamma \boldsymbol{v}_{t-1}+(1-\gamma)\left(\frac{\eta_{t}}{1-\gamma} \boldsymbol{g}_{t}\right) vt←γvt−1+(1−γ)(1−γηtgt)
相比于小批量随机梯度下降, 动量法在每个时间步的自变量更新量近似于将最近 1 / ( 1 − γ ) 1/(1-\gamma) 1/(1−γ) 个时间步的普通更新量做了指数加权移动平均后再除以 1 − γ 1-\gamma 1−γ。所以,在动量法中,自变量在各个方向上的移动幅度不仅取决当前梯度,还取决于过去的各个梯度在各个方向上是否一致。
在2.1节中提到的例子,使用动量法后,在x轴方向上(向右)每一步的梯度方向都大致相同(向右)所以指数加权平均后也向右,而在y轴方向上,每一步梯度方向时上时下,方向不同,指数加权平均后总体的跳动幅度就会减小,这样的话,在x轴上更新加快,在y轴上震荡减缓,总体上迭代更新更快
小结
使用动量法梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。
在之前介绍过的优化算法中,目标函数自变量的每一个元素在相同时间步都使⽤用同一个学习率来自我迭代,如下所示:
x 1 ← x 1 − η ∂ f ∂ x 1 , x 2 ← x 2 − η ∂ f ∂ x 2 x_{1} \leftarrow x_{1}-\eta \frac{\partial f}{\partial x_{1}}, \quad x_{2} \leftarrow x_{2}-\eta \frac{\partial f}{\partial x_{2}} x1←x1−η∂x1∂f,x2←x2−η∂x2∂f
上一节中我们看到当 x 1 x_1 x1 和 x 2 x_2 x2 的梯度值有较大差别时,需要选择足够小的学习率使得自变量
在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能
本节AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题
s t ← s t − 1 + g t ⊙ g t \boldsymbol{s}_{t} \leftarrow \boldsymbol{s}_{t-1}+\boldsymbol{g}_{t} \odot \boldsymbol{g}_{t} st←st−1+gt⊙gt
x t ← x t − 1 − η s t + ϵ ⊙ g \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\frac{\eta}{\sqrt{\boldsymbol{s}_{t}+\epsilon}} \odot \boldsymbol{g} xt←xt−1−st+ϵη⊙g
Adagrad算法如上式所示,首先他会使用一个小批量随机梯度 g t g_t gt 将元素平方后累加到变量 s t s_t st 。其中 s 0 = 0 s_0 = 0 s0=0, ⊙ \odot ⊙表示按元素相乘;接着,将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下,其中 η \eta η 是学习率, ϵ \epsilon ϵ是非常小的常数,一般为 1 0 − 6 10^{-6} 10−6,这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
小批量随机梯度按元素平方的累加变量 s t s_t st 出现在学习率的分母项中。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢
缺点
由于 s t s_t st 一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低,当学习率在迭代早期降得较快且当前解依然不佳时, AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解
小结
Adagrad 调整学习率时分母上的变量一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时, AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。RMSprop就是为了解决这个问题
不同于Adagrad算法直接将 t t t 时刻前所有的梯度 g t g_t gt平方后累加到 s t s_t st,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数 0 ≤ γ < 1 0\leq \gamma < 1 0≤γ<1, RMSProp算法在时间步 t t t 计算
s t ← γ s t − 1 + ( 1 − γ ) g t ⊙ g t \boldsymbol{s}_{t} \leftarrow \gamma \boldsymbol{s}_{t-1}+(1-\gamma) \boldsymbol{g}_{t} \odot \boldsymbol{g}_{t} st←γst−1+(1−γ)gt⊙gt
和AdaGrad算法一样, RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量
x t ← x t − 1 − η s t + ϵ ⊙ g t \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\frac{\eta}{\sqrt{s_{t}+\epsilon}} \odot \boldsymbol{g}_{t} xt←xt−1−st+ϵη⊙gt
其中 η \eta η 是学习率, ϵ \epsilon ϵ是很小的常数,一般取 1 0 − 6 10^{-6} 10−6,因为 s t s_t st 是前 t t t 个时间步的指数加权平均,因此自变量每个元素的学习率在迭代过程中就不再一直降低
AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进
AdaDelta算法也像RMSProp算法一样,使用了小批量随机梯度 g t g_t gt按元素平方的指数加权移动平均变量 s t s_t st
s t ← ρ s t − 1 + ( 1 − ρ ) g t ⊙ g t \boldsymbol{s}_{t} \leftarrow \rho \boldsymbol{s}_{t-1}+(1-\rho) \boldsymbol{g}_{t} \odot \boldsymbol{g}_{t} st←ρst−1+(1−ρ)gt⊙gt
AdaDelta算法还维护一个额外的状态变量 Δ x t \Delta x_t Δxt,其元素同样在时间步0时被初始化为0。我们使用 Δ x t − 1 \Delta x_{t-1} Δxt−1来计算自变量的变化量:
g t ′ ← Δ x t − 1 + ϵ s t + ϵ ⊙ g t \boldsymbol{g}_{t}^{\prime} \leftarrow \sqrt{\frac{\Delta \boldsymbol{x}_{t-1}+\epsilon}{s_{t}+\epsilon}} \odot \boldsymbol{g}_{t} gt′←st+ϵΔxt−1+ϵ⊙gt
x t ← x t − 1 − g t ′ \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\boldsymbol{g}_{t}^{\prime} xt←xt−1−gt′
最后,我们使用 Δ x t \Delta x_t Δxt来记录自变量变化量 g t ′ g_t^{\prime} gt′按元素平方的指数加权移动平均
Δ x t ← ρ Δ x t − 1 + ( 1 − ρ ) g t ′ ⊙ g t ′ \Delta \boldsymbol{x}_{t} \leftarrow \rho \Delta \boldsymbol{x}_{t-1}+(1-\rho) \boldsymbol{g}_{t}^{\prime} \odot \boldsymbol{g}_{t}^{\prime} Δxt←ρΔxt−1+(1−ρ)gt′⊙gt′
可以看到,如不考虑 ϵ \epsilon ϵ 的影响, AdaDelta算法跟RMSProp算法的不同之处在于使用 Δ x t − 1 \sqrt{\Delta x_{t-1}} Δxt−1来替代学习率 η \eta η
Adam算法将动量法和RMSprop结合,Adam 中对一阶动量也是用指数移动平均计算
v t ← β 1 v t − 1 + ( 1 − β 1 ) g t \boldsymbol{v}_{t} \leftarrow \beta_{1} \boldsymbol{v}_{t-1}+\left(1-\beta_{1}\right) \boldsymbol{g}_{t} vt←β1vt−1+(1−β1)gt
s t ← β 2 s t − 1 + ( 1 − β 2 ) g t ⊙ g t \boldsymbol{s}_{t} \leftarrow \beta_{2} \boldsymbol{s}_{t-1}+\left(1-\beta_{2}\right) \boldsymbol{g}_{t} \odot \boldsymbol{g}_{t} st←β2st−1+(1−β2)gt⊙gt
其中 β 1 ∈ [ 0 , 1 ) \beta_1 \in [0, 1) β1∈[0,1),作者建议取0.9 , β 2 ∈ [ 0 , 1 ) \beta_2 \in [0, 1) β2∈[0,1),作者建议取 0.999
当 t t t 较小时,过去各时间步小批量随机梯度权值之和会较小,例如,当 β 1 = 0.9 \beta_1 = 0.9 β1=0.9 时, v 1 = 0.1 g 1 v_1=0.1g_1 v1=0.1g1 。为了消除这样的影响,对于任意时间步 t t t ,我们可以将 v t v_t vt再除以 1 − β 1 t 1-\beta_1^t 1−β1t ,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中,我们对变量 v t v_t vt 和 s t s_t st均作偏差修正
v ^ t ← v t 1 − β 1 t \hat{\boldsymbol{v}}_{t} \leftarrow \frac{\boldsymbol{v}_{t}}{1-\beta_{1}^{t}} v^t←1−β1tvt
s ^ t ← s t 1 − β 2 t \hat{\boldsymbol{s}}_{t} \leftarrow \frac{\boldsymbol{s}_{t}}{1-\beta_{2}^{t}} s^t←1−β2tst
注意:(1)之所以偏差修正除以 1 − β 1 t 1-\beta_1^t 1−β1t , 是因为在时间步 t t t, v t = ( 1 − β 1 ) ∑ i = 1 t β 1 t − i g i \boldsymbol{v}_{t}=\left(1-\beta_{1}\right) \sum_{i=1}^{t} \beta_{1}^{t-i} \boldsymbol{g}_{i} vt=(1−β1)∑i=1tβ1t−igi将过去各时间步小批量随机梯度的权值相加,得到 ( 1 − β 1 ) ∑ i = 1 t β 1 t − i = 1 − β 1 t \left(1-\beta_{1}\right) \sum_{i=1}^{t} \beta_{1}^{t-i}=1-\beta_{1}^{t} (1−β1)∑i=1tβ1t−i=1−β1t
(2)为什么要使用偏差修正?
当 t t t 较小时,过去各时间步小批量随机梯度权值之和会较小,例如,当 β 1 = 0.9 \beta_1 = 0.9 β1=0.9 时, v 1 = 0.1 g 1 v_1=0.1g_1 v1=0.1g1 。此时若不进行偏差修正,直接计算,即: g t ′ = η v 1 s t + ϵ g_t^{\prime} = \frac{\eta v_1}{\sqrt{s_t}+\epsilon} gt′=st+ϵηv1 则 g t ′ g_t^{\prime} gt′会很小,因为本身 v 1 v_1 v1就变成了原来的1/10, 然后再乘上一个学习率,就会更小
接下来调整每个变量的学习率,并更新变量
g t ′ ← η v ^ t s ^ t + ϵ \boldsymbol{g}_{t}^{\prime} \leftarrow \frac{\eta \hat{\boldsymbol{v}}_{t}}{\sqrt{\hat{\boldsymbol{s}}_{t}}+\epsilon} gt′←s^t+ϵηv^t
x t ← x t − 1 − g t ′ \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\boldsymbol{g}_{t}^{\prime} xt←xt−1−gt′
小结
L2正则如下:
l o s s = g ( w ) + 1 2 η ∣ ∣ w ∣ ∣ 2 loss = g(w)+\frac{1}{2}\eta||w||^2 loss=g(w)+21η∣∣w∣∣2
权重衰减如下:
w = w − α ( g ′ ( w ) − η w ) = w − α g ′ ( w ) − α η w w = w-\alpha (g^{\prime}(w)- \eta w)=w-\alpha g^{\prime}(w)-\alpha \eta w w=w−α(g′(w)−ηw)=w−αg′(w)−αηw
其中 α \alpha α是学习率, η \eta η是正则化系数,(2)式中每一次更新都会减去一小部分权重, 因此叫做权重衰减
从上面两个式子可知,L2正则和权重衰减的说法对于原版 SGD 是等价的,而当我们使用如 Adam 那样复杂的最优化方法,L2 正则化和权重衰减就会存在很大的不同;因为 Adam 中的 L2 正则化需要添加 η w \eta w ηw 到梯度中,并分别计算梯度及其平方的移动均值,然后再能更新权重。然而权重衰减方法只是简单地求梯度更新权重,并每次从权重中减去一点
使用Adam优化带L2正则的损失并不有效。如果引入L2正则项,在计算梯度的时候会加上对正则项求梯度的结果。那么如果本身比较大的一些权重对应的梯度也会比较大,由于Adam计算步骤中减去项会有除以梯度平方的累积,使得减去项偏小。按常理说,越大的权重应该惩罚越大,但是在Adam并不是这样。而权重衰减对所有的权重都是采用相同的系数进行更新,越大的权重显然惩罚越大。
AdamW算法如下:
v t ← β 1 v t − 1 + ( 1 − β 1 ) g t \boldsymbol{v}_{t} \leftarrow \beta_{1} \boldsymbol{v}_{t-1}+\left(1-\beta_{1}\right) \boldsymbol{g}_{t} vt←β1vt−1+(1−β1)gt
s t ← β 2 s t − 1 + ( 1 − β 2 ) g t ⊙ g t \boldsymbol{s}_{t} \leftarrow \beta_{2} \boldsymbol{s}_{t-1}+\left(1-\beta_{2}\right) \boldsymbol{g}_{t} \odot \boldsymbol{g}_{t} st←β2st−1+(1−β2)gt⊙gt
v ^ t ← v t 1 − β 1 t \hat{\boldsymbol{v}}_{t} \leftarrow \frac{\boldsymbol{v}_{t}}{1-\beta_{1}^{t}} v^t←1−β1tvt
s ^ t ← s t 1 − β 2 t \hat{\boldsymbol{s}}_{t} \leftarrow \frac{\boldsymbol{s}_{t}}{1-\beta_{2}^{t}} s^t←1−β2tst
g t ′ ← η v ^ t s ^ t + ϵ \boldsymbol{g}_{t}^{\prime} \leftarrow \frac{\eta \hat{\boldsymbol{v}}_{t}}{\sqrt{\hat{\boldsymbol{s}}_{t}}+\epsilon} gt′←s^t+ϵηv^t
g t ′ = g t ′ + δ x t \boldsymbol{g}_{t}^{\prime} = \boldsymbol{g}_{t}^{\prime}+\delta x_t gt′=gt′+δxt
x t ← x t − 1 − g t ′ \boldsymbol{x}_{t} \leftarrow \boldsymbol{x}_{t-1}-\boldsymbol{g}_{t}^{\prime} xt←xt−1−gt′
在Adam中加入L2正则,仅仅把权重的平方加入到损失函数中是不正确的,因为那样会与m和v相互作用,我们想要一种权重衰减的方式,它不会与m和v相互作用,这相当于把权重的平方加入到损失函数中向普通的SGD那样
现在有很多算法都是用AdamW,如BERT,GPT2,Transformer等
常用的平均数法是: v a v e r = v 1 + … + v 100 100 v_{a v e r}=\frac{v_{1}+\ldots+v_{100}}{100} vaver=100v1+…+v100,假设现在有100天的温度值,通过上面的公式就可以直接求出100天的平均值。
指数加权平均本质上就是一种近似求平均的方法,其公式为:
v t = β ∗ v t − 1 + ( 1 − β ) θ t v_t = \beta*v_{t-1}+(1-\beta)\theta_t vt=β∗vt−1+(1−β)θt
其中 v t v_t vt代表到第 t t t天的平均温度, θ t \theta_t θt代表第 t t t天的温度值, β \beta β代表可调节的超参数
加入 β = 0.9 \beta=0.9 β=0.9 ,可以得到指数平均公式的如下平均值求法:
v t = β v t − 1 + ( 1 − β ) θ t v 100 = 0.9 v 99 + 0.1 θ 100 v 99 = 0.9 v 98 + 0.1 θ 99 v 98 = 0.9 v 97 + 0.1 θ 98 \begin{array}{l} v_{t}=\beta v_{t-1}+(1-\beta) \theta_{t} \\ v_{100}=0.9 v_{99}+0.1 \theta_{100} \\ v_{99}=0.9 v_{98}+0.1 \theta_{99} \\ v_{98}=0.9 v_{97}+0.1 \theta_{98} \end{array} vt=βvt−1+(1−β)θtv100=0.9v99+0.1θ100v99=0.9v98+0.1θ99v98=0.9v97+0.1θ98
化简得到如下表达式:
v 100 = 0.1 θ 100 + 0.9 v 99 = 0.1 θ 100 + 0.9 ( 0.1 θ 99 + 0.9 v 98 ) = 0.1 θ 100 + 0.1 ∗ 0.9 ∗ θ 99 + 0.1 ∗ 0. 9 2 θ 98 + . . . . . v_{100} = 0.1\theta_{100} + 0.9v_{99} = 0.1\theta_{100} + 0.9(0.1\theta_{99} + 0.9v_{98})\\ =0.1\theta_{100} + 0.1*0.9*\theta_{99} + 0.1*0.9^2\theta_{98} + ..... v100=0.1θ100+0.9v99=0.1θ100+0.9(0.1θ99+0.9v98)=0.1θ100+0.1∗0.9∗θ99+0.1∗0.92θ98+.....
通过上面表达式,我们可以看到, V 100 V_{100} V100 等于每一个时刻天数的温度值再乘以一个权值。
本质就是以指数式递减加权的移动平均。各数值的权重随时间而指数式递减,越近的数据加权越重,但较旧的数据也给予一定的加权
而在我们上面提到的普通平均数求法,它的每一项的权值都是一样的,如果有n项,权值都为1/n。
指数加权平均的优势:
指数加权平均的求解过程实际上是一个递推的过程,那么这样就会有一个非常大的好处,每当我要求从0到某一时刻n的平均值的时候,并不需要像普通求解平均值那样保留所有的时刻值,类和然后除以n。
而是只需要保留0到n-1时刻的平均值和n时刻的温度值即可。也就是每次只需要保留常数值,然后进行运算即可,这对于深度学习中的海量数据来说,是一个很好的减少内存和空间的做法