[tensorflow 应用之路]Batch Normalization 原理详解及应用方法

批正则化(Batch Normalize,BN)是2015年由Sergey Ioffe提出的方法,用于消除神经网络上一层不同分布的输入导致本层参数更新困难。由于各个层的卷积核参数不同,根据反向传播法则我们知道, W ||W|| 及结果 h ||h|| 越大对梯度的影响也就越大,而同一学习率在不同层造成的影响不一样。这时,就需要BN层来消除这种差异。


原理简析

原文
BN的实现其实非常简单,主要通对过特征scale和shift来消除特征间的差异带来的参数更新的影响。具体的介绍可以参照这个视频(youtube),里面有非常详细的介绍。以下是原论文中的BN伪代码:
训练
在训练中使用移动平均(moving average)法更新均值和方差,在预测中,使用训练的均值 E [ x ] E[x] 和方差 V a r [ x ] Var[x] 作为参数,配合 γ \gamma β \beta 进行正则化。
这篇文章用各个实验对比了BN层的在不同网络中的作用差异,有兴趣的同学可以参考一下。

已知问题

1.如果使用tensorflow,不要忘记在optimizer前增加BN参数的更新操作。
2.不要将BN用在少量数据中。一旦数据总量变小,那么整体均值标准差与单个数据的均值和方法偏差过大(下面会举例子说明),导致预测和训练数据相差过大。

Tensorflow中的BN层

我们首先分析tf.layers.batch_normalization中重要的参数。

  • axis:在此维度上做归一化(Normalize)。一般选择channel维度,NHWC时为-1,NCHW时为1。
  • momentum:滑动平均时的动量。假设平均值为 v v ,当前值为 a a ,公式如下:源码参见此处
    v = v ( 1 m o m e n t u m ) ( v u ) v=v-(1-momentum) * (v - u)
  • epsilon: 防止分母为0的数
  • center/scale: 当为True时, β \beta $\gamma$会生效(详见上图代码第11行)
  • training: 一定要在训练时设置为True,在预测时设置为False。这个参数决定了bn层的归一是使用一个batch内的均值和标准差,还是使用训练的均值和标准差(下面有详解)
  • fused: 能加速bn过程的参数,建议设置为True

BN在训练时

训练时的BN层会直接使用batch内的mean和var进行归一化,公式如下:
y = x m e a n ( x ) ) v a r ( x ) y=\frac{x-mean(x))}{var(x)}
我们用以下代码段验证一下:

a = tf.Variable(tf.random_uniform((3, 4),seed=1))
bn = tf.layers.batch_normalization(a, -1, 1, 1e-6, False, False,training=True)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    while True:
        temp1, temp2 = sess.run([a, bn])

在BN之前,原始数据如下:
在这里插入图片描述
在0维度下
均值为

[0.6144663 , 0.5615021 , 0.30789164, 0.42147708]

方差为

[0.07121453, 0.14869653, 0.11407541, 0.00624432]

我们用公式算一下,BN后的值应该是:

[[-1.4068358 , 0.93072194, -0.76203936, 0.9398434 ],
[ 0.82835776, -1.3874875 , -0.6507127 , 0.44523987],
[ 0.578478 , 0.45676556, 1.4127523 , -1.3850833 ]]

而在使用tensorflow的批正则后,数值如下:
在这里插入图片描述
和我们之前推算的差不多。这说明在训练时,BN层会直接使用输入数据的均值与标准差,来作正则化处理。

训练时更新参数

在预测时,BN层需要直接用训练时统计的均值与标准差,所以在训练时需要更新moving_average和moving_variance(两者在使用BN后就会自动创建)。
具体方法如下:
1.使用tf.control_dependencies:

...
 optimizer = tf.train.AdamOptimizer(learning_rate)       #可以是任意的optimizer
 update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
 with tf.control_dependencies(update_ops):
 		train_op = optimizer.minimize(loss)
...

这里tf.control_dependencies是表示在做操作前(这里是反向传播),先进行括号里的操作(这里是更新moving_average和moving_variance)。如果直接使用optimizer的话,均值和标准差不更新,预测结果会全错

2.使用slim的方式:

optimizer = tf.train.AdamOptimizer(learning_rate)
train_op = slim.learning.create_train_op(total_loss, optimizer)

在create_train_op时,会自动调用上面的tf.control_dependencies,源码位置见此处

BN在预测时

在预测时,BN层中会使用训练时无偏统计的均值和标准差(注意,不使用输入数据的均值和标准差),这会导致训练时和预测时的结果有"偏差",数据量越大,这种“偏差”越不明显。所以我们说,在数据量小的时候,不要使用batch normalize
更新均值和标准差的方式在上文提过(momentum参数),我们看一下之前的例子。
更新前的均值和标准差:
在这里插入图片描述
在这里插入图片描述

更新后的均值和标准差(使用momentum=0):
在这里插入图片描述
在这里插入图片描述
和之前我们计算的结果一致,说明mean和variance就是用每个mini-batch的数据的均值和方差进行更新的。

总结

使用BN层可以归一化层的输入和输出,使不同分布的输入差异的影响最小,让学习率调整得更加便捷,减少过拟合风险,加快训练速度。使用BN后,会造成训练和预测的输出差异,这种差异在小数据量时尤为明显。

最后,祝您身体健康再见!

猜你喜欢

转载自blog.csdn.net/h8832077/article/details/82747741
今日推荐