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警示如下:
Note and code example of tf.layers.batch_normalization
TensorFlow中模型训练时的梯度计算、参数优化等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的具体计算/变换如图 (见论文):
Batch Normalizing Transform, applied to activation x over a mini-batch.用大白话描述就是,计算出当前batch的每个channel的均值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层。原因见论文:
where to insert BN layers
TensorFlow中的BatchNorm实现链
前半部分主要是对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正确的模式。