文章目录
优化器的挑战
局部最优
如以下函数
f
(
x
)
=
c
o
s
(
π
x
)
f(x)=cos(\pi x)
f(x)=cos(πx)
当一个优化问题的数值解在局部最优解附近时,由于目标函数有关解的梯度接近或变成零,最终迭代求得的数值解可能只令目标函数局部最小化而非全局最小化。
鞍点
梯度接近0可能是在局部最优解,但也有可能在鞍点附近。比如:
与局部最小不同,如果我们能往“正确”的方向前进一步,那么有可能逃离鞍点。幸运的是,我们有方法去判断这个点是局部最优还是在鞍点。
J
(
θ
)
=
J
(
θ
0
)
+
(
θ
−
θ
0
)
∇
θ
J
(
θ
0
)
+
1
2
(
θ
−
θ
0
)
H
(
θ
−
θ
0
)
T
J(\theta)=J(\theta_0)+(\theta-\theta_0)\nabla_\theta J(\theta_0) + \frac{1}{2}(\theta-\theta_0) H (\theta-\theta_0)^T
J(θ)=J(θ0)+(θ−θ0)∇θJ(θ0)+21(θ−θ0)H(θ−θ0)T
其中,H是海瑟矩阵。因为我们现在卡在了梯度为0的点,因此,该式子可以转化为:
J
(
θ
)
=
J
(
θ
0
)
+
1
2
(
θ
−
θ
0
)
H
(
θ
−
θ
0
)
T
J(\theta)=J(\theta_0)+\frac{1}{2}(\theta-\theta_0) H (\theta-\theta_0)^T
J(θ)=J(θ0)+21(θ−θ0)H(θ−θ0)T
那么,不难得出,如果对于所有的
θ
\theta
θ,都有:
1
2
(
θ
−
θ
0
)
H
(
θ
−
θ
0
)
T
>
0
\frac{1}{2}(\theta-\theta_0) H (\theta-\theta_0)^T > 0
21(θ−θ0)H(θ−θ0)T>0,那么我们此时再局部最小值;这意味着H的特征值全部为正。
同样的道理,我们可以得出:
当函数的海森矩阵在梯度为零的位置上的特征值全为负时,该函数得到局部最大值。
当函数的海森矩阵在梯度为零的位置上的特征值有正有负时,该函数得到鞍点。
不过,不幸的是,深度学习的参数往往数以百万计,而计算海瑟矩阵及其特征值基本不可能成为现实。因此,下文介绍一些现在常用的梯度下降方法。
GD(Gradient Descent)
梯度下降是说,给定待优化模型参数
θ
∈
R
d
\theta \in R^d
θ∈Rd和目标函数
J
(
θ
)
J(\theta)
J(θ)后,算法沿着梯度的相反方向更新
θ
\theta
θ来最小化
J
(
θ
)
J(\theta)
J(θ)。学习率
η
\eta
η决定了每一时刻的更新步长。对于每一时刻t用以下步骤来说明梯度下降的流程:
(1)计算目标函数关于参数的梯度
g
t
=
∇
θ
J
(
θ
)
g_t=\nabla_\theta J(\theta)
gt=∇θJ(θ)
(2)更新模型参数
θ
t
+
1
=
θ
−
η
g
t
\theta_{t+1}=\theta-\eta g_t
θt+1=θ−ηgt
有几种梯度下降的方式,全量梯度下降(BGD),随机梯度下降(SGD),小批量梯度下降法(MBGD)。具体的解析可以详见详解梯度下降的三种形式。这里只对这三种下降方法简单介绍一下。
BGD
BGD的方式会使得参数更新的次数减少,每次更新参数都用到了所有的训练数据,比较耗费算力和内存。
SGD
与BGD不同,SGD每次只对一个样本计算梯度并更新参数。因此,随之而来的问题是,其噪音较多,其参数的每一次迭代并不一定是沿着最优的下降方向。
MBGD
MBGD就是每次更新一个批量的参数,这里,批量的大小是一个超参数,需要自己去设定。
tips
以下是我个人的理解,如果有不同的意见,欢迎评论。
- 可能会有人说BGD的效果最优。但其实不然,可能因为SGD或者MBGD引入了噪声,可能一定程度上能够逃离一些鞍点。
- 随着并行技术的发展,MBGD是相对而言计算速度最快的。
GD与一阶泰勒展开之间的关系
此节与文章没有太大的关联,不感兴趣的同学可以略过。这里只是作为一个扩展性阅读。
假设损失函数是
J
(
θ
)
J(\theta)
J(θ),对其做一阶泰勒展开:
J
(
θ
)
=
J
(
θ
0
)
+
(
θ
−
θ
0
)
∇
θ
J
(
θ
0
)
J(\theta) = J(\theta_0)+(\theta-\theta_0)\nabla_\theta J(\theta_0)
J(θ)=J(θ0)+(θ−θ0)∇θJ(θ0)
假设,
θ
−
θ
0
=
λ
d
⃗
\theta-\theta_0=\lambda \vec d
θ−θ0=λd,其中
d
⃗
\vec d
d是方向向量,
λ
\lambda
λ为标量,描述其长度。不难得出:
(
θ
−
θ
0
)
∇
θ
J
(
θ
0
)
=
λ
∣
d
⃗
∣
∣
∇
θ
J
(
θ
0
)
∣
c
o
s
(
α
)
(\theta-\theta_0)\nabla_\theta J(\theta_0) =\lambda |\vec d| |\nabla_\theta J(\theta_0)|cos(\alpha)
(θ−θ0)∇θJ(θ0)=λ∣d∣∣∇θJ(θ0)∣cos(α),这里的
α
\alpha
α是两个向量的夹角。如果我们期望梯度下降的方向最大,那么易知,
d
⃗
\vec d
d与梯度是反向的,则:
d
⃗
=
−
∇
θ
J
(
θ
0
)
∣
∣
∇
θ
J
(
θ
0
)
∣
∣
\vec d=\frac{-\nabla_\theta J(\theta_0)}{||\nabla_\theta J(\theta_0)||}
d=∣∣∇θJ(θ0)∣∣−∇θJ(θ0)。
那么,我们有:
θ
−
θ
0
=
λ
d
⃗
=
λ
−
∇
θ
J
(
θ
0
)
∣
∣
∇
θ
J
(
θ
0
)
∣
∣
θ
=
θ
0
−
λ
∣
∣
∇
θ
J
(
θ
0
)
∣
∣
∇
θ
J
(
θ
0
)
\theta-\theta_0=\lambda \vec d = \lambda \frac{-\nabla_\theta J(\theta_0)}{||\nabla_\theta J(\theta_0)||} \\ \theta = \theta_0 - \frac{\lambda}{||\nabla_\theta J(\theta_0)||}\nabla_\theta J(\theta_0)
θ−θ0=λd=λ∣∣∇θJ(θ0)∣∣−∇θJ(θ0)θ=θ0−∣∣∇θJ(θ0)∣∣λ∇θJ(θ0)
也就得到了梯度下降的公式。
What is Momentum
Momentum其实是借助了物理学上的动量的概念。我们先来看一下他的数学公式:
再来看结论:
- Momentum 可以一定程度的缓解GD陷入鞍点的问题。
- 可以加速收敛,减少震荡。
这是为什么呢?
- 当损失到达一个梯度很小的地方时,SGD可能就难以继续下去了,但是类比于小球在坑坑洼洼的空间滚动,当其落入局部洼地(局部最小值)或平缓地带(鞍点),借助于惯性(因为带有之前的速度)可能可以冲过局部挖地或是鞍点。
- 当损失在一个平缓下降的坡度时,SGD收敛速度较慢,但是Momentum包含之前的梯度,会给他一个较大的下降速度。
- 在梯度的方向转变时,Momentum能够减少更新。
What is Nesterov Acceleration
SGD 还有一个问题是困在局部最优的沟壑里面震荡。想象一下你走到一个盆地,四周都是略高的小山,你觉得没有下坡的方向,那就只能待在这里了。可是如果你爬上高地,就会发现外面的世界还很广阔。因此,我们不能停留在当前位置去观察未来的方向,而要向前一步、多看一步、看远一些。
我们知道在时刻t的主要下降方向是由累积动量决定的,自己的梯度方向说了也不算,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,在NAG(Nesterov Accelerated Gradient)中,其梯度计算方式为:
g
t
=
∇
θ
J
(
θ
t
−
η
⋅
μ
⋅
m
t
−
1
)
g_t = \nabla_\theta J(\theta_t-\eta \cdot \mu \cdot m_{t-1})
gt=∇θJ(θt−η⋅μ⋅mt−1)
以上,我们介绍了GD及其变体,但是这些方法在所有的参数上更新的学习率是相同的。可现实是,神经网络包含大量的参数,其中一些比较平缓,我们希望在这上面学习速率大一些,而有些比较陡峭,我们希望学习速率小一些,那么,这就是自适应学习率算法。
那么,是如何实现自适应学习率的优化算法的呢?我们可以将学习率 η t \eta_t ηt 转变为与参数相关的 η t σ t i \frac{\eta_t}{\sigma_t^i} σtiηt。
AdaGrad
对于AdaGrad而言, σ t i = ∑ t = 1 T g t i 2 \sigma_t^i=\sqrt{\sum_{t=1}^T{g_t^i}^2} σti=∑t=1Tgti2。显然,如果该维度的梯度一直较小(比较平缓),会相对来说给一个较大的学习率;反之,如果该维度的梯度一直较大(比较陡峭),会相对来说给一个较小的学习率。一般来说,为了防止分母为0,会在其中增加一个较小的平滑项。
这个方法在稀疏场景下表现较好,但是也有一些问题。因为累计平方和是单调递增的,随着不断的训练,可能会使得学习率过小,从而导致训练提前结束。
RMSprop
RMSprop将其改进为指数加权移动平均和:
σ
t
i
2
=
α
σ
t
−
1
i
+
(
1
−
α
)
g
t
i
2
{\sigma_t^i}^2=\alpha \sigma_{t-1}^i+(1-\alpha){g_t^i}^2
σti2=ασt−1i+(1−α)gti2
从而避免不断累计,导致训练提前结束的问题。比较适合处理非平稳目标,如RNN。
Adam(Adaptive Momentum Estimation)
Adam 就是在RMSprop的基础上加上了动量。而NAdam呢,就是在Adam的基础上加上了Nesterov,因此,本篇就不介绍NAdam了。那么Adam的公式如下:
但,即便如此,Adam也有一些问题:
- 可能不收敛。 SGD没有用到二阶动量,因此学习率是恒定的(实际使用过程中会采用学习率衰减策略,因此学习率递减)。AdaGrad的二阶动量不断累积,单调递增,因此学习率是单调递减的。因此,这两类算法会使得学习率不断递减,最终收敛到0,模型也得以收敛。但AdaDelta和Adam则不然。二阶动量是固定时间窗口内的累积,随着时间窗口的变化,遇到的数据可能发生巨变,使得 σ t i \sigma_t^i σti 可能会时大时小,不是单调变化。这就可能在训练后期引起学习率的震荡,导致模型无法收敛。提出的修正方案为: σ t i 2 = m a x ( α σ t − 1 i + ( 1 − α ) g t i 2 , σ t − 1 i 2 ) {\sigma_t^i}^2=max(\alpha \sigma_{t-1}^i+(1-\alpha){g_t^i}^2,{\sigma_{t-1}^i}^2) σti2=max(ασt−1i+(1−α)gti2,σt−1i2),从而保证其单调递减,从而收敛。
- 可能错过全局最优解。有一种解释是说,Adam的后期的学习率太小,影响了有效的收敛,因此,可以前期用Adam,享受Adam快速收敛的优势;后期切换到SGD,慢慢寻找最优解。 但是切换的时机和切换后的学习率一般是经验设定。不过在Improving Generalization Performance by Switching from Adam to SGD中描述了具体的切换时机和切换后学习率的方法,有兴趣的同学可以学习一下。
参考资料
梯度下降与一阶泰勒展开的关系
详解梯度下降的三种形式
一个框架看懂优化算法
深度学习最全优化方法总结比较(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)
Adam的两宗罪