Optimizer in Deep Learning
0-关于优化的基础
神经网络可以简单地看成是找一个很好的非线性函数来进行端到端的映射,开始时先给了一个初始化(权重以及偏置),通过不断最小化损失函数,对神经网络这个函数来进行更新。由于激励函数的存在(sigmoid、 tanh、relu),这个函数是一个非线性的,于是优化问题就是非线性优化问题,目前还没有通用的解法来求解。
- 梯度下降
目前对于无约束优化问题最简单有效的当属梯度下降法,设输入 x = ( x 1 , x 2 , x 3 , ⋯ , x n ) T x = (x_1,x_2, x_3, \cdots, x_n)^T x=(x1,x2,x3,⋯,xn)T,对函数 f ( x ) f(x) f(x),定义函数的梯度为
∇ x f ( x ) = ( ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , , ∂ f ( x ) ∂ x 3 , ⋯ , ∂ f ( x ) ∂ x n , ) T \nabla _x f(x) = ( \frac{\partial f(x)}{\partial x_1}, \frac{\partial f(x)}{\partial x_2},, \frac{\partial f(x)}{\partial x_3}, \cdots, \frac{\partial f(x)}{\partial x_n},)^T ∇xf(x)=(∂x1∂f(x),∂x2∂f(x),,∂x3∂f(x),⋯,∂xn∂f(x),)T
f ( x ) f(x) f(x)沿方向 u u u的方向导数定义为 ∂ ∂ α f ( x + α u ) \frac{\partial }{\partial \alpha} f(x+\alpha u) ∂α∂f(x+αu),通过链式法则,可以写成 u T ∇ x f ( x ) u^T\nabla _xf(x) uT∇xf(x)。
为了最优化
f
(
x
)
f(x)
f(x),我们的目标是找一个方向使得函数减小的最快,即
min
u
u
T
∇
x
f
(
x
)
s
.
t
.
∣
∣
u
∣
∣
2
=
1
\min \limits _u u^T\nabla _xf(x) \\ s.t. \ \ \ ||u||_2=1
uminuT∇xf(x)s.t. ∣∣u∣∣2=1最小的值是一个向量内积形式,于是假设二者夹角为
θ
\theta
θ,则上述问题等价于
∣
∣
u
∣
∣
2
∣
∣
∇
x
f
(
x
)
∣
∣
2
cos
θ
||u||_2||\nabla _xf(x)||_2 \cos\theta
∣∣u∣∣2∣∣∇xf(x)∣∣2cosθ,
考虑到
∣
∣
u
∣
∣
2
=
1
||u||_2=1
∣∣u∣∣2=1,且梯度的大小与夹角无关,于是问题就成了
min
θ
cos
θ
\min \limits _\theta\cos \theta
θmincosθ答案肯定是
θ
=
π
\theta = \pi
θ=π,也即是说
u
u
u的方向是沿着与梯度相反的方向。
得出结论:梯度的方向是函数值增加最快的方向,梯度值的反方向是函数值减小最快的方向。
梯度下降法就是不断迭代,每次沿着梯度方向前进一点,直到梯度的值等于0或在某个允许的范围内。
方向有了,但前进多少呢?这就是关于学习率(或者称为步长)的选择问题。
- 固定长度的 η \eta η
- 线性搜索:给定多个 η \eta η,选择使得更新后的函数值最小的那个
- 最速下降法:这个学习率的选取直接影响了学习的快慢与效果,它满足 η = arg min η f ( x − η ∇ x f ( x ) ) \eta = \arg \min \limits _\eta f(x-\eta \nabla_xf(x)) η=argηminf(x−η∇xf(x))于是就继续求解子优化问题
- 海森矩阵
一阶导数反映斜率,二阶导数反映曲率。
上图说明,同样在梯度是1的情况下,步长选择 η \eta η,二阶导为负时,实际走得更多,二阶导为正时,实际走的更少。所以,好的优化是需要考虑二阶导数的。
当输入是多维时,就有了海森矩阵 H i , j = ∂ 2 ∂ x i ∂ x j f ( x ) H_{i,j} = \frac{\partial^2}{\partial x_i \partial x_j}f(x) Hi,j=∂xi∂xj∂2f(x)。
考虑二阶导之后,就有牛顿法、拟牛顿法等有效算法,但在深度学习中用得不多,这里留待以后补充。
1-梯度下降法
主要有三种常用算法:随机梯度下降(SGD, Stochastic Gradient Descent)、Mini-批处理梯度下降(Mini-Batch Gradient Descent)和批处理梯度下降(BGD, Batch Gradient Descent),三者的区别在于神经网络前向计算时使用了多少样本,分别对应单个样本、部分样本和全部样本。
梯度下降算法:
输入:学习率 η \eta η,初始参数 θ \theta θ
迭代,直到满足条件
1、从训练集随机抽取m个(m对应一个、一部分或所有),对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、更新参数: θ ← θ − η g \theta \leftarrow \theta - \eta g θ←θ−ηg
- 使用随机梯度下降会引入噪声,即单个样本的梯度不一定是整个训练集的梯度方向,所以它的更新曲线更加曲折,但已经证明,只要噪声不是特别大也能很好的收敛;且对于大型数据集,速度更快
- 使用Mini-Batch是的Batch大小很重要,太大和太小都不好,需要调到比较合适;它每次更新用到了多个样本,所以能有效降低参数更新时的方差,且用到了矩阵操作,Mini-Batch用到的最多
- 批处理抗噪能力最好,但训练速度慢,容易陷入局部最优解
2-动量(Momentum)方法
使用梯度下降算法的缺点时,虽然每次的方向找对了(梯度方向),但是不知道该迈多大的步子(学习率 η \eta η),比如在曲率很小的时候可以大胆向前走,而在曲率很复杂时需要慢点走,这样即使在接近最优值时,也不会学习率固定导致收敛变慢。
于是动量方法就通过类比物理中位移、速度、加速度关系,将算出的梯度视为加速度,然后更新速度v,最后由v来更新参数,多了一步v的缓存过程。这里简单推导(本身就是种类比,不需要太计较物理意义):
设
θ
\theta
θ为位移,
L
L
L是损失函数,也是势能,于是力
F
=
−
η
∇
θ
L
(
f
(
x
;
θ
)
,
y
)
F = -\eta \nabla _\theta L(f(x;\theta),y)
F=−η∇θL(f(x;θ),y),根据动量定理
F
Δ
t
=
Δ
(
m
×
v
)
F\Delta t = \Delta(m \times v)
FΔt=Δ(m×v)设作用时间和质量均为单位值,于是有
F
=
Δ
v
F = \Delta v
F=Δv,即
−
η
∇
θ
L
=
v
t
−
v
0
- \eta \nabla _\theta L = v_t-v_0
−η∇θL=vt−v0,对速度加一个衰减系数
α
\alpha
α,则有
v
←
α
v
−
η
∇
θ
L
v \leftarrow \alpha v - \eta \nabla _\theta L
v←αv−η∇θL这段时间的位移增量
v
Δ
t
=
v
v\Delta t = v
vΔt=v,这里考虑段时间内的速度变化不大,所以使用了初始速率,当然也可以用更新后的速率,因此有位移公式
θ
=
θ
+
v
\theta = \theta + v
θ=θ+v使用动量的SGD算法:
输入:学习率 η \eta η,初始参数 θ \theta θ,初始速度 v v v,速度衰减参数 α \alpha α
迭代,直到算法停止
1、从训练集中随机采样m个样本(一个、一部分或全部),对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、更新速度: v ← α v + η g v \leftarrow \alpha v + \eta g v←αv+ηg
4、更新参数: θ = θ + v \theta = \theta + v θ=θ+v
为什么有效:
- 在曲率变化较小的地方,应该大胆更新使劲走,SGD会走固定的步长,而加入Momentum之后,前一时刻的梯度存在v中,因为曲率变化不大,所以v的新加的值与上个时刻的v的方向基本相同,因此不怎么抵消,所以能使劲走;
- 而在曲率变化大的地方,上个时刻v与现在的梯度方向可能不太一样,所以对 θ \theta θ的更新就会通过二者的垂直分量不断抵销使得走的步子小了(当然方向也会有所折中)。
3-牛顿动量
也称作
N
e
s
t
e
r
o
v
A
c
c
e
l
e
r
a
t
e
d
G
r
a
d
i
e
n
t
(
N
G
A
)
Nesterov\ Accelerated\ Gradient(NGA)
Nesterov Accelerated Gradient(NGA),是Momentum的变种,区别在于计算梯度时使用不是上一时刻的
θ
\theta
θ,而是更新后的
θ
+
α
v
\theta+\alpha v
θ+αv。
v
←
α
v
−
η
∇
θ
J
(
θ
+
α
v
)
θ
←
θ
+
v
v \leftarrow \alpha v- \eta \nabla _\theta J(\theta+\alpha v)\\ \theta \leftarrow \theta+v
v←αv−η∇θJ(θ+αv)θ←θ+v关键之处就在于计算的梯度,相当于加了矫正因子,也就是作用力超前一步知道作用的方向,这样可以防止走得过快,刹不住,同时也减小了震荡。
4-AdaGrad
我们希望在学习过程中,梯度长大了,要学会自适应步长。该方法的思想是:参数空间每个方向的学习率反比于该方向上梯度分量所有历史平方值之和的平方根。
- 学习率是个正的,用平方和的平均值而不是平均是怕算出来某个分量为负
- 用历史之和是因为要看平时表现,不能看一次半次的
AdaGrad算法:
输入学习率 η \eta η,初始参数 θ \theta θ,足够小的常数 δ \delta δ(防止0做除数时无法计算)
初始化梯度累计变量 r r r
迭代,直到停止条件
1、从训练集中随机采样m个样,对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、计算累计平方梯度: r ← r + g ⊙ g r \leftarrow r+ g \odot g r←r+g⊙g
4、更新参数: θ ← θ − η δ + r ⊙ g \theta \leftarrow \theta - \frac{\eta}{\delta+\sqrt{r}}\odot g θ←θ−δ+rη⊙g
用这个算法的一个问题就是学习率早期降的太快,到了后期,可能还没到最优值,学习率已经没了。
5-RMSProp
看来用梯度历史的平方和有点问题,老了学不动了,所以将求和改成了指数加权的移动平均。
RMSProp算法:
输入学习率 η \eta η,初始参数 θ \theta θ,衰减速率 ρ \rho ρ,足够小的常数 δ \delta δ(防止0做除数时无法计算)
初始化梯度累计变量 r r r
迭代,直到停止条件
1、从训练集中随机采样m个样,对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、计算平方梯度: r ← ρ r + ( 1 − ρ ) g ⊙ g r \leftarrow \rho r+ (1-\rho)g \odot g r←ρr+(1−ρ)g⊙g
4、更新参数: θ ← θ − η δ + r ⊙ g \theta \leftarrow \theta - \frac{\eta}{\delta+\sqrt{r}}\odot g θ←θ−δ+rη⊙g
5.1 为何这样改动有效
为什么改动这么点效果就好了呢?我们假设更新过程中梯度是个固定值 c = g ⊙ g c=g\odot g c=g⊙g
- RMSProp算法:
其经过 τ \tau τ次计算后,平方梯度的系数为 r = c ( 1 − ρ τ ) r=c(1-\rho^\tau) r=c(1−ρτ),参数的更新大小为 η δ + 1 − ρ τ \frac{\eta}{\delta + \sqrt{1-\rho^\tau}} δ+1−ρτη - AdaGrad算法:
经过 τ \tau τ次计算后,平方梯度的系数为 r = τ c r = \tau c r=τc,参数的更新大小为 e t a δ + τ \frac{eta}{\delta + \sqrt{\tau}} δ+τeta
二者都与迭代次数有关,初始时刻二者更新均较大,经过一定次数迭代后,不管此时梯度值大小,RMSProp算法的更新依然能保持较大( η \eta η),而AdaGrad已经接近0。
5.2 RMSProp动量算法
RMSPRP目的是自适应学习率,而动量是为了更新参数,二者不冲突,因此可以结合。
RMSProp-Momentum算法:
输入学习率 η \eta η,初始参数 θ \theta θ,衰减速率 ρ \rho ρ,动量系数 α \alpha α,初始速度 v v v
初始化梯度累计变量 r r r
迭代,直到停止条件
1、从训练集中随机采样m个样,对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、向前一步走: θ = θ + α v \theta = \theta + \alpha v θ=θ+αv
3、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、计算平方梯度: r ← ρ r + ( 1 − ρ ) g ⊙ g r \leftarrow \rho r+ (1-\rho)g \odot g r←ρr+(1−ρ)g⊙g
4、更新速度: v ← α v − η r ⊙ g v \leftarrow \alpha v - \frac{\eta}{\sqrt{r}}\odot g v←αv−rη⊙g
5、更新参数: θ ← θ + v \theta \leftarrow \theta +v θ←θ+v
6-Adam
adam除了引入自适应学习率,还引入了自适应动量,是目前最常用的优化算法。
Adam算法:
输入学习率 η \eta η,初始参数 θ \theta θ,矩估计的指数衰减速率 ρ 1 \rho_1 ρ1、 ρ 2 \rho_2 ρ2,默认值为0.9和0.999,小常数 δ \delta δ
初始化一阶矩和二阶矩 s 、 r s、r s、r
初始化时间步 t = 0 t=0 t=0
迭代,直到停止条件
1、从训练集中随机采样m个样,对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、更新时间: t ← t + 1 t \leftarrow t+1 t←t+1
4、更新一阶矩估计(梯度指数加权平均,更新梯度): s ← ρ 1 s + ( 1 − ρ 1 ) g s \leftarrow \rho_1 s+ (1-\rho_1)g s←ρ1s+(1−ρ1)g
5、更新二阶矩估计(梯度平方指数加权平均,更新学习率): r ← ρ 2 r + ( 1 − ρ 2 ) g ⊙ g r \leftarrow \rho_2 r+ (1-\rho_2)g \odot g r←ρ2r+(1−ρ2)g⊙g
6、修正一阶矩的偏差(剔除时间的影响): s ← s 1 − ρ 1 t s \leftarrow \frac{s}{1-\rho_1^t} s←1−ρ1ts
7、修正二阶矩的偏差(剔除时间的影响): r ← r 1 − ρ 2 t r \leftarrow \frac{r}{1-\rho_2^t} r←1−ρ2tr
8、更新参数: θ ← θ − η r + δ s \theta \leftarrow \theta -\frac{\eta}{\sqrt{r}+\delta}s θ←θ−r+δηs
7-RAdam
使用Adam算法,在训练初期,学习率的方差很大,因此有必要针对自适应的学习率,显式修正其方差,使得训练初期也能够使方差稳定。(没细看,不要问,问就王富贵)
RAdam算法:
输入学习率 η \eta η,初始参数 θ \theta θ,矩估计的指数衰减速率 ρ 1 \rho_1 ρ1、 ρ 2 \rho_2 ρ2,近似指数 ρ ∞ \rho_\infty ρ∞
初始化一阶矩和二阶矩 s 、 r s、r s、r,初始化加权平均长度 ρ ← 2 1 − ρ 2 − 1 \rho \leftarrow \frac{2}{1-\rho_2}-1 ρ←1−ρ22−1
初始化时间步 t = 0 t=0 t=0
迭代,直到停止条件
1、从训练集中随机采样m个样,对应标签为 { y 1 , y 2 , ⋯ , y m } \{ y_1, y_2, \cdots,y_m\} {y1,y2,⋯,ym}
2、计算梯度 g ← 1 m ∑ i = 1 m ∇ θ L ( f ( x i ; θ ) , y i ) g \leftarrow \frac{1}{m}\sum\limits _{i=1}^m \nabla _\theta L(f(x_i;\theta), y_i) g←m1i=1∑m∇θL(f(xi;θ),yi)3、更新时间: t ← t + 1 t \leftarrow t+1 t←t+1
4、更新一阶矩估计(梯度指数加权平均,更新梯度): s ← ρ 1 s + ( 1 − ρ 1 ) g s \leftarrow \rho_1 s+ (1-\rho_1)g s←ρ1s+(1−ρ1)g
5、更新二阶矩估计(梯度平方指数加权平均,更新学习率): r ← ρ 2 r + ( 1 − ρ 2 ) g ⊙ g r \leftarrow \rho_2 r+ (1-\rho_2)g \odot g r←ρ2r+(1−ρ2)g⊙g
6、修正一阶矩的偏差(剔除时间的影响): s ← s 1 − ρ 1 t s \leftarrow \frac{s}{1-\rho_1^t} s←1−ρ1ts
7、计算t时刻近似指数加权平均长度: ρ ← ρ ∞ − 2 t ρ 2 t 1 − ρ 2 t \rho \leftarrow \rho_\infty - \frac{2t\rho_2^t}{1-\rho_2^t} ρ←ρ∞−1−ρ2t2tρ2t
8、if ρ < 4 \rho<4 ρ<4, θ ← θ − η s \theta \leftarrow \theta -\eta s θ←θ−ηs
else r ← r 1 − ρ 2 t p ← ( ρ − 4 ) ( ρ − 2 ) ρ ∞ ( ρ ∞ − 4 ) ( ρ ∞ − 2 ) ρ θ ← θ − η r + δ s r\leftarrow \sqrt{\frac{r}{1-\rho_2^t}}\\ p \leftarrow \sqrt{\frac{(\rho-4)(\rho -2)\rho_\infty}{(\rho_\infty-4)(\rho_\infty-2)\rho}}\\ \theta \leftarrow \theta -\frac{\eta}{\sqrt{r}+\delta}s r←1−ρ2trp←(ρ∞−4)(ρ∞−2)ρ(ρ−4)(ρ−2)ρ∞θ←θ−r+δηs
8-Lookahead
lookahead不同于已有的优化算法,它使用了两组权重,与其说其是一种优化算法,我觉得更像是交叉应用。基本思想就是下象棋时候的“走一步看k步”,求出了一次梯度不也是更新一点吗?那我就先不更新,看看你再求几步是什么情况。
但是看似每次没用梯度值,那还是没少算呀?这叫“磨刀不误砍柴工”,就是说虽然这k次没少算,但最后到的点与k次计算的点是不一样的,虽然没有避免计算这k次,但避免了因为使用k次结果所在点继续往下走可能的徘徊(步履蹒跚走一些于整体无用的路);其次,使用k次计算点作为更新方向,最终到的点可能是使用梯度下降一辈子都不会探索的区域,就很有可能越过局部极值点。
官方名称里称这k步计算的权值为快权重,最终使用的是慢权重,计算快权重使用的优化器A可以是SGD、Adam任何一种。
lookahead算法:
输入初始化参数 θ \theta θ、 ϕ \phi ϕ,同步周期数 k k k,学习率 η \eta η,优化器A(任何一个之前介绍的优化器)
迭代,直到停止条件
1、同步参数: θ ← ϕ \theta \leftarrow \phi θ←ϕ
2、k次循环: θ ← θ + A \theta \leftarrow \theta + A θ←θ+A
3、用第k次结果更新: ϕ ← ϕ + η ( θ − ϕ ) \phi \leftarrow \phi + \eta (\theta-\phi) ϕ←ϕ+η(θ−ϕ)
参考
[1] 数值计算
[2] 优化器
[3] 机器学习:各种优化器的总结与比较
[4] 深度学习最全优化方法总结比较
[5] 代码仿真