设置" training = False" " tf.layers.batch_normalization"培训将获得更好的验证结果

时间:2018-04-26 16:13:12

标签: python tensorflow deep-learning batch-normalization

我使用TensorFlow训练DNN。我了解到Batch Normalization对DNN非常有用,所以我在DNN中使用它。

我使用" tf.layers.batch_normalization"并按照API文档的说明构建网络:当培训时,设置其参数" training = True ",以及何时验证,设置" 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)

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

培训时,我设置&#34; training = True &#34;,当验证时,设置&#34; < strong> training = False &#34;,结果不好。我每0.1纪元打印验证损失,第1至第3纪元的验证损失是

 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

但是,当我更改代码&#34; sess.run(train_step,feed_dict = {tf_X:train_feature,tf_Y:train_label,flag_training:True,dropout_rate:1})&#34 ; ,即:设置&#34; training = False &#34;当培训时,设置&#34; training = False &#34;当验证时。结果很好。第1纪元的验证损失是

 0.474313
 0.391002
 0.369357
 0.366732
 0.383477
 0.346027
 0.336518
 0.368153
 0.330749
 0.322070
 0.335551

为什么会出现这种结果?是否有必要设置&#34; training = True&#34;在训练时,设置&#34; training = False&#34;验证时?

2 个答案:

答案 0 :(得分:6)

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.99 100 0.366 。如果你有数值大的值,差异可能是巨大的。

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

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

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

(对所有批量标准化层重复。)请注意,这有一个缺点。数据中的随机波动将会对您存储的均值和方差进行“拖拽”,并且这样的小动量会产生更大的影响,并且结果值(后来用于推理)会受到您完全停止训练的地方的极大影响,这显然不是最佳。拥有尽可能大的动量是有用的。根据培训步骤的数量,我们通常使用 0.9 0.99 0.999 100 1,000 10,000 分别是训练步骤。无需重复 0.999

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

旁注:众所周知,将值去零会显着加快收敛速度​​,ExponentialMovingAverage class具有此功能。但批量规范化图层没有此功能,除了tf.slimbatch_norm之外,如果您愿意重新构建代码以进行修改。

答案 1 :(得分:2)

设置Training = False可以提高性能的原因是批处理规范化具有四个变量(β,γ,均值,方差)。的确,当Training = False时,均值和方差不会更新。但是,gamma和beta仍会更新。因此,您的模型有两个额外的变量,因此具有更好的性能。

此外,我认为您的模型在不进行批量归一化的情况下具有相对较好的性能。