【问题标题】:Tensorflow and Batch Normalization with Batch Size==1 => Outputs all zeros批量大小 ==1 => 输出全零的 Tensorflow 和批量标准化
【发布时间】:2018-06-27 11:59:23
【问题描述】:

我有一个关于对 BatchNorm 的理解的问题(BN 稍后)。

我有一个运行良好的卷积网络,我正在编写测试来检查形状和输出范围。我注意到,当我设置 batch_size = 1 时,我的模型输出零(logits 和 activations)。

我用 BN 制作了最简单的卷积网络原型:

输入 => Conv + ReLU => BN => Conv + ReLU => BN => Conv Layer + Tanh

模型使用xavier 初始化进行初始化。我猜 BN 在训练期间做了一些需要 Batch_size > 1 的计算。

我在 PyTorch 中发现了一个似乎在谈论这个的问题:https://github.com/pytorch/pytorch/issues/1381

谁能解释一下?对我来说还是有点模糊。


示例运行:

重要提示:运行此脚本需要 Tensorlayer 库:pip install tensorlayer

import tensorflow as tf
import tensorlayer as tl

import numpy as np

def conv_net(inputs, is_training):

    xavier_initilizer = tf.contrib.layers.xavier_initializer(uniform=True)
    normal_initializer = tf.random_normal_initializer(mean=1., stddev=0.02)

    # Input Layers
    network = tl.layers.InputLayer(inputs, name='input')

    fx = [64, 128, 256, 256, 256]

    for i, n_out_channel in enumerate(fx):

        with tf.variable_scope('h' + str(i + 1)):

            network = tl.layers.Conv2d(
                network,
                n_filter    = n_out_channel,
                filter_size = (5, 5),
                strides     = (2, 2),
                padding     = 'VALID',
                act         = tf.identity,
                W_init      = xavier_initilizer,
                name        = 'conv2d'
            )

            network = tl.layers.BatchNormLayer(
                network,
                act        = tf.identity,
                is_train   = is_training,
                gamma_init = normal_initializer,
                name       = 'batch_norm'
            )

            network = tl.layers.PReluLayer(
                layer  = network,
                a_init = tf.constant_initializer(0.2),
                name   ='activation'
            )

    ############# OUTPUT LAYER ###############

    with tf.variable_scope('h' + str(len(fx) + 1)):
        '''

        network = tl.layers.FlattenLayer(network, name='flatten')

        network = tl.layers.DenseLayer(
            network,
            n_units = 100,
            act     = tf.identity,
            W_init  = xavier_initilizer,
            name    = 'dense'
        )

        '''

        output_filter_size = tuple([int(i) for i in network.outputs.get_shape()[1:3]])

        network = tl.layers.Conv2d(
            network,
            n_filter    = 100,
            filter_size = output_filter_size,
            strides     = (1, 1),
            padding     = 'VALID',
            act         = tf.identity,
            W_init      = xavier_initilizer,

            name        = 'conv2d'
        )

        network = tl.layers.BatchNormLayer(
            network,
            act        = tf.identity,
            is_train   = is_training,
            gamma_init = normal_initializer,
            name       = 'batch_norm'
        )

        net_logits = network.outputs

        network.outputs = tf.nn.tanh(
            x        = network.outputs,
            name     = 'activation'
        )

        net_output = network.outputs

    return network, net_output, net_logits


if __name__ == '__main__':

    tf.logging.set_verbosity(tf.logging.DEBUG)

    #################################################
    #                MODEL DEFINITION               #
    #################################################

    PLH_SHAPE = [None, 256, 256, 3]

    input_plh = tf.placeholder(tf.float32, PLH_SHAPE, name='input_placeholder')

    convnet, net_out, net_logits = conv_net(input_plh, is_training=True)


    with tf.Session() as sess:
        tl.layers.initialize_global_variables(sess)

        convnet.print_params(details=True)

        #################################################
        #                  LAUNCH A RUN                 #
        #################################################

        for BATCH_SIZE in [1, 2]:

            INPUT_SHAPE = [BATCH_SIZE, 256, 256, 3]

            batch_data = np.random.random(size=INPUT_SHAPE)

            output, logits = sess.run(
                [net_out, net_logits],
                feed_dict={
                    input_plh: batch_data
                }
            )

            if tf.logging.get_verbosity() == tf.logging.DEBUG:
                print("\n\n###########################")

                print("\nBATCH SIZE = %d\n" % BATCH_SIZE)

            tf.logging.debug("output => Shape: %s - Mean: %e - Std: %f - Min: %f - Max: %f" % (
                output.shape,
                output.mean(),
                output.std(),
                output.min(),
                output.max()
            ))

            tf.logging.debug("logits => Shape: %s - Mean: %e - Std: %f - Min: %f - Max: %f" % (
                logits.shape,
                logits.mean(),
                logits.std(),
                logits.min(),
                logits.max()
            ))

            if tf.logging.get_verbosity() == tf.logging.DEBUG:
                print("###########################")

提供以下输出:

###########################

BATCH SIZE = 1

DEBUG:tensorflow:output => Shape: (1, 1, 1, 100) - Mean: 0.000000e+00 - Std: 0.000000 - Min: 0.000000 - Max: 0.000000
DEBUG:tensorflow:logits => Shape: (1, 1, 1, 100) - Mean: 0.000000e+00 - Std: 0.000000 - Min: 0.000000 - Max: 0.000000
###########################


###########################

BATCH SIZE = 2

DEBUG:tensorflow:output => Shape: (2, 1, 1, 100) - Mean: -1.430511e-08 - Std: 0.760749 - Min: -0.779634 - Max: 0.779634
DEBUG:tensorflow:logits => Shape: (2, 1, 1, 100) - Mean: -4.768372e-08 - Std: 0.998715 - Min: -1.044437 - Max: 1.044437
###########################

【问题讨论】:

  • 展示一个可重现的例子

标签: tensorflow machine-learning deep-learning conv-neural-network batch-normalization


【解决方案1】:

您可能应该阅读有关 Batch Normalization 的说明,例如 this one。你也可以看看tensorflow's related doc。

基本上,有 2 种方法可以做 batch_norm,并且在处理 1 的批大小时都有问题:

  • 使用每个像素的移动均值和方差像素,因此它们是与批次中的每个样本具有相同形状的张量。这是@layog 的答案中使用的一个,(我认为)在the original paper 中使用的也是最多的。

  • 在整个图像/特征空间上使用移动均值和方差,因此它们只是形状为 (n_channels,) 的向量(等级 1)。

在这两种情况下,您都会:

output = gamma * (input - mean) / sigma + beta

Beta 通常设置为 0,gamma 设置为 1,因为在 BN 之后有线性函数。

在训练期间,mean 和 variance 是在当前批次中计算的,当它的大小为 1 时会出现问题:

  • 在第一种情况下,你会得到mean=input,所以output=0
  • 在第二种情况下,mean 将是所有像素的平均值,所以更好;但如果你的宽度和高度也是 1,那么你会再次得到mean=input,所以你会得到output=0。

我认为大多数人(和原始方法)使用第一种方式,这就是为什么你会得到 0(尽管 TF doc 似乎暗示第二种方法也很常见)。您提供的链接中的论点似乎正在考虑第二种方法。

在任何情况下(无论您使用哪种方式),使用 BN 时,您只有使用更大的批量大小(例如,至少 10 个)才能获得良好的结果。

【讨论】:

  • 我添加了一个小脚本来显示这种行为。这是我的理解,我只是想确认一下。非常感谢您的时间和帮助。非常有价值的解释,应该存储在某个地方;)
  • 给定批量大小为 1,仅在训练标志设置为 true 的情况下使用批量规范是否可行?
【解决方案2】:

Batch Normalization 使用以下(来自original paper)对一个完整批次中的每个输出进行标准化。

例如,对于 2 的批量大小,您有以下输出(大小为 3)

[2, 4, 6]
[4, 6, 8]

现在批次中每个输出的平均值将是

[3, 5, 7]

现在,看看上面公式中的分子。它是从输出的每个元素中减去平均值。但是,如果批量大小为 1,则均值将与输出完全相同,因此它的计算结果为 0。

附带说明一下,即使分母也将被评估为 0,但似乎tensorflow 在0/0 情况下输出0。

【讨论】:

  • 0/0=0 只是来自一个小的variance_epsilon,他们将其添加到分母中,以避免除以 0
猜你喜欢
  • 2020-04-26
  • 2018-04-09
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 2018-06-05
  • 1970-01-01
相关资源
最近更新 更多