Tensorflow 从_生成器并行化tf.data.Dataset

Tensorflow 从_生成器并行化tf.data.Dataset,tensorflow,tensorflow-datasets,Tensorflow,Tensorflow Datasets,我有一个非常重要的输入管道,它来自于_generator,非常适合 dataset=tf.data.dataset.from\u生成器(复杂的\u img\u标签\u生成器, (tf.int32,tf.string) dataset=dataset.batch(64) iter=dataset.make_one_shot_iterator() imgs,labels=iter.get_next() 其中,complex\u img\u label\u generator动态生成图像,并返回表示

我有一个非常重要的输入管道,它来自于_generator,非常适合

dataset=tf.data.dataset.from\u生成器(复杂的\u img\u标签\u生成器,
(tf.int32,tf.string)
dataset=dataset.batch(64)
iter=dataset.make_one_shot_iterator()
imgs,labels=iter.get_next()
其中,
complex\u img\u label\u generator
动态生成图像,并返回表示
(H,W,3)
图像和简单
字符串的numpy数组。我无法将处理表示为从文件读取和
tf.image
操作

我的问题是如何使发电机平行?如何让N个生成器在它们自己的线程中运行

一种想法是使用
dataset.map
num\u parallel\u调用来处理线程;但这张地图是在张量上运行的。。。另一个想法是创建多个生成器,每个生成器都有自己的
预取
,并以某种方式将它们连接起来,但我不知道如何连接N个生成器流

有什么我可以遵循的典型例子吗?

事实证明,如果我使生成器超轻(仅生成元数据),然后将实际的重照明移动到无状态函数中,我可以使用
Dataset.map
。这样,我就可以使用
py_func
使用
map
并行化重载部件

作品;但是感觉有点笨拙。。。如果能够将
num\u parallel\u调用添加到来自\u生成器的
,那就太好了。
:)

def pure_numpy_和_pil_complex_计算(元数据、标签):
#一些复杂的pil和numpy工作与tf无关
...
dataset=tf.data.dataset.from_生成器(轻量级_生成器,
输出类型=(tf.string,#元数据
tf.string)#标签
def包装复杂计算(元数据、标签):
返回tf.py_func(func=pure_numpy_和pil_complex_计算,
inp=(元数据,标签),
Tout=(tf.uint8,#(H,W,3)img
tf.string)#标签
dataset=dataset.map(包装复杂计算,
num_parallel_calls=8)
dataset=dataset.batch(64)
iter=dataset.make_one_shot_iterator()
imgs,labels=iter.get_next()

生成器中完成的工作限制在最低限度,并使用
映射将昂贵的处理并行化是明智的

或者,您可以使用
parallel_interleave
按如下方式“连接”多个生成器:

def generator(n): # returns n-th generator function def dataset(n): return tf.data.Dataset.from_generator(generator(n)) ds = tf.data.Dataset.range(N).apply(tf.contrib.data.parallel_interleave(dataset, cycle_lenght=N)) # where N is the number of generators you use def发生器(n): #返回第n个生成器函数 def数据集(n): 从_生成器(生成器(n))返回tf.data.Dataset ds=tf.data.Dataset.range(N).apply(tf.contrib.data.parallel_interleave(Dataset,cycle_lenght=N)) #其中N是您使用的发电机数量
我正在为
tf.data.Dataset

from_indexable
的优点是它可以并行化,而python生成器不能并行化

来自_indexable
的函数
生成一个
tf.data.range
,将可索引文件包装在一个通用的
tf.py_func
中,并调用map

对于那些现在想从_indexable
中获得
的人,这里是lib代码

import tensorflow as tf
import numpy as np

from tensorflow.python.framework import tensor_shape
from tensorflow.python.util import nest

def py_func_decorator(output_types=None, output_shapes=None, stateful=True, name=None):
    def decorator(func):
        def call(*args):
            nonlocal output_shapes

            flat_output_types = nest.flatten(output_types)
            flat_values = tf.py_func(
                func, 
                inp=args, 
                Tout=flat_output_types,
                stateful=stateful, name=name
            )
            if output_shapes is not None:
                # I am not sure if this is nessesary
                output_shapes = nest.map_structure_up_to(
                    output_types, tensor_shape.as_shape, output_shapes)
                flattened_shapes = nest.flatten_up_to(output_types, output_shapes)
                for ret_t, shape in zip(flat_values, flattened_shapes):
                    ret_t.set_shape(shape)
            return nest.pack_sequence_as(output_types, flat_values)
        return call
    return decorator

def from_indexable(iterator, output_types, output_shapes=None, num_parallel_calls=None, stateful=True, name=None):
    ds = tf.data.Dataset.range(len(iterator))
    @py_func_decorator(output_types, output_shapes, stateful=stateful, name=name)
    def index_to_entry(index):
        return iterator[index]    
    return ds.map(index_to_entry, num_parallel_calls=num_parallel_calls)
这里有一个例子(注意:
from_indexable
有一个num_parallel_calls
参数

更新2018年6月10日: 由于已合并,来自_indexable的
代码简化为:

import tensorflow as tf

def py_func_decorator(output_types=None, output_shapes=None, stateful=True, name=None):
    def decorator(func):
        def call(*args, **kwargs):
            return tf.contrib.framework.py_func(
                func=func, 
                args=args, kwargs=kwargs, 
                output_types=output_types, output_shapes=output_shapes, 
                stateful=stateful, name=name
            )
        return call
    return decorator

def from_indexable(iterator, output_types, output_shapes=None, num_parallel_calls=None, stateful=True, name=None):
    ds = tf.data.Dataset.range(len(iterator))
    @py_func_decorator(output_types, output_shapes, stateful=stateful, name=name)
    def index_to_entry(index):
        return iterator[index]    
    return ds.map(index_to_entry, num_parallel_calls=num_parallel_calls)

您的代码不是有效的python代码,并且您没有首先定义
ds
。我非常喜欢这样。但是,生成器(n)应该返回第n个生成器,这里n是张量。如何获得第n个生成器?您现在可以给from_generator提供参数:仅供参考,使用
tf.py_func()
的并行性本身可能无法提高速度,请参见。这一点很好。根据经验,虽然我可以说这大大加快了速度。TensorFlow在您的回答之后是否将
num\u parallel\u调用添加到了来自\u generator
?谢谢你知道为什么你会得到“巨大的提速”吗?这是否意味着即使@mikkola使您的代码实际并行运行?不幸的是,它经不起时间的考验,因为tf2没有contrib,py_func已被py_函数取代,py_函数没有输出_形状、args、kwargs和stateful。最后,py_函数的输出返回可以在图形中使用的未知形状。tf 2.x确实不再具有contrib,但您始终可以使用
set_shape
函数设置函数中张量的形状。您可以在文档中看到一个示例:
import tensorflow as tf

def py_func_decorator(output_types=None, output_shapes=None, stateful=True, name=None):
    def decorator(func):
        def call(*args, **kwargs):
            return tf.contrib.framework.py_func(
                func=func, 
                args=args, kwargs=kwargs, 
                output_types=output_types, output_shapes=output_shapes, 
                stateful=stateful, name=name
            )
        return call
    return decorator

def from_indexable(iterator, output_types, output_shapes=None, num_parallel_calls=None, stateful=True, name=None):
    ds = tf.data.Dataset.range(len(iterator))
    @py_func_decorator(output_types, output_shapes, stateful=stateful, name=name)
    def index_to_entry(index):
        return iterator[index]    
    return ds.map(index_to_entry, num_parallel_calls=num_parallel_calls)