Присоединить очередь к массиву 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)