Momentum
欲掌握本节知识,需先学习:指数加权平均
背景
Momentum是为解决SGD中出现的曲折摆动问题,如图所示,“之”字形的上下摆动,降低了损失函数到达最低点的速度。此情况下,若想减少摆动浮动,只能采用比较小的learning rate,这同样将导致寻优的速度较低。而Momentum就是为解决此问题而来。参考:SGD原理及其缺点
什么是动量?
动量是衡量让运动物体停下难度的物理量。当动量momentum越大时,其转换为势能的能量也就越大,越易摆动,难以停下来。
动量法几乎总是比标准的梯度下降法速度更快,算法的主要思想是计算梯度的指数加权平均,然后使用这个梯度来更新权重(上图中,你希望纵轴可以学习慢一点,不希望出现这些震荡,横轴上,你希望加快学习速度)
可以理解为,在到达新的一点时,SGD会直接按照该点的负梯度方向去更新,而Momentum会考虑之前的梯度及方向,即动量。更新的时候考虑梯度均值(指数加权平均),指数衰减理解为摩擦力造成的损失。
黑色虚线是负梯度方向,红色表示的是梯度,绿色表示更新方向,蓝色虚线就是动量。
Momentum 是“动量”的意思,和物理有关。用数学式表示 Momentum 方法,如下所示。
和前面的 SGD 一样, 表示要更新的权重参数(位置),∂L/∂W 表示损失函数关于 权重
W
W
W 的梯度(加速度),
η
η
η 表示学习率。这里新出现了一个变量
v
v
v ,对应物理上的动量。式(6.3)表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加这一物理法则。如图 6-4 所示,Momentum 方法给人的感觉就像是小球在地面上滚动。
式(6.3)中有 这一项。在物体不受任何力时,该项承担使物体逐渐减速的任务( α α α 设定为 0.9 之类的值),对应物理上的地面摩擦或空气阻力。下面是 Momentum 的代码实现。
class Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr #学习率
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
实例变量 v v v 会保存物体的速度。初始化时, v v v 中什么都不保存,但当第一次调用 update() 时, v v v 会以字典型变量的形式保存与参数结构相同的数据。剩余的代码部分就是将式(6.3)、式(6.4)写出来,很简单。
现在尝试使用 Momentum 解决式(6.2)的最优化问题,如图 6-5 所示。
图 6-5 中,更新路径就像小球在碗中滚动一样。和 SGD(随机梯度下降) 相比,我们发现“之”字形的“程度”减轻了。
这是因为虽然 x x x 轴方向上受到的力非常小,但是一直在同一方向上受力,所以朝同一个方向会有一定的加速。
反过来,虽然 y y y 轴方向上受到的力很大,但是因为交互地受到正方向和反方向的力,它们会互相抵消,所以 y y y 轴方向上的速度不稳定。因此,和 SGD 时的情形相比,可以更快地朝 x x x 轴方向靠近,减弱“之”字形的变动程度。