Tensorflow使用BN层,包括卷积BNRelu的融合使用(详细)
BN:Batch Normalization
简介:
指在采用梯度下降算法训练深度神经网络时,对网络中每个mini-bacth的数据进行归一化,使其均值为0,方差为1.
即 通过网络训练两个参数, 。
在神经网络训练开始前,都要对输入数据做一个归一化处理,那么具体为什么需要归一化呢?
原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。
对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。
网络一旦train起来,那么参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。以网络第二层为例:网络的第二层输入,是由第一层的参数和input计算得到的,而第一层的参数在整个训练过程中一直在变化,因此必然会引起后面每一层输入数据分布的改变。
我们把网络中间层在训练过程中,数据分布的改变称之为:“Internal Covariate Shift”。Paper所提出的算法,就是要解决在训练过程中,中间层数据分布发生改变的情况,于是就有了Batch Normalization
就像激活函数层、卷积层、全连接层、池化层一样,BN(Batch Normalization)也属于网络的一层。在前面我们提到网络除了输出层外,其它层因为低层网络在训练的时候更新了参数,而引起后面层输入数据分布的变化。这个时候我们可能就会想,如果在每一层输入的时候,再加个预处理操作那该有多好啊,比如网络第三层输入数据, 把它归一化至:均值0、方差为1,然后再输入第三层计算,这样我们就可以解决前面所提到的“Internal Covariate Shift”的问题了。
事实上,paper的算法本质原理就是这样:在网络的每一层输入的时候,又插入了一个归一化层,也就是先做一个归一化处理,然后再进入网络的下一层。不过文献归一化层,可不像我们想象的那么简单,它是一个可学习、有参数的网络层
优点:
改变原始数据存在的杂乱无章,使数据分布变得有一定规律
加快网络收敛速度,解决网络收敛慢
解决梯度爆炸
最近项目上将卷积层 conv, BN层,Relu做融合,就详细的结合tensorflow代码来看
# Author Lingge Li from XJTU(446049454@qq.com)
# BN Functions
def variable_on_device(name, shape, initializer, regularizer = None, trainable=True):
dtype = 'float'
collections = [tf.GraphKeys.GLOBAL_VARIABLES, SAVE_VARIABLES]
with tf.device('/cpu:0'):
var = tf.get_variable(
name, shape, initializer=initializer, dtype=dtype, regularizer = regularizer, collections = collections, trainable=trainable)
return var
def variable_with_weight_decay(name, shape, initializer, regularizer = None, trainable=True):
dtype = 'float'
collections = [tf.GraphKeys.GLOBAL_VARIABLES, SAVE_VARIABLES]
with tf.device('/cpu:0'):
var = tf.get_variable(
name, shape, initializer=initializer, dtype=dtype, regularizer = regularizer, collections = collections, trainable=trainable)
return var
def conv_bn_relu_layer(layer_name, inputs, filters, size, stride, padding='SAME', freeze=False, relu=True, stddev=0.001):
decay = 0.99
WEIGHT_DECAY = 0.0005
#is_training could not be defined here
is_training = True
with tf.variable_scope(layer_name) as scope:
channels = inputs.get_shape()[3]
kernel_val = tf.truncated_normal_initializer(stddev=stddev, dtype=tf.float32)
mean_val = tf.constant_initializer(0.0)
var_val = tf.constant_initializer(1.0)
gamma_val = tf.constant_initializer(1.0)
beta_val = tf.constant_initializer(0.0)
kernel = variable_with_weight_decay('kernels', shape=[size, size, int(channels), filters], wd=WEIGHT_DECAY, initializer=kernel_val, trainable=(not freeze))
conv = tf.nn.conv2d(inputs, kernel, [1, stride, stride, 1], padding=padding, name='convolution')
#parameter_bn_shape : num channels
parameter_bn_shape = conv.get_shape()[-1]
# gamma: a trainable scale factor
gamma = variable_on_device('gamma', parameter_bn_shape, gamma_val, trainable=(not freeze))
# beta: a trainable shift value
beta = variable_on_device('beta', parameter_bn_shape, beta_val, trainable=(not freeze))
moving_mean = variable_on_device('moving_mean', parameter_bn_shape, mean_val, trainable=False)
moving_variance = variable_on_device('moving_variance', parameter_bn_shape, var_val, trainable=False)
# self.model_params += [gamma, beta, moving_mean, moving_variance]
# tf.nn.moments == Calculate the mean and the variance of the tensor x
# list(range(len(conv.get_shape()) - 1)) => [0, 1, 2]
# tf.nn.moments(tensor, axis)
if is_training:
mean, variance = tf.nn.moments(conv, list(range(len(conv.get_shape()) - 1)))
update_moving_mean = moving_averages.assign_moving_average(moving_mean, mean, decay, zero_debias=False)
update_moving_variance = moving_averages.assign_moving_average(moving_variance, variance, decay, zero_debias=False)
# first run update_moving_mean, update_moving_variance, then run mean, variance
def mean_var_with_update():
with tf.control_dependencies([update_moving_mean, update_moving_variance]):
return tf.identity(mean), tf.identity(variance)
avg, var = mean_var_with_update()
else:
avg, var = moving_mean, moving_variance
Bn_layer = tf.nn.batch_normalization(x, avg, var, offset=beta, scale=gamma, variance_epsilon=0.001)
output = tf.nn.relu(Bn_layer, 'relu')
return output