【发布时间】:2017-07-16 23:45:22
【问题描述】:
我正在 InceptionV3 上对 5 种花卉的数据集执行迁移学习。除输出层外,所有层都被冻结。我的实现很大程度上基于 Tensorflow 的 Cifar10 教程,输入数据集的格式与 Cifar10 相同。
我添加了一个 MonitoredTrainingSession(就像在教程中一样)来报告一定数量的步骤后的准确性和损失。以下是 MonitoredTrainingSession 的代码部分(几乎与教程相同):
class _LoggerHook(tf.train.SessionRunHook):
def begin(self):
self._step = -1
self._start_time = time.time()
def before_run(self,run_context):
self._step+=1
return tf.train.SessionRunArgs([loss,accuracy])
def after_run(self,run_context,run_values):
if self._step % LOG_FREQUENCY ==0:
current_time = time.time()
duration = current_time - self._start_time
self._start_time = current_time
loss_value = run_values.results[0]
acc = run_values.results[1]
examples_per_sec = LOG_FREQUENCY/duration
sec_per_batch = duration / LOG_FREQUENCY
format_str = ('%s: step %d, loss = %.2f, acc = %.2f (%.1f examples/sec; %.3f sec/batch)')
print(format_str %(datetime.now(),self._step,loss_value,acc,
examples_per_sec,sec_per_batch))
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
if MODE == 'train':
file_writer = tf.summary.FileWriter(LOGDIR,tf.get_default_graph())
with tf.train.MonitoredTrainingSession(
save_checkpoint_secs=70,
checkpoint_dir=LOGDIR,
hooks=[tf.train.StopAtStepHook(last_step=NUM_EPOCHS*NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN),
tf.train.NanTensorHook(loss),
_LoggerHook()],
config=config) as mon_sess:
original_saver.restore(mon_sess,INCEPTION_V3_CHECKPOINT)
print("Proceeding to training stage")
while not mon_sess.should_stop():
mon_sess.run(train_op,feed_dict={training:True})
print('acc: %f' %mon_sess.run(accuracy,feed_dict={training:False}))
print('loss: %f' %mon_sess.run(loss,feed_dict={training:False}))
当去除mon_sess.run(train_op...下打印accuracy和loss的两行后,从after_run打印的loss和accuracy,在训练了惊人的20分钟后,报告模型在训练集上的表现非常好并且损失在减少。即使是移动平均线损失也报告了很好的结果。对于多个随机批次,它最终接近 90% 以上的准确率。
之后,训练课程报告了一段时间的高精度,我停止了训练课程,恢复了模型,并在同一训练集中的随机批次上运行它。它表现不佳,只能达到 50% 到 85% 的准确率。我确认它已正确恢复,因为它确实比具有未经训练的输出层的模型表现更好。
然后我从最后一个检查点再次回到训练中。最初的准确度很低,但在大约 10 次小批量运行后,准确度又回到了 90% 以上。然后我重复了这个过程,但这次添加了两行来评估训练操作后的损失和准确性。这两项评估报告称,该模型存在收敛问题和表现不佳。虽然通过before_run 和after_run, 进行的评估现在只是偶尔显示出高精度和低损失(结果跳跃)。但 after_run 有时仍报告 100% 的准确度(我认为它不再一致的事实是因为 after_run 也被称为 mon_sess.run(accuracy...) 和 mon_sess.run(loss...))。
为什么 MonitoredTrainingSession 报告的结果表明模型表现良好,而实际上并非如此? SessionRunArgs 中的两个操作不是和train_op 使用了相同的 mini batch,表明模型在梯度更新之前的 batch 性能?
这是我用于恢复和测试模型的代码(基于 cifar10 教程):
elif MODE == 'test':
init = tf.global_variables_initializer()
ckpt = tf.train.get_checkpoint_state(LOGDIR)
if ckpt and ckpt.model_checkpoint_path:
with tf.Session(config=config) as sess:
init.run()
saver = tf.train.Saver()
print(ckpt.model_checkpoint_path)
saver.restore(sess,ckpt.model_checkpoint_path)
global_step = tf.contrib.framework.get_or_create_global_step()
coord = tf.train.Coordinator()
threads =[]
try:
for qr in tf.get_collection(tf.GraphKeys.QUEUE_RUNNERS):
threads.extend(qr.create_threads(sess, coord=coord, daemon=True,start=True))
print('model restored')
i =0
num_iter = 4*NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN/BATCH_SIZE
print(num_iter)
while not coord.should_stop() and i < num_iter:
print("loss: %.2f," %loss.eval(feed_dict={training:False}),end="")
print("acc: %.2f" %accuracy.eval(feed_dict={training:False}))
i+=1
except Exception as e:
print(e)
coord.request_stop(e)
coord.request_stop()
coord.join(threads,stop_grace_period_secs=10)
更新:
所以我能够解决这个问题。但是,我不确定它为什么起作用。在初始模型的arg_scope 中,我传递了一个is_training 布尔占位符,用于Batch Norm 和初始使用的dropout。但是,当我删除占位符并将is_training关键字设置为true时,模型恢复时在训练集上的准确率非常高。这与之前表现不佳的模型检查点相同。当我训练它时,我总是将is_training 占位符设置为true。在测试时将is_training 设置为true 意味着batch Norm 现在正在使用样本均值和方差。
为什么告诉 Batch Norm 现在像在训练期间那样使用样本平均值和样本标准差来提高准确度?
这也意味着 dropout 层正在丢弃单元,并且在启用 dropout 层的情况下,模型在训练集和测试集上的测试期间的准确性更高。
更新 2
我浏览了上面代码中的arg_scope 引用的tensorflow slim inceptionv3 模型代码。我在 Avg pool 8x8 之后移除了最后的 dropout 层,准确率保持在 99% 左右。但是,当我仅将批量规范层的 is_training 设置为 False 时,准确度下降到 70% 左右。这是来自slim\nets\inception_v3.py 的 arg_scope 和我的修改。
with variable_scope.variable_scope(
scope, 'InceptionV3', [inputs, num_classes], reuse=reuse) as scope:
with arg_scope(
[layers_lib.batch_norm],is_training=False): #layers_lib.dropout], is_training=is_training):
net, end_points = inception_v3_base(
inputs,
scope=scope,
min_depth=min_depth,
depth_multiplier=depth_multiplier)
我尝试了这个,同时删除了 dropout 层,并保留了 dropout 层,并将 is_training=True 传递给 dropout 层。
【问题讨论】:
-
original_saver.restore行是否曾被删除?似乎您在第一次开始重新训练时只需要一次,然后您将使用MonitoredTrainingSession保存的检查点。 -
我没有删除它,但它只是恢复了 Inception 的参数,这些参数都被冻结了。我不认为我说得好,但是当我第一次恢复模型时,为了测试它,我没有通过
MonitoredTrainingSession评估它,但在部分代码中我没有显示,但是我会加。我使用不同的保护程序恢复了模型,该保护程序从训练会话留下的检查点文件中恢复了所有参数,包括新的输出层。然后我启动了QueueRunners并检查了准确性。但是是的,当我回到再培训时,我离开了那句话。 -
@Allen Lavoie 我尝试删除
original_saver.restore行,但似乎并没有改变结果。然而,正如我上面提到的,我意识到当我恢复模型并在 inceptionV3 的 arg_scope 中设置is_training=True时,模型的准确性现在与MonitoredTrainingSession所说的相符。然而,在推理过程中启用 dropout 如何显着提高模型的准确性似乎没有意义...... -
有趣。您正在使用
tf.contrib.layers.dropout?它确实在训练期间重新缩放(以便在训练/推理期间期望值相等),尽管这并不能保证它会很好地工作。你能把影响隔离到辍学吗? -
@Allen Lavoie 请发表您关于更新操作的评论作为答案,我会接受。它解决了我的问题。由于样本均值和方差是在训练时使用的,并且在测试时从未考虑到长期运行的统计数据,因此我在训练期间获得了很高的准确性;该模型似乎表现良好。当使用
is_training=False对模型进行训练和测试时,我也获得了很高的准确性。我相信这是因为来自检查点的原始长期运行统计信息同时用于训练和测试。
标签: python-3.x tensorflow deep-learning