tf.data : TensorFlow 입력 파이프 라인 빌드

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

tf.data API를 사용하면 간단하고 재사용 가능한 부분에서 복잡한 입력 파이프 라인을 구축 할 수 있습니다. 예를 들어 이미지 모델의 파이프 라인은 분산 파일 시스템의 파일에서 데이터를 집계하고, 각 이미지에 무작위 섭동을 적용하고, 무작위로 선택한 이미지를 학습용 배치로 병합 할 수 있습니다. 텍스트 모델의 파이프 라인에는 원시 텍스트 데이터에서 기호를 추출하고, 조회 테이블이있는 포함 식별자로 변환하고, 서로 다른 길이의 시퀀스를 일괄 처리하는 작업이 포함될 수 있습니다. tf.data API를 사용하면 많은 양의 데이터를 처리하고 다양한 데이터 형식에서 읽고 복잡한 변환을 수행 할 수 있습니다.

tf.data API는 각 요소가 하나 이상의 구성 요소로 구성된 일련의 요소를 나타내는tf.data.Dataset 추상화를 도입합니다. 예를 들어 이미지 파이프 라인에서 요소는 이미지와 해당 레이블을 나타내는 텐서 구성 요소 쌍이있는 단일 학습 예제 일 수 있습니다.

데이터 세트를 만드는 방법에는 두 가지가 있습니다.

  • 데이터 소스 는 메모리 또는 하나 이상의 파일에 저장된 Dataset 에서 데이터 Dataset 를 구성합니다.

  • 데이터 변환 은 하나 이상의tf.data.Dataset 개체에서 데이터 집합을 구성합니다.

import tensorflow as tf
import pathlib
import os
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

np.set_printoptions(precision=4)

기본 역학

입력 파이프 라인을 생성하려면 데이터 소스로 시작해야합니다. 예를 들어 메모리의 Dataset 에서 데이터 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 반복 가능입니다. 이렇게하면 for 루프를 사용하여 요소를 사용할 수 있습니다.

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 반복기를 명시 적으로 생성하고 next 사용하여 해당 요소를 사용합니다.

it = iter(dataset)

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

또는 데이터 세트 요소는 reduce 변환을 사용하여 소비 될 수 있으며, 이는 모든 요소를 ​​줄여 단일 결과를 생성합니다. 다음 예제는 reduce 변환을 사용하여 정수 데이터 세트의 합계를 계산하는 방법을 보여줍니다.

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

데이터 세트 구조

데이터 세트는 각각의 요소는 동일한 구성 요소 (중첩) 구조 요소의 시퀀스를 생성한다. 구조의 개별 성분에 의한 형 표현할 될 수 tf.TypeSpec 포함 tf.Tensor , tf.sparse.SparseTensor ,tf.RaggedTensor , tf.TensorArray 또는tf.data.Dataset .

요소의 (중첩 된) 구조를 표현하는 데 사용할 수있는 Python 구조에는 tuple , dict , NamedTupleOrderedDict 있습니다. 특히 list 는 데이터 셋 요소의 구조를 표현하는 데 유효한 구조가 아닙니다. 이는 초기 tf.data 사용자가 list 입력 (예 : tf.data.Dataset.from_tensors )이 텐서로 자동 압축되고 list 출력 (예 : 사용자 정의 함수의 반환 값)이 tuple 로 강제 변환되는 것에 대해 강하게 느꼈기 때문입니다. 결과적으로 list 입력을 구조로 처리하려면이를 tuple 로 변환해야하며 list 출력이 단일 구성 요소가되도록하려면 tf.stack 을 사용하여 명시 적으로 tf.stack 해야합니다. .

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)
# 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.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())
[2 7 7 7 5 9 3 3 5 2]
[9 9 7 3 6 2 3 8 7 1]
[2 9 7 1 8 7 8 3 8 2]
[7 5 2 6 9 4 4 1 6 4]
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,)

입력 데이터 읽기

NumPy 배열 사용

더 많은 예제는 NumPy 배열로드를 참조하십시오.

모든 입력 데이터가 메모리에 맞으면 Dataset 를 만드는 가장 간단한 방법은 tf.Tensor 객체로 변환하고 Dataset.from_tensor_slices() 사용하는 Dataset.from_tensor_slices() 입니다.

train, test = tf.keras.datasets.fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
images, labels = train
images = images/255

dataset = tf.data.Dataset.from_tensor_slices((images, labels))
dataset
<TensorSliceDataset shapes: ((28, 28), ()), types: (tf.float64, tf.uint8)>

Python 생성기 사용

tf.data.Dataset 으로 쉽게 수집 할 수있는 또 다른 일반적인 데이터 소스는 Python 생성기입니다.

def count(stop):
  i = 0
  while i<stop:
    yield i
    i += 1
for n in count(5):
  print(n)
0
1
2
3
4

Dataset.from_generator 생성자는 파이썬 생성기를 완전한 기능을tf.data.Dataset 로 변환합니다.

생성자는 반복자가 아닌 콜 러블을 입력으로 사용합니다. 이를 통해 발전기가 끝에 도달하면 다시 시작할 수 있습니다. 선택적 args 인수를 취하며, 이는 콜 러블의 인수로 전달됩니다.

output_types 때문에 인수 필요 tf.data 빌드 tf.Graph 내부적으로, 그래프의 에지는 필요 tf.dtype .

ds_counter = tf.data.Dataset.from_generator(count, args=[25], output_types=tf.int32, output_shapes = (), )
for count_batch in ds_counter.repeat().batch(10).take(10):
  print(count_batch.numpy())
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24  0  1  2  3  4]
[ 5  6  7  8  9 10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24  0  1  2  3  4]
[ 5  6  7  8  9 10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]

output_shapes 인수는 필수는 아니지만 많은 tensorflow 작업이 순위를 알 수없는 텐서를 지원하지 않기 때문에 권장됩니다. 특정 축의 길이를 알 수 없거나 가변적이면 output_shapes 에서 None 으로 설정하십시오.

output_shapesoutput_types 는 다른 데이터 세트 메소드와 동일한 중첩 규칙을 따른다는 점에 유의해야합니다.

다음은 두 가지 측면을 모두 보여주는 생성기의 예입니다. 두 번째 배열은 길이를 알 수없는 벡터 인 배열의 튜플을 반환합니다.

def gen_series():
  i = 0
  while True:
    size = np.random.randint(0, 10)
    yield i, np.random.normal(size=(size,))
    i += 1
for i, series in gen_series():
  print(i, ":", str(series))
  if i > 5:
    break
0 : [0.2357]
1 : [-0.4635  0.0882 -0.7401 -1.2436 -0.1392  1.8694 -2.2567  1.5493 -1.0368]
2 : []
3 : []
4 : [1.1482 1.0136]
5 : [ 0.7923 -2.2942  0.4162  1.5056  1.6008  0.1861]
6 : [ 0.7311  0.9217  1.3697 -1.0795  1.0586 -1.0768]

첫 번째 출력은 int32 이고 두 번째 출력은 float32 입니다.

첫 번째 항목은 스칼라 인 shape () 이고 두 번째 항목은 길이를 알 수없는 모양 (None,) 의 벡터입니다.

ds_series = tf.data.Dataset.from_generator(
    gen_series, 
    output_types=(tf.int32, tf.float32), 
    output_shapes=((), (None,)))

ds_series
<FlatMapDataset shapes: ((), (None,)), types: (tf.int32, tf.float32)>

이제 일반tf.data.Dataset 처럼 사용할 수 있습니다. 가변 형태가있는 데이터 세트를 일괄 처리 할 때는 Dataset.padded_batch 를 사용해야 Dataset.padded_batch .

ds_series_batch = ds_series.shuffle(20).padded_batch(10)

ids, sequence_batch = next(iter(ds_series_batch))
print(ids.numpy())
print()
print(sequence_batch.numpy())
[17 11 18  8 23  2  6 13 26 28]

[[ 1.0784e+00  6.2397e-01  3.3750e-01 -2.1123e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [ 1.3756e-01 -1.5717e+00 -8.0335e-01  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-9.1093e-01  6.3951e-01  4.9384e-04  2.0273e+00 -3.6473e-01 -3.6264e-02
  -7.3862e-01 -5.3504e-01]
 [ 1.6893e-01  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-1.1584e+00 -7.9125e-01  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-1.7383e-01 -2.5935e-01  4.8755e-01  1.5578e+00 -4.7534e-01  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-4.0072e-01 -7.4969e-01 -1.1954e+00  9.0354e-02  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-8.6638e-02 -1.4680e+00  1.3155e+00  1.1772e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-1.1352e-01 -1.1264e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]
 [-1.2858e+00  7.7001e-02 -1.7588e-01  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00]]

보다 현실적인 예를 보려면 preprocessing.image.ImageDataGeneratortf.data.Dataset 로 래핑tf.data.Dataset .

먼저 데이터를 다운로드하십시오.

flowers = tf.keras.utils.get_file(
    'flower_photos',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    untar=True)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 1s 0us/step

만들기 image.ImageDataGenerator

img_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255, rotation_range=20)
images, labels = next(img_gen.flow_from_directory(flowers))
Found 3670 images belonging to 5 classes.
print(images.dtype, images.shape)
print(labels.dtype, labels.shape)
float32 (32, 256, 256, 3)
float32 (32, 5)
ds = tf.data.Dataset.from_generator(
    lambda: img_gen.flow_from_directory(flowers), 
    output_types=(tf.float32, tf.float32), 
    output_shapes=([32,256,256,3], [32,5])
)

ds.element_spec
(TensorSpec(shape=(32, 256, 256, 3), dtype=tf.float32, name=None),
 TensorSpec(shape=(32, 5), dtype=tf.float32, name=None))
for images, label in ds.take(1):
  print('images.shape: ', images.shape)
  print('labels.shape: ', labels.shape)
Found 3670 images belonging to 5 classes.
images.shape:  (32, 256, 256, 3)
labels.shape:  (32, 5)

TFRecord 데이터 사용

종단 간 예제는 TFRecord로드를 참조하십시오.

tf.data API는 다양한 파일 형식을 지원하므로 메모리에 맞지 않는 큰 데이터 세트를 처리 할 수 ​​있습니다. 예를 들어 TFRecord 파일 형식은 많은 TensorFlow 애플리케이션이 데이터 학습에 사용하는 간단한 레코드 지향 바이너리 형식입니다. tf.data.TFRecordDataset 클래스를 사용하면 입력 파이프 라인의 일부로 하나 이상의 TFRecord 파일 콘텐츠를 스트리밍 할 수 있습니다.

다음은 FNS (French Street Name Signs)의 테스트 파일을 사용하는 예입니다.

# Creates a dataset that reads all of the examples from two files.
fsns_test_file = tf.keras.utils.get_file("fsns.tfrec", "https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001")
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001
7905280/7904079 [==============================] - 0s 0us/step

TFRecordDataset 이니셜 라이저에 대한 filenames 인수는 문자열, 문자열 목록 또는 문자열 tf.Tensor 일 수 있습니다. 따라서 학습 및 검증 목적으로 두 세트의 파일이있는 경우 파일 이름을 입력 인수로 사용하여 데이터 세트를 생성하는 팩토리 메서드를 만들 수 있습니다.

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

많은 TensorFlow 프로젝트는 TFRecord 파일에서 직렬화 된 tf.train.Example 레코드를 사용합니다. 검사하려면 먼저 디코딩해야합니다.

raw_example = next(iter(dataset))
parsed = tf.train.Example.FromString(raw_example.numpy())

parsed.features.feature['image/text']
bytes_list {
  value: "Rue Perreyon"
}

텍스트 데이터 사용

종단 간 예제는 텍스트로드를 참조하십시오.

많은 데이터 세트가 하나 이상의 텍스트 파일로 배포됩니다. tf.data.TextLineDataset 은 하나 이상의 텍스트 파일에서 줄을 쉽게 추출 할 수있는 방법을 제공합니다. 하나 이상의 파일 이름이 주어지면 TextLineDataset 은 해당 파일의 줄당 하나의 문자열 값 요소를 생성합니다.

directory_url = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/'
file_names = ['cowper.txt', 'derby.txt', 'butler.txt']

file_paths = [
    tf.keras.utils.get_file(file_name, directory_url + file_name)
    for file_name in file_names
]
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt
819200/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step
dataset = tf.data.TextLineDataset(file_paths)

다음은 첫 번째 파일의 처음 몇 줄입니다.

for line in dataset.take(5):
  print(line.numpy())
b"\xef\xbb\xbfAchilles sing, O Goddess! Peleus' son;"
b'His wrath pernicious, who ten thousand woes'
b"Caused to Achaia's host, sent many a soul"
b'Illustrious into Ades premature,'
b'And Heroes gave (so stood the will of Jove)'

파일 사이를 번갈아 Dataset.interleave 사용 Dataset.interleave . 이렇게하면 파일을 쉽게 섞을 수 있습니다. 다음은 각 번역의 첫 번째, 두 번째 및 세 번째 줄입니다.

files_ds = tf.data.Dataset.from_tensor_slices(file_paths)
lines_ds = files_ds.interleave(tf.data.TextLineDataset, cycle_length=3)

for i, line in enumerate(lines_ds.take(9)):
  if i % 3 == 0:
    print()
  print(line.numpy())
b"\xef\xbb\xbfAchilles sing, O Goddess! Peleus' son;"
b"\xef\xbb\xbfOf Peleus' son, Achilles, sing, O Muse,"
b'\xef\xbb\xbfSing, O goddess, the anger of Achilles son of Peleus, that brought'

b'His wrath pernicious, who ten thousand woes'
b'The vengeance, deep and deadly; whence to Greece'
b'countless ills upon the Achaeans. Many a brave soul did it send'

b"Caused to Achaia's host, sent many a soul"
b'Unnumbered ills arose; which many a soul'
b'hurrying down to Hades, and many a hero did it yield a prey to dogs and'

기본적으로 TextLineDataset 은 각 파일의 모든 행을 생성합니다. 예를 들어 파일이 헤더 행으로 시작하거나 주석이 포함 된 경우 바람직하지 않을 수 있습니다. 이러한 줄은 Dataset.skip() 또는 Dataset.filter() 변환을 사용하여 제거 할 수 있습니다. 여기에서 첫 번째 줄을 건너 뛰고 생존자 만 찾기 위해 필터링합니다.

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)
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv
32768/30874 [===============================] - 0s 0us/step
for line in titanic_lines.take(10):
  print(line.numpy())
b'survived,sex,age,n_siblings_spouses,parch,fare,class,deck,embark_town,alone'
b'0,male,22.0,1,0,7.25,Third,unknown,Southampton,n'
b'1,female,38.0,1,0,71.2833,First,C,Cherbourg,n'
b'1,female,26.0,0,0,7.925,Third,unknown,Southampton,y'
b'1,female,35.0,1,0,53.1,First,C,Southampton,n'
b'0,male,28.0,0,0,8.4583,Third,unknown,Queenstown,y'
b'0,male,2.0,3,1,21.075,Third,unknown,Southampton,n'
b'1,female,27.0,0,2,11.1333,Third,unknown,Southampton,n'
b'1,female,14.0,1,0,30.0708,Second,unknown,Cherbourg,n'
b'1,female,4.0,1,1,16.7,Third,G,Southampton,n'
def survived(line):
  return tf.not_equal(tf.strings.substr(line, 0, 1), "0")

survivors = titanic_lines.skip(1).filter(survived)
for line in survivors.take(10):
  print(line.numpy())
b'1,female,38.0,1,0,71.2833,First,C,Cherbourg,n'
b'1,female,26.0,0,0,7.925,Third,unknown,Southampton,y'
b'1,female,35.0,1,0,53.1,First,C,Southampton,n'
b'1,female,27.0,0,2,11.1333,Third,unknown,Southampton,n'
b'1,female,14.0,1,0,30.0708,Second,unknown,Cherbourg,n'
b'1,female,4.0,1,1,16.7,Third,G,Southampton,n'
b'1,male,28.0,0,0,13.0,Second,unknown,Southampton,y'
b'1,female,28.0,0,0,7.225,Third,unknown,Cherbourg,y'
b'1,male,28.0,0,0,35.5,First,A,Southampton,y'
b'1,female,38.0,1,5,31.3875,Third,unknown,Southampton,n'

CSV 데이터 사용

더 많은 예제는 CSV 파일 로드Pandas 데이터 프레임로드를 참조하십시오.

CSV 파일 형식은 일반 텍스트로 표 형식 데이터를 저장하는 데 널리 사용되는 형식입니다.

예를 들면 :

titanic_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
df = pd.read_csv(titanic_file)
df.head()

데이터가 메모리에 Dataset.from_tensor_slices 동일한 Dataset.from_tensor_slices 메서드가 사전에서 작동 Dataset.from_tensor_slices 데이터를 쉽게 가져올 수 있습니다.

titanic_slices = tf.data.Dataset.from_tensor_slices(dict(df))

for feature_batch in titanic_slices.take(1):
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
'survived'          : 0
  'sex'               : b'male'
  'age'               : 22.0
  'n_siblings_spouses': 1
  'parch'             : 0
  'fare'              : 7.25
  'class'             : b'Third'
  'deck'              : b'unknown'
  'embark_town'       : b'Southampton'
  'alone'             : b'n'

보다 확장 가능한 접근 방식은 필요에 따라 디스크에서로드하는 것입니다.

tf.data 모듈은 RFC 4180 을 준수하는 하나 이상의 CSV 파일에서 레코드를 추출하는 방법을 제공합니다.

experimental.make_csv_dataset 함수는 csv 파일 세트를 읽기위한 고급 인터페이스입니다. 열 유형 추론 및 일괄 처리 및 셔플 링과 같은 기타 많은 기능을 지원하여 사용을 단순화합니다.

titanic_batches = tf.data.experimental.make_csv_dataset(
    titanic_file, batch_size=4,
    label_name="survived")
for feature_batch, label_batch in titanic_batches.take(1):
  print("'survived': {}".format(label_batch))
  print("features:")
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
'survived': [1 1 1 1]
features:
  'sex'               : [b'female' b'female' b'male' b'female']
  'age'               : [40. 30. 17. 19.]
  'n_siblings_spouses': [0 0 0 0]
  'parch'             : [0 0 2 0]
  'fare'              : [ 13.      12.475  110.8833  26.    ]
  'class'             : [b'Second' b'Third' b'First' b'Second']
  'deck'              : [b'unknown' b'unknown' b'C' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Cherbourg' b'Southampton']
  'alone'             : [b'y' b'y' b'n' b'y']

열의 하위 집합 만 필요한 경우 select_columns 인수를 사용할 수 있습니다.

titanic_batches = tf.data.experimental.make_csv_dataset(
    titanic_file, batch_size=4,
    label_name="survived", select_columns=['class', 'fare', 'survived'])
for feature_batch, label_batch in titanic_batches.take(1):
  print("'survived': {}".format(label_batch))
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
'survived': [0 0 0 0]
  'fare'              : [ 15.5   55.9    8.05 108.9 ]
  'class'             : [b'Third' b'First' b'Third' b'First']

또한 더 세밀한 제어를 제공하는 낮은 수준의 experimental.CsvDataset 클래스도 있습니다. 열 유형 추론을 지원하지 않습니다. 대신 각 열의 유형을 지정해야합니다.

titanic_types  = [tf.int32, tf.string, tf.float32, tf.int32, tf.int32, tf.float32, tf.string, tf.string, tf.string, tf.string] 
dataset = tf.data.experimental.CsvDataset(titanic_file, titanic_types , header=True)

for line in dataset.take(10):
  print([item.numpy() for item in line])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 38.0, 1, 0, 71.2833, b'First', b'C', b'Cherbourg', b'n']
[1, b'female', 26.0, 0, 0, 7.925, b'Third', b'unknown', b'Southampton', b'y']
[1, b'female', 35.0, 1, 0, 53.1, b'First', b'C', b'Southampton', b'n']
[0, b'male', 28.0, 0, 0, 8.4583, b'Third', b'unknown', b'Queenstown', b'y']
[0, b'male', 2.0, 3, 1, 21.075, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 27.0, 0, 2, 11.1333, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 14.0, 1, 0, 30.0708, b'Second', b'unknown', b'Cherbourg', b'n']
[1, b'female', 4.0, 1, 1, 16.7, b'Third', b'G', b'Southampton', b'n']
[0, b'male', 20.0, 0, 0, 8.05, b'Third', b'unknown', b'Southampton', b'y']

일부 열이 비어있는 경우이 하위 수준 인터페이스를 사용하면 열 유형 대신 기본값을 제공 할 수 있습니다.

%%writefile missing.csv
1,2,3,4
,2,3,4
1,,3,4
1,2,,4
1,2,3,
,,,
Writing missing.csv
# Creates a dataset that reads all of the records from two CSV files, each with
# four float columns which may have missing values.

record_defaults = [999,999,999,999]
dataset = tf.data.experimental.CsvDataset("missing.csv", record_defaults)
dataset = dataset.map(lambda *items: tf.stack(items))
dataset
<MapDataset shapes: (4,), types: tf.int32>
for line in dataset:
  print(line.numpy())
[1 2 3 4]
[999   2   3   4]
[  1 999   3   4]
[  1   2 999   4]
[  1   2   3 999]
[999 999 999 999]

기본적으로 CsvDataset 은 파일의 모든 행의 모든 열을 생성합니다. 예를 들어 파일이 무시해야하는 헤더 행으로 시작하거나 입력에 일부 열이 필요하지 않은 경우 바람직하지 않을 수 있습니다. 이러한 행과 필드는 각각 headerselect_cols 인수를 사용하여 제거 할 수 있습니다.

# Creates a dataset that reads all of the records from two CSV files with
# headers, extracting float data from columns 2 and 4.
record_defaults = [999, 999] # Only provide defaults for the selected columns
dataset = tf.data.experimental.CsvDataset("missing.csv", record_defaults, select_cols=[1, 3])
dataset = dataset.map(lambda *items: tf.stack(items))
dataset
<MapDataset shapes: (2,), types: tf.int32>
for line in dataset:
  print(line.numpy())
[2 4]
[2 4]
[999   4]
[2 4]
[  2 999]
[999 999]

파일 세트 사용

파일 세트로 배포 된 많은 데이터 세트가 있으며 각 파일은 예제입니다.

flowers_root = tf.keras.utils.get_file(
    'flower_photos',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    untar=True)
flowers_root = pathlib.Path(flowers_root)

루트 디렉토리에는 각 클래스에 대한 디렉토리가 있습니다.

for item in flowers_root.glob("*"):
  print(item.name)
sunflowers
daisy
LICENSE.txt
roses
tulips
dandelion

각 클래스 디렉토리의 파일은 다음과 같습니다.

list_ds = tf.data.Dataset.list_files(str(flowers_root/'*/*'))

for f in list_ds.take(5):
  print(f.numpy())
b'/home/kbuilder/.keras/datasets/flower_photos/roses/3871586333_5a708d5cf4_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/19551343954_83bb52f310_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/13289268363_b9337d751e.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/3253243865_435c1f2c2b_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/22244161124_53e457bb66_n.jpg'

tf.io.read_file 함수를 사용하여 데이터를 읽고 경로에서 레이블을 추출하여 (image, label) 쌍을 반환합니다.

def process_path(file_path):
  label = tf.strings.split(file_path, os.sep)[-2]
  return tf.io.read_file(file_path), label

labeled_ds = list_ds.map(process_path)
for image_raw, label_text in labeled_ds.take(1):
  print(repr(image_raw.numpy()[:100]))
  print()
  print(label_text.numpy())
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xfe\x00\x0cAppleMark\n\xff\xe2\x05XICC_PROFILE\x00\x01\x01\x00\x00\x05Happl\x02 \x00\x00scnrRGB XYZ \x07\xd3\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00acspAPPL\x00\x00\x00\x00'

b'roses'

데이터 세트 요소 일괄 처리

간단한 일괄 처리

가장 간단한 형태의 일괄 처리는 데이터 세트의 n 연속 요소를 단일 요소로 스택합니다. Dataset.batch() 변환은 요소의 각 구성 요소에 적용된 tf.stack() 연산자와 동일한 제약 조건을 사용하여 tf.stack() 을 수행합니다. 즉, 각 구성 요소 i 에 대해 모든 요소는 정확히 동일한 모양의 텐서를 가져야합니다.

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 가 모양 정보를 전파하려고 시도하는 동안 tf.data 의 기본 설정으로 인해 마지막 배치가 가득 Dataset.batch 않을 수 있으므로 알 수없는 배치 크기가 발생합니다. 모양에서 None 을 확인하십시오.

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

패딩으로 텐서 일괄 처리

위의 레시피는 크기가 모두 같은 텐서에 적용됩니다. 그러나 많은 모델 (예 : 시퀀스 모델)은 다양한 크기 (예 : 다른 길이의 시퀀스)를 가질 수있는 입력 데이터로 작동합니다. 이 경우를 처리하기 위해 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]]

Dataset.padded_batch 변환을 사용하면 각 구성 요소의 각 차원에 대해 다른 패딩을 설정할 수 있으며 가변 길이 (위의 예에서 None 으로 표시) 또는 고정 길이 일 수 있습니다. 패딩 값 (기본값은 0)을 재정의 할 수도 있습니다.

교육 워크 플로

여러 시대 처리

tf.data API는 동일한 데이터의 여러 tf.data 를 처리하는 두 가지 주요 방법을 제공합니다.

여러 Dataset.repeat() 에서 데이터 세트를 반복하는 가장 간단한 방법은 Dataset.repeat() 변환을 사용하는 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() 변환을 적용하면 입력이 무기한 반복됩니다.

Dataset.repeat 변환은 한 시대의 끝과 다음 시대의 시작을 알리지 않고 인수를 연결합니다. 이 때문에 Dataset.batch 후에 적용된 Dataset.repeat 는 epoch 경계를 가로 지르는 배치를 생성합니다.

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

png

명확한 epoch 분리가 필요한 경우 반복하기 전에 Dataset.batch 를 넣으 Dataset.batch .

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

plot_batch_sizes(titanic_batches)

png

각 세대가 끝날 때 사용자 지정 계산 (예 : 통계 수집)을 수행하려면 각 세대에서 데이터 집합 반복을 다시 시작하는 것이 가장 간단합니다.

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

입력 데이터를 무작위로 섞기

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이고 배치 크기가 20이므로 첫 번째 배치에는 인덱스가 120을 초과하는 요소가 없습니다.

n,line_batch = next(iter(dataset))
print(n.numpy())
[ 39   5  30  77  45 104 102  54  31  89  93  29  38  85  60   9  78  76
   1  41]

와 마찬가지로 Dataset.batch 을 기준으로 순서 Dataset.repeat 문제.

Dataset.shuffle 은 셔플 버퍼가 비워 질 때까지 epoch의 끝을 Dataset.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:

[618 522 447 528 576 514 626 610 502 404]
[601 577 586 560 490 469 604 275 551 561]
[567 550 423 486 544 457 578 448]
[85 78 32 73 45 99 37 94 13 54]
[101  55   4 109  41  25  80 106   9 113]
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()
<matplotlib.legend.Legend at 0x7fe00c013690>

png

그러나 셔플 이전의 반복은 시대 경계를 함께 혼합합니다.

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())
Here are the item ID's near the epoch boundary:

[611 401 577 625   2 557 585   6 618 502]
[ 31 607 588  24   0 627 455 613 547 605]
[537 544 240  15  43 555 621  34 590   8]
[ 27 475 528 546 560  48  53 489  54  37]
[599 561  30 570  21 499 586  10   5  12]
[367  60 568 525   1 619 589  23 548  35]
[ 17 470 616  42 569  83  70 405  46 463]
[ 50  72 612 623  28 522 581  86  77  76]
[474 598 609  25  65 491 543  97 536  93]
[ 16 101  58  90  19  38 111 615 119  49]
[ 39 110  75  95 122  94   4  67  64  51]
[ 22 606 610  99 601 526 116 571  80 109]
[ 29  32 125 138 608  33 139 106 147 127]
[130 114 117  18  59  79  66 123 124 155]
[103 579 100 107 165 115  92  84   3  52]
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()
<matplotlib.legend.Legend at 0x7fe004095b50>

png

데이터 전처리

Dataset.map(f) 변환은 입력 데이터 세트의 각 요소에 지정된 함수 f 를 적용하여 새 데이터 세트를 생성합니다. 함수형 프로그래밍 언어의 목록 (및 기타 구조)에 일반적으로 적용되는 map() 함수를 기반으로합니다. 함수 f 는 입력에서 단일 요소를 나타내는 tf.Tensor 객체를 취하고 새 데이터 세트의 단일 요소를 나타내는 tf.Tensor 객체를 반환합니다. 구현은 표준 TensorFlow 작업을 사용하여 한 요소를 다른 요소로 변환합니다.

이 섹션에서는 Dataset.map() 을 사용하는 방법에 대한 일반적인 예를 다룹니다.

이미지 데이터 디코딩 및 크기 조정

실제 이미지 데이터에서 신경망을 훈련 할 때 고정 된 크기로 일괄 처리 할 수 ​​있도록 크기가 다른 이미지를 공통 크기로 변환해야하는 경우가 많습니다.

꽃 파일 이름 데이터 세트를 다시 빌드합니다.

list_ds = tf.data.Dataset.list_files(str(flowers_root/'*/*'))

데이터 세트 요소를 조작하는 함수를 작성하십시오.

# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def parse_image(filename):
  parts = tf.strings.split(filename, os.sep)
  label = parts[-2]

  image = tf.io.read_file(filename)
  image = tf.image.decode_jpeg(image)
  image = tf.image.convert_image_dtype(image, tf.float32)
  image = tf.image.resize(image, [128, 128])
  return image, label

작동하는지 테스트하십시오.

file_path = next(iter(list_ds))
image, label = parse_image(file_path)

def show(image, label):
  plt.figure()
  plt.imshow(image)
  plt.title(label.numpy().decode('utf-8'))
  plt.axis('off')

show(image, label)

png

데이터 세트에 매핑합니다.

images_ds = list_ds.map(parse_image)

for image, label in images_ds.take(2):
  show(image, label)

png

png

임의의 Python 논리 적용

성능상의 이유로 가능한 한 데이터를 사전 처리하는 데 TensorFlow 작업을 사용하세요. 그러나 입력 데이터를 구문 분석 할 때 외부 Python 라이브러리를 호출하는 것이 유용한 경우가 있습니다. Dataset.map() 변환에서 tf.py_function() 작업을 사용할 수 있습니다.

예를 들어, 임의 회전을 적용하려는 경우 tf.image 모듈에는 tf.image.rot90 만 있으며 이미지 확대에는 그다지 유용하지 않습니다.

tf.py_function 을 시연하려면 scipy.ndimage.rotate 함수를 대신 사용해보세요.

import scipy.ndimage as ndimage

def random_rotate_image(image):
  image = ndimage.rotate(image, np.random.uniform(-30, 30), reshape=False)
  return image
image, label = next(iter(images_ds))
image = random_rotate_image(image)
show(image, label)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

이 기능을 사용하려면 Dataset.map 같은주의 사항이와 마찬가지로 적용 Dataset.from_generator , 당신은 당신이 기능을 적용 할 때 반환 모양과 유형을 설명합니다 :

def tf_random_rotate_image(image, label):
  im_shape = image.shape
  [image,] = tf.py_function(random_rotate_image, [image], [tf.float32])
  image.set_shape(im_shape)
  return image, label
rot_ds = images_ds.map(tf_random_rotate_image)

for image, label in rot_ds.take(2):
  show(image, label)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

png

tf.Example 프로토콜 버퍼 메시지 구문 분석

많은 입력 파이프 라인이 TFRecord 형식에서 tf.train.Example 프로토콜 버퍼 메시지를 추출합니다. 각 tf.train.Example 레코드에는 하나 이상의 "특징"이 포함되며 입력 파이프 라인은 일반적으로 이러한 특성을 텐서로 변환합니다.

fsns_test_file = tf.keras.utils.get_file("fsns.tfrec", "https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001")
dataset = tf.data.TFRecordDataset(filenames = [fsns_test_file])
dataset
<TFRecordDatasetV2 shapes: (), types: tf.string>

데이터를 이해하기 위해tf.data.Dataset 외부의 tf.train.Example protos로 작업 할 수 있습니다.

raw_example = next(iter(dataset))
parsed = tf.train.Example.FromString(raw_example.numpy())

feature = parsed.features.feature
raw_img = feature['image/encoded'].bytes_list.value[0]
img = tf.image.decode_png(raw_img)
plt.imshow(img)
plt.axis('off')
_ = plt.title(feature["image/text"].bytes_list.value[0])

png

raw_example = next(iter(dataset))
def tf_parse(eg):
  example = tf.io.parse_example(
      eg[tf.newaxis], {
          'image/encoded': tf.io.FixedLenFeature(shape=(), dtype=tf.string),
          'image/text': tf.io.FixedLenFeature(shape=(), dtype=tf.string)
      })
  return example['image/encoded'][0], example['image/text'][0]
img, txt = tf_parse(raw_example)
print(txt.numpy())
print(repr(img.numpy()[:20]), "...")
b'Rue Perreyon'
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02X' ...
decoded = dataset.map(tf_parse)
decoded
<MapDataset shapes: ((), ()), types: (tf.string, tf.string)>
image_batch, text_batch = next(iter(decoded.batch(10)))
image_batch.shape
TensorShape([10])

시계열 윈도우

종단 간 시계열 예는 시계열 예측을 참조하십시오.

시계열 데이터는 종종 시간 축이 손상되지 않은 상태로 구성됩니다.

간단한 Dataset.range 를 사용하여 Dataset.range 을 보여줍니다.

range_ds = tf.data.Dataset.range(100000)

일반적으로 이러한 종류의 데이터를 기반으로하는 모델은 연속적인 시간 조각을 원합니다.

가장 간단한 방법은 데이터를 일괄 처리하는 것입니다.

batch 사용

batches = range_ds.batch(10, drop_remainder=True)

for batch in batches.take(5):
  print(batch.numpy())
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]

또는 조밀 한 예측을 한 단계 앞으로 수행하려면 기능과 레이블을 서로에 대해 한 단계 씩 이동할 수 있습니다.

def dense_1_step(batch):
  # Shift features and labels one step relative to each other.
  return batch[:-1], batch[1:]

predict_dense_1_step = batches.map(dense_1_step)

for features, label in predict_dense_1_step.take(3):
  print(features.numpy(), " => ", label.numpy())
[0 1 2 3 4 5 6 7 8]  =>  [1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18]  =>  [11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28]  =>  [21 22 23 24 25 26 27 28 29]

고정 오프셋 대신 전체 창을 예측하려면 배치를 두 부분으로 분할 할 수 있습니다.

batches = range_ds.batch(15, drop_remainder=True)

def label_next_5_steps(batch):
  return (batch[:-5],   # Inputs: All except the last 5 steps
          batch[-5:])   # Labels: The last 5 steps

predict_5_steps = batches.map(label_next_5_steps)

for features, label in predict_5_steps.take(3):
  print(features.numpy(), " => ", label.numpy())
[0 1 2 3 4 5 6 7 8 9]  =>  [10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]  =>  [25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]  =>  [40 41 42 43 44]

한 배치의 기능과 다른 배치의 라벨간에 일부 겹침을 허용하려면 Dataset.zip 사용 Dataset.zip .

feature_length = 10
label_length = 3

features = range_ds.batch(feature_length, drop_remainder=True)
labels = range_ds.batch(feature_length).skip(1).map(lambda labels: labels[:label_length])

predicted_steps = tf.data.Dataset.zip((features, labels))

for features, label in predicted_steps.take(5):
  print(features.numpy(), " => ", label.numpy())
[0 1 2 3 4 5 6 7 8 9]  =>  [10 11 12]
[10 11 12 13 14 15 16 17 18 19]  =>  [20 21 22]
[20 21 22 23 24 25 26 27 28 29]  =>  [30 31 32]
[30 31 32 33 34 35 36 37 38 39]  =>  [40 41 42]
[40 41 42 43 44 45 46 47 48 49]  =>  [50 51 52]

window 사용

Dataset.batch 사용이 작동하는 동안 더 세밀한 제어가 필요한 상황이 있습니다. Dataset.window 메서드는 완전한 제어를 제공하지만주의가 필요합니다. DatasetsDataset 을 반환합니다. 자세한 내용은 데이터 세트 구조 를 참조하세요.

window_size = 5

windows = range_ds.window(window_size, shift=1)
for sub_ds in windows.take(5):
  print(sub_ds)
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>

Dataset.flat_map 메서드는 데이터 세트의 데이터 세트를 가져 와서 단일 데이터 세트로 병합 할 수 있습니다.

for x in windows.flat_map(lambda x: x).take(30):
   print(x.numpy(), end=' ')
0 1 2 3 4 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9

거의 모든 경우에 데이터 세트를 먼저 .batch 하는 것이 .batch .

def sub_to_batch(sub):
  return sub.batch(window_size, drop_remainder=True)

for example in windows.flat_map(sub_to_batch).take(5):
  print(example.numpy())
[0 1 2 3 4]
[1 2 3 4 5]
[2 3 4 5 6]
[3 4 5 6 7]
[4 5 6 7 8]

이제 shift 인수가 각 창이 이동하는 정도를 제어하는 ​​것을 볼 수 있습니다.

이것을 합치면 다음 함수를 작성할 수 있습니다.

def make_window_dataset(ds, window_size=5, shift=1, stride=1):
  windows = ds.window(window_size, shift=shift, stride=stride)

  def sub_to_batch(sub):
    return sub.batch(window_size, drop_remainder=True)

  windows = windows.flat_map(sub_to_batch)
  return windows
ds = make_window_dataset(range_ds, window_size=10, shift = 5, stride=3)

for example in ds.take(10):
  print(example.numpy())
[ 0  3  6  9 12 15 18 21 24 27]
[ 5  8 11 14 17 20 23 26 29 32]
[10 13 16 19 22 25 28 31 34 37]
[15 18 21 24 27 30 33 36 39 42]
[20 23 26 29 32 35 38 41 44 47]
[25 28 31 34 37 40 43 46 49 52]
[30 33 36 39 42 45 48 51 54 57]
[35 38 41 44 47 50 53 56 59 62]
[40 43 46 49 52 55 58 61 64 67]
[45 48 51 54 57 60 63 66 69 72]

그러면 이전과 같이 레이블을 쉽게 추출 할 수 있습니다.

dense_labels_ds = ds.map(dense_1_step)

for inputs,labels in dense_labels_ds.take(3):
  print(inputs.numpy(), "=>", labels.numpy())
[ 0  3  6  9 12 15 18 21 24] => [ 3  6  9 12 15 18 21 24 27]
[ 5  8 11 14 17 20 23 26 29] => [ 8 11 14 17 20 23 26 29 32]
[10 13 16 19 22 25 28 31 34] => [13 16 19 22 25 28 31 34 37]

리샘플링

클래스 불균형이 매우 심한 데이터 세트로 작업 할 때 데이터 세트를 리샘플링 할 수 있습니다. tf.data 는이를 수행하는 두 가지 방법을 제공합니다. 신용 카드 사기 데이터 세트는 이러한 종류의 문제에 대한 좋은 예입니다.

zip_path = tf.keras.utils.get_file(
    origin='https://storage.googleapis.com/download.tensorflow.org/data/creditcard.zip',
    fname='creditcard.zip',
    extract=True)

csv_path = zip_path.replace('.zip', '.csv')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/creditcard.zip
69156864/69155632 [==============================] - 2s 0us/step
creditcard_ds = tf.data.experimental.make_csv_dataset(
    csv_path, batch_size=1024, label_name="Class",
    # Set the column types: 30 floats and an int.
    column_defaults=[float()]*30+[int()])

이제 클래스 분포를 확인하십시오.

def count(counts, batch):
  features, labels = batch
  class_1 = labels == 1
  class_1 = tf.cast(class_1, tf.int32)

  class_0 = labels == 0
  class_0 = tf.cast(class_0, tf.int32)

  counts['class_0'] += tf.reduce_sum(class_0)
  counts['class_1'] += tf.reduce_sum(class_1)

  return counts
counts = creditcard_ds.take(10).reduce(
    initial_state={'class_0': 0, 'class_1': 0},
    reduce_func = count)

counts = np.array([counts['class_0'].numpy(),
                   counts['class_1'].numpy()]).astype(np.float32)

fractions = counts/counts.sum()
print(fractions)
[0.9961 0.0039]

불균형 데이터 세트로 훈련하는 일반적인 접근 방식은 균형을 맞추는 것입니다. tf.data 에는이 워크 플로를 가능하게하는 몇 가지 메서드가 포함되어 있습니다.

데이터 세트 샘플링

데이터 세트를 리샘플링하는 한 가지 방법은 sample_from_datasets 를 사용하는 것입니다. 이는 각 클래스에 대해 별도의data.Dataset 가있을 때 더 적합합니다.

여기에서 필터를 사용하여 신용 카드 사기 데이터에서 생성하십시오.

negative_ds = (
  creditcard_ds
    .unbatch()
    .filter(lambda features, label: label==0)
    .repeat())
positive_ds = (
  creditcard_ds
    .unbatch()
    .filter(lambda features, label: label==1)
    .repeat())
for features, label in positive_ds.batch(10).take(1):
  print(label.numpy())
[1 1 1 1 1 1 1 1 1 1]

tf.data.experimental.sample_from_datasets 를 사용하려면 데이터 세트와 각각에 대한 가중치를 전달합니다.

balanced_ds = tf.data.experimental.sample_from_datasets(
    [negative_ds, positive_ds], [0.5, 0.5]).batch(10)

이제 데이터 세트는 50/50 확률로 각 클래스의 예를 생성합니다.

for features, labels in balanced_ds.take(10):
  print(labels.numpy())
[1 1 1 0 1 0 1 1 0 1]
[1 1 1 0 0 1 0 0 0 0]
[0 1 1 1 1 1 1 1 0 0]
[0 0 1 0 0 0 1 0 0 0]
[0 1 0 0 1 0 1 1 0 1]
[1 0 0 0 0 1 1 1 0 1]
[0 0 0 1 1 0 0 1 0 1]
[1 0 0 1 1 0 0 1 1 0]
[0 0 1 0 0 0 0 1 0 0]
[1 1 1 1 0 0 1 1 1 0]

거부 리샘플링

위의 experimental.sample_from_datasets 접근 방식의 한 가지 문제는 클래스마다 별도의tf.data.Dataset 이 필요하다는 것입니다. Dataset.filter 사용 Dataset.filter 작동하지만 모든 데이터가 두 번로드됩니다.

data.experimental.rejection_resample 함수는 데이터 세트에 적용하여 한 번만로드하면서 재조정 할 수 있습니다. 균형을 이루기 위해 데이터 세트에서 요소가 삭제됩니다.

data.experimental.rejection_resampleclass_func 인수를 사용합니다. 이 class_func 는 각 데이터 세트 요소에 적용되며 균형 조정을 위해 예제가 속하는 클래스를 결정하는 데 사용됩니다.

creditcard_ds 의 요소는 이미 (features, label) 쌍입니다. 따라서 class_func 해당 레이블 만 반환하면됩니다.

def class_func(features, label):
  return label

리 샘플러에는 대상 분포도 필요하며 선택적으로 초기 분포 추정치가 필요합니다.

resampler = tf.data.experimental.rejection_resample(
    class_func, target_dist=[0.5, 0.5], initial_dist=fractions)

unbatch 는 개별 예제를 처리하므로 리 unbatch 를 적용하기 전에 데이터 세트의 배치를 해제해야합니다.

resample_ds = creditcard_ds.unbatch().apply(resampler).batch(10)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/resampling.py:156: Print (from tensorflow.python.ops.logging_ops) is deprecated and will be removed after 2018-08-20.
Instructions for updating:
Use tf.print instead of tf.Print. Note that tf.print returns a no-output operator that directly prints the output. Outside of defuns or eager mode, this operator will not be executed unless it is directly specified in session.run or used as a control dependency for other operators. This is only a concern in graph mode. Below is an example of how to ensure tf.print executes in graph mode:

class_funcclass_func 의 출력에서 ​​생성 (class, example) 쌍을 반환합니다. 이 경우 example 는 이미 (feature, label) 쌍이므로 map 을 사용하여 레이블의 추가 사본을 삭제하십시오.

balanced_ds = resample_ds.map(lambda extra_label, features_and_label: features_and_label)

이제 데이터 세트는 50/50 확률로 각 클래스의 예를 생성합니다.

for features, labels in balanced_ds.take(10):
  print(labels.numpy())
[1 0 0 0 1 0 1 1 0 0]
[1 1 0 0 0 1 0 0 0 0]
[1 1 1 1 0 0 0 0 0 1]
[1 0 0 1 0 1 1 1 1 1]
[1 0 1 0 1 1 0 0 1 1]
[1 1 0 1 0 1 0 0 0 0]
[0 0 0 1 1 1 0 1 1 1]
[1 0 0 0 1 0 0 0 0 1]
[1 1 0 1 0 0 1 0 1 0]
[0 1 1 1 0 1 0 1 1 1]

반복기 검사 점

Tensorflow는 학습 프로세스가 다시 시작될 때 최신 체크 포인트를 복원하여 대부분의 진행 상황을 복구 할 수 있도록 체크 포인트 획득을 지원합니다. 모델 변수를 체크 포인트하는 것 외에도 데이터 세트 반복기의 진행 상황을 체크 포인트 할 수도 있습니다. 이는 큰 데이터 세트가 있고 다시 시작할 때마다 데이터 세트를 처음부터 시작하지 않으려는 경우 유용 할 수 있습니다. 그러나 shuffleprefetch 와 같은 변환에는 반복기 내에서 버퍼링 요소가 필요하기 때문에 반복기 체크 포인트가 클 수 있습니다.

체크 포인트에 반복기를 포함하려면 반복기를 tf.train.Checkpoint 생성자에 전달합니다.

range_ds = tf.data.Dataset.range(20)

iterator = iter(range_ds)
ckpt = tf.train.Checkpoint(step=tf.Variable(0), iterator=iterator)
manager = tf.train.CheckpointManager(ckpt, '/tmp/my_ckpt', max_to_keep=3)

print([next(iterator).numpy() for _ in range(5)])

save_path = manager.save()

print([next(iterator).numpy() for _ in range(5)])

ckpt.restore(manager.latest_checkpoint)

print([next(iterator).numpy() for _ in range(5)])
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
[5, 6, 7, 8, 9]

tf.keras와 함께 tf.data 사용

tf.keras API는 기계 학습 모델 생성 및 실행의 여러 측면을 단순화합니다. .fit().fit().evaluate() .predict() API는 데이터 세트를 입력으로 지원합니다. 다음은 빠른 데이터 세트 및 모델 설정입니다.

train, test = tf.keras.datasets.fashion_mnist.load_data()

images, labels = train
images = images/255.0
labels = labels.astype(np.int32)
fmnist_train_ds = tf.data.Dataset.from_tensor_slices((images, labels))
fmnist_train_ds = fmnist_train_ds.shuffle(5000).batch(32)

model = tf.keras.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(10)
])

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), 
              metrics=['accuracy'])

Model.fitModel.evaluate 필요한 것은 (feature, label) 쌍의 데이터 세트를 전달하는 Model.evaluate .

model.fit(fmnist_train_ds, epochs=2)
Epoch 1/2
1875/1875 [==============================] - 3s 1ms/step - loss: 0.5999 - accuracy: 0.7962
Epoch 2/2
1875/1875 [==============================] - 3s 1ms/step - loss: 0.4618 - accuracy: 0.8428
<tensorflow.python.keras.callbacks.History at 0x7fdffe98bd10>

예를 들어 Dataset.repeat() 를 호출하여 무한 데이터 세트를 전달하는 경우 steps_per_epoch 인수도 전달하면 steps_per_epoch .

model.fit(fmnist_train_ds.repeat(), epochs=2, steps_per_epoch=20)
Epoch 1/2
20/20 [==============================] - 0s 1ms/step - loss: 0.3764 - accuracy: 0.8656
Epoch 2/2
20/20 [==============================] - 0s 1ms/step - loss: 0.4980 - accuracy: 0.8406
<tensorflow.python.keras.callbacks.History at 0x7fe00c129050>

평가를 위해 여러 평가 단계를 통과 할 수 있습니다.

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 2s 951us/step - loss: 0.4384 - accuracy: 0.8501
Loss : 0.4384005665779114
Accuracy : 0.8500999808311462

긴 데이터 세트의 경우 평가할 단계 수를 설정합니다.

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 1ms/step - loss: 0.4299 - accuracy: 0.8625
Loss : 0.4299231171607971
Accuracy : 0.862500011920929

Model.predict 호출 할 때 레이블이 필요하지 않습니다.

predict_ds = tf.data.Dataset.from_tensor_slices(images).batch(32)
result = model.predict(predict_ds, steps = 10)
print(result.shape)
(320, 10)

그러나 라벨이 포함 된 데이터 세트를 전달하면 라벨이 무시됩니다.

result = model.predict(fmnist_train_ds, steps = 10)
print(result.shape)
(320, 10)