一、批量标准化(BN,Batch Normalization)

1、BN 简介

a、协变量偏移问题

  • 我们知道,在统计机器学习中算法中,一个常见的问题是 协变量偏移(Covariate Shift),协变量可以看作是 输入变量。一般的深度神经网络都要求输入变量在训练数据和测试数据上的分布是相似的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障。
  • 传统的深度神经网络在训练时,随着参数的不算更新,中间每一层输入的数据分布往往会和参数更新之前有较大的差异,导致网络要去不断的适应新的数据分布,进而使得训练变得异常困难,我们只能使用一个很小的学习速率和精调的初始化参数来解决这个问题。而且这个中间层的深度越大时,这种现象就越明显。由于是对层间数据的分析,也即是内部(internal),因此这种现象叫做内部协变量偏移(internal Covariate Shift)

b、解决方法

  • 为了解决这个问题,Sergey Ioffe’s 和 Christian Szegedy’s 在2015年首次提出了批量标准化(Batch Normalization,BN)的想法。该想法是:不仅仅对输入层做标准化处理,还要对 每一中间层的输入(激活函数前) 做标准化处理,使得输出服从均值为 0,方差为 1 的正态分布,从而避免内部协变量偏移的问题。之所以称之为 标准化:是因为在训练期间,我们仅通过计算 当前层一小批数据 的均值和方差来标准化每一层的输入。
  • BN 的具体流程如下图所示:首先,它将隐藏层的输出结果(如,第一层: W h 1 . x W_{h_1}.x Wh1.x,状态值)在 batch 上进行标准化;然后,经过缩放(scale)和平移(shift)处理,最后经过 RELU 激活函数得到 h 1 h_1 h1送入下一层(就像我们在数据预处理中将 x 进行标准化后送入神经网络的第一层一样)。

这里写图片描述

  • γ \gamma γ β \beta β 的解释: γ \gamma γ β \beta β 都是可学习参数,分别用作对标准化后的值进行缩放(scale)和平移(shift),提高网络的表达能力(不加此参数的话,中间层的输出将被限制在标准正态分布下)。当参数分别初始化为 σ B \sigma_{B} σB μ B \mu_B μB时,标准化的变量被还原为原来的值。随着网络的训练,网络会学到最合适的 γ \gamma γ β \beta β ,最终中间层的输出将服从均值为 β \beta β,方差为 γ 2 \gamma^{2} γ2 的正态分布
  • ϵ \epsilon ϵ 的解释: 为了避免方差为 0 而加入的微小正数(eg:0.001)
  • b h 1 b_{h1} bh1 的解释: 减去第一层 batch 个数据的均值 μ B \mu_B μB 后, 偏置项 b b b 的作用会被抵消掉,所以在使用 BN 的层中没必要加入偏置项 b b b (红色删除线),其平移的功能将由 β \beta β 来代替

2、BN 的优点

现在几乎所有的卷积神经网络都会使用批量标准化操作,它可以为我们的网络训练带来一系列的好处。具体如下:

  • 首先,通过对输入和中间网络层的输出进行标准化处理后,减少了内部神经元分布的改变,使降低了不同样本间值域的差异性,得大部分的数据都其处在非饱和区域,从而保证了梯度能够很好的回传,避免了梯度消失和梯度爆炸
  • 其次,通过减少梯度对参数或其初始值尺度的依赖性,使得我们可以使用较大的学习速率对网络进行训练,从而加速网络的收敛
  • 最后,由于在训练的过程中批量标准化所用到的均值和方差是在一小批样本(mini-batch)上计算的,而不是在整个数据集上,所以均值和方差会有一些小噪声产生,同时缩放过程由于用到了含噪声的标准化后的值,所以也会有一点噪声产生,这迫使后面的神经元单元不过分依赖前面的神经元单元。所以,它也可以看作是一种正则化手段,提高了网络的泛化能力,使得我们可以减少或者取消 Dropout,优化网络结构

3、BN 的使用注意事项

  • BN 通常应用于输入层或任意中间层,且最好在激活函数前使用
  • 使用 BN 的层不需要加 bias 项了,因为减去batch 个数据的均值 μ B \mu_B μB 后, 偏置项 b b b 的作用会被抵消掉,其平移的功能将由 β \beta β 来代替
  • BN 的训练阶段均值和方差的求法
    • 全连接层:对每个节点(node) 对应的 batch 个数据(所有像素)分别求均值和方差
    • 二维卷积层:对每一个 channel 所对应的 batch 个特征图(所有像素)求均值和方差,输出 C 维
    • BN 层参数:mean(C 维)、variance(C 维)、moving average factor(1 维,caffe 中默认为 0.999)
      在这里插入图片描述
  • BN 的测试阶段均值和方差的求法:测试时,使用的是 训练集 的每一个 BN 层累积下来的 均值和方差(C 维);训练阶段使用 指数衰减滑动平均(EMA) 将每个 batch 的均值和方差不断的累积下来,从而近似得到整个样本集的均值和方差
  • 指数衰减滑动平均(EMA)介绍见:指数加权移动平均(Exponential Weighted Moving Average),均值的计算公式如下(标准差的计算同理)
    在这里插入图片描述

4、TensorFLow 中的 BN 怎么使用?

  • Added is_training, a placeholder to store a boolean value indicating whether or not the network is training.
  • Passed is_training to the conv_layer and fully_connected functions.
  • Each time we call run on the session, we added to feed_dict the appropriate value for is_training
  • Moved the creation of train_step inside a with tf.control_dependencies statement. This is necessary to get the normalization layers created with tf.layers.batch_normalization to update their population statistics, which we need when perform in inference.
##### 设计计算图阶段 #####

# 1、去掉 bias 项,且在此阶段不使用激活函数
linear_output = tf.layers.dense(prev_layer, num_units, use_bias=False, activation=None)
conv_output = tf.layers.conv2d(prev_layer, layer_depth*4, 3, strides, 'same', use_bias=False, activation=None)

# 2、使用 tf.contrib.layers.batch_norm() 或者 tf.layers.batch_normalization()进行标准化处理
# 注意要传入占位符 is_training 表明是训练阶段还是测试阶段
bn_linear_output = tf.layers.batch_normalization(linear_output, training=is_training)
bn_conv_output = tf.layers.batch_normalization(conv_output, training=is_training)

# 3、将标准化后的值传递给激活函数 
liner_layer = tf.nn.relu(bn_linear_output)
conv_layer = tf.nn.relu(bn_conv_output)

# 4、训练之前必须先更新所有 BN 层的 moving_mean 和 moving_variance,因为测试阶段要用到训练集的均值和方差
# 另解:设置 tf.contrib.layers.batch_norm() 函数中的参数 updates_collections 值为 None 强制其原地更新,就不用加 with 语句中的控制依赖了
with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)
 

*********************************************************************


##### 执行计算图阶段 #####

# 1、添加一个布尔型占位符 is_training 来表明当前是训练阶段还是测试阶段
is_training = tf.placeholder(tf.bool, name="is_training")

# 2、训练阶段将feed_dict 中的参数 is_training 置为 True, 测试阶段置为 False
sess.run(train_step, feed_dict={input_layer: batch_xs, 
                                labels: batch_ys, 
                                is_training: True})

sess.run(accuracy, feed_dict={input_layer: mnist.validation.images,
                              labels: mnist.validation.labels,
                              is_training: False})

5、Caffe 中的 BN 怎么使用?

layer {
    bottom: "data"
    top: "conv1"
    name: "conv1"
    type: "Convolution"
    convolution_param {
        num_output: 64
        kernel_size: 7
        pad: 3
        stride: 2
        weight_filler {
            type: "msra"
        }
        bias_term: false  # BN 前卷积的偏置项就不需要了,因为减均值那一步把偏置项的作用抵消了

    }
}

# caffe 中 BN 层的三个参数:moving_mean、moving_variance、moving_average_fraction 是不需要学习的
# 均值和方差均为 C 维,训练的时候通过指数衰减滑动平均来实现,测试的时候使用训练时的均值和方差!
layer {
    bottom: "conv1"
    top: "conv1"
    name: "bn_conv1"
    type: "BatchNorm"
    param {
        lr_mult: 0 
        decay_mult: 0
    }
    param {
        lr_mult: 0 
        decay_mult: 0
    }
    param {
        lr_mult: 0 
        decay_mult: 0
    }
    batch_norm_param {
      moving_average_fraction: 0.9
      use_global_stats: false  # 训练阶段设置为 false,测试阶段设置为 true  
    }
}

# BN 中的可学习参数 scale 和 shift 通过 Scale 层实现,注意 bias_term 设置为 true
# 我们一般不对 scale 和 shift 参数使用正则化
layer {
    bottom: "conv1"
    top: "conv1"
    name: "scale_conv1"
    type: "Scale"
    param {
        lr_mult: 1 
        decay_mult: 0
    }
    param {
        lr_mult: 1 
        decay_mult: 0
    }
    scale_param {
        bias_term: true
    }
}


二、实例标准化(IN,Instance Normalization)

  • BN 和 IN 其实本质上是同一个东西,只是 IN 是作用于单张图片(对单个图片的所有像素求均值和标准差),但是 BN 作用于一个 batch(对一个batch里所有的图片的所有像素求均值和标准差)。但是为什么 IN 还会被单独提出,而且在 Style Transfer、GAN 等任务中大放异彩呢?
    • 通过调整 BN 统计量,或学习的参数 γ \gamma γ β \beta β ,BN 可以用来做 domain adaptation
    • Style Transfer 是一个把每张图片当成一个 domain 的 domain adaptation 问题
  • IN 在训练和测试阶段都用,BN 只在训练阶段用,测试阶段用训练时通过指数衰减滑动平均保存的均值和方差

三、特征标准化(FN,Feature Normalization)

  • FN 是 BN 的特例,只需令 BN 中的缩放和平移参数 β = 0 \beta=0 β=0 γ = 1 \gamma=1 γ=1 即可,FN 服从标准正态分布。
  • FN 作用于最后一层的特征表示上(FN 的下一层便是目标函数层),it ensures that all of the features have equal contribution to the cost function。
  • FN 可提高习得特征的分辨能力,适用于类似人脸识别(face recognition)、行人重检测(person re-identification)、车辆重检测(car re-identification)等任务。
    这里写图片描述

四、BN/LN/IN/GN 的区别

  • 相同点:均为减均值除标准差,然后再进行缩放和平移;都是基于 feature map 做归一化
  • 不同点:均值和方差求取方式不同,LN/IN/GN 这三个解决的主要问题是 BN 的效果依赖于 batch size,当 batch size 比较小时,性能退化严重。可以看到,IN,LN 和 GN 都与 batch size 无关
    • BN 计算 所有样本一个通道 上的均值和方差,在检测、分类等任务上效果较好
    • LN 计算 单个样本在所有通道 上的均值和方差,适用于 RNN,transformer
    • IN 值计算 单个样本在单个通道 上的均值和方差,在 GAN, style transfer 和 domain adaptation 这类生成任务上,IN 的效果明显比 BN 更好
    • GN 将每个样本的通道分成 g 组,计算每组(单个样本 g 个通道上)的均值和方差,适用于大分辨率较小 batch size 的任务
      在这里插入图片描述

五、参考资料

1、YJango的Batch Normalization–介绍
2、https://github.com/udacity/deep-learning/tree/master/batch-norm
2、tensorflow中 batch normalization的用法
3、深度学习中 Batch Normalization为什么效果好?
4、深度学习(二十九)Batch Normalization 学习笔记
5、为什么会出现 Batch Normalization 层?
6、详解深度学习中的Normalization,不只是BN
7、Batch normalization和Instance normalization 的对比?

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐