使用TensorFlow中的3D卷积进行批量标准化

Ale*_*o B 17 python machine-learning deep-learning tensorflow batch-normalization

我正在实施一个依赖于3D卷积的模型(用于类似于动作识别的任务),我想使用批量标准化(参见[Ioffe&Szegedy 2015]).我找不到任何专注于3D转换的教程,因此我在这里做一个简短的教程,我想和你一起回顾.

下面的代码引用TensorFlow r0.12并且它显式实例变量 - 我的意思是我没有使用tf.contrib.learn,除了tf.contrib.layers.batch_norm()函数.我这样做是为了更好地了解事情如何在幕后工作并具有更多的实现自由(例如,可变摘要).

通过首先编写完全连接层的示例,然后进行2D卷积,最后编写3D情况,我将顺利地进入3D卷积情况.在浏览代码时,如果你能检查一切是否正确完成会很好 - 代码运行,但我不能100%确定应用批量规范化的方式.我以更详细的问题结束这篇文章.

import tensorflow as tf

# This flag is used to allow/prevent batch normalization params updates
# depending on whether the model is being trained or used for prediction.
training = tf.placeholder_with_default(True, shape=())
Run Code Online (Sandbox Code Playgroud)

完全连接(FC)外壳

# Input.
INPUT_SIZE = 512
u = tf.placeholder(tf.float32, shape=(None, INPUT_SIZE))

# FC params: weights only, no bias as per [Ioffe & Szegedy 2015].
FC_OUTPUT_LAYER_SIZE = 1024
w = tf.Variable(tf.truncated_normal(
    [INPUT_SIZE, FC_OUTPUT_LAYER_SIZE], dtype=tf.float32, stddev=1e-1))

# Layer output with no activation function (yet).
fc = tf.matmul(u, w)

# Batch normalization.
fc_bn = tf.contrib.layers.batch_norm(
    fc,
    center=True,
    scale=True,
    is_training=training,
    scope='fc-batch_norm')

# Activation function.
fc_bn_relu = tf.nn.relu(fc_bn)
print(fc_bn_relu)  # Tensor("Relu:0", shape=(?, 1024), dtype=float32)
Run Code Online (Sandbox Code Playgroud)

2D卷积(CNN)层情况

# Input: 640x480 RGB images (whitened input, hence tf.float32).
INPUT_HEIGHT = 480
INPUT_WIDTH = 640
INPUT_CHANNELS = 3
u = tf.placeholder(tf.float32, shape=(None, INPUT_HEIGHT, INPUT_WIDTH, INPUT_CHANNELS))

# CNN params: wights only, no bias as per [Ioffe & Szegedy 2015].
CNN_FILTER_HEIGHT = 3  # Space dimension.
CNN_FILTER_WIDTH = 3  # Space dimension.
CNN_FILTERS = 128
w = tf.Variable(tf.truncated_normal(
    [CNN_FILTER_HEIGHT, CNN_FILTER_WIDTH, INPUT_CHANNELS, CNN_FILTERS],
    dtype=tf.float32, stddev=1e-1))

# Layer output with no activation function (yet).
CNN_LAYER_STRIDE_VERTICAL = 1
CNN_LAYER_STRIDE_HORIZONTAL = 1
CNN_LAYER_PADDING = 'SAME'
cnn = tf.nn.conv2d(
    input=u, filter=w,
    strides=[1, CNN_LAYER_STRIDE_VERTICAL, CNN_LAYER_STRIDE_HORIZONTAL, 1],
    padding=CNN_LAYER_PADDING)

# Batch normalization.
cnn_bn = tf.contrib.layers.batch_norm(
    cnn,
    data_format='NHWC',  # Matching the "cnn" tensor which has shape (?, 480, 640, 128).
    center=True,
    scale=True,
    is_training=training,
    scope='cnn-batch_norm')

# Activation function.
cnn_bn_relu = tf.nn.relu(cnn_bn)
print(cnn_bn_relu)  # Tensor("Relu_1:0", shape=(?, 480, 640, 128), dtype=float32)
Run Code Online (Sandbox Code Playgroud)

3D卷积(CNN3D)层案例

# Input: sequence of 9 160x120 RGB images (whitened input, hence tf.float32).
INPUT_SEQ_LENGTH = 9
INPUT_HEIGHT = 120
INPUT_WIDTH = 160
INPUT_CHANNELS = 3
u = tf.placeholder(tf.float32, shape=(None, INPUT_SEQ_LENGTH, INPUT_HEIGHT, INPUT_WIDTH, INPUT_CHANNELS))

# CNN params: wights only, no bias as per [Ioffe & Szegedy 2015].
CNN3D_FILTER_LENGHT = 3  # Time dimension.
CNN3D_FILTER_HEIGHT = 3  # Space dimension.
CNN3D_FILTER_WIDTH = 3  # Space dimension.
CNN3D_FILTERS = 96
w = tf.Variable(tf.truncated_normal(
    [CNN3D_FILTER_LENGHT, CNN3D_FILTER_HEIGHT, CNN3D_FILTER_WIDTH, INPUT_CHANNELS, CNN3D_FILTERS],
    dtype=tf.float32, stddev=1e-1))

# Layer output with no activation function (yet).
CNN3D_LAYER_STRIDE_TEMPORAL = 1
CNN3D_LAYER_STRIDE_VERTICAL = 1
CNN3D_LAYER_STRIDE_HORIZONTAL = 1
CNN3D_LAYER_PADDING = 'SAME'
cnn3d = tf.nn.conv3d(
    input=u, filter=w,
    strides=[1, CNN3D_LAYER_STRIDE_TEMPORAL, CNN3D_LAYER_STRIDE_VERTICAL, CNN3D_LAYER_STRIDE_HORIZONTAL, 1],
    padding=CNN3D_LAYER_PADDING)

# Batch normalization.
cnn3d_bn = tf.contrib.layers.batch_norm(
    cnn3d,
    data_format='NHWC',  # Matching the "cnn" tensor which has shape (?, 9, 120, 160, 96).
    center=True,
    scale=True,
    is_training=training,
    scope='cnn3d-batch_norm')

# Activation function.
cnn3d_bn_relu = tf.nn.relu(cnn3d_bn)
print(cnn3d_bn_relu)  # Tensor("Relu_2:0", shape=(?, 9, 120, 160, 96), dtype=float32)
Run Code Online (Sandbox Code Playgroud)

我想确定的是上面的代码是否完全实现了批次规范化,如[Ioffe&Szegedy 2015]中所述.3.2:

对于卷积层,我们还希望归一化遵循卷积属性 - 以便在不同位置对同一特征映射的不同元素以相同方式进行归一化.为实现这一目标,我们联合规范了所有地点的小批量活动.[...] Alg.类似地修改图2,使得在推理期间,BN变换对给定特征图中的每个激活应用相同的线性变换.

更新 我猜上面的代码对于3D转换情况也是正确的.事实上,当我定义我的模型时,如果我打印所有可训练的变量,我也会看到β和γ变量的预期数量.例如:

Tensor("conv3a/conv3d_weights/read:0", shape=(3, 3, 3, 128, 256), dtype=float32)
Tensor("BatchNorm_2/beta/read:0", shape=(256,), dtype=float32)
Tensor("BatchNorm_2/gamma/read:0", shape=(256,), dtype=float32)
Run Code Online (Sandbox Code Playgroud)

这对我来说没问题,因为由于BN,每个特征映射都学习了一对beta和gamma(总共256个).


[Ioffe&Szegedy 2015]:批量标准化:通过减少内部协变量转换来加速深度网络训练

Max*_*xim 2

这是一篇关于 3D batchnorm 的很棒的文章,人们常常没有注意到,batchnorm 可以应用于任何秩大于 1 的张量。您的代码是正确的,但我忍不住对此添加了一些重要注释:

  • “标准”2D batchnorm(接受 4D 张量)在张量流中比 3D 或更高版本要快得多,因为它支持fused_batch_norm应用一个内核操作的实现:

    融合批量归一化将批量归一化所需的多个操作组合到一个内核中。批归一化是一个昂贵的过程,对于某些模型来说,它占据了操作时间的很大一部分。使用融合批量归一化可以带来 12%-30% 的加速。

    GitHub 上也有一个支持 3D 滤镜的问题,但最近没有任何活动,目前该问题尚未解决。

  • 尽管原始论文规定在 ReLU 激活之前使用 batchnorm(这就是您在上面的代码中所做的),但有证据表明在激活之后使用 batchnorm 可能更好。以下是 Francois Chollet 在Keras GitHub上的评论:

    ...我可以保证 Christian [Szegedy] 最近编写的代码在 BN 之前应用了 relu。不过,这偶尔仍然是一个争论的话题。

  • 对于任何有兴趣在实践中应用归一化思想的人,这个思想最近有研究进展,即权重归一化层归一化,它们修复了原始批归一化的某些缺点,例如它们更适合 LSTM 和循环网络。