tf.data: Membangun saluran input TensorFlow

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHubUnduh buku catatan

API tf.data memungkinkan Anda membangun saluran input yang kompleks dari bagian sederhana yang dapat digunakan kembali. Misalnya, saluran untuk model gambar mungkin mengumpulkan data dari file dalam sistem file terdistribusi, menerapkan gangguan acak ke setiap gambar, dan menggabungkan gambar yang dipilih secara acak ke dalam kumpulan untuk pelatihan. Pipeline untuk model teks mungkin melibatkan ekstraksi simbol dari data teks mentah, mengonversinya menjadi penyematan pengidentifikasi dengan tabel pencarian, dan mengelompokkan 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 abstraksi tf.data.Dataset yang mewakili urutan elemen, di mana setiap elemen terdiri dari satu atau lebih komponen. Misalnya, dalam saluran gambar, sebuah elemen mungkin berupa contoh pelatihan tunggal, dengan sepasang komponen tensor yang mewakili gambar dan labelnya.

Ada dua cara berbeda untuk membuat kumpulan data:

  • Sumber data membangun Dataset dari data yang disimpan dalam memori atau dalam satu atau lebih file.

  • Transformasi data membuat kumpulan data dari satu atau beberapa objek tf.data.Dataset .

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

np.set_printoptions(precision=4)

mekanika dasar

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

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

Objek Dataset adalah Python yang dapat diubah. Ini memungkinkan untuk mengkonsumsi elemen-elemennya menggunakan for loop:

dataset = tf.data.Dataset.from_tensor_slices([8, 3, 0, 8, 2, 1])
dataset
<TensorSliceDataset element_spec=TensorSpec(shape=(), dtype=tf.int32, name=None)>
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

Atau, elemen dataset dapat dikonsumsi menggunakan transformasi reduce , yang mereduksi 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 adalah struktur komponen yang sama (bersarang). Komponen individu dari struktur dapat berupa jenis apa pun yang dapat diwakili oleh tf.TypeSpec , termasuk tf.Tensor , tf.sparse.SparseTensor , tf.RaggedTensor , tf.TensorArray , atau tf.data.Dataset .

Konstruksi Python yang dapat digunakan untuk mengekspresikan struktur elemen (bersarang) termasuk tuple , dict , NamedTuple , dan OrderedDict . Secara khusus, list bukanlah konstruksi yang valid untuk mengekspresikan struktur elemen kumpulan data. Ini karena pengguna tf.data awal sangat merasakan masukan list (misalnya diteruskan ke tf.data.Dataset.from_tensors ) secara otomatis dikemas sebagai tensor dan keluaran list (misalnya mengembalikan nilai fungsi yang ditentukan pengguna) dipaksa 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, maka Anda perlu mengemasnya secara eksplisit menggunakan tf.stack .

Properti Dataset.element_spec memungkinkan Anda untuk memeriksa jenis setiap komponen elemen. Properti mengembalikan struktur bersarang dari objek tf.TypeSpec , mencocokkan struktur elemen, yang mungkin berupa komponen tunggal, tupel komponen, atau tupel komponen bersarang. 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 element_spec=TensorSpec(shape=(10,), dtype=tf.int32, name=None)>
for z in dataset1:
  print(z.numpy())
[3 3 7 5 9 8 4 2 3 7]
[8 9 6 7 5 6 1 6 2 3]
[9 8 4 4 8 7 1 5 6 7]
[5 9 5 4 2 5 7 8 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 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
<ZipDataset element_spec=(TensorSpec(shape=(10,), dtype=tf.int32, name=None), (TensorSpec(shape=(), dtype=tf.float32, name=None), TensorSpec(shape=(100,), dtype=tf.int32, name=None)))>
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 array NumPy untuk contoh lainnya.

Jika semua data masukan Anda muat di memori, cara paling sederhana untuk membuat Dataset dari data tersebut 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
40960/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
26435584/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/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
4431872/4422102 [==============================] - 0s 0us/step
images, labels = train
images = images/255

dataset = tf.data.Dataset.from_tensor_slices((images, labels))
dataset
<TensorSliceDataset element_spec=(TensorSpec(shape=(28, 28), dtype=tf.float64, name=None), TensorSpec(shape=(), dtype=tf.uint8, name=None))>

Mengkonsumsi generator Python

Sumber data umum lainnya yang dapat dengan mudah diserap sebagai tf.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 menjadi tf.data.Dataset yang berfungsi penuh.

Konstruktor mengambil callable sebagai input, bukan iterator. Hal ini memungkinkan untuk me-restart generator ketika mencapai akhir. Dibutuhkan argumen args opsional, yang diteruskan sebagai argumen yang dapat dipanggil.

Argumen output_types diperlukan karena tf.data membangun tf.Graph secara 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, atur sebagai None di output_shapes .

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

Berikut adalah contoh generator yang menunjukkan 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.3939]
1 : [ 0.9282 -0.0158  1.0096  0.7155  0.0491  0.6697 -0.2565  0.487 ]
2 : [-0.4831  0.37   -1.3918 -0.4786  0.7425 -0.3299]
3 : [ 0.1427 -1.0438  0.821  -0.8766 -0.8369  0.4168]
4 : [-1.4984 -1.8424  0.0337  0.0941  1.3286 -1.4938]
5 : [-1.3158 -1.2102  2.6887 -1.2809]
6 : []

Output pertama adalah int32 yang kedua adalah float32 .

Item pertama adalah skalar, bentuk () , dan yang kedua adalah vektor yang panjangnya 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 element_spec=(TensorSpec(shape=(), dtype=tf.int32, name=None), TensorSpec(shape=(None,), dtype=tf.float32, name=None))>

Sekarang dapat digunakan seperti tf.data.Dataset biasa. Perhatikan bahwa saat mengelompokkan 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())
[ 8 10 18  1  5 19 22 17 21 25]

[[-0.6098  0.1366 -2.15   -0.9329  0.      0.    ]
 [ 1.0295 -0.033  -0.0388  0.      0.      0.    ]
 [-0.1137  0.3552  0.4363 -0.2487 -1.1329  0.    ]
 [ 0.      0.      0.      0.      0.      0.    ]
 [-1.0466  0.624  -1.7705  1.4214  0.9143 -0.62  ]
 [-0.9502  1.7256  0.5895  0.7237  1.5397  0.    ]
 [ 0.3747  1.2967  0.      0.      0.      0.    ]
 [-0.4839  0.292  -0.7909 -0.7535  0.4591 -1.3952]
 [-0.0468  0.0039 -1.1185 -1.294   0.      0.    ]
 [-0.1679 -0.3375  0.      0.      0.      0.    ]]

Untuk contoh yang lebih realistis, coba bungkus preprocessing.image.ImageDataGenerator sebagai tf.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 [==============================] - 10s 0us/step
228827136/228813984 [==============================] - 10s 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.

tf.data API mendukung berbagai format file sehingga Anda dapat memproses kumpulan data besar yang tidak muat di memori. Misalnya, format file TFRecord adalah format biner berorientasi rekaman sederhana yang digunakan banyak aplikasi TensorFlow untuk data pelatihan. Kelas tf.data.TFRecordDataset memungkinkan Anda untuk mengalirkan konten dari satu atau beberapa file TFRecord sebagai bagian dari saluran 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 [==============================] - 1s 0us/step
7913472/7904079 [==============================] - 1s 0us/step

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

dataset = tf.data.TFRecordDataset(filenames = [fsns_test_file])
dataset
<TFRecordDatasetV2 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>

Banyak proyek TensorFlow menggunakan catatan tf.train.Example serial dalam file TFRecord mereka. Ini perlu didekodekan sebelum dapat diperiksa:

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 dataset 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 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
827392/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
819200/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step
819200/807992 [==============================] - 0s 0us/step
dataset = tf.data.TextLineDataset(file_paths)

Berikut adalah 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 bersama-sama. 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 menyaring untuk menemukan hanya 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
40960/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 Pandas DataFrames untuk contoh lainnya.

Format file CSV adalah format populer untuk menyimpan data tabular 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 muat di memori, metode Dataset.from_tensor_slices yang sama berfungsi di kamus, memungkinkan data ini 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 terukur adalah memuat dari disk seperlunya.

Modul tf.data menyediakan metode untuk mengekstrak catatan dari satu atau lebih 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 shuffle, 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 0 0 0]
features:
  'sex'               : [b'female' b'female' b'male' b'male']
  'age'               : [32. 28. 37. 50.]
  'n_siblings_spouses': [0 3 0 0]
  'parch'             : [0 1 1 0]
  'fare'              : [13.     25.4667 29.7    13.    ]
  'class'             : [b'Second' b'Third' b'First' b'Second']
  'deck'              : [b'unknown' b'unknown' b'C' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Cherbourg' b'Southampton']
  'alone'             : [b'y' b'n' b'n' b'y']

Anda dapat menggunakan argumen select_columns jika Anda hanya membutuhkan subset 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 1 1 0]
  'fare'              : [ 7.05 15.5  26.25  8.05]
  'class'             : [b'Third' b'Third' b'Second' b'Third']

Ada juga kelas experimental.CsvDataset tingkat rendah yang menyediakan kontrol berbutir lebih halus. Itu tidak mendukung inferensi tipe kolom. Sebaliknya Anda harus menentukan jenis 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 element_spec=TensorSpec(shape=(4,), dtype=tf.int32, name=None)>
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 element_spec=TensorSpec(shape=(2,), dtype=tf.int32, name=None)>
for line in dataset:
  print(line.numpy())
[2 4]
[2 4]
[999   4]
[2 4]
[  2 999]
[999 999]

Mengkonsumsi set file

Ada banyak kumpulan data yang didistribusikan sebagai kumpulan file, di mana 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-file di setiap direktori kelas adalah contohnya:

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/5018120483_cc0421b176_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/8642679391_0805b147cb_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/8266310743_02095e782d_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/13176521023_4d7cc74856_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/19437578578_6ab1b3c984.jpg'

Baca data menggunakan fungsi tf.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 kumpulan data

Pengelompokan sederhana

Bentuk paling sederhana dari kumpulan tumpukan n elemen berurutan dari kumpulan data menjadi satu elemen. Dataset.batch() melakukan hal ini, 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])]

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

batched_dataset
<BatchDataset element_spec=(TensorSpec(shape=(None,), dtype=tf.int64, name=None), TensorSpec(shape=(None,), dtype=tf.int64, name=None))>

Gunakan argumen drop_remainder untuk mengabaikan kumpulan terakhir itu, dan dapatkan propagasi bentuk penuh:

batched_dataset = dataset.batch(7, drop_remainder=True)
batched_dataset
<BatchDataset element_spec=(TensorSpec(shape=(7,), dtype=tf.int64, name=None), TensorSpec(shape=(7,), dtype=tf.int64, name=None))>

Batching tensor dengan padding

Resep di atas berfungsi untuk tensor yang semuanya memiliki ukuran yang sama. Namun, banyak model (misalnya model urutan) bekerja dengan data input yang dapat memiliki ukuran yang bervariasi (misalnya urutan dengan panjang yang berbeda). Untuk menangani kasus ini, transformasi Dataset.padded_batch memungkinkan Anda untuk mengelompokkan tensor dengan bentuk yang berbeda dengan menentukan satu atau beberapa dimensi tempat mereka 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 untuk mengatur padding yang berbeda untuk setiap dimensi setiap komponen, dan mungkin panjang variabel (ditandai dengan None dalam contoh di atas) atau panjang konstan. Dimungkinkan juga untuk mengganti nilai padding, yang defaultnya adalah 0.

Alur kerja pelatihan

Memproses beberapa zaman

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

Cara paling sederhana untuk mengulangi kumpulan data dalam beberapa zaman adalah dengan menggunakan transformasi Dataset.repeat() . Pertama, buat kumpulan data titanic data:

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 input tanpa batas.

Transformasi Dataset.repeat menggabungkan argumennya tanpa menandakan akhir dari satu epoch dan awal dari epoch berikutnya. Karena itu, Dataset.batch yang diterapkan setelah Dataset.repeat akan menghasilkan kumpulan yang melintasi 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 perhitungan khusus (misalnya untuk mengumpulkan statistik) di akhir setiap epoch, maka paling mudah untuk memulai ulang iterasi kumpulan 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 input secara acak

Dataset.shuffle() mempertahankan buffer berukuran tetap dan memilih elemen berikutnya 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 element_spec=(TensorSpec(shape=(None,), dtype=tf.int64, name=None), TensorSpec(shape=(None,), dtype=tf.string, name=None))>

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())
[ 52  94  22  70  63  96  56 102  38  16  27 104  89  43  41  68  42  61
 112   8]

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

Dataset.shuffle tidak menandakan akhir dari sebuah epoch sampai buffer shuffle kosong. Jadi shuffle yang ditempatkan sebelum pengulangan akan menampilkan setiap elemen dari satu zaman 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:

[509 595 537 550 555 591 480 627 482 519]
[522 619 538 581 569 608 531 558 461 496]
[548 489 379 607 611 622 234 525]
[ 59  38   4  90  73  84  27  51 107  12]
[77 72 91 60  7 62 92 47 70 67]
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 0x7f7e7061c650>

png

Tetapi pengulangan sebelum shuffle 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:

[  6   8 528 604  13 492 308 441 569 475]
[  5 626 615 568  20 554 520 454  10 607]
[510 542   0 363  32 446 395 588  35   4]
[  7  15  28  23  39 559 585  49 252 556]
[581 617  25  43  26 548  29 460  48  41]
[ 19  64  24 300 612 611  36  63  69  57]
[287 605  21 512 442  33  50  68 608  47]
[625  90  91 613  67  53 606 344  16  44]
[453 448  89  45 465   2  31 618 368 105]
[565   3 586 114  37 464  12 627  30 621]
[ 82 117  72  75  84  17 571 610  18 600]
[107 597 575  88 623  86 101  81 456 102]
[122  79  51  58  80  61 367  38 537 113]
[ 71  78 598 152 143 620 100 158 133 130]
[155 151 144 135 146 121  83  27 103 134]
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 0x7f7e706013d0>

png

Data pra-pemrosesan

Dataset.map(f) menghasilkan kumpulan data baru dengan menerapkan fungsi f yang diberikan ke setiap elemen kumpulan data masukan. Ini didasarkan pada fungsi map() yang umumnya diterapkan pada daftar (dan struktur lainnya) dalam bahasa pemrograman fungsional. Fungsi f mengambil objek tf.Tensor yang mewakili satu elemen dalam input, dan mengembalikan objek tf.Tensor yang akan mewakili satu elemen dalam kumpulan data 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 saraf pada data gambar dunia nyata, sering kali diperlukan untuk mengonversi gambar dengan ukuran berbeda ke ukuran umum, sehingga dapat dikelompokkan menjadi 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.io.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 kinerja, gunakan operasi TensorFlow untuk pra-pemrosesan data Anda jika memungkinkan. Namun, terkadang berguna untuk memanggil pustaka Python eksternal saat mem-parsing data input Anda. Anda dapat menggunakan operasi tf.py_function() Dataset.map() dalam transformasi Dataset.map().

Misalnya, jika Anda ingin menerapkan rotasi acak, modul tf.image hanya memiliki tf.image.rot90 , yang tidak terlalu berguna untuk pembesaran 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 dengan Dataset.from_generator , Anda perlu menjelaskan bentuk dan tipe kembalian saat Anda 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 buffering protokol

Banyak jalur pipa masukan mengekstrak pesan buffering protokol tf.train.Example dari format TFRecord. Setiap record tf.train.Example berisi satu atau beberapa "fitur", dan jalur 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 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>

Anda dapat bekerja dengan tf.train.Example protos di luar tf.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 element_spec=(TensorSpec(shape=(), dtype=tf.string, name=None), TensorSpec(shape=(), dtype=tf.string, name=None))>
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: Peramalan deret waktu .

Data deret waktu sering diatur dengan sumbu waktu yang utuh.

Gunakan Dataset.range sederhana untuk menunjukkan:

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

Biasanya, model yang didasarkan pada jenis data ini akan menginginkan irisan waktu yang berdekatan.

Pendekatan paling sederhana adalah dengan mengelompokkan 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 padat selangkah ke depan, Anda dapat menggeser fitur dan label satu langkah 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 kumpulan menjadi dua bagian:

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

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

predict_5_steps = batches.map(label_next_5_steps)

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

Untuk memungkinkan beberapa tumpang tindih antara fitur satu batch dan label yang 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 kontrol penuh, tetapi memerlukan perhatian: metode ini mengembalikan Dataset of Datasets . Lihat Struktur kumpulan data untuk detailnya.

window_size = 5

windows = range_ds.window(window_size, shift=1)
for sub_ds in windows.take(5):
  print(sub_ds)
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>

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 dataset 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 kelasnya, Anda mungkin ingin membuat sampel ulang kumpulan data tersebut. tf.data menyediakan dua metode untuk melakukan ini. Dataset penipuan kartu kredit adalah contoh yang baik 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 [==============================] - 2s 0us/step
69165056/69155632 [==============================] - 2s 0us/step
creditcard_ds = tf.data.experimental.make_csv_dataset(
    csv_path, batch_size=1024, label_name="Class",
    # Set the column types: 30 floats and an int.
    column_defaults=[float()]*30+[int()])

Sekarang, periksa distribusi kelas, 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.9956 0.0044]

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

Pengambilan sampel kumpulan data

Salah satu pendekatan untuk resampling dataset adalah dengan menggunakan sample_from_datasets . Ini lebih berlaku bila Anda memiliki data.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.Dataset.sample_from_datasets , lewati kumpulan data, dan bobot untuk masing-masing:

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

Sekarang dataset menghasilkan contoh dari setiap kelas dengan probabilitas 50/50:

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

Pengambilan sampel ulang penolakan

Satu masalah dengan pendekatan Dataset.sample_from_datasets di atas adalah diperlukannya tf.data.Dataset terpisah per kelas. Anda dapat menggunakan Dataset.filter untuk membuat kedua kumpulan data tersebut, tetapi hal itu mengakibatkan semua data dimuat dua kali.

Metode data.Dataset.rejection_resample dapat diterapkan ke kumpulan data untuk menyeimbangkannya kembali, sementara hanya memuatnya sekali. Elemen akan dikeluarkan dari dataset untuk mencapai keseimbangan.

data.Dataset.rejection_resample mengambil argumen class_func . class_func ini diterapkan ke setiap elemen dataset, dan digunakan untuk menentukan kelas mana yang menjadi milik contoh untuk tujuan penyeimbangan.

Tujuannya di sini adalah untuk menyeimbangkan distribusi label, dan elemen creditcard_ds sudah (features, label) berpasangan. Jadi class_func hanya perlu mengembalikan label itu:

def class_func(features, label):
  return label

Metode resampling berkaitan dengan contoh individual, jadi dalam hal ini Anda harus unbatch kumpulan data sebelum menerapkan metode itu.

Metode ini membutuhkan distribusi target, dan secara opsional estimasi distribusi awal sebagai input.

resample_ds = (
    creditcard_ds
    .unbatch()
    .rejection_resample(class_func, target_dist=[0.5,0.5],
                        initial_dist=fractions)
    .batch(10))
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/ops/dataset_ops.py:5797: 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:

Metode rejection_resample mengembalikan (class, example) pasangan di mana class adalah output dari class_func . Dalam hal 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())
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
[0 1 1 1 0 1 1 0 1 1]
[1 1 0 1 0 0 0 0 1 1]
[1 1 1 1 0 0 0 0 1 1]
[1 0 0 1 0 0 1 0 1 1]
[1 0 0 0 0 1 0 0 0 0]
[1 0 0 1 1 0 1 1 1 0]
[1 1 0 0 0 0 0 0 0 1]
[0 0 1 0 0 0 1 0 1 1]
[0 1 0 1 0 1 0 0 0 1]
[0 0 0 0 0 0 0 0 1 1]

Pemeriksaan Iterator

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 kumpulan data. Ini bisa berguna jika Anda memiliki kumpulan data yang besar dan tidak ingin memulai kumpulan data dari awal setiap kali restart. Namun perhatikan bahwa pos pemeriksaan iterator mungkin besar, karena transformasi seperti shuffle dan prefetch memerlukan elemen buffering di dalam iterator.

Untuk memasukkan iterator Anda ke dalam 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() nya mendukung kumpulan data sebagai input. Berikut adalah set data cepat dan pengaturan model:

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

Melewati 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.5984 - accuracy: 0.7973
Epoch 2/2
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4607 - accuracy: 0.8430
<keras.callbacks.History at 0x7f7e70283110>

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.4574 - accuracy: 0.8672
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4216 - accuracy: 0.8562
<keras.callbacks.History at 0x7f7e144948d0>

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.4350 - accuracy: 0.8524
Loss : 0.4350026249885559
Accuracy : 0.8524333238601685

Untuk kumpulan data yang panjang, tetapkan jumlah langkah untuk dievaluasi:

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.4345 - accuracy: 0.8687
Loss : 0.43447819352149963
Accuracy : 0.8687499761581421

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 akan diabaikan jika Anda meneruskan kumpulan data yang berisi label tersebut:

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