入门神经网络优化算法(一):Gradient Descent,Momentum,Nesterov accelerated gradient

梯度下降

基于梯度的优化算法,Gradient based optimization,也往往被称为一阶优化算法。所以很容易猜到,还有二阶优化算法等的高阶优化算法,但是在实际应用中,基于梯度的一阶优化算法是目前的绝对主流方法(当前是,2~3年以后未必),本文就重点罗列一下基于梯度的优化算法。

最典型以及简单的是:梯度下降算法。梯度下降法是神经网络求解优化中最常用的一类算法(实际上是在数值优化方法里的一种常用方法,常常用以求解连续可微函数的局部极小值)。

  • gradient descent:梯度下降,又叫做Vanilla gradient descent,或者Batch gradient descent,特指用整个数据集来计算变量的梯度,它的形式是这样
    θ = θ − η ⋅ ∇ θ J ( θ ) \theta = \theta - \eta \cdot \nabla_\theta J( \theta) θ=θηθJ(θ)
    其中 θ \theta θ是求解法变量或者叫weight,也叫parameter, J J J是cost function或者叫loss function, η \eta η是学习率learning rate。相信很多人初学优化算法一定会看资料[1],其中有说道:Batch gradient descent is guaranteed to converge to the global minimum for convex error surfaces and to a local minimum for non-convex surfaces[1]。前半句global minimum for convex是对的,后半句不完全对,除了local minimum也可能会遇到鞍点(saddle point),以及大片的平坦区域(plateau)。

  • stochastic gradient descent:随机梯度下降,最早提出来是相反于gd每次要用所有的数据,而每次更新只用一个样本:
    θ = θ − η ⋅ ∇ θ J ( θ ; x ( i ) , y ( i ) ) \theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i)}, y^{(i)}) θ=θηθJ(θ;x(i),y(i))
    最大的不同就是只用一个样本来计算梯度,这样做的好处是计算一次梯度代价是O(1),但是往往需要反复计算,而且一般variance比较大,学习率比较难调,容易震荡甚至不收敛。所以实际上我们平时用的最多的是mini-batch stochastic gradient descent,很容易想到,是一个折中方案——每次取一个小batch来计算梯度(平均梯度),比如常用的batch = 32,128,256等,通过一个batch既一定程度上减少了方差,也不会太多增加单次梯度计算cost。然后神奇的地方是,现在的神经网络,只要喂进去数据,用一些default的学习率策略,比如multi-step(0.1),或者poly,总能够收敛,真是香。当然,需要知道,现在很少有人直接用基本的SGD了(还是有些不稳定),而是用Momentum SGD,或者用Nesterov Momentum SGD,我个人觉得这两个优化器才是深度学习算法如此低门槛(好用)可用的关键,虽然说有一阶需要调节的超参数,但是实际上都有一些default经验值非常好用。

Momentum SGD,Nesterov accelerated gradient

先统一下叫法,我们指的Momentum SGD算法一般是指最简单的Polyak‘s momentum(Boris Polyak在1964年提出),又名Heavy Ball算法。Nesterov‘s momentum也就是我们一般指的Nesterov accelerated gradient(NAG)。

  • Momentum SGD:动量SGD,最简单的理解就是在梯度上做一个moving avg,如下形式:
    v t = m v t − 1 − η ∇ θ J ( θ t ) θ t + 1 = θ t + v t v_t = m v_{t-1} - \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t + v_t vt=mvt1ηθJ(θt)θt+1=θt+vt
    其中 m m m叫做momentum parameter, m ∈ [ 0 , 1 ) m \in [0,1) m[0,1),当 m = 0 m=0 m=0的时候,就退化成了原始的Vanilla Gradient Method (VGM)。 η ≥ 0 \eta\ge0 η0是学习率,在这里假设 m m m η \eta η都是常数,因此不写下标了。注意的是,在不同的文献或代码中,有几种实现方式,都是等价的。比如把加减号换下位置:
    v t = m v t − 1 + η ∇ θ J ( θ t ) θ t + 1 = θ t − v t v_t = m v_{t-1} + \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t - v_t vt=mvt1+ηθJ(θt)θt+1=θtvt
    或者把学习率写在更新项上(第二行):
    v t = m v t − 1 − ∇ θ J ( θ t ) θ t + 1 = θ t + η v t v_t = m v_{t-1} - \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t + \eta v_t vt=mvt1θJ(θt)θt+1=θt+ηvt
    当然这个是假设lr是一个常数,所以可以提出来,如果不是一个常数,形式上要想提出来就没那么简单了。但是因为lr本身也很经验性去调,实际中好像也没什么关系。这里我还要再推一个形式,是比较有意思的(看上面第一种形式):
    θ t + 1 = θ t + m v t − 1 − η ∇ θ J ( θ t ) θ t + 1 = θ t − η ∇ θ J ( θ t ) + m ( θ t − θ t − 1 ) \theta_{t+1} = \theta_t + m v_{t-1} - \eta \nabla_\theta J( \theta_t) \\ \theta_{t+1} = \theta_t - \eta \nabla_\theta J( \theta_t) + m (\theta_{t} - \theta_{t-1}) θt+1=θt+mvt1ηθJ(θt)θt+1=θtηθJ(θt)+m(θtθt1)
    这个的解释看这一段很有启发[3]:

其实momentum的中文就是高中物理学到的动量。之所以被称作动量,是因为使用momentum的初衷,就是模拟有质量的粒子在有阻尼的场里运动轨迹 。空间内的反梯度方向就是场的作用力方向,粒子会从能量高的位置(初始解)向能量低的位置(最优解)移动,momentum parameter决定阻尼系数。因为粒子有质量,所以具有惯性,而惯性的定性定义是为物体抵抗“动量”改变的性质。换句话说,粒子具有保持静止状态或匀速直线运动状态的性质,所以粒子的运动的瞬时方向,会是力的作用方向和轨迹切线方向的一个组合。
这个物理解释可以用于理解MGM的加速效果,粒子从起点到距离终点 [公式] 距离的时间可以解释为算法的迭代复杂度(iteration complexity)。如果阻尼小,会使得粒子产生很高的移动速度,但过大的动量会使得粒子震荡,并非保持向终点移动。而当阻尼过大,又会导致粒子移动过于缓慢的话,会花很久时间接近终点,同时震荡会减小。那么如何选择最优的设置阻尼,这就是已有的大量文献里提供的参数选择了。知道了这个简单的物理解释,其实就可以理解为什么momentum会被称作overshoot了。因为vanilla gradient method其实对应0质量粒子,所以粒子不具备惯性,会始终按作用力方向运动(加速度无穷大)。

很多帖子里写到动量SGD会用类似下面这个图,说左边是sgd,而右边是带动量的。其实这个图的本意是说动量的会快一些,但是实际上对sgd的示意图有问题。可以参考【3】进行理解,我这里就不赘述了。
入门神经网络优化算法(一):Gradient Descent,Momentum,Nesterov accelerated gradient_第1张图片

  • Nesterov‘s momentum:NAG算法是Yurii Nesterov在1983年提出的对冲量梯度下降算法的改进版本,其速度更快。其变化之处在于计算“超前梯度”更新冲量项
    v t = γ v t − 1 − η ∇ θ J ( θ t + γ v t − 1 ) θ t + 1 = θ t + v t v_t = \gamma v_{t-1} - \eta \nabla_\theta J( \theta_t + \gamma v_{t-1} ) \\ \theta_{t+1} = \theta_t + v_t vt=γvt1ηθJ(θt+γvt1)θt+1=θt+vt
    一个NAG计算的梯度的更新方向的示意图如下:
    入门神经网络优化算法(一):Gradient Descent,Momentum,Nesterov accelerated gradient_第2张图片

While Momentum first computes the current gradient (small blue vector) and then takes a big jump in the direction of the updated accumulated gradient (big blue vector), NAG first makes a big jump in the direction of the previous accumulated gradient (brown vector), measures the gradient and then makes a correction (red vector), which results in the complete NAG update (green vector).

以上,总结来看可以看下TensorFlow的描述https://tensorflow.google.cn/api_docs/python/tf/keras/optimizers/SGD?hl=en

v(t+1) = momentum * v(t) - learning_rate * gradient
theta(t+1) = theta(t) + v(t+1)
if `nesterov` is False, gradient is evaluated at theta(t).
if `nesterov` is True, gradient is evaluated at theta(t) + momentum * v(t),
    and the variables always store theta + m v instead of theta

本篇就到这里,下一篇继续。

参考资料

[1] https://ruder.io/optimizing-gradient-descent/index.html#gradientdescentvariants
[2] 数值优化(Numerical Optimization)学习系列-目录
[3] 知乎:常见的关于momentum的误解(上)
[4] 知乎:一文看懂常用的梯度下降算法
[5] 知乎:Nesterov加速和Momentum动量方法

你可能感兴趣的:(机器学习,Machine,Learning,深度学习,Deep,Learning)