Ответ 1
Я наткнулся на этот вопрос, имея дело с подобной проблемой. Я придумал решение, основанное на использовании генератора 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
. Подробнее об генераторах см. В этом отличном Q & A.
Конечно, вам придется заменить что - нибудь внутри 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
Опять же, в моем случае я сохранил uint8
изображения высотой 427
, ширину 561
и 3
цветовых канала в моем наборе данных, поэтому вам нужно будет изменить их в приведенном выше вызове, чтобы соответствовать вашему варианту использования.
Обработка нескольких файлов
У меня есть предлагаемое решение для обработки нескольких файлов 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 на устройстве выполняется тот же процесс, что и программа Python, которая называется Dataset.from_generator(). Тело генератора не будет сериализовано в GraphDef, и вы не должны использовать этот метод, если вам нужно сериализовать свою модель и восстановить ее в другой среде.
Будьте осторожны, если генератор зависит от внешнего состояния:
ПРИМЕЧАНИЕ. Если генератор зависит от изменяемых глобальных переменных или другого внешнего состояния, имейте в виду, что среда выполнения может вызывать генератор несколько раз (для поддержки повторения набора данных) и в любое время между вызовом Dataset.from_generator() и производством первый элемент из генератора. Мутирование глобальных переменных или внешнего состояния может вызвать неопределенное поведение, и мы рекомендуем вам явно кэшировать любое внешнее состояние в генераторе до вызова Dataset.from_generator().