[英]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
管道之前,使用RandomShuffleQueue
和enqueue_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
,寬度561
和3
顏色通道的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.