Catat tanggalnya! Google I / O mengembalikan 18-20 Mei Daftar sekarang
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 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 satu contoh pelatihan, dengan sepasang komponen tensor yang mewakili gambar dan labelnya.

Ada dua cara berbeda untuk membuat kumpulan 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 sebuah file dalam format TFRecord yang direkomendasikan, Anda dapat menggunakan tf.data.TFRecordDataset() .

Setelah Anda 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

Dataset menghasilkan urutan elemen , di mana setiap elemen memiliki struktur komponen yang sama (bersarang). Komponen individual struktur dapat berupa jenis apa pun yang dapat direpresentasikan oleh tf.TypeSpec , termasuk tf.Tensor , tf.sparse.SparseTensor ,tf.RaggedTensor , tf.TensorArray , atautf.data.Dataset .

Konstruksi Python yang dapat digunakan untuk mengekspresikan struktur (bersarang) elemen termasuk tuple , dict , NamedTuple , dan OrderedDict . Secara khusus, list bukanlah konstruksi yang valid untuk mengekspresikan struktur elemen set data. Ini karena pengguna tf.data awal merasa kuat tentang input list (misalnya diteruskan ke tf.data.Dataset.from_tensors ) yang secara otomatis dikemas sebagai tensor dan output list (misalnya nilai kembalian dari fungsi yang ditentukan pengguna) yang dipaksakan menjadi tuple . Akibatnya, jika Anda ingin input list diperlakukan sebagai struktur, Anda perlu mengubahnya menjadi tuple dan jika Anda ingin output list menjadi satu komponen, Anda perlu mengemasnya secara eksplisit menggunakan tf.stack .

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 mungkin berupa komponen tunggal, 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())
[8 6 2 1 5 3 8 8 3 6]
[6 8 8 8 5 4 5 1 3 3]
[2 9 9 1 9 2 8 9 4 6]
[1 1 4 1 5 7 1 9 6 7]
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 input Anda pas dengan memori, cara termudah untuk membuat 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 [==============================] - 1s 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 ketika 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 direkomendasikan 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.7193  0.1489  0.3015]
1 : [ 0.9257 -0.8073  1.1848 -0.4203 -0.5188 -1.7014 -1.0934  2.6323]
2 : [-0.2845 -0.3942  0.8429 -0.2441  0.8038]
3 : [ 0.0553 -0.2246]
4 : [ 0.5449 -0.0552 -0.4308 -0.5846  0.2252  0.475  -0.6507  1.3336 -0.6148]
5 : []
6 : [-0.0788 -0.2671 -0.5868 -1.038  -0.145 ]

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())
[14  2 16 18  5 13  3  4  0 25]

[[ 2.2655e-03 -1.1356e+00 -6.3269e-01  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 5.9163e-01  1.1532e-01 -9.4578e-01 -5.7305e-01  5.8756e-01 -8.3086e-01
   1.0705e+00  4.6149e-01 -7.8259e-01]
 [ 8.7261e-01 -1.4031e+00  2.1707e-01  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  0.0000e+00  0.0000e+00]
 [ 2.2473e-01 -4.9448e-02 -5.0318e-01 -7.6812e-01 -5.9628e-01  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [-5.1477e-01  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.3577e+00  7.6869e-02  1.3755e+00  1.1951e-01  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 1.4727e+00  5.2939e-01  1.1665e-01  6.6326e-01  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [-1.5769e+00  7.4631e-01  8.4916e-01  5.3781e-01 -2.3621e+00  1.0438e-01
   9.3234e-01  0.0000e+00  0.0000e+00]
 [-1.7867e+00 -1.5707e+00  1.7925e-02  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 [==============================] - 6s 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 dalam 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 ke 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 project 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 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': [1 1 0 1]
features:
  'sex'               : [b'female' b'male' b'male' b'female']
  'age'               : [27. 48. 28. 50.]
  'n_siblings_spouses': [0 1 0 0]
  'parch'             : [2 0 0 0]
  'fare'              : [11.1333 76.7292  7.8958 10.5   ]
  'class'             : [b'Third' b'First' b'Third' b'Second']
  'deck'              : [b'unknown' b'D' b'unknown' b'unknown']
  'embark_town'       : [b'Southampton' b'Cherbourg' b'Southampton' b'Southampton']
  'alone'             : [b'n' b'n' b'y' b'y']

Anda bisa 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': [1 0 0 0]
  'fare'              : [29.7     7.8958 15.0458  7.75  ]
  'class'             : [b'First' 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 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/sunflowers/7652532108_01ef94c476.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/3276552939_8c31b22d3e.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/4363734507_5cc4ed6e01.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/20754920332_53b995fc63_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/15219268336_f2460fca88_m.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\xed\x00\x1cPhotoshop 3.0\x008BIM\x04\x04\x00\x00\x00\x00\x00\x00\xff\xdb\x00C\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\n\x07\x07\x06\x08\x0c\n\x0c\x0c\x0b\n\x0b\x0b\r\x0e\x12\x10\r\x0e\x11\x0e\x0b\x0b\x10'

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 kelompok yang tidak diketahui karena kelompok 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 yang berbeda-beda (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 dapat 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 ini 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 kumpulan 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 memberi sinyal 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())
[ 36  72  81  39  19  32  70  11  64  18  56  86  30  40 111  80  87  13
 110 116]

Seperti halnya Dataset.batch , urutan yang terkait dengan Dataset.repeat penting.

Dataset.shuffle tidak menandakan akhir zaman 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:

[622 295 617 552 465 394 592 593 442 488]
[469 543 329 600 359 471 554 627 620 456]
[565 550 563 464 556 618 587 449]
[20  0 71  8 25 86 10 57 72  2]
[ 78  99  85  36  62  13 108  46  56  97]
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 0x7fcccbcea860>

png

Tapi pengulangan sebelum pengacakan mencampurkan batas-batas zaman bersama-sama:

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:

[607 519 555 627  23 469   9 501   2 500]
[546 470  14  21 583 552  15 539 340  30]
[620  29 586 570 250   6 599 498 618 610]
[ 16  25 518 505 465 418  12  22  59  51]
[ 10 558  47 553  52   7 616 454 561 597]
[363  68 441 530  75 603  57 339  64  37]
[ 46 388 626  39 600 577 609 467 451  44]
[601  78 537  69 514  54  11 613 502  32]
[ 62  76  85 391  34  65  36 105 541  96]
[548 576 107  61 543  19 527  72 111  38]
[ 98  26  43  70 463 581 115 559 590  86]
[ 50 594  77 490  55 108 370  63  87  94]
[122  27 136  73 617 535  40 144 112  24]
[ 67  99 151 128   5 154 120   4 343 102]
[625 132  83  35 356  80 130  60 156 100]
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 0x7fccb44c5f28>

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 ke dalam ukuran tetap.

Bangun kembali kumpulan data nama file bunga:

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

Tulis fungsi yang memanipulasi elemen dataset.

# 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 input 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 alih-alih 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 satu kelompok dan label kelompok lain, gunakan Dataset.zip :

feature_length = 10
label_length = 3

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

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

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

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=' ')
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.9952 0.0048]

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())
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 set data menghasilkan contoh setiap kelas dengan probabilitas 50/50:

for features, labels in balanced_ds.take(10):
  print(labels.numpy())
[0 0 1 1 0 0 1 1 0 1]
[0 1 1 0 0 1 0 0 1 1]
[0 0 0 1 0 1 1 0 0 1]
[1 0 0 1 1 0 1 1 1 1]
[1 0 0 1 1 1 0 1 1 1]
[0 0 1 0 1 1 0 1 0 0]
[1 0 1 0 0 1 0 1 1 0]
[0 0 0 0 1 0 0 1 0 1]
[0 0 0 0 0 0 1 1 0 1]
[1 0 1 0 0 0 1 0 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 set data menghasilkan contoh setiap kelas dengan probabilitas 50/50:

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

Iterator Checkpointing

Tensorflow mendukung pengambilan checkpoint sehingga saat proses pelatihan Anda dimulai ulang, ia dapat memulihkan checkpoint 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
1875/1875 [==============================] - 4s 2ms/step - loss: 0.7767 - accuracy: 0.7407
Epoch 2/2
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4709 - accuracy: 0.8387
<tensorflow.python.keras.callbacks.History at 0x7fcca40a2080>

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.4301 - accuracy: 0.8609
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4454 - accuracy: 0.8281
<tensorflow.python.keras.callbacks.History at 0x7fccb416bd30>

Untuk evaluasi, Anda dapat melewati sejumlah langkah evaluasi:

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4484 - accuracy: 0.8435
Loss : 0.4483910799026489
Accuracy : 0.843500018119812

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.4643 - accuracy: 0.8531
Loss : 0.4643370509147644
Accuracy : 0.8531249761581421

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)