深度学习 --- 改善深度神经网络 2


1. 参数初始化方法

一组合理的W和b的初始值虽然不能解决梯度爆炸或者梯度消失的问题,但是却能够很好的缓解这些问题,下面我们介绍两点注意事项和两种初始化方法:

  • W不能为0。否则会有对称性的问题,这会导致每层的隐藏单元都做同样的训练,起到的效果和每层只有一个隐藏单元是一样的,丝毫体现不了深度神经网络的价值。
  • W和b不能太大。否则很容易导致梯度爆炸的问题。

所以,一个好的参数初始化方法,要使得W和b不会比1大很多,也不会比1小很多,不但可以加快训练速度,也能够缓解梯度爆炸和梯度消失的问题。接下来主要介绍两种方法:

  • Xavier Initialization
    首先按照标准正态分布产生随机数,然后再乘以比例因子(scaling factor)1n[l1]\sqrt{\frac{1}{n^{[l-1]}}}

      W = np.random.randn(n_out, n_in) * np.sqrt(1/n_in)
    

当**函数为tanh时,效果很好。

  • He Initialization
    它和Xavier很相似,就是比例因子为其2倍,即2n[l1]\sqrt{\frac{2}{n^{[l-1]}}}

      W = np.random.randn(n_out, n_in) * np.sqrt(2/n_in)
    

该方法非常适用于当**函数为ReLU时。

2. 深度学习优化算法

一种好的优化算法可以加快训练速度,尤其是当你面对大量的训练数据时,下面介绍几种快速的算法。

2.1 Mini-Batch Gradient Descent

一般情况下分为Train set,Dev set和Test set之后,就可以开始训练。但是当Train Set非常大的时候,比如5 million或者50 million时,每个epoch都需要将所有的想计算一遍,然后才能更新一次参数。按照这样的算法,参数更新起来很慢,训练时间会需要很长。

Mini-Batch Gradient Descent就是为了解决这个问题。它的方法就是将m个样本分割成多个小批量的样本,然后每次梯度下降只在该小批量样本上进行。
下面以5,000,000个样本为例,batch_size为1000(需要循环5000次),循环1个epoch,步骤如下:

for t = 1, 2, … 5000 {

  • 1.forward propagation on x{t}x^{\{t\}}

  • 2.compute cost J{t}=11000i=11000L(y^(i),y(i))+λ2000l=1LW[l]F2J^{\{t\}} = \frac{1}{1000}\sum_{i=1}^{1000}L(\hat y^{(i)}, y^{(i)}) + \frac{\lambda}{2000}\sum_{l=1}^L||W^{[l]}||^2_F

  • 3.backward propagation on J{t}J^{\{t\}}

  • 4.update parameters: W:=WαdWW := W - \alpha dWb:=bαdbb := b - \alpha db
    }

如果需要训练多个epoch,只需在此循环外面再加上epoch的循环即可。
总之,Mini-batch Gradient Descent的过程和Batch Gradient Descent一样,差别就是每次迭代一次的样本数量不同,这样的好处就是小批量多次更新参数,能够及早的得到结果或者发现问题。
另外一个差别就是Cost J的曲线图:

深度学习 --- 改善深度神经网络 2

深度学习 --- 改善深度神经网络 2

当batch_size=1时,即随机在每个样本上面进行一次前后向传播,更细一次参数,这种方法称为:随机梯度下降(Stochatic Gradient Descent)。下面比较这三种Gradient Descent的方法:

Stochastic Gradient Descent Mini-Batch Gradient Descent Batch Gradient Descent
batch_size=1 batch_size=t介于[1,m]之间 batch_size=m
样本只有1个,振荡严重,总体趋势收敛。但是无法收敛至最小值,只能在其附近徘徊(可以通过小的学习率缓解)。另外一个问题是无法向量化计算,导致计算效率不高 样本不大不小,振荡较小,总体趋势收敛。但是无法收敛至最小值,只能在其附近小区域徘徊(可以通过小的学习率缓解)。两个优点:向量化运算效率高;迭代更新快 全部样本,收敛趋势平滑。但是每次迭代训练时间很长,参数更新很慢

深度学习 --- 改善深度神经网络 2

下面需要讨论的是如何选择batch size
如果train set的样本比较小,一般2000\le2000时,可以直接使用batch gradient descent。
如果train set样本比较大时,可考虑使用mini-batch gradient descent,常用的batch size是64,128,256,512。

2.2 Gradient Descent With Momentum

基于动量的梯度下降,这里面涉及到Exponentially Weighted Averages,原理可以参考《指数加权移动平均》,以温度为例总结下来:

  • Vt=βVt1+(1β)θtV_t = \beta V_{t-1} + (1-\beta)\theta_t,公式中θt\theta_t为 t 时刻的实际温度;VtV_t为 t 时刻 EMA 的值;β\beta为权重系数,范围(0, 1).
  • V0=0V_0=0 时,可得:Vt=(1β)(θt+βθt1+β2θt2++βt1θ1)V_t = (1 - \beta)(\theta_t + \beta \theta_{t-1} + \beta^2 \theta_{t-2} + \cdots + \beta^{t-1} \theta_1 ),由此可知:每天的实测温度(θt\theta_t)的权重系数以指数等比形式缩小,时间越接近当前时刻,权重系数β\beta对当前时刻的VtV_t的影响越大,而离当前时刻越远,权重影响越小。
  • Vt=βVt1+(1β)θt11βV_t = \beta V_{t-1} + (1-\beta)\theta_t \approx \frac{1}{1 - \beta}天之前的温度平均值

总之,通过上述Exponentially Weighted Averages后,我们可以减少振动,提高稳定性。如下图所示:
随着β\beta的增大,VtV_t的趋势是越来越平滑稳定。我们接下来就是用这个原理来加快训练速度。

深度学习 --- 改善深度神经网络 2

对于Mini-Batch Gradient Descent,我们知道它在迭代的过程中是上下振荡地向最小点靠近的,如果我们能够减少它在垂直方向的振荡,加速在水平方向的收敛,那么我们就能使用较少的迭代次数使它接近最小点,因此,基于动量的梯度下降的算法由于单纯的梯度下降算法。如下图所示:

深度学习 --- 改善深度神经网络 2

具体的代码实现如下:
on iteration t:

  1. compute the dWdWdbdb on mini-batch
  2. VdW=βVdW+(1β)dWV_{dW} = \beta V_{dW} + (1 - \beta)dW, Vdb=βVdb+(1β)dbV_{db} = \beta V_{db} + (1 - \beta)db
  3. W:=WαVdWW := W - \alpha V_{dW}b:=bαVdbb := b - \alpha V_{db}

在实践中,一般设置β=0.9\beta = 0.9,后续基本不需要更改这个值,它能工作的很好。

2.3 RMSprop[Root Mean Square prop]

该方法的目标和momentum一样,同样参考上图,纵轴方向为b,横轴方向为W,它也要在垂直方向上抑制振荡,而在水平方向上面加快收敛。

算法如下:
on iteration t:

  1. compute the dWdWdbdb on mini-batch
  2. SdW=βSdW+(1β)dW2S_{dW} = \beta S_{dW} + (1 - \beta)dW^2Sdb=βSdb+(1β)db2S_{db} = \beta S_{db} + (1 - \beta)db^2
  3. W:=WαdWSdW+ϵW := W - \alpha \frac{dW}{\sqrt{S_{dW} + \epsilon}}b:=bαdbSdb+ϵb := b - \alpha \frac{db}{\sqrt{S_{db} + \epsilon}}

现在说明为什么这种方法能够工作:
在上图中,由于垂直方向振荡很大,说明dbdb的值很大,因此SdbS_{db}也就很大,dbSdb\frac{db}{\sqrt{S_{db}}}就很小,bb减去了一个较小的数,最终使得bb变化很小,这样就抑制了垂直方向的振动。

同理,在水平方向上,dWdW的变化比价小,因此SdWS_{dW}比较小,dWSdW\frac{dW}{\sqrt{S_{dW}}}就比较大,WW减去了一个比较大的值,最终使得WW表换很大,这样就加快了在水平方向的收敛。

注意:ϵ\epsilon主要是为了避免分母为0,一般ϵ=108\epsilon = 10^{-8}

2.4 Adam[Adaptive Moment Estimation]

这是目前被广泛使用的一种算法,它是将momentum和RMSprop结合在一起。方法如下:

VdW=Vdb=SdW=Sdb=0V_{dW} = V_{db} = S_{dW} = S_{db} = 0
on iteration t:

  1. compute the dWdWdbdb on mini-batch
  2. momentum:
    VdW=β1VdW+(1β1)dWV_{dW} = \beta_1 V_{dW} + (1 - \beta_1)dW, Vdb=β1Vdb+(1β1)dbV_{db} = \beta_1 V_{db} + (1 - \beta_1)db
  3. RMSprop:
    SdW=β2SdW+(1β2)dW2S_{dW} = \beta_2 S_{dW} + (1 - \beta_2)dW^2Sdb=β2Sdb+(1β2)db2S_{db} = \beta_2 S_{db} + (1 - \beta_2)db^2
  4. Bias correction:
    VdWcorrected=VdW(1β1t)V_{dW}^{corrected} = \frac{V_{dW}}{(1 - \beta_1^t)}Vdbcorrected=Vdb(1β1t)V_{db}^{corrected} = \frac{V_{db}}{(1 - \beta_1^t)}
    SdWcorrected=SdW(1β2t)S_{dW}^{corrected} = \frac{S_{dW}}{(1 - \beta_2^t)}Sdbcorrected=Sdb(1β2t)S_{db}^{corrected} = \frac{S_{db}}{(1 - \beta_2^t)}
  5. update parameters:
    W:=WαVdWcorrectedSdWcorrected+ϵW := W - \alpha \frac{V_{dW}^{corrected}}{\sqrt{S_{dW}^{corrected}} + \epsilon}
    b:=bαVdbcorrectedSdbcorrected+ϵb := b - \alpha \frac{V_{db}^{corrected}}{\sqrt{S_{db}^{corrected}} + \epsilon}

hyperparameters:

  • α\alpha needs to be tune
  • β1:0.9\beta_1: 0.9 (for dWdW)
  • β2:0.999\beta_2: 0.999 (for dW2dW^2)
  • ϵ:108\epsilon: 10^{-8}
    以上这些参数基本不需要调整,这些默认值已经能够很好地适用很多的深度神经网络了。

3 其他加速训练的方法

3.1 Learning rate decay[学习率衰减]

在Mini-Batch Gradient Descend过程中,如果batch size比较小,它无法接近最小值,而只是在最小值附近振动。这时采用学习率衰减可以改善这个问题,使它更快的收敛至最小值,减少迭代次数。

深度学习 --- 改善深度神经网络 2

常见的方法主要有:

  • Inverse time decay:逆时间衰减
    α=11+decay_rateepoch_numα0\alpha = \frac{1}{1 + {decay\_rate}*epoch\_num} \alpha_0
    decayed_learning_rate = learning_rate / (1 + decay_rate * global_step / decay_step)

  • Exponential Decay:指数衰减
    α=decay_rateepoch_numα0\alpha = decay\_rate^{epoch\_num}\alpha_0
    decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)

  • Natural exponential decay:自然指数衰减
    α=edecay_rateepoch_numα0\alpha = e^{-decay\_rate*epoch\_num}\alpha_0
    decayed_learning_rate = learning_rate * exp(-decay_rate * global_step)

  • Polynomial decay:多项式衰减
    global_step = min(global_step, decay_steps)
    decayed_learning_rate = (learning_rate - end_learning_rate) *(1 - global_step / decay_steps) ^ (power) + end_learning_rate

参数:
learning_rate:初始值
global_step:全局step数(每个step对应一次batch)
decay_steps:learning rate更新的step周期,即每隔多少step更新一次learning rate的值
decay_rate:指数衰减参数
end_learning_rate:衰减最终值
power:多项式衰减系数

总之,目的都是随着epoch_num的增加缓慢减低α\alpha,更快的收敛至最小值。
当然现在也有论文开始反驳这种方法了,如下:
DON’T DECAY THE LEARNING RATE, INCREASE THE BATCH SIZE

注意,加快训练的方法有很多种,Learning rate decay并不是那个最先就要去调试的超参

3.2 Batch Normalization[批量归一化]

在上一讲中,我们要求对输入数据X进行归一化,过程如下:

  • zero mean
    μ=1mi=1mx(i)\mu = \frac{1}{m}\sum_{i=1}^{m}x^{(i)}
    x(i)=x(i)μx^{(i)} = x^{(i)} - \mu

  • scale normalization
    σ2=1mi=1m(x(i))2\sigma^2 = \frac{1}{m}\sum_{i=1}^{m}(x^{(i)})^2
    x(i)=x(i)σx^{(i)} = \frac{x^{(i)}}{\sigma}

由此,可以使用较大的α\alpha,并能提高梯度下降速度。在深度学习中,上一层的输出都是作为了下一层的输入,那么是否可以对上一层的输出都进行一次一样的归一化呢,这样同样也能加快训练速度。这就是Batch Normalization的思想。

在讲述神经网络时,我们知道每一层都有线性输出ZZ和**输出AA,那么Batch Normalization是应用在哪一层呢?目前还没有一个统一的结论,更多人采用的是将其应用在ZZ上面。

BN的实现过程如下:(对于ll层的Z[l]Z^{[l]}进行BN)
μ=1mi=1mZ[l](i)\mu = \frac{1}{m}\sum_{i=1}^{m}Z^{[l](i)}

σ2=1mi=1m(Z[l](i)μ)2\sigma^2 = \frac{1}{m}\sum_{i=1}^{m}(Z^{[l](i)} - \mu)^2

Znorm[l](i)=Z[l](i)μσ2+ϵZ_{norm}^{[l](i)} = \frac{Z^{[l](i)} - \mu}{\sqrt{\sigma^2 + \epsilon}}

以上就可以使得Znorm[l]Z_{norm}^{[l]}均值为0方差为1了。但是输入层的归一化和隐藏层的归一化还是不一样的。在隐藏层,也许你不希望得到的输出是均值0方差1的数据,比如**函数是sigmoid或tanh时,就不希望方差为1,而是希望方差能够更大,以便充分利用**函数非线性的作用,而不是方差为1时,**函数那段近似直线的线性趋于。因此对于隐藏层的归一化需要新增两个超参:

Z~[l](i)=γZnorm[l](i)+β\tilde Z^{[l](i)} = \gamma Z_{norm}^{[l](i)} + \beta

γ=1σ2+ϵ,β=μσ2+ϵ\gamma = \frac{1}{\sqrt{\sigma^2 + \epsilon}}, \beta = - \frac{\mu}{\sqrt{\sigma^2 + \epsilon}},它得到的就是上述的均值0方差1的结果。

通过调整γ,β\gamma, \beta就可以调节输出数据的均值和方差。

接下来我们介绍如何在深度学习网络中使用BN:

深度学习 --- 改善深度神经网络 2

如图所示,在前向传播中,每层的线性输出Z[l]Z^{[l]}后面都要再进行一次BN,得到新的输出Z~[l]\tilde Z^{[l]},然后再将其输入到**函数,同时多记录两个超参γ[l],β[l]\gamma^{[l]}, \beta^{[l]}

同样,在反向传播中,我们同样需要更新这两个参数(和处理W[l],b[l]W^{[l]}, b^{[l]}一样):
γ[l]:=γ[l]αdγ[l]\gamma^{[l]} := \gamma^{[l]} - \alpha d\gamma^{[l]}
β[l]:=β[l]αdβ[l]\beta^{[l]} := \beta^{[l]} - \alpha d\beta^{[l]}

为什么BN能够工作

  • 在前几层的参数变化时,当前层的输出值Z也会变化,但是当使用了BN算法后,它能够保证无论Z值怎么变化,Z的均值和方差将保持不变,这也间接地限制了前几层参数的更新,从而使得Z值变得稳定,后面层适用前面层变化的力量被减弱。实际上,BN算法消弱了前面层和后面层参数之间的耦合,使得当前层更加的独立,这将有助于提高网络学习速度。从后面层的角度来看,前面层的影响不大,因为它们被同一均值和方差所限制。
  • 由于对小批量数据进行归一化,而小批量中也包含噪声,所以均值和方差也有点噪声存在,起到了轻微的正则化的效果,实际和dropout的作用类似,使得后续隐藏单元不要过度依赖前面的隐藏单元。

如何在测试时BN
之前在训练时都是采用了批量的样本,64-512之间的样本,但是在测试时,往往都是针对一个样本的,这时对一个样本求均值μ\mu和方差σ2\sigma_2显然是不合理的,那么在测试时该如何计算Znorm[l]Z_{norm}^{[l]}呢?
Znorm[l]=Z[l]μσ2+ϵZ_{norm}^{[l]} = \frac{Z^{[l]} - \mu}{\sqrt{\sigma^2 + \epsilon}}
既然我们无法针对一个样本求均值μ\mu和方差σ2\sigma_2,那么一般的解决方法是记录下在网络训练时每一层的最新的均值μ\mu和方差σ2\sigma_2,然后在测试时直接带入计算Znorm[l]Z_{norm}^{[l]}

当然,另外一种更加通用的方法就是使用之前经过的指数加权平均Exponentially Weighted Averages方法,记录下最后的均值μ\mu和方差σ2\sigma_2,然后在测试时直接带入计算Znorm[l]Z_{norm}^{[l]},然后在使用训练后的参数γ,β\gamma, \beta求出Z~[l]\tilde Z^{[l]}

至此,我们已经讲述了多种提高训练速度的方法,中间也提出了更多的超参数,它们对训练结果起着重要的作用。因此,接下来我们将讲解,如何选择这些超参数以及如何调试这些超参数。