Введение в TensorFlow TFRecord и QueueRunner

искусственный интеллект TensorFlow protobuf

Обычно наборы данных, которые мы загружаем, имеют формат сжатых файлов, и после распаковки будет несколько папок, напримерtrain,test,valи Т. Д. И файлов может быть несколько десятков тысяч или миллионов. Наборы данных в такой форме сложны, медленно читаются и занимают место на диске. В это время проявятся преимущества файла бинарного формата. Мы можем хранить набор данных какдвоичный файл, значит нетtrain,test,valпапка. Что еще более важно, эти данные будут занимать только блок памяти (Block of Memory), и нет необходимости загружать файлы один за другим. Так что используйтебинарный файлболее высокая эффективность.

Считаете ли вы, что TensorFlow инкапсулирует для вас способ чтения, записи и анализа двоичных файлов? Да, все готово~ В этой статье рассказывается, как конвертировать данные в формат TFRecord.

Набор данных CIFAR-10

В этой статье в качестве примера используется набор данных CIFAR-10.Что такое набор данных CIFAR-10? посмотри здесь =>Набор данных изображения ~

Предположим, у вас уже есть следующие данные:

CIFAR-10

Напишите, сохраните в формате TFRecord

Определены некоторые константы:

_NUM_TRAIN_FILES = 5
# The height and width of each image.
_IMAGE_SIZE = 32
# The names of the classes.
_CLASS_NAMES = [
    'airplane',
    'automobile',
    'bird',
    'cat',
    'deer',
    'dog',
    'frog',
    'horse',
    'ship',
    'truck',
]

Здесь мы создаем два разделенных файла для хранения данных, необходимых для обучения и тестирования соответственно:

dataset_dir = 'data'
if not tf.gfile.Exists(dataset_dir):
    tf.gfile.MakeDirs(dataset_dir)
training_filename = _get_output_filename(dataset_dir, 'train')
testing_filename = _get_output_filename(dataset_dir, 'test')

_get_output_filenameФункция используется для генерации имени файла:

def _get_output_filename(dataset_dir, split_name):
    """Creates the output filename.
    Args:
      dataset_dir: The dataset directory where the dataset is stored.
      split_name: The name of the train/test split.
    Returns:
      An absolute file path.
    """
    return '%s/cifar10_%s.tfrecord' % (dataset_dir, split_name)

Затем обработайте обучающие данные:

# First, process the training data:
with tf.python_io.TFRecordWriter(training_filename) as tfrecord_writer:
    offset = 0
    for i in range(_NUM_TRAIN_FILES):
        filename = os.path.join('./cifar-10-batches-py', 'data_batch_%d' % (i + 1))
        offset = _add_to_tfrecord(filename, tfrecord_writer, offset)

читать последовательноdata_batch_?файл, звонок_add_to_tfrecordСохраните его в формате TFRecord.

def _add_to_tfrecord(filename, tfrecord_writer, offset=0):
    """Loads data from the cifar10 pickle files and writes files to a TFRecord.
    Args:
      filename: The filename of the cifar10 pickle file.
      tfrecord_writer: The TFRecord writer to use for writing.
      offset: An offset into the absolute number of images previously written.
    Returns:
      The new offset.
    """
    with tf.gfile.Open(filename, 'rb') as f:
        data = pickle.load(f, encoding='bytes')
    images = data[b'data']
    num_images = images.shape[0]

    images = images.reshape((num_images, 3, 32, 32))
    labels = data[b'labels']
    with tf.Graph().as_default():
        image_placeholder = tf.placeholder(tf.uint8)
        encoded_image = tf.image.encode_png(image_placeholder)
        with tf.Session() as sess:
            for j in range(num_images):
                sys.stdout.write('\r>> Reading file [%s] image %d/%d' % (filename, offset + j + 1, offset + num_images))
                sys.stdout.flush()
                image = np.squeeze(images[j]).transpose((1, 2, 0))
                label = labels[j]
                png_string = sess.run(encoded_image, feed_dict={image_placeholder: image})
                example = image_to_tfexample(png_string, b'png', _IMAGE_SIZE, _IMAGE_SIZE, label)
                tfrecord_writer.write(example.SerializeToString())
    return offset + num_images

Поскольку изображения набора данных CIFAR-1010000x3072 numpy arrayформате, поэтому требуетreshapeзаtf.image.encode_pngТребуемый формат:[height, width, channels].tf.image.encode_pngВозвращает закодированную строку, а затем также необходимо сохранить информацию о ширине, высоте и формате изображения. перечислитьimage_to_tfexampleсохранить эти данные вtf.train.Exampleсередина:

def image_to_tfexample(image_data, image_format, height, width, class_id):
    return tf.train.Example(features=tf.train.Features(feature={
        'image/encoded': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_data])),
        'image/format': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_format])),
        'image/class/label': tf.train.Feature(int64_list=tf.train.Int64List(value=[class_id])),
        'image/height': tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),
        'image/width': tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),
    }))

TensorFlow преобразует данные вtf.train.Exampleобъекты протобафа,ExampleВключаютFeaturesОсобенности содержитdict, различать разныеFeature.Featureможет содержатьFloatList,ByteListилиInt64List. Обратите внимание здесь ключ,image/encoded,image/formatИ т. д., могут быть определены свободно, вот ключ набора данных изображений TensorFlow по умолчанию, мы обычно берем значение TensorFlow по умолчанию.

имеютexample, мы преобразуем его в строку и записываем в файл, чтобы завершить создание всего файла формата TFRecord.

tfrecord_writer.write(example.SerializeToString())

Таким же образом сделайте тестовый набор данных:

# Next, process the testing data:
with tf.python_io.TFRecordWriter(testing_filename) as tfrecord_writer:
    filename = os.path.join('./cifar-10-batches-py', 'test_batch')
    _add_to_tfrecord(filename, tfrecord_writer)

В итоге у нас получится два файла:

file

Это окончательный файл формата TFRecord, двоичный файл.

читать

Проще всего прочитать прямо:

reconstructed_images = []
record_iterator = tf.python_io.tf_record_iterator(path='./data/cifar10_train.tfrecord')
for string_iterator in record_iterator:
    example = tf.train.Example()
    example.ParseFromString(string_iterator)
    height = example.features.feature['image/height'].int64_list.value[0]
    width = example.features.feature['image/width'].int64_list.value[0]
    png_string = example.features.feature['image/encoded'].bytes_list.value[0]
    label = example.features.feature['image/class/label'].int64_list.value[0]
    with tf.Session() as sess:
        image_placeholder = tf.placeholder(dtype=tf.string)
        decoded_img = tf.image.decode_png(image_placeholder, channels=3)
        reconstructed_img = sess.run(decoded_img, feed_dict={image_placeholder: png_string})
    reconstructed_images.append((reconstructed_img, label))

По сути, это процесс, обратный «письму». генерироватьExample, анализирует прочитанную строку, затем запускаетfeaturesВы можете получить соответствующий объект по ключу. Для изображений мы используемtf.image.decode_pngдекодировать, т.е.tf.image.encode_pngобратный процесс.

После прочтения его можно отобразить напрямую:

plt.imshow(reconstructed_images[0][0])
plt.title(_CLASS_NAMES[reconstructed_images[0][0]])
plt.show()

Этот метод относительно прост, чтение и анализ непосредственно из файла, но если данных много, вы будете работать медленнее, и он не учитывает проблемы распределения, очереди и многопоточности. Мы также можем использовать очередь файлов для чтения.

очередь

# first construct a queue containing a list of filenames.
# this lets a user split up there dataset in multiple files to keep
# size down
filename_queue = tf.train.string_input_producer(['./data/cifar10_train.tfrecord'])
# Unlike the TFRecordWriter, the TFRecordReader is symbolic, 即所做的操作不会立即执行
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(serialized_example, features={
    'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
    'image/format': tf.FixedLenFeature((), tf.string, default_value='png'),
    'image/height': tf.FixedLenFeature((), tf.int64),
    'image/width': tf.FixedLenFeature((), tf.int64),
    'image/class/label': tf.FixedLenFeature([], tf.int64, default_value=tf.zeros([], dtype=tf.int64))
})
image = tf.image.decode_png(features['image/encoded'], channels=3)
image = tf.image.resize_image_with_crop_or_pad(image, 32, 32)
label = features['image/class/label']
init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer())
with tf.Session() as sess:
    sess.run(init_op)
    tf.train.start_queue_runners()
    # grab examples back.
    # first example from file
    image_val_1, label_val_1 = sess.run([image, label])
    # second example from file
    image_val_2, label_val_2 = sess.run([image, label])
    print(image_val_1, label_val_1)
    print(image_val_2, label_val_2)
    plt.imshow(image_val_1)
    plt.title(_CLASS_NAMES[label_val_1])
    plt.show()

Сначала определите нашу очередь имен файловfilename_queue, который содержит список имен файлов, так что мы можем разделить большой файл на несколько файлов меньшего размера, гарантируя, что один файл не будет слишком большим, в данном случае это только один файл. затем используйтеTFRecordReaderчитать. Графики TensorFlow содержат некоторые переменные состояния, которые позволяютTFRecordReaderПомнитеtfrecordгде мы прочитали и с чего начать дальше, поэтому нам нужноsess.run(init_op)для инициализации этих состояний. иtf.python_io.tf_record_iteratorразница в томTFRecordReaderвсегда работает с именами файлов (filename_queue), он будет извлекать имя файла для чтения данных до тех пор, покаtfrecordпуст, затем прочитайте файл, соответствующий следующему имени файла.

Как сгенерировать очередь имен файлов, тогда нам нужноQueueRunnersсделать это.QueueRunnersПо сути, это поток, использующийsessionНепрерывно выполнять операции постановки в очередь, TensorFlow был упакованtf.train.QueueRunnerобъект. но большую часть времениQueueRunnerЭто просто низкоуровневая операция, мы не будем работать с ней напрямую, в этом примере используетсяtf.train.string_input_producerгенерировать.

На этом этапе вам нужно отправить сигнал, чтобы позволить TensorFlow запустить поток и выполнить его.QueueRunners, в противном случае код заблокируется навсегда, ожидая постановки данных в очередь. Поэтому необходимо выполнитьtf.train.start_queue_runners(), поток будет создан сразу после выполнения этой строки кода. Уведомление,необходимо использовать в операторе инициализации (sess.run(init_op)) вызывается после выполнения.

tf.parse_single_exampleПо нашему определениюfeaturesПарсинг формата данных. наконец-то,image_val_1изображение в наборе данных изображения, форма( 32, 32, 3).

Блок-схема чтения очереди выглядит следующим образом:

file

Batch

В приведенном выше примере мы получаемimageиlabelВсе они представляют собой один примерный объект, представляющий часть данных в наборе данных. Во время обучения нельзя тренироваться с одним фрагментом данных Как генерировать пакеты?

images_batch, labels_batch = tf.train.shuffle_batch(
    [image, label], batch_size=128,
    capacity=2000,
    min_after_dequeue=1000)

with tf.Session() as sess:
    sess.run(init_op)
    tf.train.start_queue_runners()
    labels, images = sess.run([labels_batch, images_batch])
    print(labels.shape)

Здесь мы используемtf.train.shuffle_batchпоставить синглimageиlabelПримеры объектов генерируют пакеты.tf.train.shuffle_batchна самом деле строит другойQueueRunner,RandomShuffleQueue.RandomShuffleQueueпоставить синглimageиlabelНакапливается в очередь до тех пор, пока не будет содержатьbatch_size + min_after_dequeueКусок. затем случайным образом выберитеbatch_sizeфрагменты данных возвращаются, поэтомуshuffle_batchВозвращаемое значение на самом делеRandomShuffleQueueвоплощать в жизньdequeue_manyВозвращаемое значение.

Если форма тензора[x, y, z],shuffle_batchСоответствующая возвращенная форма тензора[batch_size, x, y, z], в таком случаеlabelsиimagesФормы(128, )и(128, 32, 32, 3).

DatasetDataProvider

если мы используемtf.contrib.slim, мы можем более элегантно инкапсулировать процесс чтения.

Определить наш набор данныхcifar10.py, как это определить? Я считаю, что после прочтения приведенного выше кода следующий код можно понять без пояснений~

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import tensorflow as tf

slim = tf.contrib.slim

_FILE_PATTERN = 'cifar10_%s.tfrecord'

SPLITS_TO_SIZES = {'train': 50000, 'test': 10000}

_NUM_CLASSES = 10

_ITEMS_TO_DESCRIPTIONS = {
    'image': 'A [32 x 32 x 3] color image.',
    'label': 'A single integer between 0 and 9',
}

def get_split(split_name, dataset_dir, file_pattern=None, reader=None):
    """Gets a dataset tuple with instructions for reading cifar10.
    Args:
      split_name: A train/test split name.
      dataset_dir: The base directory of the dataset sources.
      file_pattern: The file pattern to use when matching the dataset sources.
        It is assumed that the pattern contains a '%s' string so that the split
        name can be inserted.
      reader: The TensorFlow reader type.
    Returns:
      A `Dataset` namedtuple.
    Raises:
      ValueError: if `split_name` is not a valid train/test split.
    """
    if split_name not in SPLITS_TO_SIZES:
        raise ValueError('split name %s was not recognized.' % split_name)

    if not file_pattern:
        file_pattern = _FILE_PATTERN
    file_pattern = os.path.join(dataset_dir, file_pattern % split_name)

    # Allowing None in the signature so that dataset_factory can use the default.
    if not reader:
        reader = tf.TFRecordReader

    keys_to_features = {
        'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
        'image/format': tf.FixedLenFeature((), tf.string, default_value='png'),
        'image/class/label': tf.FixedLenFeature(
            [], tf.int64, default_value=tf.zeros([], dtype=tf.int64)),
    }

    items_to_handlers = {
        'image': slim.tfexample_decoder.Image(shape=[32, 32, 3]),
        'label': slim.tfexample_decoder.Tensor('image/class/label'),
    }

    decoder = slim.tfexample_decoder.TFExampleDecoder(
        keys_to_features, items_to_handlers)

    labels_to_names = None
    if has_labels(dataset_dir):
        labels_to_names = read_label_file(dataset_dir)

    return slim.dataset.Dataset(
        data_sources=file_pattern,
        reader=reader,
        decoder=decoder,
        num_samples=SPLITS_TO_SIZES[split_name],
        items_to_descriptions=_ITEMS_TO_DESCRIPTIONS,
        num_classes=_NUM_CLASSES,
        labels_to_names=labels_to_names)

def has_labels(dataset_dir, filename='labels.txt'):
    """Specifies whether or not the dataset directory contains a label map file.
    Args:
      dataset_dir: The directory in which the labels file is found.
      filename: The filename where the class names are written.
    Returns:
      `True` if the labels file exists and `False` otherwise.
    """
    return tf.gfile.Exists(os.path.join(dataset_dir, filename))

def read_label_file(dataset_dir, filename='labels.txt'):
    """Reads the labels file and returns a mapping from ID to class name.
    Args:
      dataset_dir: The directory in which the labels file is found.
      filename: The filename where the class names are written.
    Returns:
      A map from a label (integer) to class name.
    """
    labels_filename = os.path.join(dataset_dir, filename)
    with tf.gfile.Open(labels_filename, 'rb') as f:
        lines = f.read().decode()
    lines = lines.split('\n')
    lines = filter(None, lines)

    labels_to_class_names = {}
    for line in lines:
        index = line.index(':')
        labels_to_class_names[int(line[:index])] = line[index + 1:]
    return labels_to_class_names

Читается очень просто:

dataset = cifar10.get_split('train', DATA_DIR)
provider = slim.dataset_data_provider.DatasetDataProvider(dataset)
[image, label] = provider.get(['image', 'label'])

Все приведенные выше коды можно найти в репозитории tensorflow/model.slimнайдено в ~

Суммировать

Процесс:

  1. Сгенерировать файл формата TFRecord
  2. Определить средство чтения записей для анализа файлов TFRecord.
  3. определить дозатор
  4. Построить сетевую модель
  5. инициализировать все операторы
  6. Запустите бегунов очередей.
  7. тренировочный цикл

Ссылаться на

  1. Tfrecords Guide
  2. TensorFlow Data Input (Part 1): Placeholders, Protobufs & Queues
  3. Об управлении потоками чтения данных в tensorflow QueueRunner

-- END

Хорошо написано, это помогло вам, пожалуйста, спонсируйте плату за хостинг ~