Tensorflow多线程QueueRunner

时间:2017-01-29 11:27:05

标签: multithreading tensorflow

我有一张图表:

                                     - preprocessing_op1 -> op2 -> img
                                    /                                 \
slice_input_producer([imgs, labels])                              tf.train.batch(num_threads=n)
                                    \- - - - - - - label - - - - - - -/

这是典型的数据I / O管道。

问题:tf.train.batch()的多个线程都有竞争条件。 例如Thread1获取sample1_img和sample2_label,因为Thread2已经采用了sample1_label,形成了一对(sample1_img,sample2_label)。我想这是因为slice_input_producer有两个独立的队列用于imgs和标签,两个队列独立工作。

Q1。 n个排队线程中的每一个都运行自己的子图副本吗?如果是,设置num_threads = n需要运行时相应子图的n倍内存?如果不是,那么线程是否会运行子图的不同部分以进行一次排队操作?

Q2(已解决)。如果我创建一个FIFOQueue并将(img,label)的元组排入队列,那么该对将会原子地出列,并且多线程实际上会有所帮助。它是否正确? (虽然它不是100%利用率,因为标签张量等待img tensor的预处理)

Q3(已解决)。是否有像tuple_input_producer()这样的函数,它采用张量列表并在内部只使用一个队列?

更新(Q2,Q3)

我对slice_input_tensor错了。 问题只发生在两个队列中,而不是slice_input_producer。

所以只需使用slice_input_producer,如果两个张量需要进入不同的队列,我可以使用单线程瓶颈(QueueRunner)将它们捆绑在一起。

示例代码(0.11):

import tensorflow as tf
import numpy as np

a = tf.train.string_input_producer(map(str,range(100)), shuffle=False).dequeue()
b = tf.train.string_input_producer(map(str,range(100)), shuffle=False).dequeue()
op1 = tf.identity(a)
op2 = tf.identity(op1)
c1, c2 = tf.train.batch([op2,b], num_threads=10, batch_size=10)

with tf.Session() as sess, tf.device('/gpu:0'):
    sess.run([tf.initialize_all_variables()])

    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess, coord)

    for i in range(10):
        d1, d2 = sess.run([c1,c2])
        print d1, d2

    coord.request_stop()
    coord.join(threads)

结果(见第一行):

['0' '2' '1' '7' '4' '3' '6' '8' '9' '5'] ['0' '2' '1' '6' '5' '4' '7' '8' '9' '3']
['10' '11' '12' '13' '14' '15' '16' '17' '18' '19'] ['10' '11' '12' '13' '14' '15' '16' '17' '18' '19']
['20' '21' '22' '23' '24' '25' '26' '27' '28' '29'] ['20' '21' '22' '23' '24' '25' '26' '27' '28' '29']
['30' '31' '33' '32' '34' '35' '36' '37' '38' '39'] ['30' '31' '33' '32' '34' '35' '36' '37' '38' '39']
['40' '41' '42' '43' '44' '45' '46' '47' '48' '49'] ['40' '41' '42' '43' '44' '45' '46' '47' '48' '49']
['50' '51' '52' '53' '54' '55' '56' '57' '58' '59'] ['50' '51' '52' '53' '54' '55' '56' '57' '58' '59']
['60' '61' '62' '63' '64' '65' '66' '67' '68' '69'] ['60' '61' '62' '63' '64' '65' '66' '67' '68' '69']
['70' '71' '72' '73' '74' '75' '76' '77' '78' '79'] ['70' '71' '72' '73' '74' '75' '76' '77' '78' '79']
['80' '81' '82' '83' '84' '85' '86' '87' '88' '89'] ['80' '81' '82' '83' '84' '85' '86' '88' '89' '87']
['90' '91' '92' '93' '94' '95' '96' '97' '98' '99'] ['90' '91' '92' '93' '94' '95' '96' '97' '98' '99']

2 个答案:

答案 0 :(得分:0)

您有并行运行调用,称为"步骤"并且每个步骤都维护自己在执行期间生成的张量副本。因此,在最坏的情况下,N个并行步骤需要N倍的内存。在实践中,它往往比N倍更好,因为只要不再需要张量,就会释放内存。队列和变量等有状态对象跨步骤共享。

您的案例中发生的情况如下:

step1: dequeue queue1
step2: dequeue queue1
step2: dequeue queue2
step1: dequeue queue2

您可以看到两个步骤的队列都不同步。两种避免它的方法:

  1. 不要发出并行运行调用(num_threads = 1)
  2. 将两个队列合并到一个带有图像/标签的队列中,并从该队列中并行出列。
  3. 在第二个示例中,您将有一个dequeue op返回一对image/label,并且事情应保持同步,因为dequeues是原子的

答案 1 :(得分:0)

我已尝试使用tf = 0.12.1

的Update示例代码

当迭代从范围(10)变为范围(100)时,也会发生不匹配。

如果我用num_threads = 1修改了tf.train.batch参数,它就解决了。

我还没试过Combine two queues into a single queue with images/labels, and dequeue from that queue in parallel.