TensorFlow:从多个线程

时间:2017-02-28 16:39:47

标签: tensorflow python-multithreading

我想解决的问题如下: 我有一个trainimgs个文件名列表。我定义了一个

  • tf.RandomShuffleQueue及其capacity=len(trainimgs)min_after_dequeue=0
  • tf.RandomShuffleQueue预计会被trainimgs填充指定的epochlimit次。
  • 预计会有许多线程并行工作。每个线程从tf.RandomShuffleQueue中取出一个元素,并对其进行一些操作并将其排入另一个队列。我有那个部分。
  • 但是,1 epoch的{​​{1}}处理后trainimgs为空,假设当前纪元tf.RandomShuffleQueue,则必须再次填充队列和线程必须再次工作。

好消息是:我已经在某种情况下工作了(最后见 PS !!)

坏消息是:我认为有更好的方法可以做到这一点。

我现在使用的方法如下(我已经简化了功能并删除了基于预处理和后续排队的e图像处理,但处理的核心保持不变!!):

e < epochlimit

工作职能如下:

with tf.Session() as sess:
    train_filename_queue = tf.RandomShuffleQueue(capacity=len(trainimgs), min_after_dequeue=0, dtypes=tf.string, seed=0)
    queue_size = train_filename_queue.size()
    trainimgtensor = tf.constant(trainimgs)
    close_queue = train_filename_queue.close()
    epoch = tf.Variable(initial_value=1, trainable=False, dtype=tf.int32)
    incrementepoch = tf.assign(epoch, epoch + 1, use_locking=True)
    supplyimages = train_filename_queue.enqueue_many(trainimgtensor)
    value = train_filename_queue.dequeue()

    init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer())
    sess.run(init_op)
    coord = tf.train.Coordinator()
    tf.train.start_queue_runners(sess, coord)
    sess.run(supplyimages)
    lock = threading.Lock()
    threads = [threading.Thread(target=work, args=(coord, value, sess, epoch, incrementepoch, supplyimages, queue_size, lock, close_queue)) for  i in range(200)] 
    for t in threads:
        t.start()
    coord.join(threads)

所以,虽然这有效,但我觉得有更好,更清洁的方法来实现这一目标。所以,简而言之,我的问题是:

  1. 在TensorFlow中是否有更简单,更清晰的方法来实现此任务?
  2. 此代码的逻辑有问题吗?我对多线程场景不是很有经验,所以任何引起我注意的明显错误都对我很有帮助。
  3. P.S:看来这段代码毕竟不完美。当我运行120万个图像和200个线程时,它运行了。但是,当我为10个图像和20个线程运行它时,它会出现以下错误:

    def work(coord, val, sess, epoch, incrementepoch, supplyimg, q, lock,\
             close_op):
    while not coord.should_stop():
        if sess.run(q) > 0:
            filename, currepoch = sess.run([val, epoch])
            filename = filename.decode(encoding='UTF-8')
            print(filename + ' ' + str(currepoch))
        elif sess.run(epoch) < 2:
            lock.acquire()
            try:
                if sess.run(q) == 0:
                    print("The previous epoch = %d"%(sess.run(epoch)))
                    sess.run([incrementepoch, supplyimg])
                    sz = sess.run(q)
                    print("The new epoch = %d"%(sess.run(epoch)))
                    print("The new queue size = %d"%(sz))
            finally:
                lock.release()
        else:
            try:
                sess.run(close_op)
            except tf.errors.CancelledError:
                print('Queue already closed.')
            coord.request_stop()
    return None
    

    我以为自己被CancelledError (see above for traceback): RandomShuffleQueue '_0_random_shuffle_queue' is closed. [[Node: random_shuffle_queue_EnqueueMany = QueueEnqueueManyV2[Tcomponents=[DT_STRING], timeout_ms=-1, _device="/job:localhost/replica:0/task:0/cpu:0"](random_shuffle_queue, Const)]] 覆盖了。这到底是怎么回事?

3 个答案:

答案 0 :(得分:3)

我终于找到了答案。问题是多个线程在work()函数中的各个点上发生冲突。 以下work()功能完美无缺。

def work(coord, val, sess, epoch, maxepochs, incrementepoch, supplyimg, q, lock, close_op):
    print('I am thread number %s'%(threading.current_thread().name))
    print('I can see a queue with size %d'%(sess.run(q)))
    while not coord.should_stop():
        lock.acquire()
        if sess.run(q) > 0:
            filename, currepoch = sess.run([val, epoch])
            filename = filename.decode(encoding='UTF-8')
            tid = threading.current_thread().name
            print(filename + ' ' + str(currepoch) + ' thread ' + str(tid))
        elif sess.run(epoch) < maxepochs:
            print('Thread %s has acquired the lock'%(threading.current_thread().name))
            print("The previous epoch = %d"%(sess.run(epoch)))
            sess.run([incrementepoch, supplyimg])
            sz = sess.run(q)
            print("The new epoch = %d"%(sess.run(epoch)))
            print("The new queue size = %d"%(sz))
    else:
            coord.request_stop()
        lock.release()

    return None

答案 1 :(得分:1)

我建议让一个线程调用enqueue_many epochs次数排列正确数量的图像。然后它可以关闭队列。这样可以简化您的工作职能和其他线程。

答案 2 :(得分:1)

我认为GIL会阻止在这些线程中完成任何实际的并行性。

要获得张量流的性能,您需要将数据保持在张量流中。

Tensor Flow的reading data guide解释了如何解决类似的问题。

更具体地说,您似乎重写了string_input_producer的重要部分。