繁体   English   中英

TensorFlow-tf.data.Dataset读取大型HDF5文件

[英]TensorFlow - tf.data.Dataset reading large HDF5 files

我正在设置一个TensorFlow管道来读取大型HDF5文件,作为我的深度学习模型的输入。 每个HDF5文件包含100个长度可变的视频,这些视频存储为压缩JPG图像的集合(以使磁盘上的大小易于管理)。 使用tf.data.Dataset和到tf.py_func的映射,使用自定义Python逻辑从HDF5文件读取示例非常容易。 例如:

def read_examples_hdf5(filename, label):
    with h5py.File(filename, 'r') as hf:
        # read frames from HDF5 and decode them from JPG
    return frames, label

filenames = glob.glob(os.path.join(hdf5_data_path, "*.h5"))
labels = [0]*len(filenames) # ... can we do this more elegantly?

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(
    lambda filename, label: tuple(tf.py_func(
        read_examples_hdf5, [filename, label], [tf.uint8, tf.int64]))
)

dataset = dataset.shuffle(1000 + 3 * BATCH_SIZE)
dataset = dataset.batch(BATCH_SIZE)
iterator = dataset.make_one_shot_iterator()
next_batch = iterator.get_next()

此示例有效,但问题是tf.py_func似乎tf.py_func只能处理一个示例。 当我的HDF5容器存储了100个示例时,此限制会导致相当大的开销,因为不断需要打开,读取,关闭和重新打开文件。 将所有100个视频示例读入数据集对象,然后继续下一个HDF5文件(最好是在多个线程中,每个线程处理自己的HDF5文件集合),效率会更高。

因此,我想在后台运行多个线程,从HDF5文件读取视频帧,从JPG解码它们,然后将其输入到数据集对象中。 在引入tf.data.Dataset管道之前,使用RandomShuffleQueueenqueue_many ops相当容易,但是似乎目前尚无优雅的方法(或者缺少文档)。

有谁知道实现我的目标的最佳方法是什么? 我还使用tfrecord文件研究(并实现了)管道,但是对tfrecord文件中存储的视频帧进行随机采样似乎是完全不可能的(请参阅此处 )。 此外,我已经看过了from_generator()用于输入tf.data.Dataset但绝对不会在多线程似乎运行。 任何建议都值得欢迎。

在处理类似问题时,我偶然发现了这个问题。 我想出了一个基于Python生成器的解决方案,以及TF数据集构造方法from_generator 因为我们使用生成器,所以HDF5文件应只打开一次以读取一次,并在有要读取的条目的情况下保持打开状态。 因此,它将不会为每次获取下一个数据元素的每次调用而打开,读取和关闭。

发电机定义

为了允许用户将HDF5文件名作为参数传递,我生成了一个具有__call__方法的类,因为from_generator指定生成器必须是可调用的。 这是生成器:

import h5py
import tensorflow as tf

class generator:
    def __init__(self, file):
        self.file = file

    def __call__(self):
        with h5py.File(self.file, 'r') as hf:
            for im in hf["train_img"]:
                yield im

通过使用生成器,代码应该从上次返回结果以来的每次调用停止的地方开始,而不是从头开始运行所有内容。 在这种情况下,它位于内部for循环的下一次迭代中。 因此,这应该再次跳过打开文件以进行读取,只要有要yield数据就保持打开状态。 有关发电机的更多信息,请参见此出色的问答

当然,您将必须替换with块中的所有内容,以匹配数据集的构造方式和要获取的输出。

使用范例

ds = tf.data.Dataset.from_generator(
    generator(hdf5_path), 
    tf.uint8, 
    tf.TensorShape([427,561,3]))

value = ds.make_one_shot_iterator().get_next()

# Example on how to read elements
while True:
    try:
        data = sess.run(value)
        print(data.shape)
    except tf.errors.OutOfRangeError:
        print('done.')
        break

同样,在我的情况下,我在数据集中存储了高度427 ,宽度5613颜色通道的uint8图像,因此您需要在上述调用中修改这些图像以匹配您的用例。

处理多个文件

我有一个用于处理多个HDF5文件的建议解决方案。 基本思想是照常从文件名构造一个Dataset ,然后使用interleave方法同时处理许多输入文件,例如,从每个文件中获取样本以形成一个批处理。

这个想法如下:

ds = tf.data.Dataset.from_tensor_slices(filenames)
# You might want to shuffle() the filenames here depending on the application
ds = ds.interleave(lambda filename: tf.data.Dataset.from_generator(
        generator(filename), 
        tf.uint8, 
        tf.TensorShape([427,561,3])),
       cycle_length, block_length)

这样做是同时打开cycle_length文件,并在移动到下一个文件之前从每个文件中产生block_length项-有关详细信息,请参见interleave文档。 您可以在此处设置值以匹配适合您的应用程序的值:例如,您是否需要一次处理一个文件或同时处理多个文件,是否只希望一次从每个文件中获取一个样本,等等? 。

编辑 :对于并行版本,请看一下tf.contrib.data.parallel_interleave

可能的警告

如果决定使用解决方案,请注意使用from_generator的特殊性。 对于Tensorflow 1.6.0,该的文档from_generator提到这两个音符。

在不同的环境中或在分布式培训中应用此方法可能会具有挑战性:

注意:Dataset.from_generator()的当前实现使用tf.py_func并继承相同的约束。 特别是,它要求将与Dataset和Iterator相关的操作放在与名为Dataset.from_generator()的Python程序相同的进程中的设备上。 生成器的主体不会在GraphDef中进行序列化,并且如果需要序列化模型并将其还原到其他环境中,则不应使用此方法。

如果生成器取决于外部状态,请小心:

注意:如果generator依赖于可变的全局变量或其他外部状态,请注意,运行时可能会多次调用generator(以支持重复数据集),并且在调用Dataset.from_generator()和生成数据之间的任何时间都可以调用该生成器。生成器的第一个元素。 突变全局变量或外部状态可能导致未定义的行为,我们建议您在调用Dataset.from_generator()之前在生成器中显式缓存任何外部状态。

我花了一段时间来弄清楚这一点,所以我认为我应该在这里记录下来。 根据mikkola的回答,这是处理多个文件的方法:

import h5py
import tensorflow as tf

class generator:
    def __call__(self, file):
        with h5py.File(file, 'r') as hf:
            for im in hf["train_img"]:
                yield im

ds = tf.data.Dataset.from_tensor_slices(filenames)
ds = ds.interleave(lambda filename: tf.data.Dataset.from_generator(
        generator(), 
        tf.uint8, 
        tf.TensorShape([427,561,3]),
        args=(filename,)),
       cycle_length, block_length)

关键是您不能将filename直接传递给generator ,因为它是Tensor 您必须将其传递给args ,tensorflow会对其求值并将其转换为常规python变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM