深度学习模型的优化算法

本文讲解训练深度学习模型的优化方法,包括优化器初始化参数的策略。

问题的引入

一个有偏置项的全连接网络可以通过如下方式定义:

zi+1=σi(WiTzi+bi), i=1,...,LhθzL+1z1x

其中,σi 为非线性激活函数,ziRniWiRni×ni+1biRni+1,需要训练的参数为 θ={W1:L,b1:L}

当一个 batch 有多个样本时,则可写成矩阵的形式如下:

Zi+1=σi(ZiWi+1biT)

注意,biRni+1,而 Zi+1Rm×ni+1,为了保证能够正确相加,则需要用 1Rm+1 与之相乘。而在实践中,通常并不这样做,而是利用广播机制将其自动对齐。因此,上式也可以直接写为:

Zi+1=σi(ZiWi+biT)

为了去训练一个全连接网络或者任何深度学习模型,我们不得不思考如下几个问题:

优化算法

这里仅讨论梯度算法,先考虑以下两类:

综合以上算法,使用一个以上而又不是全部的训练样本就有了深度学习中最常用的小批量(minibatch)随机梯度下降法,现在,通常也将其叫作随机梯度下降法,后文皆用该名称指代。

随机梯度下降法

提到优化算法,首先想到的就是随机梯度下降法(SGD)。每次抽取 m 个小批量的样本,通过计算它们的梯度均值,即可得到梯度的估计 g^,算法如下:

o60ly

优点:

缺点:

比如,对于 θR2,考虑函数 f(θ)=12θTPθ+qTθ,它对于不同学习率的优化情况如图所示:

8h1hj

牛顿法

考虑上面随机梯度下降算法的缺点,一个重要的方面就是震荡问题,如何减轻震荡呢?牛顿法就是这样一种更全局的方法,它在选择下一步的更新方向时,不仅仅考虑一阶导数最大的方向,还会考虑二阶导数,即走完这一步后,导数会不会变得更大。

参数更新步骤如下:

θθα(θ2f(θ))1θf(θ)

其中,θ2f(θ)Rn×n 为 Hessian 矩阵。

牛顿法默认 α=1,同样求解上面的方程,效果如图:

z9o8l

优点:

缺点:

动量法

受以上两种方法的启发,考虑在梯度下降法中结合一些像牛顿法一样的全局结构,这就是动量法的思想。动量法引入动量项 u ,和折扣因子 β,更新步骤如下:

ut+1=βut+(1β)θf(θt)θt+1=θtαut+1

v 本质是负梯度的指数衰减平均,保留了前面的梯度信息。当许多连续的梯度与当前梯度方向相同时,步长最大,否则,前面的梯度就起到了平滑的作用,很好地抑制了震荡。

从物理角度上来理解,可以将该算法视为牛顿力学下的粒子运动,当前的负梯度为粒子受到的力,目标函数的值为粒子的位置。那么每一步的负梯度也就是粒子受到的力会改变粒子的动量,当力的方向改变时,则根据矢量加法得到粒子下一步的动量方向,这也解释了该算法求历史负梯度平均的意义。

下图就是普通随机梯度下降法(α=0)与使用动量的随机梯度下降法对比:

gskxb

动量在计算初期很小,参数更新会很慢,通常在更新时给其乘以一个按时间衰减的系数:

θt+1=θtα1βt+1vt

Nesterov 动量法

这是动量法的一个改进。它的改变在于动量的更新:它根据超前一步的梯度来更新动量,更新步骤如下:

θ~t+1=θtαutg=θ~f(θ~t+1)ut+1=βut+(1β)gθt+1=θtαut+1

事实上,也可以写成下面的形式:

ut+1=βut+(1β)θf(θtαut)θt+1=θtαut+1

这种改进本质上是考虑了目标函数的二阶导信息,所以有了更快的收敛速度。这里不详细推导,可以参考下面这篇知乎文章:

比Momentum更快:揭开Nesterov Accelerated Gradient的真面目 - 知乎 (zhihu.com)

如图是二者的对比:

x3mg9

Adam 算法

为了解决学习率的设置问题,近年来提出了一些自适应学习率算法,本文只介绍 Adam 算法。这个算法也可以看作结合了动量的算法,更新步骤如下:

ut+1=β1ut+(1β1)θf(θt)vt+1=β2vt+(1β2)(θf(θt))2θt+1=θtαϵ+vt+1ut+1

事件中,通常还要做无偏修正:

ut+1=β1ut+(1β1)θf(θt)vt+1=β2vt+(1β2)(θf(θt))2u^t+1=ut+1/(1β1t)(bias correction)v^t+1=vt+1/(1β2t)(bias correction)θt+1=θtαϵ+v^t+1u^t+1

效果如图所示,非常惊艳!

vjgkv

初始化算法

模型参数的初始化对深度学习模型训练的影响非常大。比如,回顾我们前面的 MLP,假设将 Wibi 都初始化为 0,则反向传播时会计算梯度为 0,整个模型将无法正常训练。我们期望有好的初始化算法,能使神经网络在正向传播和反向传播时,激活范数和梯度范数保持稳定,即方差一致性,这也是所有初始化算法的基本思想。

fan_in 为输入维度,fan_out 为输出维度。

Xavier 初始化

这种初始化算法适合激活函数为 sigmoid 或 tanh 的情况。

均匀分布

Wi,jU(a,a),其中

a=gain×6fan_in+fan_out

高斯分布:

Wi,jN(0,std2) ,其中

std=gain×2fan_in+fan_out

这里不做推导,可见论文 Understanding the difficulty of training deep feedforward neural networks

Kaiming 初始化

Kaiming 初始化是针对 Xavier 初始化在 ReLU 这一类整流线性激活函数表现不佳而提出的改进。

均匀分布

Wi,jU(bound,bound),其中

bound=gain×3fan_in

高斯分布:

Wi,jN(0,std2) ,其中

std=gainfan_in

对于 ReLU 函数,gain=2

下面对高斯分布的正向过程做一些理论说明:

考虑使用高斯分布初始化参数,即 WiN(0,σ2I),在 MNIST 数据集上,用有 50 层隐藏层的 ReLU 为激活函数的神经网络训练,选取不同的的初始化方差得到正向传播时的激活范数和反向传播时的梯度范数如图:

7n8oy

通过上图,我们发现方差的选择对训练有巨大的影响,在 σ2=2n 时,激活范数和梯度范数在所有层中都保持相近,而 σ2=3n 时,激活范数却在层间传递时不断上升,这显然不是我们期望的情况。为保证方差基本不变,我们显然应该选择 σ2=2n。下面证明:

考虑将网络的中间层变量视为符合高斯分布的相互独立的随机变量, xN(0,1)WN(0,1n),则 E[wTx]=0Var[wTx]=1(中心极限定理)。

所以,如果我们使用线性激活函数,ziN(0,I)WiN(0,1nI),则 zi+1=WiTziN(0,I)。由此,可知:如果我们仅仅使用线性层,并且初始化参数的方差 σ2=1n,那么下一层的输出将与上一层有着相同的概率分布。换句话说,选择这个方差可以保持后续层范数不变。

回到上面的例子,如果使用 ReLU 激活函数,有一半的 zi 会被置 0,此时为了取得相同的效果,则要两倍的方差,所以选择 WiN(0,2nI),这就解释了为什么上图 σ2=2n 时,各层范数近乎不变。

这个结果与前面给出的公式 std2=2fan_in 一致。

参考资料