Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

tf.data: Buat pipeline input TensorFlow

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

API tf.data memungkinkan Anda membuat pipeline input yang kompleks dari bagian yang sederhana dan dapat digunakan kembali. Misalnya, pipeline untuk model gambar mungkin menggabungkan data dari file dalam sistem file terdistribusi, menerapkan gangguan acak ke setiap gambar, dan menggabungkan gambar yang dipilih secara acak ke dalam satu batch untuk pelatihan. Pipeline untuk model teks mungkin melibatkan ekstraksi simbol dari data teks mentah, mengonversinya menjadi pengidentifikasi penyematan dengan tabel pencarian, dan menggabungkan urutan dengan panjang yang berbeda. API tf.data memungkinkan untuk menangani data dalam jumlah besar, membaca dari format data yang berbeda, dan melakukan transformasi yang kompleks.

API tf.data memperkenalkan abstraksitf.data.Dataset yang merepresentasikan urutan elemen, di mana setiap elemen terdiri dari satu atau lebih komponen. Misalnya, dalam pipeline gambar, sebuah elemen mungkin berupa contoh pelatihan tunggal, dengan sepasang komponen tensor yang mewakili gambar dan labelnya.

Ada dua cara berbeda untuk membuat set data:

  • Sumber data menyusun Dataset Data dari data yang disimpan dalam memori atau dalam satu atau beberapa file.

  • Transformasi data membangun kumpulan data dari satu atau beberapa objektf.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)

Mekanika dasar

Untuk membuat pipeline input, Anda harus mulai dengan sumber data. Misalnya, untuk membuat Dataset dari data dalam memori, Anda dapat menggunakan tf.data.Dataset.from_tensors() atau tf.data.Dataset.from_tensor_slices() . Alternatifnya, jika data input Anda disimpan dalam file dalam format TFRecord yang disarankan, Anda dapat menggunakan tf.data.TFRecordDataset() .

Setelah memiliki objek Dataset , Anda dapat mengubahnya menjadi Dataset baru dengan memanggil metode chaining pada objektf.data.Dataset . Misalnya, Anda dapat menerapkan transformasi per elemen seperti Dataset.map() , dan transformasi multi-elemen seperti Dataset.batch() . Lihat dokumentasi untuktf.data.Dataset untuk daftar lengkap transformasi.

Objek Dataset adalah iterable Python. Ini memungkinkan untuk mengkonsumsi elemennya menggunakan for loop:

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

Atau dengan secara eksplisit membuat iterator Python menggunakan iter dan menggunakan elemennya menggunakan next :

it = iter(dataset)

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

Alternatifnya, elemen dataset dapat digunakan menggunakan transformasi reduce , yang mengurangi semua elemen untuk menghasilkan satu hasil. Contoh berikut mengilustrasikan cara menggunakan transformasi reduce untuk menghitung jumlah kumpulan data bilangan bulat.

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

Struktur kumpulan data

Set data berisi elemen yang masing-masing memiliki struktur (bersarang) yang sama dan komponen individu dari struktur tersebut dapat berupa jenis apa pun yang dapat direpresentasikan oleh tf.TypeSpec , termasuk tf.Tensor , tf.sparse.SparseTensor ,tf.RaggedTensor , tf.TensorArray , atautf.data.Dataset .

Properti Dataset.element_spec memungkinkan Anda memeriksa tipe setiap komponen elemen. Properti mengembalikan struktur bertingkat dari objek tf.TypeSpec , yang cocok dengan struktur elemen, yang bisa berupa satu komponen, tupel komponen, atau tupel bertingkat komponen. Sebagai contoh:

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

Transformasi Dataset mendukung kumpulan data dari struktur apa pun. Saat menggunakan Dataset.map() , dan Dataset.filter() , yang menerapkan fungsi ke setiap elemen, struktur elemen menentukan argumen fungsi:

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

Membaca data masukan

Mengkonsumsi array NumPy

Lihat Memuat larik NumPy untuk contoh selengkapnya.

Jika semua data masukan Anda cocok dengan memori, cara termudah untuk membuat tf.Tensor Dataset darinya adalah dengan mengonversinya menjadi objek tf.Tensor dan menggunakan 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)>

Mengkonsumsi generator Python

Sumber data umum lainnya yang dapat dengan mudah diserap sebagaitf.data.Dataset adalah generator 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

Konstruktor Dataset.from_generator mengonversi generator python menjaditf.data.Dataset berfungsi penuh.

Konstruktor mengambil callable sebagai input, bukan iterator. Ini memungkinkannya untuk memulai ulang generator saat mencapai akhir. Dibutuhkan argumen opsional args , yang diteruskan sebagai argumen yang dapat dipanggil.

Argumen output_types diperlukan karena tf.data membangun tf.Graph internal, dan tepi grafik memerlukan 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]

Argumen output_shapes tidak diperlukan tetapi sangat disarankan karena banyak operasi tensorflow tidak mendukung tensor dengan peringkat yang tidak diketahui. Jika panjang sumbu tertentu tidak diketahui atau variabel, setel sebagai None di output_shapes .

Penting juga untuk diperhatikan bahwa output_shapes dan output_types mengikuti aturan output_shapes yang sama seperti metode output_shapes data lainnya.

Berikut adalah contoh generator yang mendemonstrasikan kedua aspek, ia mengembalikan tupel array, di mana array kedua adalah vektor dengan panjang yang tidak diketahui.

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]

Output pertama adalah int32 yang kedua adalah float32 .

Item pertama adalah skalar, bentuk () , dan yang kedua adalah vektor dengan panjang yang tidak diketahui, bentuk (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)>

Sekarang dapat digunakan sepertitf.data.Dataset biasa. Perhatikan bahwa saat mengumpulkan kumpulan data dengan bentuk variabel, Anda perlu menggunakan 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]]

Untuk contoh yang lebih realistis, cobatf.data.Dataset preprocessing.image.ImageDataGenerator sebagaitf.data.Dataset .

Download dulu datanya:

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

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

Mengkonsumsi data TFRecord

Lihat Memuat TFRecords untuk contoh ujung-ke-ujung.

API tf.data mendukung berbagai format file sehingga Anda dapat memproses kumpulan data besar yang tidak muat di memori. Misalnya, format file TFRecord adalah format biner berorientasi record sederhana yang digunakan banyak aplikasi TensorFlow untuk data pelatihan. Kelas tf.data.TFRecordDataset memungkinkan Anda melakukan streaming konten dari satu atau beberapa file TFRecord sebagai bagian dari pipeline input.

Berikut adalah contoh menggunakan file tes dari French Street Name Signs (FSNS).

# 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

Argumen filenames untuk penginisialisasi TFRecordDataset dapat berupa string, daftar string, atau tf.Tensor . tf.Tensor string. Oleh karena itu, jika Anda memiliki dua set file untuk tujuan pelatihan dan validasi, Anda dapat membuat metode pabrik yang menghasilkan set data tersebut, dengan menggunakan nama file sebagai argumen input:

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

Banyak proyek TensorFlow menggunakan serialized tf.train.Example record dalam file TFRecord-nya. Ini perlu diterjemahkan sebelum dapat diinspeksi:

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

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

Mengkonsumsi data teks

Lihat Memuat Teks untuk contoh ujung ke ujung.

Banyak set data didistribusikan sebagai satu atau lebih file teks. tf.data.TextLineDataset menyediakan cara mudah untuk mengekstrak baris dari satu atau lebih file teks. Diberikan satu atau lebih nama file, TextLineDataset akan menghasilkan satu elemen bernilai string per baris dari file tersebut.

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)

Berikut ini beberapa baris pertama dari file pertama:

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

Untuk mengganti baris antar file, gunakan Dataset.interleave . Ini membuatnya lebih mudah untuk mengacak file menjadi satu. Berikut adalah baris pertama, kedua dan ketiga dari setiap terjemahan:

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'

Secara default, TextLineDataset menghasilkan setiap baris dari setiap file, yang mungkin tidak diinginkan, misalnya, jika file dimulai dengan baris header, atau berisi komentar. Baris ini dapat dihapus menggunakan Dataset.skip() atau Dataset.filter() . Di sini, Anda melewati baris pertama, lalu memfilter untuk hanya menemukan yang selamat.

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'

Mengkonsumsi data CSV

Lihat Memuat File CSV , dan Memuat DataFrames Pandas untuk contoh lainnya.

Format file CSV adalah format yang populer untuk menyimpan data tabel dalam teks biasa.

Sebagai contoh:

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

Jika data Anda cocok dengan memori, metode Dataset.from_tensor_slices sama berfungsi pada kamus, sehingga data ini dapat diimpor dengan mudah:

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'

Pendekatan yang lebih skalabel adalah memuat dari disk jika diperlukan.

Modul tf.data menyediakan metode untuk mengekstrak catatan dari satu atau beberapa file CSV yang sesuai dengan RFC 4180 .

Fungsi experimental.make_csv_dataset adalah antarmuka tingkat tinggi untuk membaca kumpulan file csv. Ini mendukung inferensi tipe kolom dan banyak fitur lainnya, seperti batching dan shuffling, untuk mempermudah penggunaan.

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']

Anda dapat menggunakan argumen select_columns jika Anda hanya membutuhkan subset dari kolom.

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']

Ada juga kelas experimental.CsvDataset level rendah yang menyediakan kontrol yang lebih terperinci. Itu tidak mendukung inferensi tipe kolom. Sebagai gantinya Anda harus menentukan tipe setiap kolom.

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']

Jika beberapa kolom kosong, antarmuka tingkat rendah ini memungkinkan Anda untuk memberikan nilai default, bukan jenis kolom.

%%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]

Secara default, CsvDataset menghasilkan setiap kolom dari setiap baris file, yang mungkin tidak diinginkan, misalnya jika file dimulai dengan baris header yang harus diabaikan, atau jika beberapa kolom tidak diperlukan dalam input. Baris dan bidang ini dapat dihapus dengan argumen header dan select_cols masing-masing.

# 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]

Mengkonsumsi kumpulan file

Ada banyak dataset yang didistribusikan sebagai sekumpulan file, dimana setiap file adalah contohnya.

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)

Direktori root berisi direktori untuk setiap kelas:

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

File di setiap direktori kelas adalah contoh:

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'

Baca data menggunakan fungsitf.io.read_file dan ekstrak label dari jalur, kembalikan pasangan (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'

Mengelompokkan elemen set data

Pengelompokan sederhana

Bentuk paling sederhana dari tumpukan tumpukan n elemen berturut-turut dari kumpulan data menjadi satu elemen. Dataset.batch() melakukan hal ini dengan tepat, dengan batasan yang sama seperti operator tf.stack() , diterapkan ke setiap komponen elemen: yaitu untuk setiap komponen i , semua elemen harus memiliki tensor dengan bentuk yang sama persis.

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

Saat tf.data mencoba menyebarkan informasi bentuk, pengaturan default Dataset.batch menghasilkan ukuran kumpulan yang tidak diketahui karena kumpulan terakhir mungkin tidak penuh. Perhatikan None dalam bentuknya:

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

Gunakan argumen drop_remainder untuk mengabaikan batch terakhir tersebut, dan dapatkan propagasi bentuk penuh:

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

Tensor batch dengan bantalan

Resep di atas berfungsi untuk tensor yang semuanya memiliki ukuran yang sama. Namun, banyak model (misalnya model urutan) bekerja dengan data masukan yang dapat memiliki ukuran berbeda (misalnya urutan dengan panjang yang berbeda). Untuk menangani kasus ini, transformasi Dataset.padded_batch memungkinkan Anda untuk Dataset.padded_batch tensor dari bentuk yang berbeda dengan menentukan satu atau beberapa dimensi yang mungkin diisi.

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


Transformasi Dataset.padded_batch memungkinkan Anda menyetel padding yang berbeda untuk setiap dimensi dari setiap komponen, dan itu mungkin memiliki panjang variabel (ditandai dengan None dalam contoh di atas) atau panjang konstan. Dimungkinkan juga untuk mengganti nilai padding, yang defaultnya 0.

Alur kerja pelatihan

Memproses beberapa zaman

API tf.data menawarkan dua cara utama untuk memproses beberapa periode dari data yang sama.

Cara termudah untuk melakukan iterasi atas Dataset.repeat() data dalam beberapa zaman adalah dengan menggunakan transformasi Dataset.repeat() . Pertama, buat kumpulan data titanic:

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

Menerapkan transformasi Dataset.repeat() tanpa argumen akan mengulangi masukan tanpa batas.

Transformasi Dataset.repeat menggabungkan argumennya tanpa menandakan akhir dari satu epoch dan awal epoch berikutnya. Karenanya, Dataset.batch diterapkan setelah Dataset.repeat akan menghasilkan batch yang Dataset.repeat batas zaman:

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

png

Jika Anda membutuhkan pemisahan epoch yang jelas, letakkan Dataset.batch sebelum pengulangan:

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

plot_batch_sizes(titanic_batches)

png

Jika Anda ingin melakukan penghitungan khusus (mis. Untuk mengumpulkan statistik) di akhir setiap epoch, yang paling mudah adalah memulai ulang iterasi set data di setiap epoch:

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

Mengacak data masukan secara acak

Dataset.shuffle() mempertahankan buffer berukuran tetap dan memilih elemen berikutnya secara seragam secara acak dari buffer tersebut.

Tambahkan indeks ke dataset sehingga Anda dapat melihat efeknya:

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

Karena buffer_size adalah 100, dan ukuran batch adalah 20, batch pertama tidak berisi elemen dengan indeks lebih dari 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]

Seperti halnya Dataset.batch , urutan relatif terhadap Dataset.repeat penting.

Dataset.shuffle tidak menandakan akhir dari sebuah epoch hingga buffer acak kosong. Jadi pengacakan yang ditempatkan sebelum pengulangan akan menampilkan setiap elemen dari satu periode sebelum pindah ke yang berikutnya:

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

Tapi pengulangan sebelum pengacakan mencampurkan batas waktu menjadi satu:

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

Memproses data

Dataset.map(f) menghasilkan dataset baru dengan menerapkan fungsi f diberikan ke setiap elemen dari dataset input. Ini didasarkan pada fungsi map() yang biasanya diterapkan ke daftar (dan struktur lain) dalam bahasa pemrograman fungsional. Fungsi f mengambil objek tf.Tensor yang merepresentasikan satu elemen dalam input, dan mengembalikan objek tf.Tensor yang akan merepresentasikan elemen tunggal dalam dataset baru. Implementasinya menggunakan operasi TensorFlow standar untuk mengubah satu elemen menjadi elemen lainnya.

Bagian ini membahas contoh umum tentang cara menggunakan Dataset.map() .

Mendekode data gambar dan mengubah ukurannya

Saat melatih jaringan neural pada data gambar dunia nyata, sering kali perlu untuk mengonversi gambar dengan ukuran berbeda ke ukuran umum, sehingga gambar tersebut dapat dikelompokkan menjadi ukuran tetap.

Bangun kembali set data nama file bunga:

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

Tulis fungsi yang memanipulasi elemen set data.

# 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

Uji apakah itu berhasil.

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

Petakan di atas kumpulan data.

images_ds = list_ds.map(parse_image)

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

png

png

Menerapkan logika Python sewenang-wenang

Untuk alasan performa, gunakan operasi TensorFlow untuk praproses data Anda jika memungkinkan. Namun, terkadang berguna untuk memanggil pustaka Python eksternal saat mengurai data masukan Anda. Anda dapat menggunakan tf.py_function() operasi dalam Dataset.map() transformasi.

Misalnya, jika Anda ingin menerapkan rotasi acak, modul tf.image hanya memiliki tf.image.rot90 , yang tidak terlalu berguna untuk augmentasi gambar.

Untuk mendemonstrasikan tf.py_function , coba gunakan fungsi scipy.ndimage.rotate sebagai gantinya:

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

Untuk menggunakan fungsi ini dengan Dataset.map , peringatan yang sama berlaku seperti pada Dataset.from_generator , Anda perlu mendeskripsikan bentuk dan jenis yang ditampilkan saat menerapkan fungsi:

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

Parsing tf.Example pesan tf.Example protokol

Banyak pipeline masukan mengekstrak tf.train.Example pesan tf.train.Example protokol dari format TFRecord. Setiap catatan tf.train.Example berisi satu atau beberapa "fitur", dan pipeline input biasanya mengubah fitur ini menjadi tensor.

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>

Anda dapat bekerja dengan tf.train.Example protos di luartf.data.Dataset untuk memahami data:

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

Jendela deret waktu

Untuk contoh deret waktu ujung ke ujung, lihat: Perkiraan deret waktu .

Data deret waktu sering kali diatur dengan sumbu waktu yang utuh.

Gunakan Dataset.range sederhana untuk mendemonstrasikan:

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

Biasanya, model yang didasarkan pada jenis data ini membutuhkan pembagian waktu yang berdekatan.

Pendekatan paling sederhana adalah dengan mengumpulkan data:

Menggunakan 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]

Atau untuk membuat prediksi yang padat satu langkah ke depan, Anda dapat mengubah fitur dan label satu langkah secara relatif satu sama lain:

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]

Untuk memprediksi seluruh jendela dan bukan offset tetap, Anda dapat membagi kelompok menjadi dua bagian:

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]

Untuk memungkinkan beberapa tumpang tindih antara fitur dari satu kelompok dan label kelompok lainnya, gunakan 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]

Menggunakan window

Saat menggunakan Dataset.batch berfungsi, ada situasi di mana Anda mungkin memerlukan kontrol yang lebih baik. Metode Dataset.window memberi Anda kendali penuh, tetapi membutuhkan perhatian: metode ini mengembalikan Dataset dari Datasets Dataset . Lihat Struktur kumpulan data untuk mengetahui detailnya.

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>

Metode Dataset.flat_map dapat mengambil kumpulan data dari kumpulan data dan meratakannya menjadi satu kumpulan data:

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 

Di hampir semua kasus, Anda ingin. .batch kumpulan data terlebih dahulu:

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]

Sekarang, Anda dapat melihat bahwa argumen shift mengontrol seberapa banyak setiap jendela berpindah.

Menyatukan ini, Anda dapat menulis fungsi ini:

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]

Maka mudah untuk mengekstrak label, seperti sebelumnya:

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]

Pengambilan sampel ulang

Saat bekerja dengan kumpulan data yang sangat tidak seimbang dengan kelas, Anda mungkin ingin mengambil sampel ulang kumpulan data tersebut. tf.data menyediakan dua metode untuk melakukan ini. Dataset penipuan kartu kredit adalah contoh bagus dari masalah semacam ini.

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

Sekarang, periksa distribusi kelas, itu sangat miring:

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]

Pendekatan umum untuk pelatihan dengan set data yang tidak seimbang adalah dengan menyeimbangkannya. tf.data menyertakan beberapa metode yang memungkinkan alur kerja ini:

Pengambilan sampel kumpulan data

Salah satu pendekatan untuk mengambil sampel ulang kumpulan data adalah dengan menggunakan sample_from_datasets . Ini lebih berlaku jika Anda memilikidata.Dataset terpisah untuk setiap kelas.

Di sini, cukup gunakan filter untuk menghasilkannya dari data penipuan kartu kredit:

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]

Untuk menggunakan tf.data.experimental.sample_from_datasets teruskan tf.data.experimental.sample_from_datasets data, dan bobot untuk masing-masing:

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

Sekarang kumpulan data menghasilkan contoh setiap kelas dengan probabilitas 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]

Penolakan pengambilan sampel ulang

Satu masalah dengan pendekatan experimental.sample_from_datasets atas adalah bahwa ia membutuhkantf.data.Dataset terpisah per kelas. Menggunakan Dataset.filter berfungsi, tetapi menghasilkan semua data yang dimuat dua kali.

Fungsi data.experimental.rejection_resample dapat diterapkan ke kumpulan data untuk menyeimbangkannya kembali, sementara hanya memuatnya sekali. Elemen akan dihapus dari kumpulan data untuk mencapai keseimbangan.

data.experimental.rejection_resample mengambil argumen class_func . class_func ini diterapkan ke setiap elemen dataset, dan digunakan untuk menentukan kelas mana dari sebuah contoh untuk tujuan penyeimbangan.

Elemen creditcard_ds sudah merupakan pasangan (features, label) . Jadi class_func hanya perlu mengembalikan label tersebut:

def class_func(features, label):
  return label

Resampler juga membutuhkan distribusi target, dan secara opsional perkiraan distribusi awal:

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

Resampler menangani masing-masing contoh, jadi Anda harus unbatch kumpulan kumpulan data sebelum menerapkan resampler:

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:


Pengembalian resampler membuat pasangan (class, example) dari keluaran class_func . Dalam kasus ini, example sudah menjadi pasangan (feature, label) , jadi gunakan map untuk melepaskan salinan tambahan dari label:

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

Sekarang dataset menghasilkan contoh dari setiap kelas dengan probabilitas 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]

Iterator Checkpointing

Tensorflow mendukung pengambilan pos pemeriksaan sehingga saat proses pelatihan Anda dimulai ulang, ia dapat memulihkan pos pemeriksaan terbaru untuk memulihkan sebagian besar kemajuannya. Selain memeriksa variabel model, Anda juga dapat memeriksa kemajuan iterator set data. Ini bisa berguna jika Anda memiliki kumpulan data yang besar dan tidak ingin memulai kumpulan data dari awal setiap kali memulai ulang. Namun perlu diperhatikan bahwa checkpoint iterator mungkin besar, karena transformasi seperti shuffle dan prefetch memerlukan elemen buffering di dalam iterator.

Untuk menyertakan iterator Anda di checkpoint, teruskan iterator ke konstruktor 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]

Menggunakan tf.data dengan tf.keras

API tf.keras menyederhanakan banyak aspek dalam membuat dan menjalankan model pembelajaran mesin. .fit() dan .evaluate() dan .predict() mendukung .predict() data sebagai input. Berikut adalah set data dan model cepat:

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

Meneruskan kumpulan data pasangan (feature, label) adalah semua yang diperlukan untuk Model.fit dan 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>

Jika Anda meneruskan kumpulan data tak terbatas, misalnya dengan memanggil Dataset.repeat() , Anda hanya perlu meneruskan argumen 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>

Untuk evaluasi, Anda dapat melewati sejumlah langkah evaluasi:

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

Untuk kumpulan data yang panjang, setel jumlah langkah untuk mengevaluasi:

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

Label tidak diperlukan saat memanggil 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)

Tetapi label diabaikan jika Anda meneruskan kumpulan data yang berisi mereka:

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