Batch Normalization在TensorFlow中有三个接口调用 (不包括slim、Keras模块中的),分别是:
通过观察这三个接口的参数列表可以得到一个初步的结论,tf.layers.batch_normalization
和tf.contrib.layers.batch_norm
可以用来构建待训练的神经网络模型,而tf.nn.batch_normalization
一般只用来构建推理模型。简洁起见,本文把神经网络模型分为训练模式和推理模式(包括推理、测试和评估等)。由于tf.contrib
包的不稳定性,本文将主要介绍如何使用tf.layers.batch_normalization
这个方法在模型中添加BN layer
。
tf.layers.batch_normalization()方法
方法接口如下:
tf.layers.batch_normalization( inputs, axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer=tf.zeros_initializer(), gamma_initializer=tf.ones_initializer(), moving_mean_initializer=tf.zeros_initializer(), moving_variance_initializer=tf.ones_initializer(), beta_regularizer=None, gamma_regularizer=None, beta_constraint=None, gamma_constraint=None, training=False, trainable=True, name=None, reuse=None, renorm=False, renorm_clipping=None, renorm_momentum=0.99, fused=None, virtual_batch_size=None, adjustment=None )
这里有几个重要参数需要注意:
axis
的值取决于按照input
的哪一个维度进行BN,例如输入为channel_last
format,即[batch_size, height, width, channel]
,则axis
应该设定为4,如果为channel_first
format,则axis
应该设定为1.momentum
的值用在训练时,滑动平均的方式计算滑动平均值moving_mean
和滑动方差moving_variance
。 后面做更详细的说明。center
为True
时,添加位移因子beta
到该BN层,否则不添加。添加beta
是对BN层的变换加入位移操作。注意,beta
一般设定为可训练参数,即trainable=True
。scale
为True
是,添加缩放因子gamma
到该BN层,否则不添加。添加gamma
是对BN层的变化加入缩放操作。注意,gamma
一般设定为可训练参数,即trainable=True
。training
表示模型当前的模式,如果为True
,则模型在训练模式,否则为推理模式。要非常注意这个模式的设定,这个参数默认值为False
。如果在训练时采用了默认值False
,则滑动均值moving_mean
和滑动方差moving_variance
都不会根据当前batch的数据更新,这就意味着在推理模式下,均值和方差都是其初始值,因为这两个值并没有在训练迭代过程中滑动更新。
TensorFlow的api文档中对该方法标星出了Note警示如下:
train_op
并没有依赖滑动均值moving_mean
和滑动方差moving_variance
,则moving_mean
和moving_variance
不会自动更新,所以必须加入负责更新这些参数的update_ops
到依赖中,且应该在执行前向计算结束后、后向计算开始前执行update_ops
,所以添加依赖的位置不能出错。实际中,只需要在构建模型代码中,添加完所有BN层之后获取update_ops
就不会出错,切记!切记!这是TensorFlow的图计算模式造成的编程影响,在其他深度学习框架中可能会有差别。
Batch Normalization的作用
- 效果
该方法出自Sergey Ioffe和Christian Szegedy的《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》一文,BN方法到底能否减少Internal Covariate Shift可能还无法下最终结论 (参见《How Does Batch Normalization Help Optimization?》),但的确可以加速训练平稳收敛!所以,BatchNorm成为了居家旅行、训练模型之必备layer。 - 计算
BatchNorm的具体计算/变换如图 (见论文):mean
,计算出当前batch的每个channel的方差variance
,令输入减去均值再除以标准差delta
,得到normalized输出x-hat
,最后乘以scale参数gamma
,加上shift参数beta
,得到最终变换后的输出y
。 - BN层在train与inference时的差别
在训练时,我们可以计算出batch的均值和方差,迭代训练过程中,均值和方差一直在发生变化。但是在推理时,均值和方差是固定的,它们在训练过程中就被确定下来。《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》中给出的确定方式和TensorFlow中存在不同,这里我们介绍TensorFlow中的方式,即采用滑动平均MovingAverage的方法,公式为:moving_average_value * momentum + value * (1 - momentum)
,其中value
为当前batch的平均值或方差,moving_average_value
为滑动均值或滑动方差。
最终,模型训练完毕后,在推理时使用滑动平均值moving_mean
和滑动方差moving_variance
对feature maps进行变换。 - 在模型中的哪些位置插入BN层
推荐在Conv层或FC层之后,非线性变换激活层之前插入BN层。原因见论文:
TensorFlow中的BatchNorm实现链
tf.layers.batch_normalization -->
tf.layers.BatchNormalization -->
tf.python.keras.layers.BatchNormalization -->
keras.backend.normalize_batch_in_training -->
keras.backend._fused_normalize_batch_in_training -->
tf.python.ops.nn.fused_batch_norm
tf.python.keras.layers.BatchNormalization -->
keras.backend.moving_average_update -->
tf.python.training import moving_averages.moving_averages
前半部分主要是对batch_norm的变换,后半部分是对滑动均值的更新。
后记
写这篇文章是为了介绍BatchNorm在TensorFlow中的正确使用方法。作者曾因为没有正确传递training
参数给tf.layers.batch_normalization
,导致模型在训练完成后所有moving_mean都是0,所有moving_variance都是1.0,在读取并量化模型进行batch norm fold时,无法读入这两个参数,提示NoneType
错误。经过研究一番才发现是调用tf.layers.batch_normalization
方法是必须pass正确的模式。
链接:https://www.jianshu.com/p/437fb1a5823e