本文内容
本文对深度学习中一些比较经典的最优化方法(梯度下降、基于动量的梯度下降、AdaGrad、RMSprop和Adam)进行了总结。本人也是一边学习一边总结,所以文中仅包含一些浅显基础的知识,已有一定基础的同学可酌情浏览。
梯度下降法
基本原理
梯度下降法是求解无约束最优化问题的一种常用方法,它是一种迭代算法,通过不断迭代使得目标函数最小化。以求解
f
(
x
)
f(x)
f(x)在
R
n
R^n
Rn上的最小值为例。假设
f
(
x
)
f(x)
f(x)在
R
n
R^n
Rn上的具有一阶连续导数,求:
m
i
n
x
∈
R
n
f
(
x
)
\underset{x\in R^n}{min}f(x)
x∈Rnminf(x)
首先选择一个初值
x
(
0
)
x^{(0)}
x(0),然后开始迭代,在每次迭代中以负梯度方向更新
x
x
x,从而使得函数值不断减小,直至收敛。其
k
+
1
k+1
k+1 次迭代的公式如下:
x
(
k
+
1
)
=
x
(
k
)
−
η
∇
f
(
x
(
k
)
)
x^{(k+1)}=x^{(k)}-\eta\nabla f(x^{(k)})
x(k+1)=x(k)−η∇f(x(k))
其中
η
\eta
η是学习率,
∇
f
(
x
(
k
)
)
\nabla f(x^{(k)})
∇f(x(k))为
f
(
x
)
f(x)
f(x)在
x
(
k
)
x^{(k)}
x(k)梯度。
梯度下降法存在的问题
梯度下降法简单,容易实现,但是它不保证最终求得的解为全局最优解(仅当目标函数为凸函数时梯度下降法的解是全局最优解)。另外梯度下降法的收敛速度也不能保证。
1.梯度下降法不能保证得到全局最优解
前面提到梯度下降法在每次迭代中以负梯度方向更新参数,这意味着当梯度等于0时,参数停止更新。但是我们都知道,当梯度为0时,参数可能停在函数的全局最优点,也可能在局部最优点甚至是驻点(连局部最优都不是!)。
2.很多时候梯度下降法是低效的
确切地说,当函数的形状是非均向的(anisotropic),搜索路径会非常低效,原因是负梯度的方向没有指向最小值的方向。下面以《深度学习入门基于Python的理论与实现》中的例子说明,考虑下面这个函数的最小值
f
(
x
,
y
)
=
1
20
x
2
+
y
2
f(x,y)=\frac1{20}x^2+y^2
f(x,y)=201x2+y2
这个函数的图象如下图所示:
而它在各个的梯度方向如下图所示
稍有一点数学知识都可以知道,
f
(
x
,
y
)
f(x,y)
f(x,y)的最小值在
(
0
,
0
)
(0,0)
(0,0)点处,但是上图中并非所有的点都指向
(
0
,
0
)
(0,0)
(0,0)点,在这种情况下使用梯度下降法,很容易使参数更新的路径呈“之”字形移动,效率极为低下。
基于动量的梯度下降法
前面说到,梯度下降法对于形状非均向的的函数,梯度下降法的参数更新的路径常常呈“之”字形移动,效率低下。为了减缓这个问题,人们提出了基于动量的梯度下降法。基于动量的梯度下降法的思想十分简单:使参数更新的方向不仅受当前梯度影响,还受之前梯度影响。
指数加权平均
在介绍具体公式之前首先了解一下指数加权平均的思想。设
v
t
v_t
vt、
v
t
−
1
…
v
0
v_t-1 \dots v_0
vt−1…v0有如下关系
v
t
=
β
v
t
−
1
+
(
1
−
β
)
S
t
v
t
−
1
=
β
v
t
−
2
+
(
1
−
β
)
S
t
−
1
⋮
v
1
=
β
v
0
+
(
1
−
β
)
S
1
v
0
=
0
v_t=\beta v_{t-1}+(1-\beta)S_t\\ v_{t-1}=\beta v_{t-2}+(1-\beta)S_{t-1}\\ \vdots\\ v_{1}=\beta v_{0}+(1-\beta)S_{1}\\ v_{0}=0
vt=βvt−1+(1−β)Stvt−1=βvt−2+(1−β)St−1⋮v1=βv0+(1−β)S1v0=0
其中
β
\beta
β大于0小于1,可以发现
v
t
v_t
vt既受
v
t
−
1
v_{t-1}
vt−1的影响,又受
S
t
S_{t}
St的影响,而
v
t
−
1
v_{t-1}
vt−1既受
v
t
−
2
v_{t-2}
vt−2的影响,又受
S
t
−
1
S_{t-1}
St−1的影响,将上式合并化简,可以得到
v
t
=
β
β
…
β
t
−
1
个
(
1
−
β
)
S
1
+
⋯
+
β
(
1
−
β
)
S
t
−
1
+
(
1
−
β
)
S
t
v_t=\underset{t-1个}{\beta\beta\dots\beta}(1-\beta)S_1+\dots+\beta(1-\beta)S_{t-1}+(1-\beta)S_t
vt=t−1个ββ…β(1−β)S1+⋯+β(1−β)St−1+(1−β)St
我们发现
v
t
v_t
vt受
S
t
、
S
t
−
1
…
S
1
S_t、S_{t-1} \dots S_1
St、St−1…S1的影响,且越靠前的
S
S
S对
v
t
v_t
vt的影响越小。
基于动量的梯度下降
下面基于指数加权平均的思想对梯度下降进行改进,其
k
+
1
k+1
k+1 次迭代的公式如下
v
t
=
β
v
t
−
1
+
(
1
−
β
)
∇
f
(
x
(
k
)
)
v_t=\beta v_{t-1}+(1-\beta)\nabla f(x^{(k)})
vt=βvt−1+(1−β)∇f(x(k))
x
(
k
+
1
)
=
x
(
k
)
−
α
v
t
x^{(k+1)}=x^{(k)}-\alpha v_t
x(k+1)=x(k)−αvt
由于指数加权平均,参数每次更新的方向不仅受当前梯度影响,还受之前梯度影响,这使得参数更新的路径更加平缓,减小了更新路径的“之”化程度。
AdaGrad 与 RMSprop
梯度下降法还存在另外一个问题,那就是学习率的选取。学习率过大可能会导致算法无法收敛,学习率过小会导致学习花费过长时间。对于这个问题,人们提出一种被称为学习率衰减的方法,即随着学习的进行,学习率越来越小。
AdaGrad
AdaGrad就是基于学习率衰减的方法提出的,它会为参数的每一个元素是当地调整学习率,其
k
+
1
k+1
k+1 次迭代的公式如下
h
=
h
+
∇
f
(
x
k
)
⊙
∇
f
(
x
(
k
)
)
x
(
k
+
1
)
=
x
(
k
)
−
η
1
h
∇
f
(
x
(
k
)
)
h=h+\nabla f(x^k)\odot\nabla f(x^{(k)})\\ x^{(k+1)}=x^{(k)}-\eta\frac1{\sqrt h}\nabla f(x^{(k)})
h=h+∇f(xk)⊙∇f(x(k))x(k+1)=x(k)−ηh1∇f(x(k))
x
x
x表示要更新的参数,
η
\eta
η表示学习率,
∇
f
(
x
(
k
)
)
\nabla f(x^{(k)})
∇f(x(k))表示损失函数关于
x
x
x的梯度。
h
h
h是所有梯度值的平方和,在更新参数时,通过乘
1
h
\frac1{\sqrt h}
h1动态地调整每次更新的尺度。
可以发现AdaGrad的学习率是越来越小的,这样,我们可以在一开始设置一个较大的学习率,加速算法收敛,而算法在学习的过程中会自动将学习率减小,保证算法能够收敛。
但是AdaGrad带来了新的问题,由于学习率是越来越小的,如果一直学习,学习率会趋于0,导致参数不再更新,如果此时算法还未收敛,我们的训练任务就失败了。
RMSprop
RMSprop对AdaGrad存在的问题进行了改善。它引入了之前提到的指数加权平均,它逐渐地遗忘过去的梯度,而将新梯度的信息更多地反映出来,其
k
+
1
k+1
k+1 次迭代的公式如下
h
=
β
h
+
(
1
−
β
)
∇
f
(
x
(
k
)
)
⊙
∇
f
(
x
(
k
)
)
x
(
k
+
1
)
=
x
(
k
)
−
η
1
h
∇
f
(
x
(
k
)
)
h=\beta h+(1-\beta)\nabla f(x^{(k)})\odot\nabla f(x^{(k)})\\ x^{(k+1)}=x^{(k)}-\eta\frac1{\sqrt h}\nabla f(x^{(k)})\\
h=βh+(1−β)∇f(x(k))⊙∇f(x(k))x(k+1)=x(k)−ηh1∇f(x(k))
x
x
x表示要更新的参数,
η
\eta
η表示学习率,
∇
f
(
x
(
k
)
)
\nabla f(x^{(k)})
∇f(x(k))表示损失函数关于
x
x
x的梯度。
h
h
h是所有梯度值的加权平方和。同样,在更新参数时,通过乘
1
h
\frac1{\sqrt h}
h1动态地调整每次更新的尺度。
h
h
h是所有梯度值的加权平方和,且越靠前的梯度影响越小,从而可以缓解学习率下降过快的问题。
Adam
前面介绍了两种改进梯度下降的思路,一种是基于动量的改进,一种是基于学习率衰减的改进。将这两种方法结合起来,就得到了Adam。其
k
+
1
k+1
k+1 次迭代的公式如下
m
t
=
β
1
m
t
−
1
+
(
1
−
β
1
)
∇
f
(
x
(
k
)
)
①
n
t
=
β
2
n
t
−
1
+
(
1
−
β
2
)
∇
f
(
x
(
k
)
)
⊙
∇
f
(
x
(
k
)
)
②
m
^
t
=
m
t
1
−
β
1
t
③
n
^
t
=
n
t
1
−
β
2
t
④
x
(
k
+
1
)
=
x
(
k
)
−
η
1
n
^
t
m
^
t
⑤
m_t=\beta_1m_{t-1}+(1-\beta_1)\nabla f(x^{(k)})\;\;\;\;\;①\\ n_t=\beta_2n_{t-1}+(1-\beta_2)\nabla f(x^{(k)})\odot\nabla f(x^{(k)})\;\;\;\;\;②\\ {\widehat m}_t=\frac{m_t}{1-\beta_1^t}\;\;\;\;\;③\\ {\widehat n}_t=\frac{n_t}{1-\beta_2^t}\;\;\;\;\;④\\ x^{(k+1)}=x^{(k)}-\eta\frac1{\sqrt{{\widehat n}_t}}{\widehat m}_t\;\;\;\;\;⑤
mt=β1mt−1+(1−β1)∇f(x(k))①nt=β2nt−1+(1−β2)∇f(x(k))⊙∇f(x(k))②m
t=1−β1tmt③n
t=1−β2tnt④x(k+1)=x(k)−ηn
t1m
t⑤
x
x
x表示要更新的参数,
η
\eta
η表示学习率,
∇
f
(
x
(
k
)
)
\nabla f(x^{(k)})
∇f(x(k))表示损失函数,
β
1
\beta_1
β1和
β
2
\beta_2
β2为超参数,通常取0.9和0.999。
Adam原理比较复杂,严谨可靠的论述请参照原文(参考文献[6]),以下仅为个人理解。前面说过Adam结合了动量的思想和学习率衰减的思想进行改进,从公式上很容易看出这两种思想的影子,公式①和③很显然反映了动量的思想,使参数更新的方向不仅受当前梯度影响,还受之前梯度影响。而公式②和④反映学习率衰减的思想,使学习率越来愈小。最后公式⑤对上述公式进行汇总,即完成了Adam的参数更新公式。
总结
本文介绍的几种算法各有各的特点,但是基本的原理都是梯度下降(废话)。相对这几种算法,Adam是目前深度学习中主流且常用的算法,但这并不代表Adam是所有问题的最优解。对于不同的问题应当选择不同的算法,有的问题不能使用基于动量的梯度下降(例如训练WGAN),此时选用RMSProp反而有较好的效果。
参考文献
[1]《统计学习方法》李航
[2]《深度学习入门基于Python的理论与实现》[日]斋藤康毅
[3] 一文搞懂深度学习中的梯度下降
[4] Adaptive Subgradient Methods for Online Learning and Stochastic Optimization
[5] rmsprop: Divide the gradient by a running average of its recent magnitude
[6] Adam - A Method for Stochastic Optimization