TensorFlow中tf.layers.batch_normalization函数详解
1. batch normalization作用
Batch normalization(批量归一化)是深度学习中一种常用的正则化方式,加快神经网络训练的收敛速度,并且增强神经网络对输入数据的鲁棒性。
batch normalization实际做了两件事:
- 对每个batch内的数据进行归一化
- 按照一定比例将数据进行重构,使得模型对于某些特定的数据的变化更加敏感。
2. tf.layers.batch_normalization函数
batch normalization包括很多具体的实现方式,其中在TensorFlow里,tf.layers
模块提供了实现batch normalization的API函数tf.layers.batch_normalization
。下面我来具体讲解这个函数的使用方法。
2.1 函数参数
进入正题,我们先来看下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
)
可以看到,这个函数的参数有点多,但仔细看一下会发现很多参数都是可以默认的。那下面我来逐一解释下每个参数的作用:
- inputs:输入的tensor。一般是2~5维的tensor,代表不同batch大小的多维数据。
- axis:代表需要归一化的维度,默认是最后一维,即按照channel数进行归一化。
- momentum:动量参数,一般取0.99,代表模型更新时新权重与旧权重的加权平均值。
- epsilon:防止除数为0的参数,默认是0.001。
- center:是否添加偏移参数beta,类似于偏置项,对应原公式中的beta,默认为True。
- scale:是否添加缩放参数gamma,对应原公式中的gamma,默认为True。
- beta_initializer:偏移量的初始化函数,默认用全零函数。
- gamma_initializer:缩放量的初始化函数,默认用全一的函数。
- moving_mean_initializer:求均值时的初始化函数,默认为全零函数。
- moving_variance_initializer:求方差时的初始化函数,默认为全一函数。
- beta_regularizer:偏移量的正则化参数,可选参数。
- gamma_regularizer:缩放量的正则化参数,可选参数。
- beta_constraint:偏移量的约束条件,可选参数。
- gamma_constraint:缩放量的约束条件,可选参数。
- training:训练或测试的时间,主要用于batch内归一化的算法选择。默认是False.
- trainable:是否更新beta和gamma参数,如果为False则其不更新。默认是True。
- name:tf.variable_scope下的名字,默认是’BatchNorm’
- reuse:是否reuse该变量,默认是False。
- renorm:是否进行重归一化,一般不用设置,默认是False。
- renorm_clipping:修整(l2范数)落在[trainable_variables_full_batch_size/renorm_clipping, trainable_variables_full_batch_size*renorm_clipping]之间。默认为None。
- renorm_momentum:重归一化动量,默认是0.99。
- fused:主要是cuda加速,设置False代表不使用cuda性能加速,默认是True。
- virtual_batch_size:这个我不太清楚,可以忽略。
- adjustment:似乎是一个系数,类似于momentum参数,使用攻略的过程中并没有太用到。
2.2 demo1:训练过程中使用tf.layers.batch_normalization
现在我们来看一个样例,在训练的过程中如何使用tf.layers.batch_normalization
函数。假设我们的原始数据是一个$1x28x28x3$的tensor,batch_size是128。
import tensorflow as tf
def conv2d(inputs, filters, kernel_size, strides, padding='SAME', activation=tf.nn.relu, name=None):
return tf.layers.conv2d(
inputs=inputs,
filters=filters,
kernel_size=kernel_size,
strides=strides,
padding=padding,
activation=activation,
kernel_initializer=tf.contrib.layers.xavier_initializer(),
bias_initializer=tf.zeros_initializer(),
name=name
)
def batch_norm(inputs, training=True, name=None):
return tf.layers.batch_normalization(
inputs=inputs,
axis=-1,
momentum=0.99,
epsilon=0.001,
center=True,
scale=True,
training=training,
name=name
)
def dense(inputs, units, activation=tf.nn.relu, name=None):
return tf.layers.dense(
inputs=inputs,
units=units,
activation=activation,
kernel_initializer=tf.contrib.layers.xavier_initializer(),
bias_initializer=tf.zeros_initializer(),
name=name
)
# 输入参数
inputs = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28, 3])
targets = tf.placeholder(dtype=tf.int64, shape=[None])
training_flag = tf.placeholder(dtype=tf.bool)
# 构建模型
x = inputs
x = conv2d(inputs=x, filters=32, kernel_size=3, strides=1, padding='VALID', name='conv2d_1')
x = batch_norm(inputs=x, training=training_flag, name='batch_norm_1')
x = conv2d(inputs=x, filters=64, kernel_size=3, strides=1, padding='VALID', name='conv2d_2')
x = batch_norm(inputs=x, training=training_flag, name='batch_norm_2')
x = tf.contrib.layers.flatten(x)
x = dense(inputs=x, units=256, name='dense_1')
x = batch_norm(inputs=x, training=training_flag, name='batch_norm_3')
output = dense(inputs=x, units=10, activation=None, name='output')
# 定义损失函数和优化器
loss = tf.losses.sparse_softmax_cross_entropy(logits=output, labels=targets)
opt = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
# 开始训练
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(10):
# 训练过程中需要设置training_flag为True
# 训练过程中需要将trainable参数设置为True,使其可以更新beta和gamma参数
train_loss = 0
for _ in range(total_batch):
batch_x, batch_y = ...
_, l = sess.run([opt, loss], feed_dict={inputs: batch_x, targets: batch_y, training_flag: True})
train_loss += l
train_loss /= total_batch
print('Epoch {}/{}, training loss: {:.6f}'.format(epoch + 1, train_loss))
在这个样例中,我们用了两个卷积层和两个batch normalization层,其中在batch_norm
函数的training
参数设置为了True,代表着现在是训练过程中,需要使用到批量均一化的算法。
另外,在调用这个函数之前,我们需要先调用dense
和conv2d
函数来定义神经网络的结构。
2.3 demo2:测试过程中使用tf.layers.batch_normalization
下面我们来看一个在测试过程中使用tf.layers.batch_normalization
函数的样例。假设我们在MNIST手写数字识别的测试集上进行预测,我们的原始数据是一个$1x28x28x1$的tensor。
import tensorflow as tf
import numpy as np
def conv2d(inputs, filters, kernel_size, strides, padding='SAME', activation=tf.nn.relu, name=None):
return tf.layers.conv2d(
inputs=inputs,
filters=filters,
kernel_size=kernel_size,
strides=strides,
padding=padding,
activation=activation,
kernel_initializer=tf.contrib.layers.xavier_initializer(),
bias_initializer=tf.zeros_initializer(),
name=name
)
def batch_norm(inputs, training=True, name=None):
return tf.layers.batch_normalization(
inputs=inputs,
axis=-1,
momentum=0.99,
epsilon=0.001,
center=True,
scale=True,
training=training,
name=name
)
def dense(inputs, units, activation=tf.nn.relu, name=None):
return tf.layers.dense(
inputs=inputs,
units=units,
activation=activation,
kernel_initializer=tf.contrib.layers.xavier_initializer(),
bias_initializer=tf.zeros_initializer(),
name=name
)
# 输入参数
inputs = tf.placeholder(dtype=tf.float32, shape=[None, 28, 28, 1])
training_flag = tf.placeholder(dtype=tf.bool)
# 构建模型
x = inputs
x = conv2d(inputs=x, filters=32, kernel_size=3, strides=1, padding='VALID', name='conv2d_1')
x = batch_norm(inputs=x, training=training_flag, name='batch_norm_1')
x = conv2d(inputs=x, filters=64, kernel_size=3, strides=1, padding='VALID', name='conv2d_2')
x = batch_norm(inputs=x, training=training_flag, name='batch_norm_2')
x = tf.contrib.layers.flatten(x)
x = dense(inputs=x, units=256, name='dense_1')
x = batch_norm(inputs=x, training=False, name='batch_norm_3')
output = dense(inputs=x, units=10, activation=None, name='output')
# 定义预测函数
predict = tf.argmax(output, axis=-1)
# 开始测试
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver.restore(sess, 'model/')
# 测试过程中需要将training_flag设置为False,
# 这样的话模型将不再进行batch内归一化的操作,
# 而是根据之前训练出的均值和方差进行归一化
test_pred = []
for idx in range(data_num):
test_x = ...
pred = sess.run(predict, feed_dict={inputs: test_x, training_flag: False})
test_pred.append(pred)
在这个样例中,我们同样定义了两个卷积层和两个batch normalization层,但这次的不同在于,我们将batch_norm
函数的training
参数设置成了False,代表是测试过程,需要根据之前训练好的均值和方差进行激活值的归一化。