一、前置知识
(一)随机梯度下降
对于深度学习,目标函数常常是损失函数的平均值:
f
(
x
)
=
1
n
∑
i
=
1
n
f
i
(
x
)
f(x)=\frac{1}{n}\sum_{i=1}^n{f_i(x)}
f(x)=n1i=1∑nfi(x)
对上述目标函数求梯度,可以得到:
∇
f
(
x
)
=
1
n
∑
i
=
1
n
∇
f
i
(
x
)
\nabla f(x)=\frac{1}{n}\sum_{i=1}^n{\nabla f_i(x)}
∇f(x)=n1i=1∑n∇fi(x)
以上的计算开销过大,随着数据集的增长(
n
n
n的增长)而线性增长,故需要想办法减少开销。
通过随机梯度下降(SGD),在每次迭代过程中抽取一个
i
i
i,计算梯度:
∇
f
(
x
)
=
∇
f
i
(
x
)
\nabla f(x)={\nabla f_i(x)}
∇f(x)=∇fi(x)
这样每次迭代的计算复杂度从
O
(
n
)
O(n)
O(n)下降至
O
(
1
)
O(1)
O(1)。
我们来看看这样做产生的影响:
E
(
f
i
(
x
)
)
=
1
n
∑
i
=
1
n
∇
f
i
(
x
)
E(f_i(x))=\frac{1}{n}\sum_{i=1}^n{\nabla f_i(x)}
E(fi(x))=n1i=1∑n∇fi(x)
可以看出,随机梯度是对平均梯度的良好估计。
(二)小批量随机梯度下降
使用小批量的决策核心是计算效率。
1)第一次存取的额外开销很大,而按序存取和突发存取的开销较小,这主要是由于缓存命中和不命中造成的;
2)当数据集非常相似时,我们计算整个数据集的平均梯度和计算某一个子集上的平均梯度相差不大,对模型的优化效果是相似的;
3)现代CPU或GPU能够执行的操作数量(处理速度)远比主内存接口能够提供的(读取速度)多得多。
考虑以上三点因素,我们决定采用小批量来训练模型。
小批量为我们带来的另外一个好处就是减少了梯度的方差,使数值更加稳定。
(三)动态学习率
来看一下小批量随机梯度下降的参数更新公式:
x
=
x
−
η
∇
f
i
(
x
)
x=x-\eta \nabla f_i(x)
x=x−η∇fi(x)
其中
η
\eta
η被我们称为学习率。
当学习率选取过大,我们很难收敛到最优解(形象地来说就是跑得太快,以至于在目标点左右反复横跳,而无法停留在目标位置);
当学习率选取过小,我们很难对模型优化产生进展。
所以我们需要应用动态学习率。
动态学习率通常有三种策略:
1)分段常数: η ( t ) = η i ( t i ≤ t ≤ t i + 1 ) \eta(t)=\eta_i(t_i\le t\le t_{i+1}) η(t)=ηi(ti≤t≤ti+1);
2)指数衰减: η ( t ) = η 0 e − λ t \eta(t)=\eta_0e^{-\lambda t} η(t)=η0e−λt;
3)多项式衰减: η ( t ) = η 0 ( β t + 1 ) − α \eta(t)=\eta_0(\beta t+1)^{-\alpha} η(t)=η0(βt+1)−α。
最常见使用的是多项式衰减。
(四)动量法
我们用
g
t
g_t
gt来表示第
t
t
t个时间步的梯度,对于一般模型
f
(
x
,
w
)
f(x,w)
f(x,w)(
x
x
x是数据,
w
w
w是可学习参数),有:
g
t
=
∂
w
1
∣
B
t
∣
∑
i
∈
B
t
f
(
x
i
,
w
t
)
g_t=\partial_w\frac{1}{|B_t|}\sum_{i\in B_t}f(x_i,w_t)
gt=∂w∣Bt∣1i∈Bt∑f(xi,wt)
我们还记得,从小批量中获取到的益处有一条是减少了方差。动量法的目的也同样如此,为了减少方差,从中获取收益。
为此,动量法采用了泄露平均值(leaky average),计算公式为:
v
t
=
β
v
t
−
1
+
g
t
v_t=\beta v_{t-1}+g_t
vt=βvt−1+gt
其中,
v
v
v称为动量(momentum),
β
∈
(
0
,
1
)
\beta\in(0,1)
β∈(0,1)。
动量法的参数更新公式简单来说就是:
x
t
=
x
t
−
1
−
η
t
v
t
x_t=x_{t-1}-\eta_tv_t
xt=xt−1−ηtvt
动量法有效地将瞬时梯度替换为多个“过去”梯度的平均值,动量的指向不是特定实例下降最陡的方向,而是指向过去梯度的加权平均值的方向。
之所以叫做动量法是因为它给人一种“惯性”优化的直觉,和正在奔驰的汽车一样,不会立刻大幅度调转方向。这为我们提供了更稳定的下降方向,也允许我们在某些优化进展缓慢的方向加速优化。
二、Adam
在了解了前置知识之后,我们在介绍Adam之前再介绍另外两个优化算法:AdaGrad和RMSProp。
(一)AdaGrad
在之前介绍的动态学习率中,我们都是对学习率整体进行操作,忽略了每个参数的差异性。
举个例子,假设我们正在训练一个语言模型,在语料库中有常见词元和稀有词元。在学习率衰减到一个很小的值(近乎停止)的过程中,我们观察到了很多次的常见词元,因此它得到了充分的优化;但与此同时,我们仅仅观察到了很少的稀有词元,可是优化已经停止了。
对于这一问题,AdaGrad的解决方法如下:
η
i
=
η
0
s
(
i
,
t
)
+
c
\eta_i=\frac{\eta_0}{\sqrt{s(i,t)+c}}
ηi=s(i,t)+cη0
其中,
s
(
i
,
t
)
s(i,t)
s(i,t)为“过去”观测所得的梯度平方的累加和,它的计算公式为:
s
(
i
,
t
+
1
)
=
s
(
i
,
t
)
+
(
g
t
)
2
s(i,t+1)=s(i,t)+(g_t)^2
s(i,t+1)=s(i,t)+(gt)2
通常对应于梯度较大的坐标会显著缩小,而梯度较小的坐标会得到平滑处理。
(二)RMSProp
然而以上的算法还是存在一点问题:缺乏规范化,没有约束力,在算法收敛的同时, s t s_t st几乎是呈现线性增长。
对此,采用泄露平均值:
s
t
+
1
=
γ
s
t
+
(
1
−
γ
)
(
g
t
)
2
s_{t+1}=\gamma s_t+(1-\gamma )(g_t)^2
st+1=γst+(1−γ)(gt)2
改进
s
t
s_t
st计算方式后的AdaGrad算法就是RMSProp算法。
(三)Adam
最后,就到了我们的主角Adam了。与RMSProp相比,Adam有两点不同:
RMSProp的参数更新公式为:
x
t
=
x
t
−
1
−
η
0
s
t
+
c
g
t
x_t=x_{t-1}-\frac{\eta_0}{\sqrt{s_t+c}}g_t
xt=xt−1−st+cη0gt
在此基础上,Adam将
g
t
g_t
gt梯度也进行了平滑优化:
x
t
=
x
t
−
1
−
η
0
s
t
+
c
v
t
x_t=x_{t-1}-\frac{\eta_0}{\sqrt{s_t+c}}v_t
xt=xt−1−st+cη0vt
其中,
s
t
,
v
t
s_t,v_t
st,vt的更新公式如下:
v
t
=
β
1
v
t
−
1
+
(
1
−
β
1
)
g
t
s
t
=
β
2
s
t
−
1
+
(
1
−
β
2
)
g
t
2
v_t=\beta_1v_{t-1}+(1-\beta_1)g_t\\ s_t=\beta_2s_{t-1}+(1-\beta_2)g_t^2
vt=β1vt−1+(1−β1)gtst=β2st−1+(1−β2)gt2