Tensorflow:在cpu上的多个线程中加载数据

时间:2017-11-30 08:34:45

标签: python multithreading tensorflow keras

我有一个python类SceneGenerator,它有多个用于预处理的成员函数和一个生成器函数generate_data()。基本结构是这样的:

class SceneGenerator(object):
    def __init__(self):
       # some inits

    def generate_data(self):
        """
        Generator. Yield data X and labels y after some preprocessing
        """
        while True:
            # opening files, selecting data
            X,y = self.preprocess(some_params, filenames, ...)            

            yield X, y

我在keras model.fit_generator()函数中使用了类成员函数sceneGenerator.generate_data()来从磁盘读取数据,对其进行预处理并将其生成。在keras中,如果workers的{​​{1}}参数设置为>,则在多个CPU线程上完成此操作。 1。

我现在想在tensorflow中使用相同的model.fit_generator()类。我目前的做法是:

SceneGenerator

然而,这很慢并且不使用多个线程。我发现tf.data.Dataset api有一些documentation,但我没有实现这些方法。

编辑:请注意,我不使用图像,因此带有文件路径等的图像加载机制在此处不起作用。 我的sceneGenerator = SceneGenerator(some_params) for X, y in sceneGenerator.generate_data(): feed_dict = {ops['data']: X, ops['labels']: y, ops['is_training_pl']: True } summary, step, _, loss, prediction = sess.run([optimization_op, loss_op, pred_op], feed_dict=feed_dict) 从hdf5文件加载数据。但不是完整的数据集,而是 - 取决于初始化参数 - 只有数据集的一部分。我希望保持生成器功能不变,并了解如何将此生成器直接用作tensorflow的输入并在CPU上的多个线程上运行。将数据从hdf5文件重写为csv不是一个好选择,因为它复制了大量数据。

编辑2::我认为类似的内容可能有所帮助:parallelising tf.data.Dataset.from_generator

2 个答案:

答案 0 :(得分:6)

假设您正在使用最新的Tensorflow(撰写本文时为1.4),您可以保留生成器并使用tf.data.* API,如下所示(我选择了线程编号的任意值,预取缓冲区大小,批量大小和输出数据类型):

ERRORS=$( reposync 2>&1)

为了表明它实际上是从生成器中提取的多个线程,我修改了你的类,如下所示:

NUM_THREADS = 5
sceneGen = SceneGenerator()
dataset = tf.data.Dataset.from_generator(sceneGen.generate_data, output_types=(tf.float32, tf.int32))
dataset = dataset.map(lambda x,y : (x,y), num_parallel_calls=NUM_THREADS).prefetch(buffer_size=1000)
dataset = dataset.batch(42)
X, y = dataset.make_one_shot_iterator().get_next()

这样,创建Tensorflow会话并获取一个批处理会显示获取数据的线程的线程ID。在我的电脑上,运行:

import threading    
class SceneGenerator(object):
  def __init__(self):
    # some inits
    pass

  def generate_data(self):
    """
    Generator. Yield data X and labels y after some preprocessing
    """
    while True:
      # opening files, selecting data
      X,y = threading.get_ident(), 2 #self.preprocess(some_params, filenames, ...)            
      yield X, y

打印

sess = tf.Session()
print(sess.run([X, y]))

注意:您可能想要尝试删除[array([ 8460., 8460., 8460., 15912., 16200., 16200., 8460., 15912., 16200., 8460., 15912., 16200., 16200., 8460., 15912., 15912., 8460., 8460., 6552., 15912., 15912., 8460., 8460., 15912., 9956., 16200., 9956., 16200., 15912., 15912., 9956., 16200., 15912., 16200., 16200., 16200., 6552., 16200., 16200., 9956., 6552., 6552.], dtype=float32), array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])] 调用(我们只用于拥有多个线程)并检查map的缓冲区是否足够要消除输入管道中的瓶颈(即使只有一个线程,输入预处理通常比实际的图形执行速度快,因此缓冲区足以使预处理尽可能快)。

答案 1 :(得分:2)

使用feed_dict运行会话的确是pretty slow

  

Feed_dict从Python运行时到TensorFlow运行时执行单线程内容memcpy。

提供数据的更快捷方式是使用tf.train.string_input_producer + *Reader + tf.train.Coordinator,这将在多个线程中批量处理数据。为此,您直接将数据读入张量,例如,这是一种读取和处理csv文件的方法:

def batch_generator(filenames):
  filename_queue = tf.train.string_input_producer(filenames)
  reader = tf.TextLineReader(skip_header_lines=1)
  _, value = reader.read(filename_queue)

  content = tf.decode_csv(value, record_defaults=record_defaults)
  content[4] = tf.cond(tf.equal(content[4], tf.constant('Present')),
                       lambda: tf.constant(1.0),
                       lambda: tf.constant(0.0))

  features = tf.stack(content[:N_FEATURES])
  label = content[-1]

  data_batch, label_batch = tf.train.shuffle_batch([features, label],
                                                   batch_size=BATCH_SIZE,
                                                   capacity=20*BATCH_SIZE,
                                                   min_after_dequeue=10*BATCH_SIZE)
  return data_batch, label_batch

此函数获取输入文件列表,创建读取器和数据转换并输出张量,它们将根据这些文件的内容进行评估。您的场景生成器可能会进行不同的转换,但想法是一样的。

接下来,您启动tf.train.Coordinator来并行化:

with tf.Session() as sess:
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(coord=coord)
    for _ in range(10):  # generate 10 batches
        features, labels = sess.run([data_batch, label_batch])
        print(features)
    coord.request_stop()
    coord.join(threads)

根据我的经验,这种方式可以更快地提供数据并允许利用整个可用的GPU功率。完整的工作示例可以找到here