吴恩达deeplearning.ai专项课程
指数加权(滑动)平均 exponentially weighted(moving)averages
$vt=\beta v\{t-1}+(1-\beta) \thetat$
$v{0}=0$
$\beta = 0.9$
其中$vt$为迭代次数为t的时刻的指数加权平均值,我们一般设置$v_0=0$,$\beta = 0.9$。
指数加权平均大概为最近$\frac{1}{1-\beta}$次迭代的平均,因为$(1-(1-\beta))^{\frac{1}{1-\beta}}\approx \frac{1}{e}$。
提示:$\lim {x \rightarrow \infty}\left(1-\frac{1}{x}\right)^x=\frac{1}{\mathrm{e}}$
偏差修正bias correction
因为$v_0=0$,所以指数加权平均刚开始偏差比较大。这时候我们可以用偏差修正bias correction:
$v_t:=\frac{v_t}{1-\beta^t}$
指数加权平均在迭代几步之后一般都没什么偏差了,开始的偏差无伤大雅,所以偏差修正一般很少用。
梯度下降的Challenges
1.很难选择合适的学习率$\alpha$。学习率太小收敛很慢,学习率太大会阻碍收敛,导致损失函数在极小值附近震荡甚至发散。
2.梯度下降不应该对所有的参数使用相同的学习率$\alpha$,如果我们的数据是稀疏的,对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些。对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。
3.对于非凸的损失函数的优化,关键挑战在于避免陷入次优的局部极小值(suboptimal local minima),还有鞍点(saddle points)。鞍点附近损失函数数值接近,形状像一个平台,梯度在所有维度上都接近于零,这使得SGD很难逃脱。
一个框架回顾优化算法
深度学习优化算法经历了 SGD -> SGDM -> NAG -> AdaGrad -> AdaDelta -> Adam -> Nadam 这样的发展历程。这里我们用一个框架来梳理所有的优化算法,做一个对比。
首先定义:待优化参数:$w$,目标函数:$f(w)$,初始学习率$\alpha$。
而后,开始进行迭代优化。在每个epoch t:
1.计算目标函数关于当前参数的梯度:$gt=\nabla f\left(w_t\right)$
2.根据历史梯度计算梯度的一阶矩first moment和二阶矩second moment:$m_t=\phi\left(g_1, g_2, \cdots, g_t\right) ; V_t=\psi\left(g_1, g_2, \cdots, g_t\right)$
3.计算当前时刻的下降梯度:$\eta_t=\frac{\alpha}{\sqrt{V_t}+\epsilon} m_t$
4.根据下降梯度进行更新:$w{t+1}=w_t-\eta_t$
SGD with Momentum
如果损失函数是狭长沟壑状的,梯度下降常常会在沟壑里震荡。momentum方法能帮助SGD加快收敛并抑制振荡。SGDM全称是SGD with momentum,在SGD基础上引入了一阶动量:
一阶动量是各个时刻梯度的指数加权平均值,约等于最近$1 /\left(1-\beta_1\right)$个时刻的梯度向量和的平均值。
也就是说,t时刻的下降方向,不仅由当前点的梯度方向决定,而且由此前累积的下降方向决定。$\beta_1$的经验值为0.9,这就意味着下降方向主要是此前累积的下降方向,并略微偏向当前时刻的下降方向。
Nesterov accelerated gradient
NAG全称Nesterov Accelerated Gradient,是在SGD、SGD-M的基础上的进一步改进,改进点在于步骤1。我们知道在时刻t的下降方向主要是由累积动量决定的,自己目前梯度方向只决定0.1,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,NAG在步骤1,不计算当前位置的梯度方向,而是计算如果按照累积动量走了一步,那个时候的下降方向:
AdaGrad
此前我们都没有用到二阶动量。二阶动量的出现,才意味着“自适应学习率”优化算法时代的到来。SGD及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到(想想大规模的embedding)。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。
怎么样去度量历史更新频率呢?那就是二阶动量,对应于某一维度上,迄今为止所有梯度值的平方和:
t代表t时刻,i代表向量的第i个分量
我们再回顾一下步骤3中的下降梯度:
可以看出,此时实质上的学习率由$\alpha$变成了$\frac{\alpha}{\sqrt{V_t}}$,$\alpha$一般设置为0.01。 一般为了避免分母为0,会在分母上加一个小的平滑项$\epsilon$(通常是1e−8的量级)。因此$\sqrt{V_t}+\epsilon$是恒大于0的。而且某一维度上参数更新越频繁,这个维度上二阶动量越大,学习率就越小。
这一方法在稀疏数据场景下表现非常好。但也存在一些问题:因为$\sqrt{V_t}+\epsilon$是单调递增的,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法有效学习
AdaDelta / RMSProp
RMSProp全称:Root Mean Square prop
由于AdaGrad单调递减的学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。
修改的思路很简单。前面我们讲到,指数加权平均值大约就是过去$\frac{1}{1-\beta_1}$个时刻的平均值,因此我们用这一方法来计算二阶累积动量:
这就避免了二阶动量持续累积,导致训练过程提前结束的问题了。
Adam(Adaptive Moment Estimation):自适应矩估计
谈到这里,Adam和Nadam的出现就很自然而然了——它们是前述方法的集大成者。我们看到,SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。
SGD的一阶动量:
加上AdaDelta的二阶动量:
优化算法里最常见的两个超参数$\beta_1, \beta_2$就都在这里了,前者控制一阶动量,后者控制二阶动量。实际使用过程中,参数的经验值是:
$\beta_1=0.9, \beta_2=0.999$
初始化:$m_0=0, V_0=0$
在初期,$m_t, V_t$都会接近于0,这个估计是有问题的。因此我们常常根据下式进行指数移动平均值的偏差修正:
$\tilde{m}_t=m_t /\left(1-\beta_1^t\right)$
$\tilde{V}_t=V_t /\left(1-\beta_2^t\right)$
Nadam
我们说Adam是集大成者,把Adam加上Nesterov,就是Nadam:
这就是Nesterov + Adam = Nadam了。
Adam的缺点:
1.可能不收敛
AdaDelta和Adam的二阶动量是固定时间窗口内的累积,随着时间窗口的变化,遇到的数据可能发生巨变,使得$Vt$可能会时大时小,不是单调变化。这就可能在训练后期引起学习率的震荡,导致模型无法收敛。可以令:
$V_t=\max \left(\beta_2 \cdot V{t-1}+\left(1-\beta2\right) g_t^2, V{t-1}\right)$
通过这样的修改,就保证了$\left|Vt\right| \geq\left|V{t-1}\right|$,从而使学习率单调递减
2.可能错过全局最优
深度神经网络往往包含大量的参数,在这样一个维度极高的空间内,非凸的目标函数往往起起伏伏,拥有无数个高地和洼地。
改进Adam的方法:前期用Adam,享受Adam快速收敛的优势;后期切换到SGD,慢慢寻找最优解。