Есть вопрос? Присоединяйтесь к сообществу на форуме TensorFlow. Посетите форум.

TFRecord и tf.train.Example

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Формат TFRecord - это простой формат для хранения последовательности двоичных записей.

Буферы протокола - это кроссплатформенная кросс-языковая библиотека для эффективной сериализации структурированных данных.

Сообщения протокола определяются файлами .proto , часто это самый простой способ понять тип сообщения.

Сообщение tf.train.Example (или protobuf) - это гибкий тип сообщения, который представляет отображение {"string": value} . Он разработан для использования с TensorFlow и используется во всех высокоуровневых API, таких как TFX .

В этой записной книжке показано, как создавать, анализировать и использовать сообщение tf.train.Example , а затем сериализовать, записывать и читать tf.train.Example сообщения в файлы .tfrecord и из .tfrecord .

Настраивать

import tensorflow as tf

import numpy as np
import IPython.display as display

tf.train.Example

Типы данных для tf.train.Example

По сути, tf.train.Example - это отображение {"string": tf.train.Feature} .

tf.train.Feature сообщения tf.train.Feature может принимать один из следующих трех типов (см. Файл .proto для справки). Большинство других универсальных типов можно принудительно преобразовать в один из следующих:

  1. tf.train.BytesList (могут быть приведены следующие типы)

    • string
    • byte
  2. tf.train.FloatList (могут быть приведены следующие типы)

    • float ( float32 )
    • double ( float64 )
  3. tf.train.Int64List (могут быть приведены следующие типы)

    • bool
    • enum
    • int32
    • uint32
    • int64
    • uint64

Чтобы преобразовать стандартный тип tf.train.Example в tf.train.Example совместимый tf.train.Feature , вы можете использовать следующие функции ярлыков. Обратите внимание, что каждая функция принимает скалярное входное значение и возвращает tf.train.Feature содержащий один из трех типов list выше:

# The following functions can be used to convert a value to a type compatible
# with tf.train.Example.

def _bytes_feature(value):
  """Returns a bytes_list from a string / byte."""
  if isinstance(value, type(tf.constant(0))):
    value = value.numpy() # BytesList won't unpack a string from an EagerTensor.
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def _float_feature(value):
  """Returns a float_list from a float / double."""
  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))

def _int64_feature(value):
  """Returns an int64_list from a bool / enum / int / uint."""
  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

Ниже приведены несколько примеров того, как работают эти функции. Обратите внимание на различные типы ввода и стандартизованные типы вывода. Если тип ввода для функции не соответствует одному из приведенных выше типов, функция вызовет исключение (например, _int64_feature(1.0) выдаст ошибку, потому что 1.0 - это число с плавающей запятой, поэтому его следует использовать с функцией _float_feature вместо ):

print(_bytes_feature(b'test_string'))
print(_bytes_feature(u'test_bytes'.encode('utf-8')))

print(_float_feature(np.exp(1)))

print(_int64_feature(True))
print(_int64_feature(1))
bytes_list {
  value: "test_string"
}

bytes_list {
  value: "test_bytes"
}

float_list {
  value: 2.7182817459106445
}

int64_list {
  value: 1
}

int64_list {
  value: 1
}

Все прото-сообщения можно сериализовать в двоичную строку с .SerializeToString метода .SerializeToString :

feature = _float_feature(np.exp(1))

feature.SerializeToString()
b'\x12\x06\n\x04T\xf8-@'

Создание сообщения tf.train.Example

Предположим, вы хотите создать сообщение tf.train.Example из существующих данных. На практике набор данных может поступать откуда угодно, но процедура создания сообщения tf.train.Example из одного наблюдения будет такой же:

  1. В каждом наблюдении каждое значение необходимо преобразовать в tf.train.Feature содержащий один из 3 совместимых типов, с помощью одной из функций, указанных выше.

  2. Вы создаете карту (словарь) из строки имени объекта в закодированное значение объекта, полученное в # 1.

  3. Карта, созданная на шаге 2, преобразуется в сообщение Features .

В этой записной книжке вы создадите набор данных с помощью NumPy.

Этот набор данных будет иметь 4 функции:

  • логическая функция, False или True с равной вероятностью
  • целочисленный признак, случайно выбранный из [0, 5]
  • строковый объект, созданный из таблицы строк с использованием целочисленного объекта в качестве индекса
  • функция с плавающей запятой из стандартного нормального распределения

Рассмотрим выборку, состоящую из 10000 независимо и одинаково распределенных наблюдений из каждого из вышеперечисленных распределений:

# The number of observations in the dataset.
n_observations = int(1e4)

# Boolean feature, encoded as False or True.
feature0 = np.random.choice([False, True], n_observations)

# Integer feature, random from 0 to 4.
feature1 = np.random.randint(0, 5, n_observations)

# String feature.
strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])
feature2 = strings[feature1]

# Float feature, from a standard normal distribution.
feature3 = np.random.randn(n_observations)

Каждую из этих функций можно преобразовать в совместимый с tf.train.Example тип с помощью одного из _bytes_feature , _float_feature , _int64_feature . Затем вы можете создать сообщение tf.train.Example из этих закодированных функций:

def serialize_example(feature0, feature1, feature2, feature3):
  """
  Creates a tf.train.Example message ready to be written to a file.
  """
  # Create a dictionary mapping the feature name to the tf.train.Example-compatible
  # data type.
  feature = {
      'feature0': _int64_feature(feature0),
      'feature1': _int64_feature(feature1),
      'feature2': _bytes_feature(feature2),
      'feature3': _float_feature(feature3),
  }

  # Create a Features message using tf.train.Example.

  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))
  return example_proto.SerializeToString()

Например, предположим, что у вас есть одно наблюдение из набора данных, [False, 4, bytes('goat'), 0.9876] . Вы можете создать и распечатать сообщение tf.train.Example для этого наблюдения с помощью create_message() . Каждое отдельное наблюдение будет записано в виде сообщения Features как указано выше. Обратите внимание, что сообщение tf.train.Example - это просто оболочка вокруг сообщения Features :

# This is an example observation from the dataset.

example_observation = []

serialized_example = serialize_example(False, 4, b'goat', 0.9876)
serialized_example
b'\nR\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04[\xd3|?\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat'

Для декодирования сообщения используйте метод tf.train.Example.FromString .

example_proto = tf.train.Example.FromString(serialized_example)
example_proto
features {
  feature {
    key: "feature0"
    value {
      int64_list {
        value: 0
      }
    }
  }
  feature {
    key: "feature1"
    value {
      int64_list {
        value: 4
      }
    }
  }
  feature {
    key: "feature2"
    value {
      bytes_list {
        value: "goat"
      }
    }
  }
  feature {
    key: "feature3"
    value {
      float_list {
        value: 0.9876000285148621
      }
    }
  }
}

Подробности формата TFRecords

Файл TFRecord содержит последовательность записей. Файл можно читать только последовательно.

Каждая запись содержит строку байтов для полезной нагрузки данных, а также длину данных и хэши CRC-32C ( 32-битный CRC с использованием полинома Кастаньоли ) для проверки целостности.

Каждая запись хранится в следующих форматах:

uint64 length
uint32 masked_crc32_of_length
byte   data[length]
uint32 masked_crc32_of_data

Записи объединяются вместе для создания файла. CRC описаны здесь , а маска CRC:

masked_crc = ((crc >> 15) | (crc << 17)) + 0xa282ead8ul

Файлы tf.data с использованием tf.data

Модуль tf.data также предоставляет инструменты для чтения и записи данных в TensorFlow.

Запись файла TFRecord

Самый простой способ получить данные в наборе данных - использовать метод from_tensor_slices .

Применительно к массиву он возвращает набор скаляров:

tf.data.Dataset.from_tensor_slices(feature1)
<TensorSliceDataset shapes: (), types: tf.int64>

Применительно к кортежу массивов он возвращает набор кортежей:

features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))
features_dataset
<TensorSliceDataset shapes: ((), (), (), ()), types: (tf.bool, tf.int64, tf.string, tf.float64)>
# Use `take(1)` to only pull one example from the dataset.
for f0,f1,f2,f3 in features_dataset.take(1):
  print(f0)
  print(f1)
  print(f2)
  print(f3)
tf.Tensor(False, shape=(), dtype=bool)
tf.Tensor(3, shape=(), dtype=int64)
tf.Tensor(b'horse', shape=(), dtype=string)
tf.Tensor(0.3707167206984876, shape=(), dtype=float64)

Используйте метод tf.data.Dataset.map чтобы применить функцию к каждому элементу Dataset .

Отображаемая функция должна работать в графическом режиме tf.Tensors - она ​​должна работать с tf.Tensors и возвращать tf.Tensors . tf.py_function функцию, такую ​​как serialize_example , можно обернуть с помощью tf.py_function чтобы сделать ее совместимой.

Использование tf.py_function требует указания информации о форме и типе, которая в противном случае недоступна:

def tf_serialize_example(f0,f1,f2,f3):
  tf_string = tf.py_function(
    serialize_example,
    (f0, f1, f2, f3),  # Pass these args to the above function.
    tf.string)      # The return type is `tf.string`.
  return tf.reshape(tf_string, ()) # The result is a scalar.
tf_serialize_example(f0, f1, f2, f3)
<tf.Tensor: shape=(), dtype=string, numpy=b'\nS\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x95\xce\xbd>\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x15\n\x08feature2\x12\t\n\x07\n\x05horse\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x03'>

Примените эту функцию к каждому элементу в наборе данных:

serialized_features_dataset = features_dataset.map(tf_serialize_example)
serialized_features_dataset
<MapDataset shapes: (), types: tf.string>
def generator():
  for features in features_dataset:
    yield serialize_example(*features)
serialized_features_dataset = tf.data.Dataset.from_generator(
    generator, output_types=tf.string, output_shapes=())
serialized_features_dataset
<FlatMapDataset shapes: (), types: tf.string>

И записываем их в файл TFRecord:

filename = 'test.tfrecord'
writer = tf.data.experimental.TFRecordWriter(filename)
writer.write(serialized_features_dataset)

Чтение файла TFRecord

Вы также можете прочитать файл TFRecord с tf.data.TFRecordDataset класса tf.data.TFRecordDataset .

Дополнительную информацию об использовании файлов TFRecord с помощью tf.data можно найти в руководстве по входным конвейерам tf.data: Build TensorFlow .

Использование TFRecordDataset может быть полезно для стандартизации входных данных и оптимизации производительности.

filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
<TFRecordDatasetV2 shapes: (), types: tf.string>

На данный момент набор данных содержит сериализованные сообщения tf.train.Example . При повторении он возвращает их как тензоры скалярных строк.

Используйте метод .take чтобы показать только первые 10 записей.

for raw_record in raw_dataset.take(10):
  print(repr(raw_record))
<tf.Tensor: shape=(), dtype=string, numpy=b'\nS\n\x15\n\x08feature2\x12\t\n\x07\n\x05horse\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x03\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x95\xce\xbd>\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nS\n\x15\n\x08feature2\x12\t\n\x07\n\x05horse\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x03\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04iX\x9a\xbe\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nS\n\x15\n\x08feature2\x12\t\n\x07\n\x05horse\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\xb6\xa2\xb5\xbf\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x03'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nU\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x17\n\x08feature2\x12\x0b\n\t\n\x07chicken\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x02\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04>!\x84\xbc'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\xa9\xdcE?'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x13\n\x08feature2\x12\x07\n\x05\n\x03cat\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x98\x8bb=\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x00'>
<tf.Tensor: shape=(), dtype=string, numpy=b"\nR\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\xb6\xe2'\xbf\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04">
<tf.Tensor: shape=(), dtype=string, numpy=b'\nS\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x15\n\x08feature2\x12\t\n\x07\n\x05horse\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x9c\xc4I>\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x03'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04p\xd3\xbd\xbc\n\x13\n\x08feature2\x12\x07\n\x05\n\x03cat\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x00'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nU\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04`\x8bp?\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x02\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x17\n\x08feature2\x12\x0b\n\t\n\x07chicken'>

Эти тензоры можно проанализировать с помощью функции ниже. Обратите внимание, что feature_description здесь необходим, потому чтоtf.data.Dataset используют выполнение графа и нуждаются в этом описании для построения своей формы и сигнатуры типа:

# Create a description of the features.
feature_description = {
    'feature0': tf.io.FixedLenFeature([], tf.int64, default_value=0),
    'feature1': tf.io.FixedLenFeature([], tf.int64, default_value=0),
    'feature2': tf.io.FixedLenFeature([], tf.string, default_value=''),
    'feature3': tf.io.FixedLenFeature([], tf.float32, default_value=0.0),
}

def _parse_function(example_proto):
  # Parse the input `tf.train.Example` proto using the dictionary above.
  return tf.io.parse_single_example(example_proto, feature_description)

В качестве альтернативы используйте tf.parse example для одновременного анализа всего пакета. Примените эту функцию к каждому элементу в наборе данных с tf.data.Dataset.map метода tf.data.Dataset.map :

parsed_dataset = raw_dataset.map(_parse_function)
parsed_dataset
<MapDataset shapes: {feature0: (), feature1: (), feature2: (), feature3: ()}, types: {feature0: tf.int64, feature1: tf.int64, feature2: tf.string, feature3: tf.float32}>

Используйте активное выполнение для отображения наблюдений в наборе данных. В этом наборе данных 10 000 наблюдений, но вы отобразите только первые 10. Данные отображаются в виде словаря функций. Каждый элемент является tf.Tensor , а элемент numpy этого тензора отображает значение tf.Tensor :

for parsed_record in parsed_dataset.take(10):
  print(repr(parsed_record))
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=3>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'horse'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.37071672>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=3>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'horse'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=-0.30145577>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=3>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'horse'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=-1.419028>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=2>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'chicken'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=-0.016129132>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=4>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'goat'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.77289826>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'cat'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.05530891>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=4>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'goat'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=-0.6558031>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=3>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'horse'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.19703907>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'cat'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=-0.02317211>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=0>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=2>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'chicken'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.9396267>}

Здесь функция tf.parse_example распаковывает поля tf.train.Example в стандартные тензоры.

Файлы TFRecord в Python

Модуль tf.io также содержит функции чистого Python для чтения и записи файлов TFRecord.

Запись файла TFRecord

Затем запишите 10 000 наблюдений в файл test.tfrecord . Каждое наблюдение преобразуется в сообщение tf.train.Example , а затем записывается в файл. Затем вы можете убедиться, что файл test.tfrecord был создан:

# Write the `tf.train.Example` observations to the file.
with tf.io.TFRecordWriter(filename) as writer:
  for i in range(n_observations):
    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])
    writer.write(example)
du -sh {filename}
984K    test.tfrecord

Чтение файла TFRecord

Эти сериализованные тензоры можно легко проанализировать с помощью tf.train.Example.ParseFromString :

filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
<TFRecordDatasetV2 shapes: (), types: tf.string>
for raw_record in raw_dataset.take(1):
  example = tf.train.Example()
  example.ParseFromString(raw_record.numpy())
  print(example)
features {
  feature {
    key: "feature0"
    value {
      int64_list {
        value: 0
      }
    }
  }
  feature {
    key: "feature1"
    value {
      int64_list {
        value: 3
      }
    }
  }
  feature {
    key: "feature2"
    value {
      bytes_list {
        value: "horse"
      }
    }
  }
  feature {
    key: "feature3"
    value {
      float_list {
        value: 0.37071672081947327
      }
    }
  }
}

Пошаговое руководство: чтение и запись данных изображения

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

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

Сначала давайте загрузим это изображение кота в снегу и эту фотографию строящегося моста Вильямсбург в Нью-Йорке.

Получить изображения

cat_in_snow  = tf.keras.utils.get_file(
    '320px-Felis_catus-cat_on_snow.jpg',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/320px-Felis_catus-cat_on_snow.jpg')

williamsburg_bridge = tf.keras.utils.get_file(
    '194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/320px-Felis_catus-cat_on_snow.jpg
24576/17858 [=========================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg
16384/15477 [===============================] - 0s 0us/step
display.display(display.Image(filename=cat_in_snow))
display.display(display.HTML('Image cc-by: <a "href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg">Von.grzanka</a>'))

jpeg

display.display(display.Image(filename=williamsburg_bridge))
display.display(display.HTML('<a "href=https://commons.wikimedia.org/wiki/File:New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg">From Wikimedia</a>'))

jpeg

Запишите файл TFRecord

Как и раньше, кодируйте функции как типы, совместимые с tf.train.Example . Здесь хранится функция строки необработанного изображения, а также высота, ширина, глубина и произвольная label . Последний используется, когда вы пишете файл, чтобы различать изображение кошки и изображение моста. Используйте 0 для изображения кошки и 1 для изображения моста:

image_labels = {
    cat_in_snow : 0,
    williamsburg_bridge : 1,
}
# This is an example, just using the cat image.
image_string = open(cat_in_snow, 'rb').read()

label = image_labels[cat_in_snow]

# Create a dictionary with features that may be relevant.
def image_example(image_string, label):
  image_shape = tf.io.decode_jpeg(image_string).shape

  feature = {
      'height': _int64_feature(image_shape[0]),
      'width': _int64_feature(image_shape[1]),
      'depth': _int64_feature(image_shape[2]),
      'label': _int64_feature(label),
      'image_raw': _bytes_feature(image_string),
  }

  return tf.train.Example(features=tf.train.Features(feature=feature))

for line in str(image_example(image_string, label)).split('\n')[:15]:
  print(line)
print('...')
features {
  feature {
    key: "depth"
    value {
      int64_list {
        value: 3
      }
    }
  }
  feature {
    key: "height"
    value {
      int64_list {
        value: 213
      }
...

Обратите внимание, что все функции теперь хранятся в сообщении tf.train.Example . Затем функционализируйте приведенный выше код и запишите примеры сообщений в файл с именем images.tfrecords :

# Write the raw image files to `images.tfrecords`.
# First, process the two images into `tf.train.Example` messages.
# Then, write to a `.tfrecords` file.
record_file = 'images.tfrecords'
with tf.io.TFRecordWriter(record_file) as writer:
  for filename, label in image_labels.items():
    image_string = open(filename, 'rb').read()
    tf_example = image_example(image_string, label)
    writer.write(tf_example.SerializeToString())
du -sh {record_file}
36K images.tfrecords

Прочтите файл TFRecord

Теперь у вас есть файл images.tfrecords и теперь вы можете перебирать записи в нем, чтобы прочитать то, что вы написали. Учитывая, что в этом примере вы будете воспроизводить только изображение, единственная функция, которая вам понадобится, - это строка необработанного изображения. Извлеките его с помощью описанных выше геттеров, а именно example.features.feature['image_raw'].bytes_list.value[0] . Вы также можете использовать метки, чтобы определить, какая запись является котом, а какая мостом:

raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')

# Create a dictionary describing the features.
image_feature_description = {
    'height': tf.io.FixedLenFeature([], tf.int64),
    'width': tf.io.FixedLenFeature([], tf.int64),
    'depth': tf.io.FixedLenFeature([], tf.int64),
    'label': tf.io.FixedLenFeature([], tf.int64),
    'image_raw': tf.io.FixedLenFeature([], tf.string),
}

def _parse_image_function(example_proto):
  # Parse the input tf.train.Example proto using the dictionary above.
  return tf.io.parse_single_example(example_proto, image_feature_description)

parsed_image_dataset = raw_image_dataset.map(_parse_image_function)
parsed_image_dataset
<MapDataset shapes: {depth: (), height: (), image_raw: (), label: (), width: ()}, types: {depth: tf.int64, height: tf.int64, image_raw: tf.string, label: tf.int64, width: tf.int64}>

Восстановите образы из файла TFRecord:

for image_features in parsed_image_dataset:
  image_raw = image_features['image_raw'].numpy()
  display.display(display.Image(data=image_raw))

jpeg

jpeg