다음 글을 참조하여 번역합니다(+ 개인 공부), 예제는 tf 2.0을 기준으로 합니다.

https://www.tensorflow.org/guide/data?hl=en

 

tf.data: Build TensorFlow input pipelines  |  TensorFlow Core

The tf.data API enables you to build complex input pipelines from simple, reusable pieces. For example, the pipeline for an image model might aggregate data from files in a distributed file system, apply random perturbations to each image, and merge random

www.tensorflow.org


tf.data API는 복잡한 input pipeline을 재사용성, 단순하게 만들어 사용할 수 있게 합니다. 예를 들어, 이미지 모델을 위한 파이프라인은 분산 파일 시스템에서 데이터를 통합하고, 각 이미지에 랜덤 변화를 주고, 학습에서 랜덤하게 선택한 이미지를 병합하여 사용할 수 있습니다. 텍스트 모델을 위한 파이프라인은 원본 텍스트 데이터에서 심볼을 추출하고, 룩업 테이블의 임베딩 식별자로 변환하여, 길이가 다른 시퀀스를 일괄 처리할 수 있습니다. tf.data API는 대용량의 데이터를 다룰 수 있게 도와주고, 서로 다른 데이터 포맷을 읽을 수 있으며, 복잡한 변환 작업을 수행합니다.

tf.data API는 일련의 요소를 나타낼 수 있는 tf.data.Dataset abstraction을 소개합니다. 예를 들어, 이미지 파이프라인에서 요소는 이미지와 레이블을 나타내는 텐서의 요소 쌍인 단일 학습 예시를 나타낼 수 있습니다.

dataset을 생성하는 두 가지 방법이 있습니다.

  • data source는 메모리 또는 하나 이상의 파일에 저장된 데이터로 구성합니다.
  • 데이터 변환은 하나 이상의 tf.data.Dataset 객체에서 데이터 세트를 구성합니다.

Basic mechanics

input pipeline을 만들기 위해서는 data source를 필수적으로 사용해야 합니다. 예를 들어, 메모리에 존재하는 데이터로 Dataset을 구성하는 경우, tf.data.Dataset.from_tensors()tf.data.Dataset.from_tensor_slices()를 사용합니다. 만약 TFRecord 포맷을 사용하고 있다면, tf.data.TFRecordDataset()을 사용합니다.

Dataset 객체를 가지고 있으면, tf.data.Dataset 객체의 메서드를 호출하여 새로운 Dataset을 만들 수 있습니다. 예를 들어, 원소당 변환을 수행하는 Dataset.map()이나 다중 원소 변환을 수행하는 Dataset.batch() 적용할 수 있습니다. 전체 변환 목록은 tf.data.Dataset 문서를 참조하세요.

Dataset 객체는 Python iterable합니다. for-loop를 통해 해당 요소를 사용할 수 있습니다.

dataset = tf.data.Dataset.from_tensor_slices([8, 3, 0, 8, 2, 1])
dataset
  • <TensorSliceDataset shapes: (), types: tf.int32>
for elem in dataset:
  print(elem.numpy())
  • 8 3 0 8 2 1

iter을 통해 명시적으로 python iterator를 생성하고, next를 통해 사용할 수 있습니다.

it = iter(dataset)

print(next(it).numpy())
  • 8

같은 방법으로 데이터셋의 원소를 모든 요소에 대해 단일 결과를 생성하는 reduce 변환을 통해 사용할 수 있습니다. 다음 예제는 데이터셋에 존재하는 숫자의 합을 계산할 때 reduce 변환을 어떻게 활용하는지 보여줍니다.

print(dataset.reduce(0, lambda state, value: state + value).numpy())
  • 22

Dataset structure

dataset는 동일한 구조의 요소를 포함하며 구조의 개별 요소는 tf.TypeSpec으로 나타낼 수 있는 Tensor, SparseTensor, RaggedTensor, TensorArray, Dataset의 구조를 가질 수 있습니다.

Dataset.element_spec 속성을 사용하면 개별 요소의 유형을 확인할 수 있습니다. 단일 요소, 튜플 요소, 중첩 튜플 요소를 가지는  tf.TypeSpec 객체를 반환합니다. 다음과 같습니다.

dataset1 = tf.data.Dataset.from_tensor_slices(tf.random.uniform([4, 10]))

dataset1.element_spec
  • TensorSpec(shape=(10,), dtype=tf.float32, name=None)
dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random.uniform([4]),
    tf.random.uniform([4, 100], maxval=100, dtype=tf.int32)))

dataset2.element_spec
  • (TensorSpec(shape=(), dtype=tf.float32, name=None), TensorSpec(shape=(100,), dtype=tf.int32, name=None))
dataset3 = tf.data.Dataset.zip((dataset1, dataset2))

dataset3.element_spec
  • (TensorSpec(shape=(10,), dtype=tf.float32, name=None), (TensorSpec(shape=(), dtype=tf.float32, name=None), TensorSpec(shape=(100,), dtype=tf.int32, name=None)))
# Dataset containing a sparse tensor.
dataset4 = tf.data.Dataset.from_tensors(tf.SparseTensor(indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))

dataset4.element_spec
  • SparseTensorSpec(TensorShape([3, 4]), tf.int32)

여기서 tf.SparseTensor(indices=[[0, 0], [1, 2]], values =[1, 2], dense_shape=[3, 4])는 다음과 같은 결과를 보여줍니다.

  • [[1, 0, 0, 0]
     
    [0, 0, 2, 0]
     
    [0, 0, 0, 0]]

indices는 [0, 0], [1, 2]에 각각 non-zero value가 있음을 명시합니다. 실제로 위의 결과에서 [0, 0]에는 value의 첫 번째 값인 1, [1, 2]에는 두 번째 값인 2가 출력되고 있습니다. dense_shape = [3, 4]는 (3, 4)의 2차원 텐서를 나타냅니다.

 

# Use value_type to see the type of value represented by the element spec
dataset4.element_spec.value_type
  • tensorflow.python.framework.sparse_tensor.SparseTensor

Dataset 변환은 어떠한 구조의 dataset도 지원할 수 있습니다.각 원소에 함수를 적용하는  Dataset.map()과 Dataset.filter() 변환을 사용할 때, 요소 구조는 함수의 인수를 결정합니다.

dataset1 = tf.data.Dataset.from_tensor_slices(
    tf.random.uniform([4, 10], minval=1, maxval=10, dtype=tf.int32))

dataset1
  • <TensorSliceDataset shapes: (10,), types: tf.int32>
for z in dataset1:
  print(z.numpy())
  • [8 9 2 7 2 4 8 8 1 7]
    [6 8 6 5 6 5 1 7 3 7]
    [8 6 9 8 7 9 4 7 4 7]
    [4 1 2 5 9 9 1 9 6 8]
dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random.uniform([4]),
    tf.random.uniform([4, 100], maxval=100, dtype=tf.int32)))

dataset2
  • <TensorSliceDataset shapes: ((), (100,)), types: (tf.float32, tf.int32)>
dataset3 = tf.data.Dataset.zip((dataset1, dataset2))

dataset3
  • <ZipDataset shapes: ((10,), ((), (100,))), types: (tf.int32, (tf.float32, tf.int32))>
for a, (b,c) in dataset3:
  print('shapes: {a.shape}, {b.shape}, {c.shape}'.format(a=a, b=b, c=c))
  • shapes: (10,), (), (100,)
    shapes: (10,), (), (100,)
    shapes: (10,), (), (100,)
    shapes: (10,), (), (100,)