TFRecord 및 tf.train.Example

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

TFRecord 형식은 이진 레코드 시퀀스를 저장하기 위한 간단한 형식입니다.

프로토콜 버퍼 는 구조화된 데이터의 효율적인 직렬화를 위한 플랫폼 간, 언어 간 라이브러리입니다.

프로토콜 메시지는 .proto 파일로 정의되며, 이는 종종 메시지 유형을 이해하는 가장 쉬운 방법입니다.

tf.train.Example 메시지(또는 protobuf)는 {"string": value} 매핑을 나타내는 유연한 메시지 유형입니다. TensorFlow와 함께 사용하도록 설계되었으며 TFX 와 같은 상위 수준 API 전체에서 사용됩니다.

이 노트북은 tf.train.Example 메시지를 생성, 구문 분석 및 사용하고 .tfrecord 파일에서 tf.train.Example 메시지를 직렬화, 작성 및 읽는 방법을 보여줍니다.

설정

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 메시지 유형은 다음 세 가지 유형 중 하나를 허용할 수 있습니다( .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

표준 TensorFlow 유형을 tf.train.Example 호환 tf.train.Feature 로 변환하기 위해 아래 단축키 기능을 사용할 수 있습니다. 각 함수는 스칼라 입력 값을 취하고 위의 세 가지 list 유형 중 하나를 포함하는 tf.train.Feature 를 반환합니다.

# 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]))

다음은 이러한 기능이 작동하는 방식에 대한 몇 가지 예입니다. 다양한 입력 유형과 표준화된 출력 유형에 유의하십시오. 함수의 입력 유형이 위에서 언급한 강제 변환 가능한 유형 중 하나와 일치하지 않으면 함수는 예외를 발생시킵니다(예: 1.0 이 부동 소수점이기 때문에 _int64_feature(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
}

모든 proto 메시지는 .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. 각 관찰 내에서 각 값은 위의 함수 중 하나를 사용하여 3가지 호환 가능한 유형 중 하나를 포함하는 tf.train.Feature 로 변환되어야 합니다.

  2. 기능 이름 문자열에서 #1에서 생성된 인코딩된 기능 값에 대한 맵(사전)을 만듭니다.

  3. 2단계에서 생성된 지도는 Features 메시지 로 변환됩니다.

이 노트북에서는 NumPy를 사용하여 데이터 세트를 생성합니다.

이 데이터세트에는 4가지 기능이 있습니다.

  • 부울 기능, 동일한 확률의 False 또는 True
  • [0, 5] 에서 균일하게 무작위로 선택된 정수 특징
  • 정수 특성을 인덱스로 사용하여 문자열 테이블에서 생성된 문자열 특성
  • 표준 정규 분포의 부동 소수점 특성

위의 각 분포에서 독립적이고 동일하게 분포된 10,000개의 관측값으로 구성된 표본을 고려합니다.

# 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)

이러한 각 기능은 _bytes_feature , _float_feature , _int64_feature 중 하나를 사용하여 tf.train.Example 호환 유형으로 강제 변환될 수 있습니다. 그런 다음 다음 인코딩된 기능에서 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] 에서 단일 관측치가 있다고 가정합니다. create_message() 를 사용하여 이 관찰에 대한 tf.train.Example 메시지를 만들고 인쇄할 수 있습니다. 각 단일 관찰은 위와 같이 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\x08feature2\x12\x08\n\x06\n\x04goat\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\x08feature3\x12\x08\x12\x06\n\x04[\xd3|?'

메시지를 디코딩하려면 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( Castagnoli 다항식 을 사용하는 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 element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>

배열의 튜플에 적용하면 튜플의 데이터 세트를 반환합니다.

features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))
features_dataset
<TensorSliceDataset element_spec=(TensorSpec(shape=(), dtype=tf.bool, name=None), TensorSpec(shape=(), dtype=tf.int64, name=None), TensorSpec(shape=(), dtype=tf.string, name=None), TensorSpec(shape=(), dtype=tf.float64, name=None))>
# 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(4, shape=(), dtype=int64)
tf.Tensor(b'goat', shape=(), dtype=string)
tf.Tensor(0.5251196235602504, shape=(), dtype=float64)

tf.data.Dataset.map 메서드를 사용하여 Dataset 의 각 요소에 함수를 적용합니다.

매핑된 함수는 TensorFlow 그래프 모드에서 작동해야 하며 tf.Tensors 에서 작동하고 반환해야 합니다. 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'\nR\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04=n\x06?\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04'>

이 함수를 데이터세트의 각 요소에 적용합니다.

serialized_features_dataset = features_dataset.map(tf_serialize_example)
serialized_features_dataset
<MapDataset element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>
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 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>

그리고 그것들을 TFRecord 파일에 씁니다:

filename = 'test.tfrecord'
writer = tf.data.experimental.TFRecordWriter(filename)
writer.write(serialized_features_dataset)
WARNING:tensorflow:From /tmp/ipykernel_25215/3575438268.py:2: TFRecordWriter.__init__ (from tensorflow.python.data.experimental.ops.writers) is deprecated and will be removed in a future version.
Instructions for updating:
To write TFRecords to disk, use `tf.io.TFRecordWriter`. To save and load the contents of a dataset, use `tf.data.experimental.save` and `tf.data.experimental.load`

TFRecord 파일 읽기

tf.data.TFRecordDataset 클래스를 사용하여 TFRecord 파일을 읽을 수도 있습니다.

tf.data를 사용한 tf.data 파일 사용에 대한 자세한 내용은 tf.data: Build TensorFlow 입력 파이프라인 가이드에서 찾을 수 있습니다.

TFRecordDataset 을 사용하면 입력 데이터를 표준화하고 성능을 최적화하는 데 유용할 수 있습니다.

filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
<TFRecordDatasetV2 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>

이 시점에서 데이터 세트에는 직렬화된 tf.train.Example 메시지가 포함됩니다. 반복될 때 이를 스칼라 문자열 텐서로 반환합니다.

.take 메서드를 사용하여 처음 10개의 레코드만 표시합니다.

for raw_record in raw_dataset.take(10):
  print(repr(raw_record))
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x00\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04=n\x06?'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x9d\xfa\x98\xbe\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x13\n\x08feature2\x12\x07\n\x05\n\x03dog\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x01\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04a\xc0r?\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x00\n\x13\n\x08feature2\x12\x07\n\x05\n\x03cat\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x92Q(?'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04>\xc0\xe5>\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'\nU\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04I!\xde\xbe\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'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x00\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\xe0\x1a\xab\xbf\n\x13\n\x08feature2\x12\x07\n\x05\n\x03cat'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nQ\n\x13\n\x08feature2\x12\x07\n\x05\n\x03cat\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x87\xb2\xd7?\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x00'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x11\n\x08feature1\x12\x05\x1a\x03\n\x01\x04\n\x11\n\x08feature0\x12\x05\x1a\x03\n\x01\x01\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04n\xe19>\n\x14\n\x08feature2\x12\x08\n\x06\n\x04goat'>
<tf.Tensor: shape=(), dtype=string, numpy=b'\nR\n\x14\n\x08feature3\x12\x08\x12\x06\n\x04\x1as\xd9\xbf\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\x08feature2\x12\x08\n\x06\n\x04goat'>

이 텐서는 아래 함수를 사용하여 구문 분석할 수 있습니다. tf.data.Dataset 은 그래프 실행을 사용하기 때문에 feature_description 이 필요하며 모양 및 유형 서명을 빌드하려면 이 설명이 필요합니다.

# 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 메서드를 사용하여 데이터세트의 각 항목에 이 함수를 적용합니다.

parsed_dataset = raw_dataset.map(_parse_function)
parsed_dataset
<MapDataset element_spec={'feature0': TensorSpec(shape=(), dtype=tf.int64, name=None), 'feature1': TensorSpec(shape=(), dtype=tf.int64, name=None), 'feature2': TensorSpec(shape=(), dtype=tf.string, name=None), 'feature3': TensorSpec(shape=(), dtype=tf.float32, name=None)}>

즉시 실행을 사용하여 데이터 세트의 관찰을 표시합니다. 이 데이터 세트에는 10,000개의 관측치가 있지만 처음 10개만 표시합니다. 데이터는 기능 사전으로 표시됩니다. 각 항목은 tf.Tensor 이고 이 텐서의 numpy 요소는 기능의 값을 표시합니다.

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=4>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'goat'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.5251196>}
{'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.29878703>}
{'feature0': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature1': <tf.Tensor: shape=(), dtype=int64, numpy=1>, 'feature2': <tf.Tensor: shape=(), dtype=string, numpy=b'dog'>, 'feature3': <tf.Tensor: shape=(), dtype=float32, numpy=0.94824797>}
{'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.65749466>}
{'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.44873232>}
{'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.4338477>}
{'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=-1.3367577>}
{'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=1.6851357>}
{'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.18152401>}
{'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=-1.6988251>}

여기에서 tf.parse_example 함수는 tf.train.Example 필드를 표준 텐서로 압축을 풉니다.

Python의 TFRecord 파일

tf.io 모듈에는 TFRecord 파일을 읽고 쓰기 위한 순수 Python 함수도 포함되어 있습니다.

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 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>
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: 4
      }
    }
  }
  feature {
    key: "feature2"
    value {
      bytes_list {
        value: "goat"
      }
    }
  }
  feature {
    key: "feature3"
    value {
      float_list {
        value: 0.5251196026802063
      }
    }
  }
}

그것은 있는 그대로 사용하기 tf.train.Example proto를 반환하지만 근본적으로 다음을 나타냅니다.

Dict[str,
     Union[List[float],
           List[int],
           List[str]]]

다음 코드는 TensorFlow Ops를 사용하지 않고 Example 를 NumPy 배열의 사전으로 수동으로 변환합니다. 자세한 내용 은 PROTO 파일 을 참조하십시오.

result = {}
# example.features.feature is the dictionary
for key, feature in example.features.feature.items():
  # The values are the Feature objects which contain a `kind` which contains:
  # one of three fields: bytes_list, float_list, int64_list

  kind = feature.WhichOneof('kind')
  result[key] = np.array(getattr(feature, kind).value)

result
{'feature3': array([0.5251196]),
 'feature1': array([4]),
 'feature0': array([0]),
 'feature2': array([b'goat'], dtype='|S4')}

연습: 이미지 데이터 읽기 및 쓰기

이것은 TFRecords를 사용하여 이미지 데이터를 읽고 쓰는 방법에 대한 종단 간 예제입니다. 이미지를 입력 데이터로 사용하여 데이터를 TFRecord 파일로 작성한 다음 파일을 다시 읽고 이미지를 표시합니다.

이는 예를 들어 동일한 입력 데이터 세트에서 여러 모델을 사용하려는 경우에 유용할 수 있습니다. 이미지 데이터를 원시로 저장하는 대신 TFRecords 형식으로 사전 처리할 수 있으며 모든 추가 처리 및 모델링에 사용할 수 있습니다.

먼저 눈 속의 고양이 이미지 와 건설 중인 NYC의 Williamsburg Bridge 사진 을 다운로드해 보겠습니다.

이미지 가져오기

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
32768/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
24576/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 )이 생겼고 이제 그 안의 레코드를 반복하여 작성한 내용을 다시 읽을 수 있습니다. 이 예에서는 이미지만 재생산한다는 점을 감안할 때 필요한 기능은 원시 이미지 문자열뿐입니다. 위에서 설명한 getter, 즉 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 element_spec={'depth': TensorSpec(shape=(), dtype=tf.int64, name=None), 'height': TensorSpec(shape=(), dtype=tf.int64, name=None), 'image_raw': TensorSpec(shape=(), dtype=tf.string, name=None), 'label': TensorSpec(shape=(), dtype=tf.int64, name=None), 'width': TensorSpec(shape=(), dtype=tf.int64, name=None)}>

TFRecord 파일에서 이미지 복구:

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

JPEG

JPEG