在tensorflow中使用batch normalization
问题
训练神经网络是一个很复杂的过程,在前面提到了深度学习中常用的激活函数,例如ELU或者Relu的变体能够在开始训练的时候很大程度上减少梯度消失或者爆炸问题,但是却不能保证在训练过程中不出现该问题,例如在训练过程中每一层输入数据分布
发生了改变了,那么我们就需要使用更小的learning rate去训练,这一现象被称为internal covariate shift
,Batch Normalization能够很好的解决这一问题。目前该算法已经被广泛应用在深度学习模型中,该算法的强大至于在于:
- 可以选择一个较大的学习率,能够达到快速收敛的效果。
- 能够起到Regularizer的效果,在一些情况下可以不使用Dropout,因为BN提高了模型的泛化能力
介绍
我们在将数据输入到神经网络中往往需要对数据进行归一化,原因在于模型的目的就是为了学习模型的数据的分布,如果训练集的数据分布和测试集的不一样那么模型的泛化能力就会很差,另一方面如果模型的每一 batch的数据分布都不一样,那么模型就需要去学习不同的分布,这样模型的训练速度会大大降低。
BN是一个独立的步骤,被应用在激活函数之前,它简单地对输入进行零中心(zero-center)和归一化(normalize),然后使用两个新参数来缩放和移动结果(一个用于缩放,另一个用于缩放转移)。 换句话说,BN让模型学习最佳的尺度和 每层的输入的平均值。
为了零中心和归一化数据的分布,BN需要去估算输入的mean和standard deviation。
应用
tensorflow中有不同级别的封装层,我一般使用的tf.layers。这次用的是tf.layers.batch_normalization.
def myConv(x_in, nf, strides=1, is_training=True, name = 'conv3d'): with tf.variable_scope(name): # x_out = Conv3D(nf, kernel_size=3, padding='same', # kernel_initializer='he_normal', strides=strides)(x_in) x_out = tf.layers.conv3d(inputs=x_in, filters=nf, kernel_size=(3, 3, 3), strides=strides, padding='same', kernel_initializer=tf.keras.initializers.he_normal(), ) x_out = batch_norm(x_out,is_training) x_out = LeakyRelU(x_out, 0.2) return x_out
def batch_norm(x, is_train=True): with tf.variable_scope("batch_norm"): return tf.layers.batch_normalization(x, epsilon=1e-5, momentum=0.99, training=is_train, )
这是因为在计算BN中需要计算moving_mean
和moving_variance
并且更新,所以在执行run
的时候需要将其添加到执行列表中。需要下面这段代码这样才能计算μ和σ的滑动平均(测试时会用到),测试时traing设为False。
extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(extra_update_ops):
self.g_A_trainer = optimizer.minimize(g_loss_A, var_list=g_A_vars)
在网上看到了很多这个问题的讨论,也有人自己写了一个BN层,最终还是去阅读了源码和官方教程这样写的。