Standard Gradient Update
沿着梯度的反方向改变参数(梯度指示增长的方向,我们通常希望最小化损失函数)。假设一个参数向量x和梯度dx,其更新公式为:
x = x - learning_rate * g
Momentum
解决SGD中出现的曲折摆动问题,如图所示,“之”字形的上下摆动,降低了损失函数到达最低点的速度。此情况下,若想减少摆动浮动,只能采用比较小的learning rate,这同样将导致寻优的速度较低。而Momentum就是为解决此问题而来。
动量是衡量让运动物体停下难度的物理量。当动量momentum越大时,其转换为势能的能量也就越大,越易摆动,难以停下来。
动量法几乎总是比标准的梯度下降法速度更快,算法的主要思想是计算梯度的指数加权平均,然后使用这个梯度来更新权重(上图中,你希望纵轴可以学习慢一点,不希望出现这些震荡,横轴上,你希望加快学习速度)
可以理解为,在到达新的一点时,SGD会直接按照该点的负梯度方向去更新,而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]
return params
v = mu * v - learning_rate * g # integrate velocity
x =x + v = x + mu *v - learning_rate * g # integrate position
# 导入相应的工具包
import tensorflow as tf
# 实例化优化方法:SGD 指定参数beta=0.9
opt = tf.keras.optimizers.SGD(learning_rate=0.1, momentum=0.9)
# 定义要调整的参数,初始值
var = tf.Variable(1.0)
val0 = var.value()
# 定义损失函数
loss = lambda: (var ** 2)/2.0
#第一次更新:计算梯度,并对参数进行更新,步长为 `- learning_rate * grad`
opt.minimize(loss, [var]).numpy()
val1 = var.value()
# 第二次更新:计算梯度,并对参数进行更新,因为加入了momentum,步长会增加
opt.minimize(loss, [var]).numpy()
val2 = var.value()
# 打印两次更新的步长
print("第一次更新步长={}".format((val0 - val1).numpy()))
print("第二次更新步长={}".format((val1 - val2).numpy()))
#第一次更新步长=0.10000002384185791
#第二次更新步长=0.18000000715255737
参考
https://blog.csdn.net/weixin_46713695/article/details/123197350
https://zhuanlan.zhihu.com/p/363052827
https://blog.csdn.net/m0_58475958/article/details/119207932
https://github.com/keras-team/keras/blob/v2.13.1/keras/optimizers/sgd.py#L26