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

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

 

Batching dataset elements

Simple batching

가장 간단한 형태의 배치는 단일 원소를 n개만큼 쌓는 것입니다. Dataset.batch() 변환은 정확히 이 작업을 수행하는데, tf.stack() 연산자와 거의 동일하게 작동합니다. 예를 들면, 각 구성 요소가 가지는 모든 원소는 전부 동일한 shape을 가져야 합니다.

inc_dataset = tf.data.Dataset.range(100)
dec_dataset = tf.data.Dataset.range(0, -100, -1)
dataset = tf.data.Dataset.zip((inc_dataset, dec_dataset))
batched_dataset = dataset.batch(4)

for batch in batched_dataset.take(4):
  print([arr.numpy() for arr in batch])
  • [array([0, 1, 2, 3]), array([ 0, -1, -2, -3])]
    [array([4, 5, 6, 7]), array([-4, -5, -6, -7])]
    [array([ 8,  9, 10, 11]), array([ -8,  -9, -10, -11])]
    [array([12, 13, 14, 15]), array([-12, -13, -14, -15])]

tf.data가 동일한 shape를 전파하는 동안, Dataset.batch는 가장 마지막 배치의 배치 크기를 알 수 없기 때문에 None shape를 default로 지정합니다. 예를 들어, 배치 크기가 32이고 데이터가 100개라면 마지막 배치 크기는 4입니다.

batched_dataset
  • <BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.int64)>

drop_remainder 인자를 사용하면, 마지막 배치 크기를 무시하고 지정한 배치 크기를 사용할 수 있습니다.

batched_dataset = dataset.batch(7, drop_remainder=True)
batched_dataset
  • <BatchDataset shapes: ((7,), (7,)), types: (tf.int64, tf.int64)>

Batching tensors with padding

위의 예제에서는 전부 같은 shape의 데이터를 사용했습니다. 그러나 많은 모델(e.g. sequence models)에서 요구되는 입력의 크기는 매우 다양할 수 있습니다(sequence data의 length는 일정하지 않습니다). 이러한 경우를 다루기 위해, Dataset.padded_batch 변환은 패딩을 사용하여 다른 크기의 배치를 사용할 수 있게 도와줍니다.

dataset = tf.data.Dataset.range(100)
dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))
dataset = dataset.padded_batch(4, padded_shapes=(None,))

for batch in dataset.take(2):
  print(batch.numpy())
  print()
  • [[0 0 0]
     [1 0 0]
     [2 2 0]
     [3 3 3]]

    [[4 4 4 4 0 0 0]
     [5 5 5 5 5 0 0]
     [6 6 6 6 6 6 0]
     [7 7 7 7 7 7 7]]
  • tf.fill([tf.cast(x, tf.int32)], x)는 임의의 숫자 x를 x개만큼 채워넣는 것을 의미합니다.

Dataset.padded_batch는 각 특성에 따라 다르게 패딩을 설정할 수 있으며, 패딩 설정은 가변 길이 또는 일정한 길이로 할 수 있습니다. 또한, 기본값은 0이지만, 다른 수를 채워넣을 수 있습니다.


Training workflows

Processing multiple epochs

tf.data API는 동일한 데이터에 대해 multiple epochs를 수행할 수 있는 두 가지 주요한 방법을 제공합니다.

multiple epochs에서 데이터셋을 반복하는 가장 단순한 방법은 Dataset.repeat()을 사용하는 것입니다. 먼저, 타이타닉 데이터셋을 불러오도록 하죠.

titanic_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic_lines = tf.data.TextLineDataset(titanic_file)
def plot_batch_sizes(ds):
  batch_sizes = [batch.shape[0] for batch in ds]
  plt.bar(range(len(batch_sizes)), batch_sizes)
  plt.xlabel('Batch number')
  plt.ylabel('Batch size')

아무런 인자를 제공하지 않고, Dataset.repeat()을 사용하면 input을 무한히 반복합니다.

Dataset.repeat은 한 에폭의 끝과 다음 에폭의 시작에 상관없이 인자만큼 반복합니다. 이 때문에 Dataset.repeat 후에 적용된 Dataset.batch는 에폭과 에폭간의 경계를 망각한 채, 데이터를 생성합니다. 이는 이번 예제가 아닌 다음 예제를 보면 이해할 수 있습니다. epoch간의 경계가 없습니다.

titanic_batches = titanic_lines.repeat(3).batch(128)
plot_batch_sizes(titanic_batches)

명확하게 epoch을 구분하기 위해서는 batch 이후에 repeat을 사용합니다.

titanic_batches = titanic_lines.batch(128).repeat(3)

plot_batch_sizes(titanic_batches)

만약 각 에폭의 끝에서 사용자 정의 연산(예를 들면, 통계적 수집)을 사용하고 싶다면, 각 에폭에서 데이터셋 반복을 restart하는 것이 가장 단순합니다.

epochs = 3
dataset = titanic_lines.batch(128)

for epoch in range(epochs):
  for batch in dataset:
    print(batch.shape)
  print("End of epoch: ", epoch)
  • (128,) (128,) (128,) (128,) (116,) End of epoch: 0
    (128,) (128,) (128,) (128,) (116,) End of epoch: 1
    (128,) (128,) (128,) (128,) (116,) End of epoch: 2

Randomly shuffling input data

Dataset.shuffle()은 고정 크기의 버퍼를 유지하면서, 해당 버퍼에서 다음 요소를 무작위로 선택합니다.

결과 확인을 위해 데이터에 인덱스를 추가합니다.

lines = tf.data.TextLineDataset(titanic_file)
counter = tf.data.experimental.Counter()

dataset = tf.data.Dataset.zip((counter, lines))
dataset = dataset.shuffle(buffer_size=100)
dataset = dataset.batch(20)
dataset
  • <BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.string)>

buffer_size가 100이고, batch_size가 20이므로, 첫 번째 배치에서는 120 이상의 인덱스 요소가 존재하지 않습니다. 사용하는 데이터의 인덱스 수가 uniform하게 증가합니다.(아마도 전체 데이터를 사용하기 위해)

n,line_batch = next(iter(dataset))
print(n.numpy())
  • [ 73  71  16  28   6  65  91  12  42  68  54  40  81  46   4  98 105  89
      67  11]

이번에도 Dataset.batchDataset.repeat을 고려해야 합니다.

Dataset.shuffle은 셔플 버퍼가 빌 때까지 에폭의 끝에 대한 정보를 알려주지 않습니다. repeat 전에 shuffle을 사용하면 다음으로 넘어가기 전에 한 에폭의 원소를 전부 확인할 수 있습니다.

dataset = tf.data.Dataset.zip((counter, lines))
shuffled = dataset.shuffle(buffer_size=100).batch(10).repeat(2)

print("Here are the item ID's near the epoch boundary:\n")
for n, line_batch in shuffled.skip(60).take(5):
  print(n.numpy())
  • Here are the item ID's near the epoch boundary:

    [541 569 508 599 578 418 559 595 401 594]
    [282 522 395 552 362 442 389 619 506 523]
    [612 585 482 518 604 617 608 622]
    [85 27 73 57 16 47 43 50 55 64]
    [ 90  89  24  59   9 101  97  65  14  99]
shuffle_repeat = [n.numpy().mean() for n, line_batch in shuffled]
plt.plot(shuffle_repeat, label="shuffle().repeat()")
plt.ylabel("Mean item ID")
plt.legend()

shuffle 전에 repeat을 사용하면 epoch의 경계가 무너집니다.

dataset = tf.data.Dataset.zip((counter, lines))
shuffled = dataset.repeat(2).shuffle(buffer_size=100).batch(10)

print("Here are the item ID's near the epoch boundary:\n")
for n, line_batch in shuffled.skip(55).take(15):
  print(n.numpy())
repeat_shuffle = [n.numpy().mean() for n, line_batch in shuffled]

plt.plot(shuffle_repeat, label="shuffle().repeat()")
plt.plot(repeat_shuffle, label="repeat().shuffle()")
plt.ylabel("Mean item ID")
plt.legend()

 

'# Machine Learning > TensorFlow doc 정리' 카테고리의 다른 글

tf.data tutorial 번역 (5)  (0) 2020.02.28
tf.data tutorial 번역 (4)  (1) 2020.02.27
tf.data tutorial 번역 (2)  (0) 2020.02.18
tf.data tutorial 번역 (1)  (0) 2020.02.14
tensorflow 2.0 keras Write custom callbacks  (2) 2019.04.18