parallel_interleave
полезно, когда у вас есть преобразование, которое преобразует каждый элемент набора данных source в несколько элементов в набор данных destination . Я не уверен, почему они используют его в репозитории для репозиториев таким образом, когда они могли просто использовать map
с параллельными вызовами.
Вот как я предлагаю использовать parallel_interleave
для чтения изображений из нескольких каталогов, каждый из которых содержит один класс:
classes = sorted(glob(directory + '/*/')) # final slash selects directories only
num_classes = len(classes)
labels = np.arange(num_classes, dtype=np.int32)
dirs = DS.from_tensor_slices((classes, labels)) # 1
files = dirs.apply(tf.contrib.data.parallel_interleave(
get_files, cycle_length=num_classes, block_length=4, # 2
sloppy=False)) # False is important ! Otherwise it mixes labels
files = files.cache()
imgs = files.map(read_decode, num_parallel_calls=20)\. # 3
.apply(tf.contrib.data.shuffle_and_repeat(100))\
.batch(batch_size)\
.prefetch(5)
Есть три шага. Сначала мы получаем список каталогов и их меток (#1
).
Затем мы сопоставляем их с набором данных файлов. Но если мы сделаем простой .flatmap()
, мы получим все файлы с меткой 0
, за которыми следуют все файлы с меткой 1
, затем 2
и т. Д. Тогда нам понадобится очень большой перемешать буферы, чтобы получить значимое перемешивание.
Итак, вместо этого мы применяем parallel_interleave
(#2
). Вот это get_files()
:
def get_files(dir_path, label):
globbed = tf.string_join([dir_path, '*.jpg'])
files = tf.matching_files(globbed)
num_files = tf.shape(files)[0] # in the directory
labels = tf.tile([label], [num_files, ]) # expand label to all files
return DS.from_tensor_slices((files, labels))
Использование parallel_interleave
обеспечивает параллельное выполнение list_files
каждого каталога, поэтому к тому времени, когда первые файлы block_length
будут перечислены из первого каталога, первые файлы block_length
из второго каталога также будут доступны (также с 3-го, 4-го и т.д.). Кроме того, результирующий набор данных будет содержать чередующиеся блоки каждой метки, например, 1 1 1 1 2 2 2 2 3 3 3 3 3 1 1 1 1 ...
(для 3 классов и block_length=4
)
Наконец, мы читаем изображения из списка файлов (#3
). Вот read_and_decode()
:
def read_decode(path, label):
img = tf.image.decode_image(tf.read_file(path), channels=3)
img = tf.image.resize_bilinear(tf.expand_dims(img, axis=0), target_size)
img = tf.squeeze(img, 0)
img = preprocess_fct(img) # should work with Tensors !
label = tf.one_hot(label, num_classes)
img = tf.Print(img, [path, label], 'Read_decode')
return (img, label)
Эта функция берет путь изображения и его метку и возвращает тензор для каждого: тензор изображения для пути и кодирование one_hot для метки. Это также место, где вы можете сделать все преобразования на изображении. Здесь я делаю изменение размера и базовую предварительную обработку.