1.原理
运用物理学上的动量思想,在梯度下降的问题中引入动量项 m m m 和折扣因子 γ \gamma γ,公式为: m t = γ m t + 1 m_t=\gamma m_{t+1} mt=γmt+1其中 m m m 是动量项 m m m的指数加权平均后的值, γ \gamma γ表示历史梯度的影响力,也就是权重值, γ \gamma γ越大,权重越大。从直观上看,如果当前时刻的梯度与历史梯度方向趋近,这种趋势会在当前时刻加强,否则减弱。
2.指数加权平均
在分析动量算法之前,我们先来了解指数加权平均的含义。假定给一个序列,例如北京一年每天的气温值,图中蓝色的点代表真实数据:
这时温度值波动比较大,我们使用加权平均值来进行平滑,如下图红线就是平滑后的结果:
计算方法如下所示:
S
t
=
{
Y
1
,
t
=
1
β
S
t
−
1
+
(
1
−
β
)
Y
t
,
t
>
1
S_t= \begin{cases} Y_1,\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ t=1\\ \beta S_t-1+(1-\beta)Y_t, t>1 \end{cases}
St={Y1, t=1βSt−1+(1−β)Yt,t>1
其中
Y
t
Y_t
Yt为
t
t
t时刻的真实值,
S
t
S_t
St为
t
t
t时刻加权后的平均值,
β
\beta
β为权重值。红线即是指数加权平均后的结果。
上图中
β
\beta
β设为0.9,那么指数加权平均的计算结果为:
S 1 = Y 1 S_1=Y_1 S1=Y1
S 2 = 0.9 S 1 + 0.1 Y 2 S_2=0.9S_1+0.1Y_2 S2=0.9S1+0.1Y2
S 3 = 0.9 S 2 + 0.1 Y 3 S_3=0.9S_2+0.1Y_3 S3=0.9S2+0.1Y3
⋯ \cdots ⋯
S 99 = 0.9 S 98 + 0.1 Y 99 S_{99}=0.9S_{98}+0.1Y_{99} S99=0.9S98+0.1Y99
S
100
=
0.9
S
99
+
0.1
Y
100
=
0.9
(
0.9
S
98
+
0.1
Y
99
)
+
0.1
Y
100
=
0.9
×
0.9
S
98
+
0.09
Y
99
+
0.1
Y
100
=
0.9
×
0.9
(
0.9
S
97
+
0.1
Y
98
)
+
0.1
×
0.9
Y
99
+
0.1
Y
100
=
0.1
Y
100
+
0.1
×
(
0.9
)
1
Y
99
+
0.1
×
(
0.9
)
2
Y
98
+
⋯
+
0.1
×
(
0.9
)
99
Y
1
\begin{aligned} S_{100}&=0.9S_{99}+0.1Y_{100}\\ &=0.9(0.9S_{98}+0.1Y_{99})+0.1Y_{100}\\ &=0.9\times0.9S_{98}+0.09Y_{99}+0.1Y_{100}\\ &=0.9\times0.9(0.9S_{97}+0.1Y_{98})+0.1\times 0.9Y_{99}+0.1Y_{100}\\ &=0.1Y_{100}+0.1\times(0.9)^1Y_{99}+0.1\times(0.9)^2Y_{98}+\cdots+0.1\times(0.9)^{99}Y_1 \end{aligned}
S100=0.9S99+0.1Y100=0.9(0.9S98+0.1Y99)+0.1Y100=0.9×0.9S98+0.09Y99+0.1Y100=0.9×0.9(0.9S97+0.1Y98)+0.1×0.9Y99+0.1Y100=0.1Y100+0.1×(0.9)1Y99+0.1×(0.9)2Y98+⋯+0.1×(0.9)99Y1
从上边式子可知:实质上是以指数式递减加权的移动平均。各数值的加权随时间而呈指数式递减,越近期的数据加权越重,越旧的数据加权越小,但是仍有相应的加权。我们可以看到指数加权平均的求解过程实际上是一个递推的过程,那么这样就会有一个非常大的好处,每当我要求从0到某一时刻(n)的平均值的时候,并不需要像普通求解平均值那样,保留所有时刻值,求和,然后除以n。而是只需要保留0~(n-1)时刻的平均值和n时刻的温度值即可。也就是每次只需要保留常数值,然后进行运算即可,这对于深度学习中的海量数据来说,是一个很好的减少内存和空间的做法。另外,
β
\beta
β值设置的越大,曲线越平滑,相反,曲线越震荡。
3 动量梯度下降算法
动量梯度下降(Gradient Descent with Momentum)计算梯度的指数加权平均数,并利用该值来更新参数值。
S d W = β S d W + ( 1 − β ) d W S_{dW}=\beta S_{dW}+(1-\beta)dW SdW=βSdW+(1−β)dW
S d b = β S d b + ( 1 − β ) d b S_{db}=\beta S_{db}+(1-\beta)db Sdb=βSdb+(1−β)db
因此:
W = W − α S d W W=W-\alpha S_{dW} W=W−αSdW
b = b − α S d b b=b-\alpha S_{db} b=b−αSdb
与原始的梯度下降相比,动量梯度下降趋势更加平滑。
在tf.keras中使用Momentum算法仍使用功能SGD方法,但要设置momentum参数,实现过程如下:
# 导入相应的工具包
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