다음 글을 참조하여 번역합니다(+ 개인 공부), 예제는 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.batch와 Dataset.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 |