Присоединить очередь к массиву numpy в тензорном потоке для извлечения данных вместо файлов?
Я прочитал Учебник CNN по TensorFlow, и я пытаюсь использовать ту же модель для своего проекта.
Теперь проблема заключается в чтении данных. У меня есть около 25000 изображений для обучения и около 5000 для тестирования и проверки каждого. Файлы находятся в формате png, и я могу их прочитать и преобразовать в numpy.ndarray.
В примере CNN в учебниках используется очередь для извлечения записей из предоставленного списка файлов. Я попытался создать свой собственный такой двоичный файл, изменив изображения в 1-D массив и добавив значение метки перед ним. Итак, мои данные выглядят как
[[1,12,34,24,53,...,105,234,102],
[12,112,43,24,52,...,115,244,98],
....
]
Единственная строка указанного массива имеет размер 22501, где первым элементом является метка.
Я сбросил файл на использование pickle и попытался прочитать из файла, используя
tf.FixedLengthRecordReader для чтения из файла как продемонстрированного в примере
Я делаю то же самое, что указано в cifar10_input.py, чтобы прочитать двоичный файл и поместить его в объект записи.
Теперь, когда я читаю файлы, метки и значения изображения различаются. Я могу понять причину этого, что pickle сбрасывает дополнительную информацию о фигурных скобках и скобках также в двоичном файле, и они меняют размер записи фиксированной длины.
В приведенном выше примере используются имена файлов и передаются в очередь для извлечения файлов, а затем очереди для чтения одной записи из файла.
Я хочу знать, могу ли я передать массив numpy, как определено выше, вместо имени файла для некоторого читателя, и он может извлекать записи один за другим из этого массива вместо файлов.
Ответы
Ответ 1
Вероятно, самый простой способ заставить ваши данные работать с примером кода CNN - сделать измененную версию read_cifar10()
и использовать ее вместо этого:
-
Запишите двоичный файл, содержащий содержимое вашего массива numpy.
import numpy as np
images_and_labels_array = np.array([[...], ...], # [[1,12,34,24,53,...,102],
# [12,112,43,24,52,...,98],
# ...]
dtype=np.uint8)
images_and_labels_array.tofile("/tmp/images.bin")
Этот файл похож на формат, используемый в файлах данных CIFAR10. Возможно, вы захотите создать несколько файлов, чтобы прочитать parallelism. Обратите внимание, что ndarray.tofile()
записывает двоичные данные в строчном порядке без других метаданных; травление массива добавит метаданные, специфичные для Python, которые не понимают подпрограммы подпрограмм TensorFlow.
-
Напишите измененную версию read_cifar10()
, которая обрабатывает ваш формат записи.
def read_my_data(filename_queue):
class ImageRecord(object):
pass
result = ImageRecord()
# Dimensions of the images in the dataset.
label_bytes = 1
# Set the following constants as appropriate.
result.height = IMAGE_HEIGHT
result.width = IMAGE_WIDTH
result.depth = IMAGE_DEPTH
image_bytes = result.height * result.width * result.depth
# Every record consists of a label followed by the image, with a
# fixed number of bytes for each.
record_bytes = label_bytes + image_bytes
assert record_bytes == 22501 # Based on your question.
# Read a record, getting filenames from the filename_queue. No
# header or footer in the binary, so we leave header_bytes
# and footer_bytes at their default of 0.
reader = tf.FixedLengthRecordReader(record_bytes=record_bytes)
result.key, value = reader.read(filename_queue)
# Convert from a string to a vector of uint8 that is record_bytes long.
record_bytes = tf.decode_raw(value, tf.uint8)
# The first bytes represent the label, which we convert from uint8->int32.
result.label = tf.cast(
tf.slice(record_bytes, [0], [label_bytes]), tf.int32)
# The remaining bytes after the label represent the image, which we reshape
# from [depth * height * width] to [depth, height, width].
depth_major = tf.reshape(tf.slice(record_bytes, [label_bytes], [image_bytes]),
[result.depth, result.height, result.width])
# Convert from [depth, height, width] to [height, width, depth].
result.uint8image = tf.transpose(depth_major, [1, 2, 0])
return result
-
Измените distorted_inputs()
, чтобы использовать новый набор данных:
def distorted_inputs(data_dir, batch_size):
"""[...]"""
filenames = ["/tmp/images.bin"] # Or a list of filenames if you
# generated multiple files in step 1.
for f in filenames:
if not gfile.Exists(f):
raise ValueError('Failed to find file: ' + f)
# Create a queue that produces the filenames to read.
filename_queue = tf.train.string_input_producer(filenames)
# Read examples from files in the filename queue.
read_input = read_my_data(filename_queue)
reshaped_image = tf.cast(read_input.uint8image, tf.float32)
# [...] (Maybe modify other parameters in here depending on your problem.)
Это будет минимальный набор шагов, учитывая вашу отправную точку. Может быть более эффективным сделать PNG-декодирование с помощью TensorFlow ops, но это было бы большим изменением.
Ответ 2
В вашем вопросе вы специально спросили:
Я хочу знать, могу ли я передать массив numpy, как определено выше, вместо имени файла для некоторого читателя, и он может извлекать записи один за другим из этого массива вместо файлов.
Вы можете напрямую подать массив numpy в очередь, но это будет более инвазивное изменение кода cifar10_input.py
, чем предлагает мой другой ответ.
Как и прежде, предположим, что у вас есть следующий массив из вашего вопроса:
import numpy as np
images_and_labels_array = np.array([[...], ...], # [[1,12,34,24,53,...,102],
# [12,112,43,24,52,...,98],
# ...]
dtype=np.uint8)
Затем вы можете определить очередь, содержащую все данные, следующим образом:
q = tf.FIFOQueue([tf.uint8, tf.uint8], shapes=[[], [22500]])
enqueue_op = q.enqueue_many([image_and_labels_array[:, 0], image_and_labels_array[:, 1:]])
... затем вызовите sess.run(enqueue_op)
для заполнения очереди.
Еще один эффективный подход - это подача записей в очередь, которые вы можете сделать из параллельного потока (см. этот ответ для получения более подробной информации о том, как это будет работать):
# [With q as defined above.]
label_input = tf.placeholder(tf.uint8, shape=[])
image_input = tf.placeholder(tf.uint8, shape=[22500])
enqueue_single_from_feed_op = q.enqueue([label_input, image_input])
# Then, to enqueue a single example `i` from the array.
sess.run(enqueue_single_from_feed_op,
feed_dict={label_input: image_and_labels_array[i, 0],
image_input: image_and_labels_array[i, 1:]})
Кроме того, для поочередного выделения очереди, что будет более эффективным:
label_batch_input = tf.placeholder(tf.uint8, shape=[None])
image_batch_input = tf.placeholder(tf.uint8, shape=[None, 22500])
enqueue_batch_from_feed_op = q.enqueue([label_batch_input, image_batch_input])
# Then, to enqueue a batch examples `i` through `j-1` from the array.
sess.run(enqueue_single_from_feed_op,
feed_dict={label_input: image_and_labels_array[i:j, 0],
image_input: image_and_labels_array[i:j, 1:]})
Ответ 3
Я хочу знать, могу ли я передать массив numpy, как определено выше от имени файла к некоторому читателю и он может забирать записи один за другим из этого массива вместо файлов.
tf.py_func
, который обертывает функцию python и использует ее как оператор TensorFlow, может помочь. Вот пример .
Однако, поскольку вы упомянули, что ваши изображения хранятся в png файлах, я думаю, что самым простым решением было бы заменить this:
reader = tf.FixedLengthRecordReader(record_bytes=record_bytes)
result.key, value = reader.read(filename_queue)
с этим:
result.key, value = tf.WholeFileReader().read(filename_queue))
value = tf.image.decode_jpeg(value)