Tensorflow 和 Batch Normalization with Batch Size==1 => Outputs all zeros
Tensorflow and Batch Normalization with Batch Size==1 => Outputs all zeros
我对BatchNorm(后面的BN)的理解有疑问。
我有一个运行良好的卷积网络,我正在编写测试来检查形状和输出范围。我注意到当我设置 batch_size = 1 时,我的模型输出零(logits 和激活)。
我用 BN 制作了最简单的 convnet 原型:
输入=>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
###########################
Batch Normalization
使用以下(来自 original paper)对整个批次的每个输出进行归一化。
举个例子,批量大小为 2 时,您有以下输出(大小 3)
[2, 4, 6]
[4, 6, 8]
现在批次的每个输出的意思是
[3, 5, 7]
现在,看看上面公式中的分子。它从输出的每个元素中减去均值。但是,如果批量大小为 1,则均值将与输出完全相同,因此计算结果为 0。
作为旁注,即使分母也会被评估为 0,但似乎 tensorflow
在 0/0
情况下输出 0
。
您可能应该阅读有关 Batch Normalization 的解释,例如 this one. You can also take a look at tensorflow's related doc。
基本上,有 2 种方法可以做到 batch_norm,并且在处理批量大小为 1 时都存在问题:
使用每个像素的移动均值和方差像素,因此它们是与批次中的每个样本具有相同形状的张量。这是@layog 的回答中使用的,并且(我认为)在 the original paper 中,也是最常用的。
对整个图像/特征使用移动均值和方差 space,因此它们只是形状为 (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 文档似乎建议第二种方法也很常见)。您提供的 link 中的论点似乎正在考虑第二种方法。
在任何情况下(无论您使用哪种),只有使用更大的批量(例如,至少 10),使用 BN 才能获得好的结果。
我对BatchNorm(后面的BN)的理解有疑问。
我有一个运行良好的卷积网络,我正在编写测试来检查形状和输出范围。我注意到当我设置 batch_size = 1 时,我的模型输出零(logits 和激活)。
我用 BN 制作了最简单的 convnet 原型:
输入=>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
###########################
Batch Normalization
使用以下(来自 original paper)对整个批次的每个输出进行归一化。
举个例子,批量大小为 2 时,您有以下输出(大小 3)
[2, 4, 6]
[4, 6, 8]
现在批次的每个输出的意思是
[3, 5, 7]
现在,看看上面公式中的分子。它从输出的每个元素中减去均值。但是,如果批量大小为 1,则均值将与输出完全相同,因此计算结果为 0。
作为旁注,即使分母也会被评估为 0,但似乎 tensorflow
在 0/0
情况下输出 0
。
您可能应该阅读有关 Batch Normalization 的解释,例如 this one. You can also take a look at tensorflow's related doc。
基本上,有 2 种方法可以做到 batch_norm,并且在处理批量大小为 1 时都存在问题:
使用每个像素的移动均值和方差像素,因此它们是与批次中的每个样本具有相同形状的张量。这是@layog 的回答中使用的,并且(我认为)在 the original paper 中,也是最常用的。
对整个图像/特征使用移动均值和方差 space,因此它们只是形状为
的向量(等级 1)(n_channels,)
.
在这两种情况下,您将拥有:
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 文档似乎建议第二种方法也很常见)。您提供的 link 中的论点似乎正在考虑第二种方法。
在任何情况下(无论您使用哪种),只有使用更大的批量(例如,至少 10),使用 BN 才能获得好的结果。