batch normalization
https://blog.csdn.net/vict_wang/article/details/88075861
【基础算法】六问透彻理解BN(Batch Normalization) - Algernon的文章 - 知乎 https://zhuanlan.zhihu.com/p/93643523
Batch Normalization原理与实战 - 天雨粟的文章 - 知乎 https://zhuanlan.zhihu.com/p/34879333
详解深度学习中的Normalization,BN/LN/WN - Juliuszh的文章 - 知乎
https://zhuanlan.zhihu.com/p/33173246

文章目录

  • 一、简介
  • 二、BN作用
  • 三、BN的原理
  • 四、BN到底解决了什么
  • 五、预测时均值和方差怎么求?
  • 六、卷积神经网络CNN中的BN

    一、简介

    BN是由Google于2015年提出,这是一个深度神经网络训练的技巧,它不仅可以加快了模型的收敛速度,而且更重要的是在一定程度缓解了深层网络中“梯度弥散(特征分布较散)”的问题,从而使得训练深层网络模型更加容易和稳定。所以目前BN已经成为几乎所有卷积神经网络的标配技巧了。
    从字面意思看来Batch Normalization(简称BN)就是对每一批数据进行归一化,确实如此,对于训练中某一个batch的数据{x1,x2,…,xn},注意这个数据是可以输入也可以是网络中间的某一层输出。在BN出现之前,我们的归一化操作一般都在数据输入层,对输入的数据进行求均值以及求方差做归一化,但是BN的出现打破了这一个规定,我们可以在网络中任意一层进行归一化处理,因为我们现在所用的优化方法大多都是min-batch SGD,所以我们的归一化操作就成为Batch Normalization。
    Batch Normalization是2015年一篇论文中提出的数据归一化方法,往往用在深度神经网络中激活层之前。其作用可以加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。

    二、BN作用

    1.特征分布对神经网络训练的作用

    在神经网络的训练过程中,我们一般会将输入样本特征进行归一化处理,使数据变为均值为0,标准差为1的分布或者范围在0~1的分布。因为当我们没有将数据进行归一化的话,由于样本特征分布较散,可能会导致神经网络学习速度缓慢甚至难以学习。
    用2维特征的样本做例子。如下两个图
    21005 - BN | batch normalization - 图1 21005 - BN | batch normalization - 图2
    左图中样本特征的分布为椭圆,当用梯度下降法进行优化学习时,其优化过程将会比较曲折,需要经过好久才能到达最优点;
    右图中样本特征的分布为比较正的圆,当用梯度下降法进行优化学习时,其有过的梯度方向将往比较正确的方向走,训练比较快就到达最优点。
    因此一个比较好的特征分布将会使神经网络训练速度加快,甚至训练效果更好。

    2.BN的作用

    但是我们以前在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过σ ( W X + b ) σ(WX+b)σ(WX+b)这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大**。如果我们能在网络的中间也进行归一化处理,是否对网络的训练起到改进作用呢?答案是肯定的。
    这种在神经网络中间层也进行归一化处理,使训练效果更好的方法,就是批归一化Batch Normalization(BN)。BN在神经网络训练中会有以下一些作用:

  • 加快训练速度

  • 可以省去dropout,L1, L2等正则化处理方法
  • 提高模型训练精度

    三、BN的原理

    image.png
    BN可以作为神经网络的一层,放在激活函数(如Relu)之前。BN的算法流程如下图:
    21005 - BN | batch normalization - 图4
  1. 求每一个小批量训练数据的均值
  2. 求每一个小批量训练数据的方差
  3. 使用求得的均值和方差对该批次的训练数据做归一化,获得0-1分布。其中ε εε是为了避免除数为0时所使用的微小正数。
  4. 尺度变换和偏移:将x i xi_xi乘以γ γγ调整数值大小,再加上β ββ增加偏移后得到y i y_iyi,这里的γ γγ是尺度因子,β ββ是平移因子。这一步是BN的精髓,由于归一化后的x i xix*i基本会被限制在正态分布下,使得网络的表达能力下降。为解决该问题,我们引入两个新的参数:γ , β γ,βγ,β。 γ γγ和β ββ_是在训练时网络自己学习得到的。

输入为数值集合( 21005 - BN | batch normalization - 图5 ),可训练参数 21005 - BN | batch normalization - 图6
BN的具体操作为:先计算 21005 - BN | batch normalization - 图7 的均值和方差,之后将 21005 - BN | batch normalization - 图8 集合的均值、方差变换为0、1(对应上式中 21005 - BN | batch normalization - 图9 ),最后将 21005 - BN | batch normalization - 图10 中每个元素乘以 21005 - BN | batch normalization - 图11 再加 21005 - BN | batch normalization - 图12 ,输出。 21005 - BN | batch normalization - 图13 是可训练参数,参与整个网络的BP;
归一化的目的:将数据规整到统一区间,减少数据的发散程度,降低网络的学习难度。BN的精髓在于归一之后,使用 21005 - BN | batch normalization - 图14 作为还原参数,在一定程度上保留原数据的分布。

四、BN到底解决了什么

一个标准的归一化步骤就是减均值除方差,那这种归一化操作有什么作用呢?我们观察下图
21005 - BN | batch normalization - 图15
21005 - BN | batch normalization - 图16
a中左图是没有经过任何处理的输入数据,曲线是sigmoid函数,如果数据在梯度很小的区域,那么学习率就会很慢甚至陷入长时间的停滞。减均值除方差后,数据就被移到中心区域如右图所示,对于大多数激活函数而言,这个区域的梯度都是最大的或者是有梯度的(比如ReLU),这可以看做是一种对抗梯度消失的有效手段。对于一层如此,如果对于每一层数据都那么做的话,数据的分布总是在随着变化敏感的区域,相当于不用考虑数据分布变化了,这样训练起来更有效率。
那么为什么要有第4步,不是仅使用减均值除方差操作就能获得目的效果吗?我们思考一个问题,减均值除方差得到的分布是正态分布,我们能否认为正态分布就是最好或最能体现我们训练样本的特征分布呢?不能,比如数据本身就很不对称(不符合正态分布),或者激活函数未必是对方差为1的数据最好的效果,比如Sigmoid激活函数,在-1~1之间的梯度变化不大,那么非线性变换的作用就不能很好的体现,换言之就是,减均值除方差操作后可能会削弱网络的性能!针对该情况,在前面三步之后加入第4步完成真正的batch normalization。
BN的本质就是利用优化变一下方差大小和均值位置,使得新的分布更切合数据的真实分布,保证模型的非线性表达能力。BN的极端的情况就是这两个参数等于mini-batch的均值和方差,那么经过batch normalization之后的数据和输入完全一样,当然一般的情况是不同的。

五、预测时均值和方差怎么求?

在训练时,我们会对同一批的数据的均值和方差进行求解,进而进行归一化操作。
但是对于预测时我们的均值和方差怎么求呢?
比如我们预测单个样本时,那还怎么求均值和方法呀!其实是这种样子的,对于预测阶段时所使用的均值和方差,其实也是来源于训练集。比如我们在模型训练时我们就记录下每个batch下的均值和方差,待训练完毕后,我们求整个训练样本的均值和方差期望值,作为我们进行预测时进行BN的的均值和方差:
21005 - BN | batch normalization - 图17
最后测试阶段,BN的使用公式就是:
21005 - BN | batch normalization - 图18
21005 - BN | batch normalization - 图19
关于BN的使用位置,在CNN中一般应作用与非线性激活函数之前,s型函数s(x)的自变量x是经过BN处理后的结果。因此前向传导的计算公式就应该是:
21005 - BN | batch normalization - 图20
其实因为偏置参数b经过BN层后其实是没有用的,最后也会被均值归一化,当然BN层后面还有个β参数作为偏置项,所以b这个参数就可以不用了。因此最后把BN层+激活函数层就变成了:
21005 - BN | batch normalization - 图21

六、卷积神经网络CNN中的BN

注意前面写的都是对于一般情况,对于卷积神经网络有些许不同。
因为卷积神经网络的特征是对应到一整张特征响应图上的,所以做BN时也应以响应图为单位而不是按照各个维度。
比如在某一层,batch大小为m,响应图大小为w×h,则做BN的数据量为m×w×h。
BN在深层神经网络的作用非常明显:若神经网络训练时遇到收敛速度较慢,或者“梯度爆炸”等无法训练的情况发生时都可以尝试用BN来解决。同时,常规使用情况下同样可以加入BN来加速模型训练,甚至提升模型精度。

七、BN中均值、方差通过哪些维度计算得到?

神经网络中传递的张量数据,其维度通常记为[N, H, W, C],其中N是batch_size,H、W是行、列,C是通道数。那么上式中BN的输入集合 21005 - BN | batch normalization - 图22 就是下图中蓝色的部分。
21005 - BN | batch normalization - 图23图一 BN输入维度示意图
均值的计算,就是在一个批次内,将每个通道中的数字单独加起来,再除以 21005 - BN | batch normalization - 图24 。举个例子:该批次内有10张图片,每张图片有三个通道RBG,每张图片的高、宽是H、W,那么均值就是计算10张图片R通道的像素数值总和除以21005 - BN | batch normalization - 图25 ,再计算B通道全部像素值总和除以21005 - BN | batch normalization - 图26,最后计算G通道的像素值总和除以21005 - BN | batch normalization - 图27。方差的计算类似。
可训练参数 21005 - BN | batch normalization - 图28 的维度等于张量的通道数,在上述例子中,RBG三个通道分别需要一个 21005 - BN | batch normalization - 图29 和一个 21005 - BN | batch normalization - 图30 ,所以 21005 - BN | batch normalization - 图31 的维度等于3。

Batch Noramlization 是想让输入满足同一个分布, 那么是让输入的什么特征满足同一分布呢?这么说有点晕,可以这么理解,这里我就只说正确答案了。那就是让每张图片的相同通道的所有像素值的均值和方差相同。比如我们有两张图片(都为3通道),我们现在只说R通道,我们希望第一张图片的R通道的均值 和 第二张图片R通道的均值相同,方差同理。
这样我们也就知道了实际运行过程中均值是如何计算的,可以有两种方法,把所有图片的相同通道的值相加,然后求平均数,也可以先求出每一个图片此通道的均值,然后再求所有图片此通道均值的均值。

八、训练与推理时BN中的均值、方差分别是什么?

此问题是BN争议最大之处,正确答案是:
训练时,均值、方差分别是该批次内数据相应维度的均值与方差;
推理时,均值、方差是基于所有批次的期望计算所得,公式如下:
21005 - BN | batch normalization - 图32
其中 21005 - BN | batch normalization - 图33 表示 x 的期望。

九、在tensorflow中如何实现BN?

如果使用tensorflow的slim框架,BN就已经集成在内了,直接打开相应的参数即可。如果调用tf.nn.batch_normalization这个接口,那么需要自己在外部做一些事。
下述代码是基于tf.nn.batch_normalization封装好的BN函数:

  1. import tensorflow as tf
  2. from tensorflow.python.training import moving_averages
  3. def batch_normalization(input, is_training, name="BN",
  4. moving_decay=0.999, eps=1e-5):
  5. input_shape = input.get_shape()
  6. params_shape = input_shape[-1]
  7. axis = list(range(len(input_shape) - 1))
  8. with tf.variable_scope(name, reuse=tf.AUTO_REUSE) as scope:
  9. beta = tf.get_variable('beta',
  10. params_shape,
  11. initializer=tf.zeros_initializer)
  12. gamma = tf.get_variable('gamma',
  13. params_shape,
  14. initializer=tf.ones_initializer)
  15. moving_mean = tf.get_variable('moving_mean',
  16. params_shape,
  17. initializer=tf.zeros_initializer,
  18. trainable=False
  19. )
  20. moving_var = tf.get_variable('moving_var',
  21. params_shape,
  22. initializer=tf.ones_initializer,
  23. trainable=False
  24. )
  25. def train():
  26. # These ops will only be preformed when training.
  27. mean, var = tf.nn.moments(input, axis)
  28. update_moving_mean = moving_averages.assign_moving_average(moving_mean,
  29. mean,
  30. moving_decay)
  31. update_moving_var = moving_averages.assign_moving_average(
  32. moving_var, var, moving_decay)
  33. return tf.identity(mean), tf.identity(var)
  34. mean, var = tf.cond(tf.equal(is_training, True), train,
  35. lambda: (moving_mean, moving_var))
  36. return tf.nn.batch_normalization(input, mean, var, beta, gamma, eps)

在代码实现中有一个技巧,如果训练几百万个Batch,那么是不是要将其均值方差全部储存,最后再计算推理时所用的均值和方差?这样显然太过笨拙,占用内存随着训练次数不断上升。为了避免该问题,上述代码使用了滑动平均,储存固定个数Batch的均值和方差,不断迭代更新推理时需要的 21005 - BN | batch normalization - 图3421005 - BN | batch normalization - 图35
注意到代码中:

  1. beta、gamma在训练状态下,是可训练参数,在推理状态下,直接加载训练好的数值。
  2. moving_mean、moving_var在训练、推理中都是不可训练参数,只根据滑动平均计算公式更新数值,不会随着网络的训练BP而改变数值;在推理时,直接加载储存计算好的滑动平均之后的数值,作为推理时的均值和方差。

    十、在网络中使用了BN,效果如何?

    BN目前仍然是一个黑盒,论文中有一定的理论推导,但是普遍还是通过对比提升来验证BN的效果。原作者给出的效果如下图:
    21005 - BN | batch normalization - 图36图三 正确率(纵坐标)与训练步数(横坐标)
    上图可见BN的两大收益:
  • 收敛速率增加
  • 可以达到更好的精度

在目标检测算法中,BN已经成为了标配。比如Yolov3引入了BN后,mAP提升了两个百分点。在更多实验中可以看到,BN同时起到了正则化作用,防止模型在训练集上过拟合,通常有BN的网络不再需要Dropout层。

十一、Batch Normalization的思考

最后是关于Batch Normalization的思考,应该说,normalization机制至今仍然是一个非常open的问题,相关的理论研究一直都有,大家最关心的是Batch Normalization怎么就有效了。

之所以只说Batch Normalization,是因为上面的这些方法的差异主要在于计算normalization的元素集合不同。Batch Normalization是NHW,Layer Normalization是CHW,Instance Normalization是HW,Group Normalization是GH*W。
关于Normalization的有效性,有以下几个主要观点:
(1) 主流观点,Batch Normalization调整了数据的分布,不考虑激活函数,它让每一层的输出归一化到了均值为0方差为1的分布,这保证了梯度的有效性,目前大部分资料都这样解释,比如BN的原始论文认为的缓解了Internal Covariate Shift(ICS)问题。
(2) 可以使用更大的学习率,文[2]指出BN有效是因为用上BN层之后可以使用更大的学习率,从而跳出不好的局部极值,增强泛化能力,在它们的研究中做了大量的实验来验证。
(3) 损失平面平滑。文[3]的研究提出,BN有效的根本原因不在于调整了分布,因为即使是在BN层后模拟ICS,也仍然可以取得好的结果。它们指出,BN有效的根本原因是平滑了损失平面。之前我们说过,Z-score标准化对于包括孤立点的分布可以进行更平滑的调整。

十二、实践中的问题

1.BN放在激活函数前面还是后面?
如果是sigmod的话放在前面,因为sigmod的梯度中间大、两端小,通过BN会将数据聚集在0附近,别的激活函数放在前后没有太大影响。
2.在test的时候如何算均值和方差?
对train时候的每一个batch的均值和方差求均值。
3.BN的超参数
BN的第一步是将原始分布转换成均值为0,方差为1,但这有可能对学习有一定的限制,所以,image.png这部分是超参,0,1的分布进行一定的尺度变换,初值γ是全1的向量,β是全零的向量。