批量归一化(BN, Batch Normalization)

  如今的神经网络一般都特别深,在输出层向输入层传播导数的过程当中,梯度很容易被激活函数或是权重以指数级的规模缩小或放大,从而产生“梯度消失”或“梯度爆炸”的现象,形成训练速度降低和效果不理想。网络

  如何避免或者减轻这一现象的发生呢?归一化就是方法的一种。归一化将网络中层与层之间传递的数据限制在必定范围内,从而避免了梯度消失和爆炸的发生。下面介绍一种最基本的归一化:批量归一化(BN, Batch Normalization)。另外还有层归一化(LN, Layer Normalization)和权重归一化(WN, Weight Normalization),和BN大同小异。函数

批量归一化

  批量归一化层的是这样定义的,当使用批量梯度降低(或小批量)时,对前一层的输出在批量的维度上进行归一化,即学习

\begin{align} &\hat{X}_i^t=\frac{X_i^{t-1}-E(X^{t-1})}{\sqrt{D(X^{t-1})+\varepsilon}} \\ \text{where}\;\; &E(X^{t-1}) = \frac{1}{n}\sum\limits_{i=1}^nX_i^{t-1}\notag\\ &D(X^{t-1}) = \frac{1}{n-1}\sum\limits_{i=1}^n\left[X_i^{t-1}-E(X^{t-1})\right]^2\notag \end{align}spa

  其中$n$是输入批量,$X_i^{t-1}$是前一层输出批量中的第$i$个,$\varepsilon$是为避免0除而设置的较小数。以上都是按元素进行的操做。这样作的显式优势在于,大部分的输出都被映射到了-1和1之间,而诸如sigmoid激活函数,在这个区间内的梯度是最大的,从而避免因激活函数值的饱和而产生的梯度消失。而且因为层输出的归一化约束,反向传播的累积不会特别显著,梯度爆炸也得以免。code

  可是,若是仅仅进行以上操做,网络的拟合能力就会降低。这是由于,神经网络强大的拟合能力在于激活函数的非线性。通过以上操做,激活函数的输入一般都集中在-1和1之间,而sigmoid函数在这区间内的导数变化率是比较低的,或者说是比较线性的。为了防止这一点,BN在这基础上再加一个“反向”操做,将权重输出再乘上自学习的标准差和均值,映射到激活函数曲率(或者说二阶导数绝对值、导数变化率)相对更大的位置,在得到较大导数的同时,保留激活非线性。公式以下:orm

$ \begin{aligned} &X_i^t= \gamma^t\hat{X}_i^t+\beta^t\\ \end{aligned} $blog

  与$(1)$式联合获得:图片

$ \begin{aligned} &X_i^t= \frac{\gamma^t}{\sqrt{D(X^{t-1})+\varepsilon}}X_i^{t-1} + \left(\beta^t-\frac{E(X^{t-1})\gamma^t}{\sqrt{D(X^{t-1})+\varepsilon}}\right)  \\ \end{aligned} $it

  其中$\gamma,\beta$都是模型中用反向传播学习的参数。这样一来,BN层能够本身“决定”将输出映射到合适位置。io

  另外,在训练结束进行推理时,咱们输入模型的一般都是单个样本,毕竟一个样本是不能求样本方差的。因此BN使用滑动平均(moving average)来保存全部输入的均值和方差,以用于对单一输入的归一化。

Keras中BN的使用

  Keras中已经实现了BN层能够直接使用,而不用咱们本身从新写这个轮子。使用方式以下:

x = keras.layers.BatchNormalization(axis=-1,#对输入的哪一个轴执行BN
                                    momentum=0.99,#滑动平均和方差的动量
                                    epsilon=0.001,#防止0除的较小值
                                    center=True,#是否使用beta调整归一化后的输出均值
                                    scale=True,#是否使用gamma调整归一化后的输出方差
                                    trainable=True)(x) 

  其中要注意axis,对于全链接层的输出,BN是如咱们所想的那样仅对批量维度进行归一化。可是若是输入是高维的,归一化操做则是对批量维度和axis维度对应的张量进行的。好比,BN层的输入是三通道图片,axis=-1,均值就是全部批量的全部像素对应的三维向量的平均,方差的计算也是以这个维度进行。对于下面的代码:

from keras import layers,Model,Input 

Input_img = Input(shape = [320,320,3])  
x = layers.BatchNormalization(axis=-1,
                              momentum=0.99,
                              epsilon=0.001,
                              center=True,
                              scale=True)(Input_img)  
model = Model(Input_img,x)
model.summary()

  summary()输出可训练参数和不可训练参数各6个。可训练参数就是$\gamma,\beta$,不可训练参数是滑动平均所保存的均值和方差。另外,若是将BN层的traninable标记设置为False,那么$\gamma,\beta$就会被固定,不会被训练;而若是设置为True,则只有$\gamma,\beta$会被训练,另外6个不可训练参数依然是不可训练状态,由于它们是经过滑动平均而不是反向传播来修改的。