【问题标题】:Set "training=False" of "tf.layers.batch_normalization" when training will get a better validation result训练时设置“tf.layers.batch_normalization”的“training=False”会得到更好的验证结果
【发布时间】:2018-10-07 10:17:18
【问题描述】:

我使用 TensorFlow 来训练 DNN。我了解到 Batch Normalization 对 DNN 很有帮助,所以我在 DNN 中使用了它。

我使用“tf.layers.batch_normalization”,按照API文档的说明搭建网络:training时,设置其参数“training=True” ,当 validate 时,设置“training=False”。并添加 tf.get_collection(tf.GraphKeys.UPDATE_OPS)

这是我的代码:

# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np

input_node_num=257*7
output_node_num=257

tf_X = tf.placeholder(tf.float32,[None,input_node_num])
tf_Y = tf.placeholder(tf.float32,[None,output_node_num])
dropout_rate=tf.placeholder(tf.float32)
flag_training=tf.placeholder(tf.bool)
hid_node_num=2048

h1=tf.contrib.layers.fully_connected(tf_X, hid_node_num, activation_fn=None)
h1_2=tf.nn.relu(tf.layers.batch_normalization(h1,training=flag_training))
h1_3=tf.nn.dropout(h1_2,dropout_rate)

h2=tf.contrib.layers.fully_connected(h1_3, hid_node_num, activation_fn=None)
h2_2=tf.nn.relu(tf.layers.batch_normalization(h2,training=flag_training))
h2_3=tf.nn.dropout(h2_2,dropout_rate)

h3=tf.contrib.layers.fully_connected(h2_3, hid_node_num, activation_fn=None)
h3_2=tf.nn.relu(tf.layers.batch_normalization(h3,training=flag_training))
h3_3=tf.nn.dropout(h3_2,dropout_rate)

tf_Y_pre=tf.contrib.layers.fully_connected(h3_3, output_node_num, activation_fn=None)

loss=tf.reduce_mean(tf.square(tf_Y-tf_Y_pre))

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
    train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    for i1 in range(3000*num_batch):
        train_feature=... # Some processing
        train_label=...  # Some processing
        sess.run(train_step,feed_dict={tf_X:train_feature,tf_Y:train_label,flag_training:True,dropout_rate:1}) # when train , set "training=True" , when validate ,set "training=False" , get a bad result . However when train , set "training=False" ,when validate ,set "training=False" , get a better result .

        if((i1+1)%277200==0):# print validate loss every 0.1 epoch
            validate_feature=... # Some processing
            validate_label=... # Some processing

            validate_loss = sess.run(loss,feed_dict={tf_X:validate_feature,tf_Y:validate_label,flag_training:False,dropout_rate:1})
            print(validate_loss)

我的代码有错误吗? 如果我的代码是正确的,我想我会得到一个奇怪的结果:

training时,我设置“training = True”,validate时,设置“training = False “,结果不好。我每 0.1 个 epoch 打印一次 validate loss ,第 1 到第 3 个 epoch 的 validate loss 是

 0.929624
 0.992692
 0.814033
 0.858562
 1.042705
 0.665418
 0.753507
 0.700503
 0.508338
 0.761886
 0.787044
 0.817034
 0.726586
 0.901634
 0.633383
 0.783920
 0.528140
 0.847496
 0.804937
 0.828761
 0.802314
 0.855557
 0.702335
 0.764318
 0.776465
 0.719034
 0.678497
 0.596230
 0.739280
 0.970555

但是,当我更改代码“sess.run(train_step,feed_dict={tf_X:train_feature,tf_Y:train_label,flag_training:True,dropout_rate:1})”时,即:设置“ training=False" 当 training 时设置 "training=False" 当 validate 时。结果是好的。第一个时期的验证损失是

 0.474313
 0.391002
 0.369357
 0.366732
 0.383477
 0.346027
 0.336518
 0.368153
 0.330749
 0.322070
 0.335551

为什么会出现这个结果?训练时是否需要设置“training=True”,验证时设置“training=False”?

【问题讨论】:

  • 你的 batch_size 有多大?
  • @Ishamael 我用于训练的数据是实时生成的。每个生成数据的batch_size不固定的,每个batch通常有300到500个样本。 (我还有一个问题:在一般的DNN训练中,很常见将batch_size设置为固定值,比如设置batch_size = 1000,那么每个batch有1000个样本。我想知道是否DNN训练必须确保batch_size固定?比如第1批有300个样本,第2批有500个样本,第3批有400个样本……这种方式是否符合DNN理论?)
  • 可变批量大小应该没问题。鉴于您的数据已生成,生成批次中的样本是否比生成批次中的样本更相似?如果是,这将是批量规范的问题
  • @Ishamael 是的,在生成的批次中的样本比跨生成的批次的样本更相似。我大概明白为什么在训练中设置“training=true”效果不好,而在训练中设置“training=false”效果很好:
  • @Ishamael 我认为原因是:因为使用了 tf.get_collection(tf.GraphKeys.UPDATE_OPS),所以全局均值 mean_global 和训练 DNN 时,全局方差 var_global 由批次均值 mean_batch 和批次方差 var_batch 更新,而不管“training= true”或“training=false”。

标签: python tensorflow deep-learning batch-normalization


【解决方案1】:

TL;DR:使用小于默认动量的标准化层,如下所示:

tf.layers.batch_normalization( h1, momentum = 0.9, training=flag_training )

TS;WM

当您设置training = False 时,这意味着批次归一化层将使用其内部存储的均值和方差来归一化批次,而不是批次本身的均值和方差。当training = False 时,这些内部变量也不会更新。由于它们被初始化为mean = 0variance = 1,这意味着批量归一化被有效地关闭 - 层减去零并将结果除以 1。

因此,如果您使用 training = False 进行训练并像这样进行评估,那仅意味着您正在训练您的网络而没有任何批量标准化。它仍然会产生合理的结果,因为嘿,在批量标准化之前有生活,尽管不可否认没有那么迷人......

如果您使用training = True 启用批次标准化,它将开始对批次内部进行标准化并收集每个批次的均值和方差的移动平均值。现在这是棘手的部分。移动平均线是指数移动平均线tf.layers.batch_normalization() 的默认动量为 0.99。均值从 0 开始,方差再次从 1 开始。但由于每次更新都应用了 ( 1 - 动量 ) 的权重,它会渐近地达到实际的均值和无穷大方差。例如,在 100 个步骤中,它将达到实际值的大约 73.4%,因为 0.991000.366。如果数值较大,则差异可能很大。

因此,如果您处理的批次数量相对较少,那么在您运行测试时,内部存储的均值和方差仍然会显着偏离。然后,您的网络会在正确规范化的数据上进行训练,并在错误规范化的数据上进行测试。

为了加快内部批量归一化值的收敛,您可以应用较小的动量,例如 0.9

tf.layers.batch_normalization( h1, momentum = 0.9, training=flag_training )

(对所有批量标准化层重复。)但是请注意,这样做有一个缺点。数据中的随机波动会以这样的小动量“拉动”您存储的均值和方差,并且结果值(稍后用于推理)可能会受到您确切停止训练的位置的极大影响,这显然不是最佳的。拥有尽可能大的动量是很有用的。根据训练步数,我们一般使用0.90.990.999 代表 1001,00010,000 em> 训练步骤。超过 0.999 没有意义。

另一个重要的事情是训练数据的适当随机化。如果您首先使用整个数据集的较小数值进行训练,那么归一化会收敛得更慢。最好完全随机化训练数据的顺序,并确保使用至少 14 的批量大小(经验法则)。


旁注:众所周知,对值进行零去偏可以显着加快收敛速度​​,ExponentialMovingAverage class 具有此功能。但是批量标准化层没有这个功能,除了tf.slimbatch_norm,如果你愿意为 slim 重构你的代码。

【讨论】:

    【解决方案2】:

    设置 Training = False 提高性能的原因是批量标准化有四个变量(beta、gamma、mean、variance)。确实,当 Training = False 时均值和方差不会更新。但是,gamma 和 beta 仍然会更新。因此,您的模型有两个额外的变量,因此具有更好的性能。

    另外,我猜你的模型在没有批量标准化的情况下具有相对较好的性能。

    【讨论】:

      猜你喜欢
      • 2015-09-17
      • 2018-10-16
      • 2019-10-11
      • 1970-01-01
      • 2019-12-06
      • 1970-01-01
      • 1970-01-01
      • 2018-08-30
      • 1970-01-01
      相关资源
      最近更新 更多