Tensorflow: 易于使用的批处理规范层。

创建于 2016-02-16  ·  127评论  ·  资料来源: tensorflow/tensorflow

许多非专家都在使用以下代码http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow?answertab=votes#tab -top。

考虑到官方批处理规范层在培训DNN中的重要性,这将是一件很不错的事情。

contributions welcome docs-bug

所有127条评论

我正在做某些事情。

我认为该层有些问题。 在训练中,每件事都可以并且损失减少得很好。 但是在测试中,我的准确度为零。
顺便说一句,当我使用is_training = False时,我得到的acc为零。
我知道批次归一化在训练和测试阶段的行为会有所不同,如批次归一化在训练时间和测试时间的行为有何不同?

同样在这里,我遇到了is_training = False的一些意外行为。 更改此标志的正确方法是什么? 我目前正在使用tf.cond因为它本身不会占用tf.placeholders

@pawni您必须为is_training使用Python布尔值。 不能是tf.cond

@ppwwyyxx好,我在做tf.cond(placeholder, batch_norm(.., is_training = True), batch_norm(.., is_training = False))还是应该只做一个batch_norm(.., is_training=variable)并在需要时在图形外更改它?

哦,我以为你在做batch_norm(.., is_training=tf.cond(placeholder)) ,这是不正确的。
您当前的方式可能也会有问题。 您需要仔细检查创建的两个batch_norm op共享相同的范围,否则它们将不会共享基本的均值/方差统计信息。

为此, reuse参数可能会有所帮助,但是我不确定,因为我使用了自己的bn层版本。

我正在使用相同的范围和reuse=True 。 有时似乎可行,但我不太确定。 最好将这一层添加到文档中,并简短说明如何最好地处理从培训到测试的更改。

@sguada仅供参考

当前batch_norm需要一个python布尔值,但是我们正在努力增加传递张量的选项。

@pawni如果您不想担心更新moving_mean和moving_variance设置为updates_collections = None,以确保它们已就位更新,否则您需要确保添加到tf.GraphKeys.UPDATE_OPS的update_ops在训练期间运行。

我认为tensorflow需要2种更改模型状态的超级方法,例如火炬。 改变模型状态。 我认为这非常简单。

是否有一个带有非常简单的NN的小脚本,显示了使用此“官方” BN层的正确方法是什么? 我真的很感激。

抱歉,如果这有点重复,但似乎API在不同的界面中谈论BN: https :

这不是使用BN的官方方式吗? 我对如何使用它感到困惑,SO似乎已经过时了,然后在与API不同的链接中存在一层,那么到底该怎么做呢? 我不清楚是去SO还是在这里问。

对垃圾邮件表示歉意,但是仅使用以下内容有什么问题:

def standard_batch_norm(l, x, n_out, phase_train, scope='BN'):
    """
    Batch normalization on feedforward maps.
    Args:
        x:           Vector
        n_out:       integer, depth of input maps
        phase_train: boolean tf.Varialbe, true indicates training phase
        scope:       string, variable scope
    Return:
        normed:      batch-normalized maps
    """
    with tf.variable_scope(scope+l):
        #beta = tf.Variable(tf.constant(0.0, shape=[n_out], dtype=tf.float64 ), name='beta', trainable=True, dtype=tf.float64 )
        #gamma = tf.Variable(tf.constant(1.0, shape=[n_out],dtype=tf.float64 ), name='gamma', trainable=True, dtype=tf.float64 )
        init_beta = tf.constant(0.0, shape=[n_out], dtype=tf.float64)
        init_gamma = tf.constant(1.0, shape=[n_out],dtype=tf.float64)
        beta = tf.get_variable(name='beta'+l, dtype=tf.float64, initializer=init_beta, regularizer=None, trainable=True)
        gamma = tf.get_variable(name='gamma'+l, dtype=tf.float64, initializer=init_gamma, regularizer=None, trainable=True)
        batch_mean, batch_var = tf.nn.moments(x, [0], name='moments')
        ema = tf.train.ExponentialMovingAverage(decay=0.5)

        def mean_var_with_update():
            ema_apply_op = ema.apply([batch_mean, batch_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(batch_mean), tf.identity(batch_var)

        mean, var = tf.cond(phase_train, mean_var_with_update, lambda: (ema.average(batch_mean), ema.average(batch_var)))
        normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
    return normed

然后简单地告诉tensorflow与供稿字典一起使用哪一个,如下所示:

feed_dict = {x: Xminibatch, y_: Yminibatch, phase_train: True}
sess.run(fetches=[merged,train_step], feed_dict=feed_dict)

由于尚不清楚实现是否会改变,因此我想提出一个建议(请注意,它很容易扩展到卷积和我刚才未粘贴该代码的内容)。

@pawni @ppwwyyxx你们是否决定过必须使用重用来解决范围问题?

@ brando90当前我正在做类似的事情:

def BatchNorm(inputT, is_training=True, scope=None):
    return tf.cond(isTraining,
                lambda: batch_norm(inputT, is_training=True,
                                   center=False, updates_collections=None, scope=scope),
                lambda: batch_norm(inputT, is_training=False,
                                   updates_collections=None, center=False, scope=scope, reuse = True))

但是,我认为#3265基本上想这样实现。 参考可以是此处的辍学实现: https :

当updates_collections = None时,更新就地发生,并且使用tf.cond()允许is_training作为Tensor更加容易,这是因为更新延迟并且update_ops稍后运行。
我将尝试尽快获得第一部分。

@ brando90 @pawni他的代码效果不错,但必须像下面这样更改

def BatchNorm(inputT, is_training=True, scope=None):
    # Note: is_training is tf.placeholder(tf.bool) type
    return tf.cond(is_training,  
                lambda: batch_norm(inputT, is_training=True,  
                                   center=False, updates_collections=None, scope=scope),  
                lambda: batch_norm(inputT, is_training=False,  
                                   updates_collections=None, center=False, scope=scope, reuse = True))  

当在训练或测试时间内跑步时,

# when training 
sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=True})  

# when test 
sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=False})  

这段代码有效,但是就像#3265所说的那样,如果tf.contrib.layers.batch_norm获得is_training变量作为tf.plcaeholer会很棒。

@nmhkahn @pawni感谢您的代码段。 它们在将批处理归一化添加到我的卷积网络中非常有用。 培训似乎效果很好。 测试不是。 在某些版本的代码中,训练精度比测试精度要高得多,这可能意味着我不共享批量标准化参数。 在其他版本的代码中,我得到“ ValueError:不允许使用变量conv1 / beta。不允许您在VarScope中设置复用= True吗?” 这似乎表明我在尝试重用...时尝试重​​新学习参数。

有人可以提供一个示例,说明如何在培训和测试期间调用“ def BatchNorm”函数,以便正确进行变量共享。

谢谢你的帮助。

2016年7月25日更新:

@nmhkahn @pawni感谢您的评论。 在仔细研究了contrib中的代码之后,我意识到了我的问题所在。 在训练和测试过程中,我们将更新或重用四个变量(beta,gamma,moving_mean和moving_variance)。 为了使这些独特,我必须为每个图层设置一个范围。 我这样做是这样的:

conv1 = tf.nn.relu(batch_norm_layer(conv2d_stride2_valid(data,W_conv1)+ b_conv1,train_phase,scope =“ conv1”))

其中batch_norm_layer与@nmhkahn @pawni中的示例相似,conv2d_stride2_valid只是定义卷积层的def,而W_conv1和b_conv1是保存权重和偏差的变量。 我可能会删除偏差项,因为我们正在使用批处理规范化。

网络现在运行良好。 在训练和测试模式下绘制精度后,我注意到测试精度在训练精度之后开始上升。 回想起来,这是有道理的,因为我们正在收集数据集统计数据进行测试。 但是似乎我在最初的测试中做错了什么。 感谢您的评论,并使社区可以使用批量标准化。

@nmhkahn与pawni的建议有何不同?

@ brando90我的版本中有一个小错误,该错误已由nmhkahn修复(将isTraining更改is_training

@diegoAtAlpine我发现了同样的问题-不知道为什么会这样。 但是,ValueError应该由代码片段解决。 不确定nmhkahn的示例似乎可以胜任此工作,您想知道如何称呼它?

@nmhkahn @pawni @当您这样做时:

sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=True})

这是否意味着您使用is_training作为占位符? 人们评论说,他们希望is_training成为放置者,但这就是我的版本:

def batch_norm_layer(x,train_phase,scope_bn):

    bn_train = batch_norm(x, decay=0.999, center=True, scale=True,
    is_training=True,
    reuse=None, # is this right?
    trainable=True,
    scope=scope_bn)
    bn_inference = batch_norm(x, decay=0.999, center=True, scale=True,
    is_training=False,
    reuse=True, # is this right?
    trainable=True,
    scope=scope_bn)
    z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
    return z

那不正确吗?

我已经扩展了tf.contrib.layers.batch_norm,以允许通过张量或占位符进行is_training。 它将很快合并到TF contrib中。

现在可用
https://github.com/tensorflow/tensorflow/commit/9da5fc8e6425cabd61fc36f0dcc1823a093d5c1d#diff -94bbcef0ec8a5cdef55f705e99c2b2ed

是我还是添加BN层会明显减慢单个纪元的训练?

@ brando90它也减慢了我的培训速度,但是我认为这是可以预期的,因为它需要计算一些统计数据。 您的版本对我来说很好。

BatchNorm当前非常慢(由于计算了所有统计信息),但是他们正在按此处所说的那样添加cudnn batchnorm op。

@nmhkahn快速提问。 当您写(用于测试)时:

sess.run([opt, loss], feed_dict={x: bx, y: by, is_training=False})

从理论上讲,bx和可以是任何数据集吗? 也就是说,即使我们没有训练,它仍然可以作为训练集吗? (即只是跟踪火车错误)

@ brando90你是对的。

我也对is_training和复用标志感到困惑。 我已按照CIFAR示例创建了一个程序,其中代码的结构与CIFAR相同:

  • 推理
  • 失利
  • 培养

而且我正在以多种GPU的方式运行它(用于培训)。
因此,我有一个用于训练的脚本(类似于cifar10_multigpu.py)和一个用于测试的脚本(类似于cifar10_eval.py)。
所以

for ii in xrange(2):  # Num of GPU
  with tf.device('/gpu:%d' % ii):
    with tf.name_scope('device_%d' % ii) as scope:

      data_batch, label_batch = factory.GetShuffleBatch(batch_size)

      unnormalized_logits = factory.MyModel(dataBatch=data_batch, numClasses=numClasses,
                                                 isTraining=True)

      More stuff happening
      tf.get_variable_scope().reuse_variables()

推断是通过函数MyModel进行的。 (下面是该功能的示例,实际上我使用了更多的层和神经元)。

def MyModel(data_batch, num_classes, feature_dim):

  # Hidden Layer 1
  with tf.variable_scope('hidden1') as scope:
    weights = variable_on_cpu('weights',[feature_dim, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden1 = tf.nn.relu(tf.matmul(data_batch, weights) + biases, name=scope.name)

  # Hidden Layer 2
  with tf.variable_scope('hidden2') as scope:
    weights = variable_on_cpu('weights',[256, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases, name=scope.name)

  # output, unnormalized softmax
  with tf.variable_scope('softmax_unnorm') as scope:

    weights = variable_on_cpu('weights', [256, num_classes], tf.truncated_normal_initializer(stddev=1/num_classes))
    biases = variable_on_cpu('biases', [num_classes], tf.constant_initializer(0.0))
    softmax_un = tf.add(tf.matmul(hidden2, weights), biases, name=scope.name)

  return softmax_un

我要执行批量标准化。 所以当我这样做时:

def MyModel(data_batch, num_classes, feature_dim, isTraining):

  with tf.variable_scope('bnormalization') as scope:
    norm_data_batch = tcl.batch_norm(inputs=dataBatch, epsilon=0.0001, is_training=isTraining, 
                                      reuse=True, scope=scope)

  # Hidden Layer 1
  with tf.variable_scope('hidden1') as scope:
    weights = variable_on_cpu('weights',[feature_dim, 256], tf.truncated_normal_initializer(stddev=0.04))
    biases = variable_on_cpu('biases', [256], tf.constant_initializer(0.001))
    hidden1 = tf.nn.relu(tf.matmul(data_batch, weights) + biases, name=scope.name)

在训练阶段出现以下错误:
不允许使用可变的bnormalization / beta。 您是说要在VarScope中设置“ reuse = None”?

从我在培训阶段在该线程中阅读的内容来看,我应该使用复用=无。 我的部分正确吗? 如果是这样,那么由于我使用的是两个GPU,是否应该在第一个GPU中重用=无,而在第二个GPU中重用= True? 还是因为我在做tf.get_variable_scope()。reuse_variables(),所以它能自行处理?

最后,在测试阶段,我是否应该具有is_training = False和复用= True?

任何帮助是极大的赞赏。

现在tf.contrib.layers.batch_norm接受张量,变量或占位符作为is_training

https://github.com/tensorflow/tensorflow/commit/9da5fc8e6425cabd61fc36f0dcc1823a093d5c1d#diff -94bbcef0ec8a5cdef55f705e99c2b2ed

批量规范化使我的实验更糟是正常的吗? 我在基于MNIST初学者教程的2层NN网络上进行了尝试,当存在BN时,我始终会得到较差的结果:使用BN(一个受过规模和中心训练而另一个没有受过训练)的精度为0.8423、0.8221,而没有BN的精度为0.9477。

我的脚本在这里https://github.com/brando90/tensor_flow_experiments/blob/master/tf_tutorials/beginner_tutorial_MNIST_BN.py

任何人都遇到过这些问题,或者BN就是这样,我需要做其他事情才能使其正常工作吗?

现在,最新版本的tf.contrib.layers.batch_norm接受了

但是重要的是,要么您传递了updates_collections = None ,要么就地更新了moving_mean和moving_variance,否则您将需要收集update_ops并确保它们已运行。

我想鼓励您使用tf.contrib.layerstf.contrib.slim建立模型。

slim = tf.contrib.slim

def build_NN_two_hidden_layers(x, is_training):
 batch_norm_params = {'is_training': is_training, 'decay': 0.9, 'updates_collections': None}
 with slim.arg_scope([slim.fully_connected], 
    activation_fn=tf.nn.relu,
    weigths_initializer=tf.contrib.layers.xavier_initializer(),
    biases_initializer=tf.constant_initializer(0.1),
    normalizer_fn=slim.batch_norm,
    normalizer_params=batch_norm_params):
   net = slim.fully_connected(x, 50, scope='A1')
   net = slim.fully_connected(net, 49, scope='A2')
   y = slim.fully_connected(net, 10, activation_fn=tf.nn.softmax, normalizer_fn=None, scope='A3')
 return y


@sguada我更改了旧的,我手动告诉它可以训练还是不训练(基于tf.cond),现在看来准确性再次达到了95。 为什么我需要将updates_collections更改为None? 您介意为我解释为什么会产生如此大的精度差异吗? 它似乎是不平凡的变化(如果它是如此重要,那么应该将None设置为默认值吗?)。 谢谢! :)

另外,我注意到您说这是一个占位符,不需要手动进行。 但是,当我通过is_training的占位符时,它说

TypeError: Using a tf.Tensor as a Python bool is not allowed. Use如果t不是None: instead of if t: to test if a tensor is defined, and use the logical TensorFlow ops to test the value of a tensor.

并指向batch_norm代码。 也许很高兴展示如何使用此占位符,因为我似乎不明白该如何使用它。 谢谢! :)

@ brando90
代码的相关部分在此处为L227-256

您会注意到,有一个强制更新的with ops.control_dependencies语句。 我相信,要“立即使用”的代码,默认值应为“无”。

至于我在1122上方的评论,我发现tf.get_variable_scope()。reuse_variables()解决了这个问题,因此在训练阶段,batch_norm的参数重用应为None。 它与语句variable_op_scope有关(请阅读tensorflow中的文档)

在tf.placeholder中使用batch_norm

x = tf.placeholder(tf.float32, [None, 784])
is_training = tf.placeholder(tf.bool, [], name='is_training')
y = build_NN_two_hidden_layers(x, is_training)

# For training
sess.run(y, {is_training: True, x: train_data})

# For eval
sess.run(y, {is_training: False, x: eval_data})

之前的问题是您没有在每个步骤之后更新moving_meanmoving_variance ,当updates_collections为“无”时,它会强制将更新作为计算的一部分。
但是,当网络具有许多batch_norm层时,收集所有更新操作并一起运行它们会更有效率,因此每个层都无需等待更新完成。

y = build_model_with_batch_norm(x, is_training)
update_ops = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))

sess.run([y, update_ops])

加快批处理规范是否取得了任何进展?

我试图将批处理规范与2层紧密连接的NN和(扁平的)MNIST(和relu单位)数据集一起使用,以进行自动编码,但是我一直遇到NaN错误。 有人知道为什么会这样吗? 国阵有可能吗? 似乎很可疑,但这不是我的学习设置,评分等。(但我认为这不应该,因为BN应该对此感到不舒服)

@sguada我不理解正确使用batch_norm的正确方式,特别是关于标志updates_collections 。 如果我正确理解了如果标志是None那么网络效率不高,那么我应该让updates_collections=tf.GraphKeys.UPDATE_OPS ,然后收集所有batch_norm更新并一起运行。

您通过执行以下操作收集batch_norms更新: update_ops = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))

我有许多使用不同的batch_norm图层的不同模型,这行不通吗?:

#model 1
y1 = build_model_with_batch_norm(x, is_training)
update_ops1 = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
sess.run([y1, update_ops1])
#model 2
y2 = build_model_with_batch_norm(x, is_training)
update_ops2 = tf.group(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
sess.run([y2, update_ops2])

您能否再详细说明一下这一部分? 非常感谢你。

只需将其放在单独的收集键中:

# While building your 1st model...
tf.contrib.layers.batch_norm(..., updates_collection="updates-model1")

# same for 2nd model with key "updates-model2"
#model 1
y1 = build_model_with_batch_norm(x, is_training)
update_ops1 = tf.group(tf.get_collection("updates-model1"))
sess.run([y1, update_ops1])
#model 2
y2 = build_model_with_batch_norm(x, is_training)
update_ops2 = tf.group(tf.get_collection("updates-model1"))
sess.run([y2, update_ops2])

但是,文档接缝过时了。 它要求执行以下操作:

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
if update_ops:
    updates = tf.group(update_ops)
    total_loss = control_flow_ops.with_dependencies([updates], total_loss)

但:

  • _tf.group()_不接受列表。 我将其替换为_tf.tuple()_
  • 我不知道如何访问_control_flow_ops.with_dependencies()_。 我已经看到仅使用tf.with_dependecies()的其他示例,但是我无法使用Tensorflow 0.10做到这一点。 我在这里找到它:_tf.python.control_flow_ops.with_dependencies()_

编辑:

该文档应更新为s.th。 像这样:

from tensorflow.python import control_flow_ops

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
if update_ops:
    updates = tf.tuple(update_ops)
    total_loss = control_flow_ops.with_dependencies(updates, total_loss)

编辑2:

在网络上进行了一些运行之后,我不得不说,与在构建图时手动获取_tf.GraphKeys.UPDATE_OPS_相比,使用_updates_collections = None_看不出任何性能差异。 即使大量使用批处理规范化(总计,我的_tf.get_collection(tf.GraphKeys.UPDATE_OPS)_也会返回140个Update-Ops,它们全部都是BN-ops)

编辑:很难说,如果我的结果是正确的,但是整个网络的速度确实提高了1.5倍。 据我所知,到目前为止,BN统计信息是在CPU而不是GPU上计算的。

你们中的任何人都能看到性能方面的好处吗? 请分享您的结果:)

回到性能问题,当前的批处理规范层是否完全受益于GPU的使用? 有人通过此批处理规范实施从GPU中受益吗?

对垃圾邮件表示抱歉,但是文档并没有真正说明如何通过卷积使用此BN(也许应该在某处提供?)。 简而言之,它如何确定应针对每个功能(而不是针对每个激活)应用和学习相同的参数?

(是否至少有一个代码片段可以做到这一点?)

细长的batch_norm包装器在输入张量的最后一个维度上进行规范化。 因此,如果它是来自完全连接层的2D输入张量,则会对批进行归一化,从而执行每次激活归一化。 如果它是来自卷积的4D张量,它将在三个第一维度(批,宽度,深度)上进行归一化,从而执行每个特征的归一化。 @sguada可能对此更具描述性。

@nmhkahn关于您的代码段,请问为什么在reuseNone is_training=True ? 是否会在每个训练步骤中重新初始化触发缩放参数gamma和偏移参数beta ? 我认为在原始论文中,“ betagamma是“与原始模型参数一起学习的”。 为此,难道不应该将它们只初始化一次然后在所有训练步骤中重用吗?

tf.cond(is_training, lambda: batch_norm(inputT, is_training=True, updates_collections=None, scope=scope), lambda: batch_norm(inputT, is_training=False, updates_collections=None, scope=scope, reuse = True))

我非常感谢TF团队在此处所做的工作,以使batch_norm可用并有效。 从我的搜索来看,该线程是如何使用它的最佳资源。 这里有许多不同的问题和想法,很难就如何使用batch_norm层的最简单的标准案例找出共识建议。 我认为扩展文档以指定确切的建议用法会有很多价值。

我尽最大努力弄清楚了这一点,将我带到了以下代码:

is_training_ph = tf.placeholder(tf.bool)
...
with tf.variable_scope('bn_test_layer') as vs:
    layer_output = tf.cond(is_training_ph,
        lambda: tf.contrib.layers.batch_norm(layer_input, is_training=True, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope=vs),
        lambda: tf.contrib.layers.batch_norm(layer_input, is_training=False, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope=vs, reuse=True))

然后,将is_training_ph设置为True进行训练,将False设置为测试。 这对我不起作用。 该模型训练得很好,但是测试性能却很糟糕。 相反,如果我将is_training_ph = True设置为测试时间,则效果很好。 因此,我猜测我仍然遇到范围问题,因此找不到合适的现有变量。

@ davek44我正在使用与您使用的相同的代码框架,并且观察到了同一件事:在训练阶段打开is_training=True并在验证和/或测试阶段关闭is_training=False ,该模型训练得像所描述的论文一样好(模型收敛更快,我能够使用更大的学习率),但是测试性能非常糟糕。 如果我一直打开is_training=True ,则模型的训练与不插入批处理规范层的训练相同。 我还没有弄清楚我做错了什么,我打算使用TensorBoard监视参数。 如果您诊断出这种现象的原因,请您进行更新?

tf.contrib.layers.batch_norm可以将张量用作is_training,因此不需要做任何特别的事情。

is_training_ph = tf.placeholder(tf.bool)

outputs = tf.contrib.layers.batch_norm(layer_input, is_training=is_training_ph, center=True, scale=True, activation_fn=tf.nn.relu, updates_collections=None, scope='batch_norm'),

我看到该代码具有同样糟糕的测试性能。

没有更多的细节是不可能知道的,我的猜测是您只训练了几次迭代,因此Moving_mean和Moving_average尚未收敛。

您可以在测试过程中更改batch_size,以查看随着使批处理变小而导致的性能下降。

我看到该代码具有同样糟糕的测试性能。

使用tf.slim batchnorm或使用tf.cond和输入is_training作为占位符时,我都遇到了完全相同的问题。
在前一种情况下,当研究训练的模型时,我发现移动平均值和移动方差全部为零。
在后一种情况下,移动平均值和方差看起来更合理(具有不同的值),但是如果我在测试时间内使用is_training = False,则性能也确实很差。 使用is_training = True,效果更好,但我认为它仅使用测试批中的移动平均值和方差。

@nmduc @ davek44我编写了一些代码来跟踪在训练和测试期间以tf.contrib.layers.batch_norm计算的移动平均值和移动方差。 我发现decay的值非常重要(它们使用指数衰减来计算移动平均值和移动方差),而decay设置更接近1.0(即decay=.999 ),移动均值下降到接近0的值。我使用完全相同的代码但在tf.contrib.layers.batch_norm不同的decay设置进行了2次测试运行,而我的验证/测试精度似乎更为合理。

测试运行结果为decay=0.9
screen shot 2016-11-16 at 1 51 51 pm

测试运行结果为decay=0.999decay=0.999tf.contrib.layers.batch_norm的默认设置)
screen shot 2016-11-16 at 2 03 58 pm

(似乎更大的衰减值也需要模型训练更长的时间才能看到验证准确性的变化)

是的,修复它。 感谢您分享您的分析@zhongyuk!

我鼓励开发人员考虑将衰减= 0.9设置为默认值。 甚至0.99也不适合我。 这也是Torch实施中的默认值。 在https://github.com/torch/nn/blob/master/BatchNormalization.lua中查看动量参数

@zhongyuk非常感谢您的分享。 现在对我有用。

这似乎很重要。 @sguada我们应该在1.0之前考虑正确的decay ? 我敢肯定,我从来不需要调整该参数,但这可能是分布式设置的副作用。

我们可以将默认值更改为0.9,或者更好地记录其在较小的数据集或很少更新中的影响。
@vincentvanhoucke在我们的分布式环境中,我们通常会进行数百万次更新,因此可以,但是在其他情况下,例如此处仅进行几百次更新的情况,则有很大的不同:
例如,使用衰减= 0.999在1000次更新后的偏差为0.36,但在10000次更新后的偏差降至0.000045,在50000次更新后的偏差降至0.0。

只是想指出,我还有一个测试性能差的问题,特别是使用小批量(任何小于10而不是我用于训练的200都会降低测试准确性)。 我使用了tf.placeholder在测试/培训模式之间切换。

批处理规范化层可以更好地实现训练收敛,这很好,但是如果您不能在生产中应用模型,那么使用它就没什么意义了。 任何人都可以使用此批规范层确认少量或单个数据样本的良好测试性能吗?

我可以确认在小批量甚至使用batch_size = 1的情况下使用is_training = False时,测试性能良好,因为它不是使用来自批次的统计信息,而是使用在训练中获得的统计信息。 只需确保统计信息已收敛到默认衰减= 0.999(意味着至少有5万次更新)即可。

为了跟进TF开发人员的确认,我使用两个不同的decay设置(和训练batch_size = 1)跟踪统计数据的收敛。 对于decay=0.99 ,经过550〜600步学习/更新后,统计收敛(bias <0.001)。 使用decay=0.9 ,统计信息在学习/更新的100个步骤内收敛(偏差<0.001)。

@sguada谢谢,这是否也意味着输出实际上与批量大小无关? 因为我注意到很小的变化会对我的准确性产生很大的影响(也许我对性能的定义更容易受到这种微小变化的影响)。 确切地说,我的128维输出张量中的所有值都增加,使得总矢量长度几乎与批处理大小成线性比例。 每个值的差别不大,但是在计算潜在空间中的矢量距离时会产生很大的影响。

@zhongyuk谢谢,我已经使用decay=0.9运行了约5k次更新,因此它应该已经收敛,并且使用大批量测试性能就可以了。 但是,即使没有,也会导致培训测试之间有所不同吗? 如果没有融合,我会在训练测试中看到糟糕的表现,对吗?

我将进行更多调查,看看是否可以在其他任务上重现该问题。 到目前为止,感谢您的快速反馈!

@dominikandreas如果您的测试性能不佳是由于统计数据未收敛导致的,那么您会看到相当不错的训练性能,但测试性能却很差。 因为在训练期间,仅使用训练的批次统计信息来完成批次归一化。 但是,在测试期间,它使用所有训练批次的移动平均统计数据来归一化输入张量。

我发现了我的代码中的错误,批处理规范化现在可以正常工作了:-)感谢您的支持

@zhongyuk ,您如何跟踪移动平均值和方差?
谢谢!

@rogertrullo通常,我设置TensorBoard来跟踪移动的均值和方差。 除此之外,我还尝试在培训和参考期间通过范围内的tf.get_variable("moving_mean")获取统计信息以监控偏差。

你好
我和其他人一样有同样的问题,我的训练效果很好,但是使用batch_norm后验证/测试却很糟糕。
我使用这样的功能:
conv_normed1 = tf.contrib.layers.batch_norm(conv1 + block1_layer3_1_biases,updates_collections = None,scale = True,衰减= batch_norm_decay,center = True,is_training = is_training)
衰减值为0.9
我需要设置重用标志吗?
我会很高兴为您提供任何帮助。

我一直在使用此线程中所述的batch_norm(使用tf.bool进行培训;以及ops.GraphKeys.UPDATE_OPS),一切正常。

使用以下方法保存和还原时:
saver = tf.train.Saver()
有用,

但是使用以下命令保存时:
保护程序= tf.train.Saver(tf.trainable_variables()+ [global_step])
这样我可以节省存储空间(不保存渐变等)
还原时出现错误:
“未初始化的值unpool4 / convc / bn / moving_mean”

显然,这是因为还没有为任何图层保存moving_mean(我想是moving_variance)。 由于我有很多(位于多个图层中),将它们添加到要保存的值列表中的最有效方法是什么? 而且,鉴于这些都是可训练的变量,为什么不将它们添加到trainable_variables集合中?

@mshunshin移动均值和方差不是可训练的变量:没有梯度出现,它们只是在小样本示例之间累积统计信息。
要保存/恢复它们,可以使用tf.global_variables()

对我来说,当我使用这个包装器时,事情开始起作用:
def batch_norm_wrapper(x, phase, decay, scope, reuse): with tf.variable_scope(scope, reuse=reuse): normed = tf.contrib.layers.batch_norm(x, center=True, scale=True, decay=decay, is_training=phase, scope='bn',updates_collections=None, reuse=reuse) return normed
我认为在此线程中,作用域和重用的整体用途尚不清楚。

非常感谢。 使用tf.global_variables()时,保存文件要大得多,因为我认为它包含渐变。 最后我使用了:

saver = tf.train.Saver([如果x.name中没有'Adam',则[x表示tf.global_variables()中的x]]

并且由于会话管理器初始化未正确初始化它们:

sess.run(tf.variables_initializer([如果x.name中为'Adam',则[x表示tf.global_variables()中的x)])

(使用tf.train.AdamOptimizer)

您还可以使用tf.model_variables()包含模型的变量,即Moving_mean

@sguada对不起,给您带来麻烦,但是可以结合使用readme.md中的slim.conv2d / slim.fully_connect来使用slim.batch_norm的示例吗?

我使用的是slim.batch_norm,但训练效果好,验证/测试性能也很差。 我认为这一定是由于不正确地使用reusescope或其他一些参数造成的。 尽管批处理规范化存在许多问题,但是很难找到关于如何使用它的完整代码段,尤其是。 有关如何在不同阶段传递不同参数的信息。

说,在我的mnist_bn代码中,我使用tf.GraphKeys.UPDATE_OPS控制依赖项,并将is_training为占位符。 但是如果输入{is_training:False},验证性能仍然很差。

如果有一个正式且完整的(意味着培训,验证,测试都包括在内)批量标准化示例,我将不胜感激。

先感谢您!

你好
您需要为每次使用批处理规范设置不同的范围,并根据适合我的训练/测试阶段(训练时为FALSE时为TRUE)为其提供重用输入。

@ishaybee谢谢您的帮助。 我发现了我的问题= =这是由于moving_mean / moving_variance的冷启动。

由于我没有训练足够的步骤,因此估计的移动平均值/方差不是那么稳定。 结果是:该模型在训练迷你批次时表现良好(您知道刚开始时损失迅速下降),但是验证性能不稳定(因为估计的总体均值/方差不够稳定)。

当我训练模型时间更长时,验证准确性也会变得更漂亮。

另一个重要的事情是,确保使用slim.learning.create_train_op来创建train op 。 不要使用tf native tf.train.GradientDescentOptimizer(0.1).minimize(loss)

所以答案是,我正确地使用了批量归一化,但是在培训期间我还没有完全了解它的动态。

================
更重要的是:

  1. 这是有关如何在MNIST数据集上使用BN层
  2. 使用较小的衰减值将加速预热阶段。 默认衰减为0.999,对于诸如MNIST之类的小型数据集,您可以选择0.99或0.95,并且它会在短时间内预热。

@soloice ,请注意,如何在注释中将以下参数传递到层中以调用batch_norm:

batch_norm_params = {'is_training':is_training,'decay':0.9,'updates_collections':无}

如果没有将updates_collections设置为None(因此平均更新已在BatchNorm中完成),则我不会期望周围的图层(例如conv2d)以某种方式执行BatchNrm图层更新运行均值所需的tf.GraphKeys.UPDATE_OPS并因此以后可以在测试数据上运行。

或者,您可以尝试在此处明确地自己运行UPDATE_OPS

    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    if update_ops:
        updates = tf.group(*update_ops)
        cross_entropy = control_flow_ops.with_dependencies([updates], cross_entropy)

更新-我发现我完全引用了您的代码,并且您确实使用了UPDATE_OPS。

至于“冷启动”,如您在上面的讨论中所见,将BatchNorm的运行平均衰减(输入参数)从默认的0.999降低到类似0.95的速度可以加快启动速度

@pavelbulanov谢谢您的帮助! 我将尝试使用较小的decay值,以了解其帮助。

================
更新:使用较小的衰减(例如0.9或0.95)确实有很大帮助。 当我将decay为0.9时,验证损失会迅速下降。 但是,小衰变的缺点是其有效范围很小:该结果受最近的一些样本支配,因此不是对总体均值/方差的良好估计。 需要在快速启动(小衰减)和更长的有效范围(大衰减)之间取得平衡。

你好
我试图借助本期中的建议来实现批处理归一化层,但是在验证和测试中我仍然有> 70%的错误...对于非训练电话,我确实有较低的衰减...

这是我的代码:

def BatchNorm(inputT, is_training=False, scope=None):
  return tf.cond(
    is_training,
    lambda: tf.contrib.layers.batch_norm(inputT, is_training=True,  reuse=None, decay=0.999, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope),
    lambda: tf.contrib.layers.batch_norm(inputT, is_training=False, reuse=True, decay=0.900, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope)
    )

先感谢您。

@Alexivia似乎您正在使用两个不同的批处理规范化层? 您应该只使用一个BN层(当然,使用不同的is_training参数)。

谢谢您的建议@soloice。
我现在尝试使用不同的is_trainingreuse参数:

lambda: tf.contrib.layers.batch_norm(inputT, is_training=True,  reuse=None, decay=0.9, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope),
lambda: tf.contrib.layers.batch_norm(inputT, is_training=False, reuse=True, decay=0.9, epsilon=1e-5, center=True, scale=True, updates_collections=None, scope=scope)

仍然没有获得良好的验证和测试结果...> 70%...

你好
请在上面看到我的包装纸。
我认为您应该使用“与tf.variable_scope(scope,复用=重用):”。

@ishaybee
我遵循了您的建议,现在我的代码是:

def BatchNorm(inputT, is_training=False, reuse=True, scope=None):
  with tf.variable_scope(scope, reuse=reuse):
    return tf.contrib.layers.batch_norm(inputT, is_training=is_training, reuse=reuse, scope=scope, updates_collections=None, decay=0.9, center=True, scale=True)

我通过feed_dict喂了is_trainingreuse ,但是现在我得到了错误ValueError("The reuse parameter must be True or False or None.")

尝试将重用作为python变量(模型的输入)和占位符。

我尝试了一下,现在它不再抱怨该值了...但是我认为没有使用占位符值,因为如果我将值强制为batch_norm函数,我看不到任何变化,而在TensorBoard中则不是连接到图形...(请参阅附件图像)
screen shot 2017-04-03 at 19 54 54

我的代码现在是这样的:
批量标准化包装

def BatchNorm(inputT, is_training=False, reuse=None, scope=None):
  with tf.variable_scope(scope):
    return tf.contrib.layers.batch_norm(inputT, is_training=is_training, reuse=reuse, scope=scope, updates_collections=None, decay=0.9, center=True, scale=True)

型号定义

def model(data, train=False, is_training=False, reuse=None):
  # 1st conv layer
  with tf.name_scope('conv1') as scope:
    conv = tf.nn.conv2d(
    <...>
    norm = BatchNorm(pool, is_training=is_training, reuse=reuse, scope=scope)

训练

feed_dict = {train_data_node: batch_data,
      train_labels_node: batch_labels,
      is_training: True,
      reuse: None}
  # Run the optimizer to update weights.
  sess.run(optimizer, feed_dict=feed_dict)

验证方式

batch_predictions = sess.run(eval_prediction, feed_dict={eval_data: data[-EVAL_BATCH_SIZE:, ...], is_training: False, reuse: True})

尽管is_traning可以使占位符重用,但是它不能是张量或占位符。

我不确定您要做什么,在大多数情况下,使用静态值可以解决问题。 例如,此模式很好用:

def model(data, is_training=False, reuse=None, scope='my_model'):
  # Define a variable scope to contain all the variables of your model
  with tf.variable_scope(scope, 'model', data, reuse=reuse):
    # 1 layer
    net = tf.contrib.layers.conv2d(data, ....)
    ....
    net = tf.contrib.layers.batch_norm(net, is_training)
   return net

train_outputs = model(train_data, is_training=True)
eval_outputs = model(eval_data, is_training=False, reuse=True)

eval_predictions = sess.run(eval_outputs, feed_dict={eval_data: data[-EVAL_BATCH_SIZE:, ...]})

除非需要动态更改模型的行为,否则不需要为is_training使用占位符。 诀窍是建立模型两次,但是第二次共享变量。

谢谢@sguada ! 应用您的建议后,我终于成功了!

如果API 1.0文档反映出您需要手动向图表添加更新操作,这将很有帮助。 作为tf的新用户,我发现我的测试错误很疯狂,然后不得不花大量时间调试我的图形,直到我意识到批量归一化是问题所在。 然后,我不得不花更多的时间来弄清楚,默认情况下,跟踪时刻的变量不会更新,除非您使用contrib函数进行优化。 由于在1.0中没有选项将update_collections设置为None,因此文档中没有指示符可能甚至是问题。 另外,似乎有一个参数可以将控制流依赖项添加到在训练案例中运行的操作中似乎很有意义。

@danrsc确实如此。 BN层的用法非常令人困惑。 我建议添加有关批标准化的文档或完整的官方教程,但不幸的是没有响应= =

完全同意。 我认为BN的使用非常棘手,目前文档还远远不够。 对于这样的常用层,应该对此进行修复。

重新打开以查看文档问题。

@sguada分配给您进行分类。 在此案上可能值得聘请技术作家。

上周这个问题让他感到困惑,并浪费了3天的培训时间...希望文档能尽快修复,并且可以在API文档中添加正式的批处理规范化示例。

@sguada我注意到您说“ tf.contrib.layers.batch_norm可以将张量作为is_training,因此不需要做任何特别的事情”。
但是,代码中的注释是
如果is_training没有常量值,因为它是Tensor
#a VariablePlaceholder则is_training_value将为None,
needs_moments将为真。
如果我将is_training设置为占位符,这是否意味着即使在测试阶段nees_moments也会成立?
据我所知,测试时不需要这些时刻。

因此,如果is_trainingVariablePlaceholder ,则意味着它可以更改,因此需要用于计算力矩的图形,因此该层将其构建。
然后在运行时,根据值$$$ TrueFalse将使用批处理momentsmoving_meanmoving_variance

因此,在测试期间,您可以将值设置为False并且不会使用moments

@sguada @ brando90

def batch_norm_layer(self, x,train_phase, scope_bn):
        bn_train = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=True,
        reuse=None,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        bn_inference = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=False,
        reuse=True,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
        return z

我建立了这样的batchnorm,但是,移动平均值和移动变量在测试期间进行了更新,我找不到原因。

我尝试创建两个模型,如@sguada所说,但是,我的模型is_training = False崩溃了。

W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_5/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_6/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_7/biases not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_6/biases not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key fully_connected_7/weights not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key history_embeddings_1 not found in checkpoint
W tensorflow/core/framework/op_kernel.cc:993] Not found: Key global_step_1 not found in checkpoint

我觉得也许应该有一个具体的示例,说明如何使用完全连接的网络以及CNN进行批处理规范。 糟糕的是,我已经训练模型好几天了,期望一切正常,然后再看到每个尝试使用此功能的人都疯了。

有趣的是,在使用batch_norm进行训练之后,要恢复模型还需要数百万年的时间。 最有可能会等到TF 2.0再次尝试这种操作。

@MisayaZ,您不需要创建两个batch_norm层,您只需将train_phase(假设它是tf.bool)传递给batch_norm。 另外,您还要传递UPDATE_OPS_COLLECTION variables_collections,它会更改将变量添加到哪些集合。

以下应该工作:

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                             is_training=train_phase, scope=scope_bn)

@OktayGardener不确定您要创建哪种模型,似乎变量未保存在检查点中。

batch_norm也适用于完全连接的层。

slim = tf.contrib.slim
def model(data, is_training=False, reuse=None, scope='my_model'):
  # Define a variable scope to contain all the variables of your model
  with tf.variable_scope(scope, 'model', data, reuse=reuse):
    # Configure arguments of fully_connected layers
    with slim.arg_scope([slim.fully_connected],
                        activation_fn=tf.nn.relu,
                        normalizer_fn=slim.batch_nom):
      # Configure arguments of batch_norm layers
      with slim.arg_scope([slim.batch_norm],
                          decay=0.9,  # Adjust decay to the number of iterations
                          update_collections=None, # Make sure updates happen automatically
                          is_training=is_training, # Switch behavior from training to non-training):
        net = slim.fully_connected(data, 100, scope='fc1')
        net = slim.fully_connected(net, 200, scope='fc2')
        ....
        # Don't use activation_fn nor batch_norm in the last layer        
        net = slim.fully_connected(net, 10, activation_fn=None, normalizer_fn=None, scope='fc10')
       return net

@sguada谢谢,我建立了一个带有bathnorm的网络,如上所述

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                             is_training=train_phase, scope=scope_bn)

速度很慢,我使用tensorflow基准测试来获取计算时间,如下所示:
我tensorflow / core / util / stat_summarizer.cc:392] ======================================= ==========================
我tensorflow / core / util / stat_summarizer.cc:392] [节点类型] [开始] [第一] [平均毫秒] [%] [cdf%] [内存KB] [名称]
我tensorflow / core / util / stat_summarizer.cc:392] Conv2D 106.164 51.354 51.004 23.145%23.145%692.224 conv8 / Conv2D
我tensorflow / core / util / stat_summarizer.cc:392] Conv2D 85.187 19.115 19.283 8.750%31.896%692.224 conv7 / Conv2D
我tensorflow / core / util / stat_summarizer.cc:392] SquaredDifference 11.967 15.105 14.331 6.503%38.399%11075.584 conv1 / batch_norm / moments / sufficient_statistics / SquaredDifference
我tensorflow / core / util / stat_summarizer.cc:392] Mul 11​​.970 14.162 13.495 6.124%44.523%11075.584 conv1 / batch_norm / batchnorm / mul_1
我tensorflow / core / util / stat_summarizer.cc:392] Conv2D 3.948 8.170 7.986 3.624%48.146%11075.584 conv1 / Conv2D
我tensorflow / core / util / stat_summarizer.cc:392] Sub 11.960 10.176 7.943 3.604%51.751%11075.584 conv1 / batch_norm / moments / sufficient_statistics / Sub
我tensorflow / core / util / stat_summarizer.cc:392] SquaredDifference 45.570 5.908 7.177 3.257%55.007%5537.792 conv2 / batch_norm / moments / sufficient_statistics / SquaredDifference
我tensorflow / core / util / stat_summarizer.cc:392] Mul 45.574 7.755 6.902 3.132%58.140%5537.792 conv2 / batch_norm / batchnorm / mul_1
我tensorflow / core / util / stat_summarizer.cc:392] Conv2D 40.692 5.408 4.845 2.199%60.338%5537.792 conv2 / Conv2D
我tensorflow / core / util / stat_summarizer.cc:392] Sub 45.563 6.067 4.784 2.171%62.509%5537.792 con

我不明白为什么在测试过程中会立即执行某些操作,并且会花费很多时间,例如conv1 / batch_norm / moments / sufficient_statistics / SquaredDifference。

测试中不需要此刻,为什么某些操作在此刻执行?

你好

使用上面的batch_norm contrib.layers batch_norm层,我得到了nan作为验证图的输出,而火车图则无缝运行。 有什么我可能会想念的吗?

我正在使用:

def batchnormlayer(inputs, numout, train_model):
    with tf.variable_scope("batch_norm") as scope_bn:
        epsilon = 1e-3
        return tf.contrib.layers.batch_norm(inputs, decay=0.9, updates_collections=None,
                                            scale=True, scope=scope_bn,
                                            is_training=train_model, epsilon=epsilon,
                                            fused=True, reuse=scope_bn.reuse)

谢谢

作为后续,我将重用16层batch_norm。
但是,我发现重用4层是可行的。

我刚刚注意到,如果我终止了tensorflow进程并重新启动它,则在几个时期内我的错误会变得更糟(即比在最后一个检查点还差)。 我还观察到,如果删除batch_norm,此问题将消失。 在看了一段时间的代码之后,我认为这可能是因为变量的值未从阴影变量中恢复,就像使用ExponentialMovingAverages类管理移动平均值时那样。 这也意味着,如果我使用单独的过程进行评估,那么无论变量的最后一个值是什么,而不是移动平均值,我都会得到。 我是否正确解释了这,这是预期的行为吗? 似乎您想还原阴影变量值...

我发现了问题,我的情况下的移动方差经过几次迭代后变为负数。

张量的输出: Model/clip_logits/batch_norm/moving_variance:0存在的tf.model_variables()

Moving variance (shape = (101,)) = 
[ 214.70379639   95.36338043    0.57885742  189.49542236  102.72473145
  137.14886475  286.57333374  111.06427002  154.98750305  167.75219727
  207.83955383  211.14007568  158.23495483  171.61665344  116.81361389
  115.77380371   43.59399796  137.75064087  181.75245667  161.37339783
  215.21934509   92.88521576  191.23846436  336.3946228   259.85919189
  299.47039795  186.23222351  165.19311523  262.82446289  170.11567688
  233.56843567  209.35050964  115.96807861  154.34109497  295.5770874
  123.6055603   295.76187134  296.88583374  240.88217163  247.32983398
   87.15661621  217.69897461  133.00698853   -4.80375671  344.77462769
  291.50601196  117.77174377  265.83712769  207.90093994  194.186203
  220.21418762  178.03738403  115.27571869  196.62184143  228.8089447
  191.53205872  331.36807251  151.55435181  197.2951355   179.67504883
  181.09727478   90.09922791  173.30133057  102.6836853   160.9434967
  236.59512329  168.05305481  403.36340332   41.14326096  185.93409729
  130.57434082  266.31509399  101.44387817  163.88059998  290.25015259
  244.52597046  229.86647034  158.14352417  202.68774414  187.78227234
  248.78218079  126.0978241   171.41891479  274.40740967  119.84254456
  202.53045654  200.20608521  214.04730225  111.53284454  222.03184509
  244.81187439  172.23052979  187.09806824  194.62802124  255.26345825
  293.63598633  307.91036987  210.86982727  308.88919067  144.94792175
  229.69013977]

如您所见,其中一个维度存在负方差。 这怎么可能呢?
PS批处理规范层仅在网络的最后一个完全连接层之后和softmax之前使用。

@ raghavgoyal14您是否在fused = True上使用它? 有一个类似的问题,当我使用融合版本时,它消失了

@abred :是的,我用fused=True ,同样的问题。

@sguada嗨,sguada,我有问题。
张量流中contrib.layers.batch_norm的定义:
def batch_norm(inputs,
衰减= 0.999,
center = True,
scale = False,
epsilon = 0.001,
activation_fn =无,
param_initializers =无,
param_regularizers =无,
updates_collections = ops.GraphKeys.UPDATE_OPS,
is_training =真,
重用=无,
variables_collections =无,
output_collections =无,
trainable =正确,
batch_weights =无,
fused =假,
data_format = DATA_FORMAT_NHWC,
zero_debias_moving_mean =否,
scope = None,
renorm = False,
renorm_clipping =无,
renorm_decay = 0.99):
标度:如果为True,则乘以gamma。 如果为False,则gamma为
未使用。 当下一层是线性的(例如nn.relu)时,可以是
禁用,因为缩放可以由下一层完成。

如果我使用tf.contrib.layers.batch_norm(input,scale = False),则“ scale = False”表示训练时在“ y = gamma * x + beta”中伽玛是否为零。 非常感谢你。

当scale = False时,伽玛为常数1。

@ppwwyyxx非常感谢您的帮助。 我在Tensorflow中使用tf.contrib.layers.batch_norm(input,scale = False),现在我将Tensorflow的batchnorm转换为Caffe。 如何在Caffe中设置BatchNormLayer和ScaleLayer的参数?
非常感谢你。

@MisayaZ我在使用Batchnorm和“ is_training”占位符时有相同的行为。 我在跟踪中看到,即使在测试时也正在计算力矩,所以我决定进入源代码,发现了这一点:

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `needs_moments` will be true.
    is_training_value = utils.constant_value(is_training)
    need_moments = is_training_value is None or is_training_value
    if need_moments:
        # here it defines the moments

看起来,当“ is_training”是变量或占位符时,即使在将占位符设置为“ False”的情况下,矩也会被定义并在运行时进行计算。 我宁愿将其保留为占位符,因为这样可以在训练期间进行定期测试而无需重新定义图形,但是我决定将其用作常量并定义火车与测试的不同行为,现在不计算力矩在测试时间。

@ tano297谢谢。 我现在也将“ is_training”用作常量。 将其保留为占位符,并进行定期测试将更改移动平均值和移动方差的值。 并且推理时间将更长,因为它将计算输入的均值和方差,并更新移动均值和移动方差。 进行测试的正确方法是为您定义的训练和测试定义不同的行为。

@ tano297 @MisayaZ
但不是“ smart_cond”

is_training_value = utils.constant_value(is_training)
need_updates = is_training_value is None or is_training_value
if need_updates:
  ...
  outputs = utils.smart_cond(is_training, _force_updates, no_updates)

确保仅在is_training评估为True时才计算和应用更新?

@abred确实是,但是您指的是第391行,它在_fused_batch_norm()中更新移动平均线:

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `need_updates` will be true.
    is_training_value = utils.constant_value(is_training)
    need_updates = is_training_value is None or is_training_value
    if need_updates:
        ...
        outputs = utils.smart_cond(is_training, _force_updates, no_updates)
        ...

我说的是batch_norm()中的753行:

    # If `is_training` doesn't have a constant value, because it is a `Tensor`,
    # a `Variable` or `Placeholder` then is_training_value will be None and
    # `needs_moments` will be true.
    is_training_value = utils.constant_value(is_training)
    need_moments = is_training_value is None or is_training_value
    if need_moments:
        ...
        mean, variance = utils.smart_cond(is_training,
                                          _force_updates,
                                          moving_vars_fn) 
        ...

在这种情况下(就我而言),智能条件决定是否更新移动平均线,但仍然可以计算矩。

@ tano297您对此表示正确,我在错误的位置,但仍然:
755-770行计算了力矩,但是力矩仅在_force_updates中使用,仅在is_training评估为True时才执行,不是吗?
因此

mean, variance = utils.smart_cond(is_training, _force_updates, moving_vars_fn) 

应该等效于804行:

mean, variance = moving_mean, moving_variance

如果is_training的值为False,则从不使用图的“矩”部分,因此不应执行

但是我还没有测试,所以我可能是错的:)

@ tano297 @吓到你了。 当我这样使用batchnorm时,移动平均值和移动方差会发生变化:

def batch_norm_layer(self, x,train_phase, scope_bn):
        bn_train = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=True,
        reuse=None,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        bn_inference = batch_norm(x, decay=0.9, center=False, scale=True,
        updates_collections=None,
        is_training=False,
        reuse=True,
        variables_collections= [UPDATE_OPS_COLLECTION],
        trainable=True,
        scope=scope_bn)
        z = tf.cond(train_phase, lambda: bn_train, lambda: bn_inference)
        return z

如果您使用如下所示:

z = batch_norm(x, decay=0.9, center=False, scale=True, updates_collections=None, 
                         is_training=train_phase, scope=scope_bn)

在测试期间,移动平均值和移动方差不会更改,但是速度非常慢。

@zhongyuk

我还遇到了这样的问题:在使用is_training = True进行训练和推理时,我可以获得良好的结果,但是在推理过程中将is_training = False设置为(与使用is_training = True的情况相比,更糟糕)时,我得到的结果不好。 根据您的分析,如果我理解正确,只需在BN中将衰减设置为0.9就可以解决此问题。 我对吗?

顺便说一句,我是否需要从头开始使用衰减= 0.9重新训练模型? 还是从检查点恢复训练(即,当衰减= 0.999时训练)也可以吗?

谢谢!

@nmduc @ davek44

嗨,我还遇到了这样的问题:在使用is_training = True进行训练和推理时,我可以获得良好的结果,但是在推理过程中将is_training = False设置时,则得到不好的结果(比使用is_training = True的情况更糟)。 你们解决了这个问题吗? 谢谢!

@tyshiwo我只是为batch_norm设置了衰减= 0.9,到目前为止效果很好。

在所有这些如何正确使用批处理规范的评论之后,我感到困惑:这就是我所拥有的。 如果我错了,请纠正我。

batch_norm = tf.contrib.layers.batch_norm(conv, center=True, scale=True, reuse=phase_train_py, scope='bn', is_training=is_training)

其中phase_train_py是python布尔变量,而is_training是使用布尔变量的占位符。 我想使用tf.cond是错误的,否则函数会带有布尔参数。 换句话说,如果tf.cond是true,那么我们应该使用batch_norm函数进行训练,而另一个函数进行测试。 因此,开发人员允许我们更改这些布尔变量,以更改函数的行为。 所以我在做的是:在训练时将phase_train_py为False,而将is_training为True。 与测试相反。 由于我们只能使用sess.run更改张量或占位符,因此在运行图形之前,我有意更改了phase_train_py 。 例如:

if condition: phase_train_py = False sess.run(to_run_list, feed_dict={phase_train: True}) else: phase_train_py = True sess.run(to_run_list, feed_dict={phase_train: False})

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++
也许您需要阅读本
++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++

TF v1.3似乎仍然存在问题。 我确定我注意到以下详细信息,但是仍然无法使用官方的tf.contrib.layers.batch_norm ,在评估过程中使用is_training=False (但是当我在评估过程中保持is_training=True不变时,好):
1. decay ,指数移动平均值实际上是信号处理中的alpha滤波器,收敛时间约为火车的1 /(1衰减)步。 对于衰减= 0.999,您需要1 / 0.001 = 1000步才能收敛。 因此,为您的训练步数设置适当的衰减。

  1. 使用占位符在训练和测试评估之间切换
  2. 如果不想将更新op的控件依赖项添加到train_op中,请使用updates_collections=None
  3. reuse为适当的值。

似乎使用正式的batch_norm的唯一方法是分别使用is_training=Trueis_training=False构建两个图形,一个用于训练,一个用于评估。 这样,您无需在训练和评估之间动态切换。 但这是一种愚蠢的方法,因为您需要构建多个图形。

最后,我自己编写了一个移动平均线,发现它起作用了! 如下(基于网络上的代码并由我自己修改)

def bn_layer(x, scope, is_training, epsilon=0.001, decay=0.99, reuse=None):
    """
    Performs a batch normalization layer

    Args:
        x: input tensor
        scope: scope name
        is_training: python boolean value
        epsilon: the variance epsilon - a small float number to avoid dividing by 0
        decay: the moving average decay

    Returns:
        The ops of a batch normalization layer
    """
    with tf.variable_scope(scope, reuse=reuse):
        shape = x.get_shape().as_list()
        # gamma: a trainable scale factor
        gamma = tf.get_variable("gamma", shape[-1], initializer=tf.constant_initializer(1.0), trainable=True)
        # beta: a trainable shift value
        beta = tf.get_variable("beta", shape[-1], initializer=tf.constant_initializer(0.0), trainable=True)
        moving_avg = tf.get_variable("moving_avg", shape[-1], initializer=tf.constant_initializer(0.0), trainable=False)
        moving_var = tf.get_variable("moving_var", shape[-1], initializer=tf.constant_initializer(1.0), trainable=False)
        if is_training:
            # tf.nn.moments == Calculate the mean and the variance of the tensor x
            avg, var = tf.nn.moments(x, np.arange(len(shape)-1), keep_dims=True)
            avg=tf.reshape(avg, [avg.shape.as_list()[-1]])
            var=tf.reshape(var, [var.shape.as_list()[-1]])
            #update_moving_avg = moving_averages.assign_moving_average(moving_avg, avg, decay)
            update_moving_avg=tf.assign(moving_avg, moving_avg*decay+avg*(1-decay))
            #update_moving_var = moving_averages.assign_moving_average(moving_var, var, decay)
            update_moving_var=tf.assign(moving_var, moving_var*decay+var*(1-decay))
            control_inputs = [update_moving_avg, update_moving_var]
        else:
            avg = moving_avg
            var = moving_var
            control_inputs = []
        with tf.control_dependencies(control_inputs):
            output = tf.nn.batch_normalization(x, avg, var, offset=beta, scale=gamma, variance_epsilon=epsilon)

    return output


def bn_layer_top(x, scope, is_training, epsilon=0.001, decay=0.99):
    """
    Returns a batch normalization layer that automatically switch between train and test phases based on the 
    tensor is_training

    Args:
        x: input tensor
        scope: scope name
        is_training: boolean tensor or variable
        epsilon: epsilon parameter - see batch_norm_layer
        decay: epsilon parameter - see batch_norm_layer

    Returns:
        The correct batch normalization layer based on the value of is_training
    """
    #assert isinstance(is_training, (ops.Tensor, variables.Variable)) and is_training.dtype == tf.bool

    return tf.cond(
        is_training,
        lambda: bn_layer(x=x, scope=scope, epsilon=epsilon, decay=decay, is_training=True, reuse=None),
        lambda: bn_layer(x=x, scope=scope, epsilon=epsilon, decay=decay, is_training=False, reuse=True),
    )

只需在构建图形时使用bn_layer_top函数,is_training参数为tf.placeholder
。 然后,您可以自由地将占位符在训练期间切换为True,在评估过程中切换为False,使用feed_dict

希望对社区有所帮助。

使用slim.batch_norm时,请确保使用“ slim.learning.create_train_op”而不是“ tf.train.GradientDecentOptimizer(lr).minimize(loss)”或其他优化程序。 试试看,看是否有效!

@vincentvanhoucke您在此线程的另一篇文章中写道:

细长的batch_norm包装器在输入张量的最后一个维度上进行规范化。 因此,如果它是来自完全连接层的2D输入张量,则会对批进行归一化,从而执行每次激活归一化。 如果它是来自卷积的4D张量,它将在三个第一维度(批,宽度,深度)上进行归一化,从而执行每个特征的归一化。 @sguada可能对此更具描述性。

您是说“ slim batch_norm包装器”功能tf.contrib.layers.batch_norm吗? 如果是这样,我建议将此信息添加到此函数的文档文本中。 因此,非常清楚的是,此函数完全按照论文中所述对FC-Layer和Conv2D-Layer执行批处理归一化。 目前,只有文本“可以用作conv2d和full_connected的规范化函数。”,尚不清楚这是否与规范化轴主题有关。

@ZahlGraf我会很乐意考虑一个PR来澄清文档。 我们从事此工作已经有很长的时间了,以至于我不再对显而易见的事物有一个很好的认识,欢迎您为对这个主题有新见识的人澄清文档。

@vincentvanhoucke
我创建了一个带有更详细描述的PR,主要是基于您在此线程中的陈述:
https://github.com/tensorflow/tensorflow/pull/15653

请删除受让人,因为此问题正在邀请外部捐款。 否则,请删除contributions welcome标签。 谢谢。

请删除受让人,因为此问题正在邀请外部捐款。 否则,请删除contributions welcome标签。 谢谢。

由于已经解决了添加批处理规范层的原始请求,因此关闭了此错误。 文档方面的一些较新问题似乎具有自己的PR
如果您看到batch_norm有任何问题,请在StackOverflow上提问或打开另一个问题。

此页面是否有帮助?
0 / 5 - 0 等级