梯度下降优化器
Momentum
如果在峡谷地区(某些方向较另一些方向上陡峭得多,常见于局部极值点),SGD会在这些地方附近振荡,从而导致收敛速度慢。这种情况下,动量(Momentum)便可以解决。动量在参数更新项中加上一次更新量(即动量项),即:
ν t ν_{t} νt = γ ν t − 1 \gammaν_{t−1} γνt−1 + η ∇ θ ∇_{θ} ∇θJ(θ)
θ = θ − ν t ν_{t} νt
其中动量项超参数 γ \gamma γ<1 一般是小于等于0.9。
其作用如下图所示:
图1 没有动量
图2 加上动量
加上动量项就像从山顶滚下一个球,求往下滚的时候累积了前面的动量(动量不断增加),因此速度变得越来越快,直到到达终点。同理,在更新模型参数时,对于那些当前的梯度方向与上一次梯度方向相同的参数,那么进行加强,即这些方向上更快了;对于那些当前的梯度方向与上一次梯度方向不同的参数,那么进行削减,即这些方向上减慢了。因此可以获得更快的收敛速度与减少振荡。
NAG(Nesterov accelerated gradient 涅斯捷罗夫梯度加速)
从山顶往下滚的球会盲目地选择斜坡。更好的方式应该是在遇到倾斜向上之前应该减慢速度。
Nesterov accelerated gradient(NAG,涅斯捷罗夫梯度加速)不仅增加了动量项,并且在计算参数的梯度时,在损失函数中减去了动量项,即计算
∇
θ
∇_{θ}
∇θJ(θ-
γ
ν
t
−
1
\gammaν_{t−1}
γνt−1),这种方式预估了下一次参数所在的位置。用 θ -
γ
ν
t
−
1
\gammaν_{t−1}
γνt−1 来近似当做参数下一步会变成的值,则在计算梯度时,不是在当前位置,而是未来的位置上。即:
ν t ν_{t} νt = γ ν t − 1 \gammaν_{t−1} γνt−1 + η ∇ θ ∇_{θ} ∇θJ(θ - γ ν t − 1 \gammaν_{t−1} γνt−1)
θ = θ − ν t ν_{t} νt
如下图所示:
图3 NAG更新
蓝色是 Momentum 的过程,会先计算当前的梯度,然后在更新后的累积梯度后会有一个大的跳跃。而 NAG 会先在前一步的累积梯度上(brown vector)有一个大的跳跃,然后衡量一下梯度做一下修正(red vector),这种预期的更新可以避免我们走的太快。
J(θ)是当前的梯度;
J(θ -
γ
ν
t
−
1
\gammaν_{t−1}
γνt−1) 是参数更新后的梯度的近似值;
因为有动量,所以θ每次值更新会大的多,如果按θ更新前的梯度计算,也就是J(θ),会和更新后的梯度J(θ -
γ
ν
t
−
1
\gammaν_{t−1}
γνt−1)差值拉大(因为动量使更新前后两个θ差距变大,同时导致更新前后的梯度值也明显有差异)。所以使用θ更新后的梯度会更接近下一次的梯度值。
通过上面的两种方法,可以做到每次学习过程中能够根据损失函数的斜率做到自适应更新来加速SGD的收敛。下一步便需要对每个参数根据参数的重要性进行各自自适应更新。
Adagrad
Adagrad也是一种基于梯度的优化算法,它能够对每个参数自适应不同的学习速率,对稀疏特征,得到大的学习更新,对非稀疏特征,得到较小的学习更新,因此该优化算法适合处理稀疏特征数据。Dean等发现Adagrad能够很好的提高SGD的鲁棒性,google便用起来训练大规模神经网络(看片识猫:recognize cats in Youtube videos)。Pennington等在GloVe中便使用Adagrad来训练得到词向量(Word Embeddings), 频繁出现的单词赋予较小的更新,不经常出现的单词则赋予较大的更新。
在前述中,每个模型参数θi使用相同的学习速率η,而Adagrad在每一个更新步骤中对于每一个模型参数
θ
i
θ_{i}
θi使用不同的学习速率
η
i
η_{i}
ηi,设第t次更新步骤中,目标函数的参数
θ
i
θ_{i}
θi梯度为
g
t
,
i
g_{t,i}
gt,i,即:
g t , i g_{t,i} gt,i = ∇ θ ∇_{θ} ∇θJ( θ i θ_{i} θi)
那么SGD更新方程为:
θ t + 1 , i θ_{t+1,i} θt+1,i = θ t , i θ_{t,i} θt,i − η⋅ g t , i g_{t,i} gt,i
而Adagrad对每一个参数使用不同的学习速率,其更新方程为:
θ t + 1 , i θ_{t+1,i} θt+1,i = θ t , i θ_{t,i} θt,i − η G t , i i + ϵ \frac{η}{\sqrt[]{G_{t,ii}+ϵ}} Gt,ii+ϵη⋅ g t , i g_{t,i} gt,i
e i i e_{ii} eii = g t , 1 2 g^2_{t,1} gt,12 + g t , 2 2 g^2_{t,2} gt,22 + … + g t , i 2 g^2_{t,i} gt,i2
其中, G t G_{t} Gt∈ R d × d R^{d×d} Rd×d是一个对角矩阵,其中第i行的对角元素 e i i e_{ii} eii为过去到当前第i个参数 θ i θ_{i} θi的梯度的平方和, ϵ \epsilon ϵ是一个平滑参数,为了使得分母不为0(通常ϵ=1e−8),另外如果分母不开根号,算法性能会很糟糕。
进一步,将所有 G t , i i G_{t,ii} Gt,ii , g t , i g_{t,i} gt,i的元素写成向量 G t G_{t} Gt , g t g_{t} gt,这样便可以使用向量点乘操作:
θ t + 1 θ_{t+1} θt+1 = θ t θ_{t} θt − η G t + ϵ \frac{η}{\sqrt[]{G_{t}+ϵ}} Gt+ϵη⊙ g t g_{t} gt
Adagrad主要优势在于它能够为每个参数自适应不同的学习速率,而一般的人工都是设定为0.01。同时其缺点在于需要计算参数梯度序列平方和,并且学习速率趋势是不断衰减最终达到一个非常小的值。下文中的Adadelta便是用来解决该问题的。
Adadelta
AdaDelta算法主要是为了解决AdaGrad算法中存在的缺陷,下面先介绍一下AdaGrad算法优点和以及存在的问题:
AdaGrad的迭代公式如下所示:
θ t + 1 , i θ_{t+1,i} θt+1,i = θ t , i θ_{t,i} θt,i − η ∑ i = 1 t g i 2 + ϵ \frac{η}{\sqrt[]{\sum\limits_{i=1}^{t}g_i^2+ϵ}} i=1∑tgi2+ϵη⋅ g t , i g_{t,i} gt,i
优点
学习率将随着梯度的倒数增长,也就是说较大梯度具有较小的学习率,而较小的梯度具有较大的学习率,可以解决普通的sgd方法中学习率一直不变的问题
缺点
还是需要自己手动指定初始学习率,而且由于分母中对历史梯度一直累加,学习率将逐渐下降至0,并且如果初始梯度很大的话,会导致整个训练过程的学习率一直很小,从而导致学习时间变长。
而AdaDelta算法的提出就是为了解决上述的问题,AdaDelta有两种解决方案:
改进方法一:Accumulate Over Window
- 在一个窗口w中对梯度进行求和,而不是对梯度一直累加
- 因为存放 w 之前的梯度是低效的,所以可以用对先前所有梯度均值(使用RMS即均方根值实现)的一个指数衰减作为代替的实现方法。
更新公式如下:
将累计梯度信息从全部历史梯度变为当前时间向前的一个窗口期内的累积:
E
[
g
2
]
t
E[g^2]_t
E[g2]t =
γ
∗
E
[
g
2
]
t
−
1
\gamma*E[g^2]_{t-1}
γ∗E[g2]t−1 +
(
1
−
γ
)
∗
g
t
2
(1-\gamma)*g^2_t
(1−γ)∗gt2
=
γ
t
−
1
∗
(
1
−
γ
)
g
1
2
\gamma^{t-1}*(1-\gamma)g^2_1
γt−1∗(1−γ)g12 +
γ
t
−
2
∗
(
1
−
γ
)
g
2
2
\gamma^{t-2}*(1-\gamma)g^2_2
γt−2∗(1−γ)g22 +…+
γ
∗
(
1
−
γ
)
g
t
−
1
2
\gamma*(1-\gamma)g^2_{t-1}
γ∗(1−γ)gt−12 +
(
1
−
γ
)
∗
g
t
2
(1-\gamma)*g^2_t
(1−γ)∗gt2
相当于历史梯度信息的累计乘上一个衰减系数 γ \gamma γ,然后用 ( 1 − γ ) (1-\gamma) (1−γ)作为当前梯度的平方加权系数相加。如果 γ \gamma γ=0.9,那么越是前面的 g 1 2 g^2_1 g12, g 2 2 g^2_2 g22的系数因为 γ \gamma γ的次数越大,所以会越小,所以值会越来越小。这就解决了AdaGrad历史梯度一直累加,学习率将逐渐下降至0的问题。
然后将上述 E [ g 2 ] t E[g^2]_t E[g2]t 开方后,作为每次迭代更新后的学习率衰减系数:
θ t + 1 , i θ_{t+1,i} θt+1,i = θ t , i θ_{t,i} θt,i − η E [ g 2 ] t + ϵ \frac{η}{\sqrt[]{E[g^2]_t+ϵ}} E[g2]t+ϵη⋅ g t , i g_{t,i} gt,i
记 R M S ( g t ) = E [ g 2 ] t + ϵ RMS(g_t) = \sqrt[]{E[g^2]_t+ϵ} RMS(gt)=E[g2]t+ϵ
其中ϵ 是为了防止分母为0而加上的一个极小值。
这种更新方法解决了对历史梯度一直累加而导致学习率一直下降的问题,当时还是需要自己选择初始的学习率。
改进方法二:Correct Units with Hessian Approximation
通过牛顿法可以知道,牛顿法迭代步长是f’’(θ)一阶牛顿迭代公式为;
θ t + 1 θ_{t+1} θt+1 = θ t θ_{t} θt − f ′ ( θ ) f ′ ′ ( θ ) \frac{f'(θ)}{f''(θ)} f′′(θ)f′(θ)
可以看出牛顿算法的迭代步长是二阶近似的解析解,不需要我们手动指定学习率。
而高阶的牛顿法迭代的步长为Hessian 矩阵。
AdaDelta算法正是采用了这种思想,采用Hessian 矩阵的对角线近似Hessian矩阵。
公式如下所示:
Δθ ≈
∂
f
∂
θ
∂
2
f
∂
θ
2
\frac{\frac{∂f}{∂θ}}{\frac{∂^2f}{∂θ^2}}
∂θ2∂2f∂θ∂f
于是有:
Δ θ ∂ f ∂ θ \frac{Δθ}{\frac{∂f}{∂θ}} ∂θ∂fΔθ = 1 ∂ 2 f ∂ θ 2 \frac{1}{\frac{∂^2f}{∂θ^2}} ∂θ2∂2f1
而更新公式为:
θ t + 1 θ_{t+1} θt+1 = θ t θ_{t} θt − 1 ∂ 2 f ∂ θ 2 ∗ g t \frac{1}{\frac{∂^2f}{∂θ^2}}*g_t ∂θ2∂2f1∗gt= θ t θ_{t} θt − Δ θ ∂ f ∂ θ ∗ g t \frac{Δθ}{\frac{∂f}{∂θ}}*g_t ∂θ∂fΔθ∗gt
同理对分子分母按照上一个方法进行处理,可以得到以下公式:
其中假设x附近的曲率是平滑的,而
θ
t
+
1
可
以
近
似
θ
t
θ_{t+1}可以近似θ_t
θt+1可以近似θt
Δθ = R M S [ Δ θ ] t − 1 R M S [ g ] t ∗ g t \frac{RMS[Δθ]_{t-1}}{RMS[g]_t}*g_t RMS[g]tRMS[Δθ]t−1∗gt
θ t + 1 θ_{t+1} θt+1 = θ t θ_{t} θt − Δθ
其中 g t g_t gt为本次迭代的梯度
由于RMS RMSRMS永远为正,所以能保证更新的方向一直为梯度的负方向
分子作为一个加速项,作为动量在时间窗口w ww上积累先前的梯度。
下面是论文中的算法展示:
RMSprop
其实RMSprop是Adadelta的中间形式,也是为了降低Adagrad中学习速率衰减过快问题,即:
E
[
g
2
]
t
E[g^2]_t
E[g2]t =
γ
∗
E
[
g
2
]
t
−
1
\gamma*E[g^2]_{t-1}
γ∗E[g2]t−1 +
(
1
−
γ
)
∗
g
t
2
(1-\gamma)*g^2_t
(1−γ)∗gt2
=
γ
t
−
1
∗
(
1
−
γ
)
g
1
2
\gamma^{t-1}*(1-\gamma)g^2_1
γt−1∗(1−γ)g12 +
γ
t
−
2
∗
(
1
−
γ
)
g
2
2
\gamma^{t-2}*(1-\gamma)g^2_2
γt−2∗(1−γ)g22 +…+
γ
∗
(
1
−
γ
)
g
t
−
1
2
\gamma*(1-\gamma)g^2_{t-1}
γ∗(1−γ)gt−12 +
(
1
−
γ
)
∗
g
t
2
(1-\gamma)*g^2_t
(1−γ)∗gt2
θ t + 1 θ_{t+1} θt+1 = θ t θ_{t} θt − η E [ g 2 ] t + ϵ \frac{η}{\sqrt[]{E[g^2]_t+ϵ}} E[g2]t+ϵη⋅ g t g_{t} gt
Hinton建议γ=0.9,η=0.001
Adam:Adaptive Moment Estimation
这个算法是另一种计算每个参数的自适应学习率的方法。相当于 RMSprop + Momentum
除了像 Adadelta 和 RMSprop 一样存储了过去梯度的平方 vt 的指数衰减平均值 ,也像 momentum 一样保持了过去梯度 mt 的指数衰减平均值:
m
t
=
β
1
m
t
−
1
+
(
1
−
β
1
)
g
t
m_t=β_1m_{t−1}+(1−β_1)g_t
mt=β1mt−1+(1−β1)gt
v
t
=
β
2
v
t
−
1
+
(
1
−
β
2
)
g
t
2
v_t=β_2v_{t−1}+(1−β_2)g^2_t
vt=β2vt−1+(1−β2)gt2
如果 m t m_t mt 和 v t v_t vt 被初始化为 0 向量,那它们就会向 0 偏置,所以做了偏差校正,通过计算偏差校正后的 m t m_t mt 和 v t v_t vt 来抵消这些偏差:
m t ^ = m t 1 − β 1 t \hat{m_t} = \frac{m_t}{1−β^t_1} mt^=1−β1tmt
v t ^ = v t 1 − β 2 t \hat{v_t} = \frac{v_t}{1−β^t_2} vt^=1−β2tvt
梯度更新规则:
θ
t
+
1
=
θ
t
−
η
v
t
^
+
ϵ
m
t
^
θ_{t+1} = θ_t − \frac{η}{\sqrt{\hat{v_t} }+ϵ}\hat{m_t}
θt+1=θt−vt^+ϵηmt^
超参数设定值:
建议
β
1
β_1
β1 = 0.9,
β
2
β_2
β2 = 0.999,ϵ = 10e−8
实践表明,Adam 比其他适应性学习方法效果要好。
各优化方法比较
下面看一下几种算法在鞍点和等高线上的表现:,如图:
图4 SGD各优化方法在损失曲面上的表现
从上图可以看出, Adagrad、Adadelta与RMSprop在损失曲面上能够立即转移到正确的移动方向上达到快速的收敛。而Momentum 与NAG会导致偏离(off-track)。同时NAG能够在偏离之后快速修正其路线,因为其根据梯度修正来提高响应性。
图5 SGD各优化方法在损失曲面鞍点处上的表现
从上图可以看出,在鞍点(saddle points)处(即某些维度上梯度为零,某些维度上梯度不为零),SGD、Momentum与NAG一直在鞍点梯度为零的方向上振荡,很难打破鞍点位置的对称性;Adagrad、RMSprop与Adadelta能够很快地向梯度不为零的方向上转移。
上面两种情况都可以看出,Adagrad, Adadelta, RMSprop 几乎很快就找到了正确的方向并前进,收敛速度也相当快,而其它方法要么很慢,要么走了很多弯路才找到。
由图可知自适应学习率方法即 Adagrad, Adadelta, RMSprop, Adam 在这种情景下会更合适而且收敛性更好。
如何选择优化算法
如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。
RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。
Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,
随着梯度变的稀疏,Adam 比 RMSprop 效果会好。
整体来讲,Adam 是最好的选择。
很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。
如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。