深度学习(一)优化算法之动量法详解

使用梯度下降法,每次都会朝着目标函数下降最快的方向,这也称为最速下降法。这种更新方法看似非常快,实际上存在一些问题。

考虑一个二维输入, [ x 1 , x 2 ] [x_1, x_2] [x1?,x2?],输出的损失函数 L : R 2 → R L: R^2 \rightarrow R L:R2R,下面是这个函数的等高线:

在这里插入图片描述

可以想象成一个很扁的漏斗,这样在竖直方向上,梯度就非常大,在水平方向上,梯度就相对较小,所以我们在设置学习率的时候就不能设置太大,为了防止竖直方向上参数更新太过了,这样一个较小的学习率又导致了水平方向上参数在更新的时候太过于缓慢,所以就导致最终收敛起来非常慢。

动量法的提出就是为了应对这个问题,我们梯度下降法做一个修改如下:

v i = γ v i ? 1 + η ? L ( θ ) v_i = \gamma v_{i-1} + \eta abla L( heta) vi?=γvi?1?+η?L(θ)

θ i = θ i ? 1 ? v i heta _i = heta_{i-1} - v_i θi?=θi?1??vi?

其中 v i v_i vi? 是当前速度, γ \gamma γ 是动量参数,是一个小于 1的正数, η \eta η 是学习率

相当于每次在进行参数更新的时候,都会将之前的速度考虑进来,每个参数在各方向上的移动幅度不仅取决于当前的梯度,还取决于过去各个梯度在各个方向上是否一致,如果一个梯度一直沿着当前方向进行更新,那么每次更新的幅度就越来越大,如果一个梯度在一个方向上不断变化,那么其更新幅度就会被衰减,这样我们就可以使用一个较大的学习率,使得收敛更快,同时梯度比较大的方向就会因为动量的关系每次更新的幅度减少,如下图

在这里插入图片描述

比如我们的梯度每次都等于 g,而且方向都相同,那么动量法在该方向上使参数加速移动,有下面的公式:

v 0 = 0 v_0 = 0 v0?=0

v 1 = γ v 0 + η g = η g v_1 = \gamma v_0 + \eta g = \eta g v1?=γv0?+ηg=ηg

v 2 = γ v 1 + η g = ( 1 + γ ) η g v_2 = \gamma v_1 + \eta g = (1 + \gamma) \eta g v2?=γv1?+ηg=(1+γ)ηg

v 3 = γ v 2 + η g = ( 1 + γ + γ 2 ) η g v_3 = \gamma v_2 + \eta g = (1 + \gamma + \gamma^2) \eta g v3?=γv2?+ηg=(1+γ+γ2)ηg

? \cdots ?

v + ∞ = ( 1 + γ + γ 2 + γ 3 + ? ? ) η g = 1 1 ? γ η g v_{+ \infty} = (1 + \gamma + \gamma^2 + \gamma^3 + \cdots) \eta g = \frac{1}{1 - \gamma} \eta g v+?=(1+γ+γ2+γ3+?)ηg=1?γ1?ηg

如果我们把 γ \gamma γ 定为 0.9,那么更新幅度的峰值就是原本梯度乘学习率的 10 倍。

本质上说,动量法就仿佛我们从高坡上推一个球,小球在向下滚动的过程中积累了动量,在途中也会变得越来越快,最后会达到一个峰值,对应于我们的算法中就是,动量项会沿着梯度指向方向相同的方向不断增大,对于梯度方向改变的方向逐渐减小,得到了更快的收敛速度以及更小的震荡。

下面我们手动实现一个动量法,公式已经在上面了:

 
 
 
 
 

可以看到,加完动量之后 loss 能下降非常快,但是一定要小心学习率和动量参数,这两个值会直接影响到参数每次更新的幅度,所以可以多试几个值

当然, 内置了动量法的实现,非常简单,直接在 即可,下面实现一下

 
 
 
 
 
 
 

在这里插入图片描述

我们可以对比一下不加动量的随机梯度下降法

 
 

补充:格式化输出

a= 1.23333233313

  • print(‘a:{:.6f}’.format(a))
  • print(f’a:{a:.6f}’)
    两种输出形式是一样的
 
 

在这里插入图片描述

可以看到加完动量之后的 loss 下降的程度更低了,可以将动量理解为一种惯性作用,所以每次更新的幅度都会比不加动量的情况更多。

参考:PyTorch中文手册

平台注册入口