批量归一化(BN:BatchNormalization)

1、背景

  • 随机梯度下降法(SGD)对于训练深度网络简单高效,但是它有个毛病,就是需要我们人为的去选择参数,比如学习率、参数初始化、权重衰减系数、Drop out比例等。这些参数的选择对训练结果至关重要,以至于我们很多时间都浪费在这些的调参上。那么使用BN之后,你可以不需要那么刻意的慢慢调整参数。

  • 首先来说归一化的问题,神经网络训练开始前,都要对数据做一个归一化处理,归一化有很多好处,原因是网络学习的过程的本质就是学习数据分布,一旦训练数据和测试数据的分布不同,那么网络的泛化能力就会大大降低,另外一方面,每一批次的数据分布如果不相同的话,那么网络就要在每次迭代的时候都去适应不同的分布,这样会大大降低网络的训练速度,这也就是为什么要对数据做一个归一化预处理的原因。另外对图片进行归一化处理还可以处理光照,对比度等影响。

  • 神经网络一旦训练起来,参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。

  • 以网络第二层为例:网络的第二层输入,是由第一层的参数和input计算得到的,而第一层的参数在整个训练过程中一直在变化,因此必然会引起后面每一层输入数据分布的改变。我们把网络中间层在训练过程中,数据分布的改变称之为:“Internal Covariate Shift”。Paper所提出的算法,就是要解决在训练过程中,中间层数据分布发生改变的情况,于是就有了Batch Normalization,这个牛逼算法的诞生。

  • BN,全称Batch Normalization,是2015年提出的一种方法,在进行深度网络训练时,大都会采取这种算法

2、BN

2.1、基本原理

  • 和卷积层,激活层,全连接层一样,BN层也是属于网络中的一层。我们前面提到了,前面的层引起了数据分布的变化,这时候可能有一种思路是说:在每一层输入的时候,在加一个预处理多好。比如归一化到均值为0,方差为1,然后再送入输入进行学习。基本思路是这样的,然而实际上没有这么简单,如果我们只是使用简单的归一化方式:
    批量归一化(BN:BatchNormalization)_卷积

  • 对某一层的输入数据做归一化,然后送入网络的下一层,这样是会影响到本层网络所学习的特征的,比如网络中学习到的数据本来大部分分布在0的右边,经过RELU激活函数以后大部分会被激活,如果直接强制归一化,那么就会有大多数的数据无法激活了,这样学习到的特征不就被破坏掉了么?论文中对上面的方法做了一些改进:变换重构,引入了可学习参数 γ 和 β:

批量归一化(BN:BatchNormalization)_归一化_02

  • 每一个神经元 x k x^k xk 都会有这样的一对参数,当:

批量归一化(BN:BatchNormalization)_数据_03

批量归一化(BN:BatchNormalization)_归一化_04

  • 上面的公式表明,通过学习到的重构参数 γ、β,是可以恢复出原始的某一层所学到的特征的:

批量归一化(BN:BatchNormalization)_卷积_05

  • 这样就可以恢复出原始的某一层学习到的特征的,因此我们引入这个可以学习的参数使得我们的网络可以恢复出原始网络所要学习的特征分布,最后BN层的前向传导公式为:

批量归一化(BN:BatchNormalization)_方差_06

流程图如下:

批量归一化(BN:BatchNormalization)_方差_07

  • 上面公式中的m指的是mini-batch size。也就是每一个batch来做一个这样的BN。代码对应也是四句话:
m = K.mean(X, axis=1, keepdims=True)        #计算均值  
std = K.std(X, axis=1, keepdims=True)           #计算标准差  
X_normed = (X - m) / (std + self.epsilon)         #归一化  
out = self.gamma * X_normed + self.beta           #重构变换  

2.2、BN测试

  • 一旦网络训练结束,就没有了batch这个概念了,测试阶段的时候我们一般只输入一个样本来看一下预测结果。因此测试样本前向传导的时候,上面式子里的均值和标准差从哪里来?其实网络一旦训练完毕,参数都是固定的,这个时候即便是训练数据进来一个batch,BN层计算的均值和标准差都是基本不变的(网络趋于稳定),我们可以采用这些数值作为测试样本所需要的均值和标准差,于是最后测试阶段的均值和标准差为:

批量归一化(BN:BatchNormalization)_方差_08

  • 上面简单理解就是:对于均值来说直接计算所有batch u值的平均值;然后对于标准偏差采用每个batch σB的无偏估计。因此,在训练过程中除了正常的前向传播和反向求导之外,我们还要记录每一个Batch的均值和方差,以便训练完成之后按照公式计算整体的均值和方差。最后测试阶段,BN的使用公式就是:

批量归一化(BN:BatchNormalization)_数据分布_09

  • BN可以用于一个神经网络的任何一个神经元上,文献中主要是把BN变换放在激活函数层的前面,所以前向传导的计算公式应该是:z=g(BN(Wu+b)),因为偏置参数经过BN层其实是不起作用的,因为也会被均值归一化(平移),所以这个参数就可以不要了,可以写成:z=g(BN(Wu))

2.3、BN在CNN上的使用

  • BN层是对于每个神经元做归一化处理,甚至只需要对某一个神经元进行归一化,而不是对一整层网络的神经元进行归一化。既然BN是对单个神经元的运算,那么在CNN中卷积层上要怎么搞?假如某一层卷积层有6个特征图,每个特征图的大小是100100,这样就相当于这一层网络有6*100*100个神经元,如果采用BN,就会有6100*100个参数γ、β,这样岂不是太恐怖了。因此卷积层上的BN使用,其实也是使用了类似权值共享的策略,把一整张特征图当做一个神经元进行处理。

  • 卷积神经网络经过卷积后得到的是一系列的特征图,如果min-batch sizes为m,那么网络某一层输入数据可以表示为四维矩阵(m,f,w,h)m为min-batch sizes,f为特征图个数,w、h分别为特征图的宽高。在CNN中我们可以把每个特征图看成是一个特征处理(一个神经元),因此在使用Batch Normalization时mini-batch size 的大小就是:m*w*h,于是对于每个特征图都只有一对可学习参数:γ、β。其实说白了就是相当于求取所有样本所对应的一个特征图的所有神经元的平均值、方差,然后对这个特征图神经元做归一化。

2.4、BN作用

  • 改善流经网络的梯度

  • 允许更大的学习率,大幅提高训练速度:

    你可以选择比较大的初始学习率,让你的训练速度飙涨。以前还需要慢慢调整学习率,甚至在网络训练到一半的时候,还需要想着学习率进一步调小的比例选择多少比较合适,现在我们可以采用初始很大的学习率,然后学习率的衰减速度也很大,因为这个算法收敛很快。当然这个算法即使你选择了较小的学习率,也比以前的收敛速度快,因为它具有快速训练收敛的特性;

  • 减少对初始化的强烈依赖

  • 改善正则化策略:作为正则化的一种形式,轻微减少了对dropout的需求

    你再也不用去理会过拟合中drop out、L2正则项参数的选择问题,采用BN算法后,你可以移除这两项了参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性;

  • 再也不需要使用使用局部响应归一化层了(局部响应归一化是Alexnet网络用到的方法,搞视觉的估计比较熟悉),因为BN本身就是一个归一化网络层;

  • 可以把训练数据彻底打乱(防止每批训练的时候,某一个样本都经常被挑选到,文献说这个可以提高1%的精度)。

注:以上为学习过程,在测试时,均值和方差(mean/std)不基于小批量进行计算, 可取训练过程中的激活值的均值。