Tensorflow使用BN层,包括卷积,BN,Relu的融合使用(详细)

Tensorflow使用BN层,包括卷积BNRelu的融合使用(详细)

BN:Batch Normalization

简介:

指在采用梯度下降算法训练深度神经网络时,对网络中每个mini-bacth的数据进行归一化,使其均值为0,方差为1.

 即   y_{i} \leftarrow \gamma *\hat{ x}_{i} + \beta \equiv BN_{\gamma ,\beta }\left ( x_{i} \right )   通过网络训练两个参数\gamma , \beta \beta

 

在神经网络训练开始前,都要对输入数据做一个归一化处理,那么具体为什么需要归一化呢?

原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值