이 페이지는 Cloud Translation API를 통해 번역되었습니다.
Switch to English

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 .

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

입력 데이터 읽기

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.1241  0.5308  0.3018]
1 : []
2 : [ 0.5769 -0.8721  2.0072 -1.7862  0.8289  0.59  ]
3 : [-1.5209 -1.5252  0.2506 -0.526   1.2647 -1.2677 -1.4078]
4 : [ 1.6039  0.2602  0.2278  1.205  -0.8033  0.3032]
5 : [ 0.5982  1.5779  0.0248  1.3666 -1.9277  1.3854 -0.4739]
6 : [ 1.0598 -0.2546  0.5908  1.3619  1.1141 -0.6058  0.8438 -2.4862]

첫 번째 출력은 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())
[ 3  7  6 22  0 13  4 23  5 17]

[[ 9.5867e-01 -2.5104e-01  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00]
 [-1.4743e-01  4.1422e-02  4.8626e-01 -4.4328e-01 -3.0196e+00  3.0172e-01
   0.0000e+00]
 [ 2.9469e-01 -2.8750e-01 -1.2391e-01 -7.7315e-01  7.5218e-01  7.9246e-01
   0.0000e+00]
 [ 1.5680e+00 -6.4869e-01 -7.5440e-01  3.3234e-01 -1.0759e+00  0.0000e+00
   0.0000e+00]
 [ 4.0357e-01 -7.8729e-01  2.1975e-02  2.4870e-02 -9.1991e-01 -2.1324e+00
   0.0000e+00]
 [-8.2417e-02  1.0919e+00 -6.6252e-01 -4.2764e-01  7.9078e-01  1.9829e-03
  -9.5911e-01]
 [-2.7661e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00]
 [ 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00]
 [ 1.7720e-01  1.1324e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00]
 [-4.8885e-01  0.0000e+00  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 [==============================] - 2s 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 파일 콘텐츠를 스트리밍 할 수 있습니다.

다음은 FSNS (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': [0 0 1 1]
features:
  'sex'               : [b'male' b'male' b'female' b'female']
  'age'               : [38. 16. 28. 28.]
  'n_siblings_spouses': [0 0 0 0]
  'parch'             : [1 0 0 0]
  'fare'              : [153.4625  10.5      7.8792   7.7333]
  'class'             : [b'First' b'Second' b'Third' b'Third']
  'deck'              : [b'C' b'unknown' b'unknown' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Queenstown' b'Queenstown']
  'alone'             : [b'n' b'y' b'y' 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'              : [25.4667  7.8958 13.      7.8958]
  'class'             : [b'Third' b'Third' b'Second' b'Third']

또한 더 세밀한 제어를 제공하는 더 낮은 수준의 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/daisy/9489270024_1b05f08492_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/11023214096_b5b39fab08.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/14928117202_139d2142cc_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/2215318403_06eb99176a.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/7481215720_73e40f178f_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\xe2\x0cXICC_PROFILE\x00\x01\x01\x00\x00\x0cHLino\x02\x10\x00\x00mntrRGB XYZ \x07\xce\x00\x02\x00\t\x00\x06\x001\x00\x00acspMSFT\x00\x00\x00\x00IEC sRGB\x00\x00\x00\x00\x00\x00'

b'daisy'

데이터 세트 요소 일괄 처리

간단한 일괄 처리

가장 간단한 형태의 일괄 처리는 데이터 세트의 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 변환은 한 Epoch의 끝과 다음 Epoch의 시작을 알리지 않고 인수를 연결합니다. 이 때문에 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())
[ 94  56  75  71  27  35  99  14  20  33  60   4  87  47  32  19  55  93
 112 103]

와 마찬가지로 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:

[371 576 469 293 598 618 559 512 491 524]
[527 613 566 625 573 621 608 375 568 587]
[600 578 617 580 496  18 541 601]
[16 54 62 59 98 18 82 61 91 99]
[50 86 75 90 40 92 63 94 51 80]

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 0x7f4ce55901d0>

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:

[549 311 612 441 614 521   8 434 606  12]
[623  31 622  22 509 435 447 448 479   6]
[487  19 358  28 545  36 593 588 502 527]
[ 45 586 596   7 154  16 559 402  43  27]
[432  25  38 417  29 570 582  46  14  11]
[594 572   2 554 563   5 556 444  21  30]
[537   0 530  33 483 608  68  18  75  10]
[ 81  84  58 626 542  86  87 610  85  41]
[  4 627 584 397 534  66  99 603 105  44]
[ 65 106  23  13 100 599 428 595  74 104]
[ 62  24  92 558 121  95 451  50 587  47]
[ 48 362  17  78  89 323  57  73 130  77]
[ 63 617  69 619 109 115   9  10  76 122]
[144  96  79 140 368 101 552 117 113 158]
[153 150  54 562 148 149 141 168 142  98]

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 0x7f4cd03727b8>

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],   # Take the first 5 steps
          batch[-5:])   # take the remainder

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 = 5

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

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

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]
[10 11 12 13 14 15 16 17 18 19]  =>  [20 21 22 23 24]
[20 21 22 23 24 25 26 27 28 29]  =>  [30 31 32 33 34]

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=' ')
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f4cd044e510> and will run it as-is.
Cause: could not parse the source code:

for x in windows.flat_map(lambda x: x).take(30):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7f4cd044e510> and will run it as-is.
Cause: could not parse the source code:

for x in windows.flat_map(lambda x: x).take(30):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
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 [==============================] - 3s 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.9957 0.0043]

불균형 데이터 세트로 훈련하는 일반적인 접근 방식은 균형을 맞추는 것입니다. 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())
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f4d44598488> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==0)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7f4d44598488> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==0)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f4d445988c8> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==1)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7f4d445988c8> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==1)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert

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 0 1 1 0 1 0 1 0]
[0 0 1 0 0 0 0 1 1 1]
[0 0 0 0 0 0 0 1 0 1]
[0 0 0 0 1 0 0 0 0 0]
[1 1 0 0 1 0 1 1 0 1]
[0 1 1 0 0 0 1 0 1 0]
[0 1 0 0 1 1 0 0 1 0]
[1 0 0 0 1 0 1 0 1 0]
[1 1 1 0 1 1 1 0 1 0]
[1 0 1 1 1 0 1 1 0 1]

거부 리샘플링

위의 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.6/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())
[0 0 0 1 1 1 0 0 0 0]
[1 0 1 0 1 1 0 1 1 1]
[1 0 1 0 0 0 0 1 1 1]
[0 0 0 1 0 1 0 1 0 1]
[1 1 1 1 0 1 1 0 0 0]
[1 0 0 0 1 0 1 0 0 1]
[0 1 1 1 0 1 0 0 0 0]
[1 0 1 1 1 0 1 1 1 1]
[0 0 0 0 0 0 1 0 1 1]
[1 1 1 1 1 0 0 0 0 0]

반복기 검사 점

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
WARNING:tensorflow:Layer flatten is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because its dtype defaults to floatx.

If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.

To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.

1875/1875 [==============================] - 3s 2ms/step - loss: 0.5997 - accuracy: 0.7981
Epoch 2/2
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4631 - accuracy: 0.8411

<tensorflow.python.keras.callbacks.History at 0x7f4cd016bc18>

예를 들어 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 2ms/step - loss: 0.3873 - accuracy: 0.8734
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4442 - accuracy: 0.8313

<tensorflow.python.keras.callbacks.History at 0x7f4d4c10edd8>

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

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4445 - accuracy: 0.8447
Loss : 0.44445285201072693
Accuracy : 0.8446999788284302

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

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.4147 - accuracy: 0.8500
Loss : 0.4146668314933777
Accuracy : 0.8500000238418579

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)