Обычно наборы данных, которые мы загружаем, имеют формат сжатых файлов, и после распаковки будет несколько папок, напримерtrain
,test
,val
и Т. Д. И файлов может быть несколько десятков тысяч или миллионов. Наборы данных в такой форме сложны, медленно читаются и занимают место на диске. В это время проявятся преимущества файла бинарного формата. Мы можем хранить набор данных какдвоичный файл, значит нетtrain
,test
,val
папка. Что еще более важно, эти данные будут занимать только блок памяти (Block of Memory), и нет необходимости загружать файлы один за другим. Так что используйтебинарный файлболее высокая эффективность.
Считаете ли вы, что TensorFlow инкапсулирует для вас способ чтения, записи и анализа двоичных файлов? Да, все готово~ В этой статье рассказывается, как конвертировать данные в формат TFRecord.
Набор данных 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)
В итоге у нас получится два файла:
Это окончательный файл формата 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)
.
Блок-схема чтения очереди выглядит следующим образом:
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найдено в ~
Суммировать
Процесс:
- Сгенерировать файл формата TFRecord
- Определить средство чтения записей для анализа файлов TFRecord.
- определить дозатор
- Построить сетевую модель
- инициализировать все операторы
- Запустите бегунов очередей.
- тренировочный цикл
Ссылаться на
- Tfrecords Guide
- TensorFlow Data Input (Part 1): Placeholders, Protobufs & Queues
- Об управлении потоками чтения данных в tensorflow QueueRunner
-- END
Хорошо написано, это помогло вам, пожалуйста, спонсируйте плату за хостинг ~