首页 文章

当训练获得更好的验证结果时,设置“tf.layers.batch_normalization”的“training = False”

提问于
浏览
1

我使用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个时期打印验证损失,第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

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

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”?

2 回答

  • 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 打开批量标准化,它将开始标准化其自身内的批次并收集每批次的均值和方差的移动平均值 . 现在这里是棘手的部分 . 移动平均线是 exponential moving averagetf.layers.batch_normalization()的默认动量为 0.99 . 平均值从0开始,方差再次为1 . 但由于每次更新的权重均为 ( 1 - momentum ) ,因此它将渐近地达到无穷大的实际均值和方差 . 例如,在 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 训练步骤 . 回顾 0.999 毫无意义 .

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


    旁注:众所周知,对值进行零偏移可以显着加快收敛速度,ExponentialMovingAverage class具有此功能 . 但批量规范化层没有此功能,如果您愿意重新构建代码,那么除了tf.slimbatch_norm之外 .

  • 2

    设置Training = False提高性能的原因是Batch规范化有四个变量(beta,gamma,mean,variance) . 确实,当训练=假时,均值和方差不会更新 . 然而,伽玛和测试版仍然会更新 . 因此,您的模型有两个额外的变量,因此具有更好的性能 .

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

相关问题