簡體   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