使用 tf.cond() 为我的图表提供训练和验证

Using tf.cond() to feed my graph for training and validation

在我的 TensorFlow 代码中,我希望我的网络根据我是想进行训练还是测试,从两个 StagingArea 对象之一获取输入。 我写的部分图构造代码如下:

with tf.device("/gpu:0"):
      for i in range(numgpus):
          with tf.variable_scope(tf.get_variable_scope(), reuse=i>0) as vscope:
              with tf.device('/gpu:{}'.format(i)):
                  with tf.name_scope('GPU-Tower-{}'.format(i)) as scope:
                      phase = tf.get_variable("phase", [], initializer=tf.zeros_initializer(),dtype=tf.uint8, trainable=False)
                      phaseassigntest = phase.assign(1)
                      phaseassigntrain = phase.assign(0)
                      phasetest = tf.equal(phase, 0)
                      is_training = tf.cond(phasetest, lambda: tf.constant(True), lambda: tf.constant(False))

                      trainstagingarea = tf.contrib.staging.StagingArea([tf.float32, tf.int32], shapes=[[trainbatchsize, 3, 221, 221], [trainbatchsize]], capacity=20)
                      putoptrain = trainstagingarea.put(train_iterator.get_next())
                      trainputop.append(putoptrain)
                      getoptrain = trainstagingarea.get()
                      traingetop.append(getoptrain)
                      trainclearop = trainstagingarea.clear()
                      trainstageclear.append(trainclearop)
                      trainsizeop = trainstagingarea.size()
                      trainstagesize.append(trainsizeop)

                      valstagingarea = tf.contrib.staging.StagingArea([tf.float32, tf.int32], shapes=[[valbatchsize, 3, 221, 221], [valbatchsize]], capacity=20)
                      putopval = valstagingarea.put(val_iterator.get_next())
                      valputop.append(putopval)
                      getopval = valstagingarea.get()
                      valgetop.append(getopval)
                      valclearop = valstagingarea.clear()
                      valstageclear.append(valclearop)
                      valsizeop = valstagingarea.size()
                      valstagesize.append(valsizeop)


                      #elem = valgetop[i]
                      elem = tf.cond(is_training,lambda: traingetop[i],lambda: valgetop[i])

                      img = elem[0]
                      label = elem[1]
                      labelonehot = tf.one_hot(label, depth=numclasses)
                      net, networksummaries =  overfeataccurate(img,numclasses=numclasses, phase=is_training)

我已经使用 tf.cond 来确保网络由两个 StagingArea 对象之一提供。一个用于训练,另一个用于验证。 现在,当我尝试按如下方式执行图表时,我没有得到任何结果,实际上代码只是挂起,我必须终止进程。

with tf.Session(graph=g,config=config) as sess:
    sess.run(init_op)
    sess.run(tf.local_variables_initializer())
    sess.run(val_initialize)
    for i in range(20):
        sess.run(valputop)
        print(sess.run(valstagesize))
    writer = tf.summary.FileWriter('.', graph=tf.get_default_graph())
    epoch = 0
    iter = 0
    print("Performing Validation")
    sess.run(phaseassigntest)
    saver = tf.train.Saver()
    while(epoch<10):
        time_init = time.time()
        while True:
            try:
                [val_accu, _, summaries] = sess.run([towervalidation, towervalidationupdateop,validation_summary_op])
                print(val_accu)

当我直接分配 elem = valgetop[i] 而不是 tf.cond() 时,代码工作正常。 我在这里错过了什么吗?

根据我是想进行训练还是测试,什么是正确的网络馈送方式?

注意 即使我将 numgpus 设置为 1,错误也不会消失。

你的问题

你怎么想的 tf.cond 怎么想的

根据标志,执行将 traingetop[i] 或 valgetop[i] 放入您的 elem 张量所需的操作。

tf.cond 的实际作用

执行获得 both traingetop[i] 和 valgetop[i] 所需的操作,然后将其中之一传递到您的 elem 张量中。

所以

它永远挂起的原因是因为它正在等待将一个元素添加到您的训练暂存区(以便它可以获取该元素并丢弃它)。您没有意识到这就是它正在做的事情,这是可以原谅的;这实际上是非常违反直觉的。文档 非常 不清楚如何处理这个问题。


推荐的解决方案(来自 Tensorflow 文档)

如果你真的需要队列在同一个图表中,那么你需要制作整个图表的两份副本,一份由你的训练集结区提供,一份由你的验证集结区提供.然后你只需在你的 sess.run 调用中使用相关的张量。我建议创建一个接受队列输出张量和 returns 一个 model_output 张量的函数。现在你有一个 train_time_output 张量和一个 validation_time_output 张量,你可以在 sess.run.

中选择要执行的张量

警告

您需要确保您实际上没有创建新变量来配合这些新操作。要做到 ,请查看 variables 上的最新文档。看起来他们已经从 v0.12 简化了它,它基本上归结为使用 tf.get_variable 而不是 tf.Variable 来创建变量。


我的首选解决方法

虽然这是推荐的解决方案(AFAIK),但我非常不满意;您正在图表上创建另一组恰好使用相同权重的操作。滥用火车时间和 test/validation 时间之间的间隔(导致模型在这些时间表现出意想不到的不同)似乎有很多程序员错误的可能性。更差;它没有解决 tf.cond 要求两个分支的输入值的问题,它只是迫使您复制整个图表,这并不总是可能的。

我更喜欢在图表中不使用我的队列,而是将模型视为一个函数,可以提供一个示例而不用关心它来自哪里。也就是说,我会使用 tf.placeholder 作为输入来实例化模型,并且在执行时我会使用 feed_dict 来实际提供值。它的功能是这样的

#inside main training loop
if time_to_train:
    example = sess.run(traingettop)
else:
    example = sess.run(valgettop)
result = sess.run(model_output, {input_placeholder: example})

值得注意的是,您可以使用 feed_dict 为模型中任何位置的任何张量提供任何值。因此,您可以更改任何模型定义,由于 tf.cond 总是需要输入,例如:

a = tf.constant(some_value)
b = tf.placeholder(tf.float32)
flag = tf.placeholder(tf.bool, [])
one_of_them = tf.cond(flag, a, b)
model_output = build_graph(one_of_them)

进入一个没有的定义,比如:

a = tf.constant(some_value)
model_output = build_graph(a)

请记住,您始终可以在执行时覆盖 a 的内容:

# In main training loop,
sess.run(train_op, {a: some_other_value})

这实质上将条件句推入了本地 python 领域。在您的代码中,您最终可能会得到如下内容:

if condition_satisfied:
    sess.run(train_op, {a:some_other_value})
else:
    sess.run(train_op)

性能问题

如果您在单台机器上使用 tensorflow,则此解决方案 几乎没有性能成本,因为 numpy array/s 放入 example python 变量实际上仍然存储在 GPU 上。

如果您以分布式方式使用 tensorflow,那么此解决方案会降低您的性能;它需要将示例从它所在的任何机器发送到主机,以便它可以将它发回。