当我增加将数据推送到tensorflow队列的线程数时,tensorflow队列的填充速度会变慢

当我增加将数据推送到tensorflow队列的线程数时,tensorflow队列的填充速度会变慢,tensorflow,python-multiprocessing,Tensorflow,Python Multiprocessing,我在tensorflow中编写了一些将数据推送到队列的代码,队列处理程序的init和所有线程运行的主函数如下所示: def __init__(self): self.X = tf.placeholder(tf.int64) self.Y = tf.placeholder(tf.int64) self.queue = tf.RandomShuffleQueue(dtypes=[tf.int64, tf.int64],

我在tensorflow中编写了一些将数据推送到队列的代码,队列处理程序的init和所有线程运行的主函数如下所示:

def __init__(self):
    self.X = tf.placeholder(tf.int64)
    self.Y = tf.placeholder(tf.int64)
    self.queue = tf.RandomShuffleQueue(dtypes=[tf.int64, tf.int64],
                                       capacity=100,
                                       min_after_dequeue=20)

    self.enqueue_op = self.queue.enqueue([self.X, self.Y])


def thread_main(self, sess, coord):
    """Cycle through the dataset until the main process says stop."""
    train_fs = open(data_train, 'r')
    while not coord.should_stop():
        X_, Y_ = get_batch(train_fs)
        if not Y: #We're at the end of the file
            train_fs = open(data_train, 'r')
            X, Y = get_batch(train_fs)
        sess.run(self.enqueue_op, feed_dict={self.X:X_, self.Y:Y_}) 
我在培训期间监控队列的大小。由于某些原因,当我增加将数据推送到队列的线程数时,队列的填充速度会变慢。知道为什么吗?是因为我同时从python文件中读取吗

编辑:

这是我正在使用的代码,除了数据和图形之外,它是完全相同的。代码在此虚拟数据上的行为与预期一致。我有两个观察:

  • 我认为我没有适当地关闭线程,似乎它们在执行后会被卡在队列中,我运行代码越多,它就越慢
  • 由于多线程正在这里完成它的工作,我想只有两个失败点是我的图形和读取数据的方式
首先,要生成虚拟数据集:

data_train = "./test.txt"

with open(data_train, 'w') as out_stream:
    out_stream.write("""[1,2,3,4,5,6]|1\n[1,2,3,4]|2\n[1,2,3,4,5,6]|0\n[1,2,3,4,5,6]|1\n[1,2,5,6]|1\n[1,2,5,6]|0""")

def get_batch(fs):
    line = fs.readline()
    X, Y = line.split('|')
    X = eval(X)
    Y = eval(Y)
    return X, Y
然后,队列控制器:

import tensorflow as tf
import numpy as np
import threading

tf.reset_default_graph()#Reset the graph essential to use with jupyter else variable conflicts

class QueueCtrl(object):

    def __init__(self):
        self.X = tf.placeholder(tf.int64)
        self.Y = tf.placeholder(tf.int64)
        self.queue = tf.RandomShuffleQueue(dtypes=[tf.int64, tf.int64],
                                           capacity=100,
                                           min_after_dequeue=20)

        self.enqueue_op = self.queue.enqueue([self.X, self.Y])


    def thread_main(self, sess, coord):
        """Cycle through the dataset until the main process says stop."""
        train_fs = open(data_train, 'r')
        while not coord.should_stop():
            X_, Y_ = get_batch(train_fs)
            if not Y_: #We're at the end of the file
                train_fs = open(data_train, 'r')
                X_, Y_ = get_batch(train_fs)
            sess.run(self.enqueue_op, feed_dict={self.X:X_, self.Y:Y_}) 

    def get_batch_from_queue(self):
        """
        Return one batch
        """
        return self.queue.dequeue()

    def start_threads(self, sess, coord, num_threads=2):
        """Start the threads"""
        threads = []
        for _ in range(num_threads):
            t = threading.Thread(target=self.thread_main, args=(sess, coord))
            t.daemon = True
            t.start()
            threads.append(t)
        return threads
然后我们构建一个虚拟图:

queue_ctrl = QueueCtrl()
X_, Y_ = queue_ctrl.get_batch_from_queue()
output = Y_ * tf.reduce_sum(X_)
init = tf.initialize_all_variables()
最后,我们对数据进行迭代:

sess = tf.Session()

sess.run(init)
coord = tf.train.Coordinator()
tf.train.start_queue_runners(sess=sess, coord=coord)
my_thread = queue_ctrl.start_threads(sess, coord, num_threads=6)

for i in range(100):
    out = sess.run(output)
    print("Iter: %d, output: %d, Element in queue: %d" 
              % (i, out, sess.run(queue_ctrl.queue.size())))

coord.request_stop()
for _ in range(len(my_thread)): #if the queue is full at that time then the threads won't see the coord.should_stop
    _ = sess.run([output])

coord.join(my_thread, stop_grace_period_secs=10)
sess.close()
以下是25个具有五个线程的第一个输出:

Iter: 0, output: 21, Element in queue: 27
Iter: 1, output: 21, Element in queue: 37
Iter: 2, output: 20, Element in queue: 51
Iter: 3, output: 21, Element in queue: 67
Iter: 4, output: 20, Element in queue: 81
Iter: 5, output: 20, Element in queue: 89
Iter: 6, output: 21, Element in queue: 100
Iter: 7, output: 20, Element in queue: 100
Iter: 8, output: 20, Element in queue: 100
Iter: 9, output: 21, Element in queue: 100
Iter: 10, output: 20, Element in queue: 100
Iter: 11, output: 20, Element in queue: 100
Iter: 12, output: 21, Element in queue: 100
Iter: 13, output: 21, Element in queue: 100
Iter: 14, output: 20, Element in queue: 100
Iter: 15, output: 20, Element in queue: 100
Iter: 16, output: 21, Element in queue: 100
Iter: 17, output: 21, Element in queue: 100
Iter: 18, output: 20, Element in queue: 100
Iter: 19, output: 21, Element in queue: 100
Iter: 20, output: 21, Element in queue: 100
Iter: 21, output: 21, Element in queue: 100
Iter: 22, output: 20, Element in queue: 100
Iter: 23, output: 21, Element in queue: 100
Iter: 24, output: 21, Element in queue: 100
Iter: 25, output: 21, Element in queue: 100
使用一个线程:

Iter: 0, output: 21, Element in queue: 22
Iter: 1, output: 20, Element in queue: 25
Iter: 2, output: 20, Element in queue: 27
Iter: 3, output: 20, Element in queue: 29
Iter: 4, output: 21, Element in queue: 31
Iter: 5, output: 20, Element in queue: 32
Iter: 6, output: 20, Element in queue: 34
Iter: 7, output: 21, Element in queue: 35
Iter: 8, output: 21, Element in queue: 36
Iter: 9, output: 21, Element in queue: 38
Iter: 10, output: 20, Element in queue: 40
Iter: 11, output: 20, Element in queue: 42
Iter: 12, output: 20, Element in queue: 43
Iter: 13, output: 21, Element in queue: 46
Iter: 14, output: 20, Element in queue: 47
Iter: 15, output: 21, Element in queue: 48
Iter: 16, output: 20, Element in queue: 53
Iter: 17, output: 20, Element in queue: 56
Iter: 18, output: 21, Element in queue: 57
Iter: 19, output: 21, Element in queue: 61
Iter: 20, output: 21, Element in queue: 63
Iter: 21, output: 20, Element in queue: 67
Iter: 22, output: 21, Element in queue: 70
Iter: 23, output: 21, Element in queue: 73
Iter: 24, output: 20, Element in queue: 76
Iter: 25, output: 20, Element in queue: 78

只是想在这里添加一些东西,我为多任务学习实现了一个基于多进程的数据馈送管道。它可以实现平均GPU利用率>90%和四核CPU利用率>95%。不易发生内存泄漏,特别适合长时间的训练。并不是说它是完美的,但在我的例子中,它至少比当前的TF队列API(1.1)好得多


如果有人感兴趣:

只想在这里添加一些东西,我为多任务学习实现了一个基于多进程的数据馈送管道。它可以实现平均GPU利用率>90%和四核CPU利用率>95%。不易发生内存泄漏,特别适合长时间的训练。并不是说它是完美的,但在我的例子中,它至少比当前的TF队列API(1.1)好得多


如果有人感兴趣:

很可能是因为线程正在争夺对队列的访问权。你能发布一个小的可复制的例子吗?我离开了办公室,明天我将再次面对代码,并将更新问题。此外,我将尝试使用FIFO队列而不是RandomShuffeQueue,并确保预处理由CPU完成。我已经添加了一些代码,我仍在研究中。好的,在修改用于读取文件的python代码后,我可以看到添加更多线程时,队列填充速度更快。我现在使用
readline()
逐行读取,而不是一个字节接一个字节地读取。我不明白为什么它会改变什么,但它确实。。。此外,队列中多个线程的填充速度更快,但训练时间是原来的两倍。。。可能线程不断地在队列上放置互斥体来进行排队,而训练op也在为互斥体而斗争。我可以尝试使用
排队\u很多
,但我有多种形状的元素,所以我无法……排队似乎是tensorflow的一个吸引人的功能,但到目前为止,我已经花了10多个小时在它上面,并没有获得速度提升。如果数据稀疏,无法以密集表示形式转换所有输入,那么队列可能不是您的最佳选择。很可能是因为线程正在争夺对队列的访问权。你能发布一个小的可复制的例子吗?我离开了办公室,明天我将再次面对代码,并将更新问题。此外,我将尝试使用FIFO队列而不是RandomShuffeQueue,并确保预处理由CPU完成。我已经添加了一些代码,我仍在研究中。好的,在修改用于读取文件的python代码后,我可以看到添加更多线程时,队列填充速度更快。我现在使用
readline()
逐行读取,而不是一个字节接一个字节地读取。我不明白为什么它会改变什么,但它确实。。。此外,队列中多个线程的填充速度更快,但训练时间是原来的两倍。。。可能线程不断地在队列上放置互斥体来进行排队,而训练op也在为互斥体而斗争。我可以尝试使用
排队\u很多
,但我有多种形状的元素,所以我无法……排队似乎是tensorflow的一个吸引人的功能,但到目前为止,我已经花了10多个小时在它上面,并没有获得速度提升。如果您有稀疏的数据,并且无法在密集表示中转换所有输入,那么队列可能不是您的最佳选择。