Muat data CSV

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

Tutorial ini memberikan contoh cara menggunakan data CSV dengan TensorFlow.

Ada dua bagian utama untuk ini:

  1. Memuat data dari disk
  2. Pra-pemrosesan menjadi bentuk yang cocok untuk pelatihan.

Tutorial ini berfokus pada pemuatan, dan memberikan beberapa contoh prapemrosesan cepat. Untuk tutorial yang berfokus pada aspek prapemrosesan, lihat panduan dan tutorial lapisan prapemrosesan.

Mempersiapkan

import pandas as pd
import numpy as np

# Make numpy values easier to read.
np.set_printoptions(precision=3, suppress=True)

import tensorflow as tf
from tensorflow.keras import layers

Dalam data memori

Untuk kumpulan data CSV kecil, cara paling sederhana untuk melatih model TensorFlow adalah dengan memuatnya ke dalam memori sebagai pandas Dataframe atau array NumPy.

Contoh yang relatif sederhana adalah kumpulan data abalon .

  • Kumpulan datanya kecil.
  • Semua fitur input adalah semua nilai floating point rentang terbatas.

Berikut cara mendownload data ke dalam Pandas DataFrame :

abalone_train = pd.read_csv(
    "https://storage.googleapis.com/download.tensorflow.org/data/abalone_train.csv",
    names=["Length", "Diameter", "Height", "Whole weight", "Shucked weight",
           "Viscera weight", "Shell weight", "Age"])

abalone_train.head()

Dataset berisi satu set pengukuran abalon , sejenis siput laut.

cangkang abalon

"Cangkang abalon" (oleh Nicki Dugan Pogue , CC BY-SA 2.0)

Tugas nominal untuk kumpulan data ini adalah memprediksi usia dari pengukuran lain, jadi pisahkan fitur dan label untuk pelatihan:

abalone_features = abalone_train.copy()
abalone_labels = abalone_features.pop('Age')

Untuk kumpulan data ini, Anda akan memperlakukan semua fitur secara identik. Kemas fitur ke dalam array NumPy tunggal.:

abalone_features = np.array(abalone_features)
abalone_features
array([[0.435, 0.335, 0.11 , ..., 0.136, 0.077, 0.097],
       [0.585, 0.45 , 0.125, ..., 0.354, 0.207, 0.225],
       [0.655, 0.51 , 0.16 , ..., 0.396, 0.282, 0.37 ],
       ...,
       [0.53 , 0.42 , 0.13 , ..., 0.374, 0.167, 0.249],
       [0.395, 0.315, 0.105, ..., 0.118, 0.091, 0.119],
       [0.45 , 0.355, 0.12 , ..., 0.115, 0.067, 0.16 ]])

Selanjutnya membuat model regresi untuk memprediksi umur. Karena hanya ada satu tensor input, model keras.Sequential sudah cukup di sini.

abalone_model = tf.keras.Sequential([
  layers.Dense(64),
  layers.Dense(1)
])

abalone_model.compile(loss = tf.losses.MeanSquaredError(),
                      optimizer = tf.optimizers.Adam())

Untuk melatih model itu, berikan fitur dan label ke Model.fit :

abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 1s 2ms/step - loss: 63.0446
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 11.9429
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 8.4836
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 8.0052
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 7.6073
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 7.2485
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 6.9883
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 6.7977
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 6.6477
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 6.5359
<keras.callbacks.History at 0x7f70543c7350>

Anda baru saja melihat cara paling dasar untuk melatih model menggunakan data CSV. Selanjutnya, Anda akan belajar bagaimana menerapkan preprocessing untuk menormalkan kolom numerik.

Pra-pemrosesan dasar

Ini adalah praktik yang baik untuk menormalkan input ke model Anda. Lapisan prapemrosesan Keras menyediakan cara yang nyaman untuk membangun normalisasi ini ke dalam model Anda.

Lapisan akan menghitung rata-rata dan varians setiap kolom, dan menggunakannya untuk menormalkan data.

Pertama Anda membuat layer:

normalize = layers.Normalization()

Kemudian Anda menggunakan metode Normalization.adapt() untuk mengadaptasi lapisan normalisasi ke data Anda.

normalize.adapt(abalone_features)

Kemudian gunakan lapisan normalisasi dalam model Anda:

norm_abalone_model = tf.keras.Sequential([
  normalize,
  layers.Dense(64),
  layers.Dense(1)
])

norm_abalone_model.compile(loss = tf.losses.MeanSquaredError(),
                           optimizer = tf.optimizers.Adam())

norm_abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 0s 2ms/step - loss: 92.5851
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 55.1199
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 18.2937
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 6.2633
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 5.1257
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 5.0217
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9775
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9730
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9348
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9416
<keras.callbacks.History at 0x7f70541b2a50>

Tipe data campuran

Dataset "Titanic" berisi informasi tentang penumpang di Titanic. Tugas nominal pada kumpulan data ini adalah untuk memprediksi siapa yang selamat.

Titanic

Gambar dari Wikimedia

Data mentah dapat dengan mudah dimuat sebagai Pandas DataFrame , tetapi tidak langsung dapat digunakan sebagai masukan ke model TensorFlow.

titanic = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic.head()
titanic_features = titanic.copy()
titanic_labels = titanic_features.pop('survived')

Karena tipe dan rentang data yang berbeda, Anda tidak bisa begitu saja menumpuk fitur ke dalam array NumPy dan meneruskannya ke model keras.Sequential . Setiap kolom perlu ditangani secara individual.

Sebagai salah satu opsi, Anda dapat melakukan praproses data secara offline (menggunakan alat apa pun yang Anda suka) untuk mengonversi kolom kategorikal menjadi kolom numerik, lalu meneruskan output yang diproses ke model TensorFlow Anda. Kerugian dari pendekatan itu adalah jika Anda menyimpan dan mengekspor model Anda, pra-pemrosesan tidak disimpan dengannya. Lapisan prapemrosesan Keras menghindari masalah ini karena mereka adalah bagian dari model.

Dalam contoh ini, Anda akan membuat model yang mengimplementasikan logika prapemrosesan menggunakan API fungsional Keras . Anda juga bisa melakukannya dengan subclassing .

API fungsional beroperasi pada tensor "simbolis". Tensor "bersemangat" normal memiliki nilai. Sebaliknya, tensor "simbolis" ini tidak. Alih-alih, mereka melacak operasi mana yang dijalankan pada mereka, dan membangun representasi perhitungan, yang bisa Anda jalankan nanti. Berikut ini contoh singkatnya:

# Create a symbolic input
input = tf.keras.Input(shape=(), dtype=tf.float32)

# Perform a calculation using the input
result = 2*input + 1

# the result doesn't have a value
result
<KerasTensor: shape=(None,) dtype=float32 (created by layer 'tf.__operators__.add')>
calc = tf.keras.Model(inputs=input, outputs=result)
print(calc(1).numpy())
print(calc(2).numpy())
3.0
5.0

Untuk membangun model prapemrosesan, mulailah dengan membangun satu set objek keras.Input simbolis, mencocokkan nama dan tipe data kolom CSV.

inputs = {}

for name, column in titanic_features.items():
  dtype = column.dtype
  if dtype == object:
    dtype = tf.string
  else:
    dtype = tf.float32

  inputs[name] = tf.keras.Input(shape=(1,), name=name, dtype=dtype)

inputs
{'sex': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'sex')>,
 'age': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'age')>,
 'n_siblings_spouses': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'n_siblings_spouses')>,
 'parch': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'parch')>,
 'fare': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'fare')>,
 'class': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'class')>,
 'deck': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'deck')>,
 'embark_town': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'embark_town')>,
 'alone': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'alone')>}

Langkah pertama dalam logika prapemrosesan Anda adalah menggabungkan input numerik bersama-sama, dan menjalankannya melalui lapisan normalisasi:

numeric_inputs = {name:input for name,input in inputs.items()
                  if input.dtype==tf.float32}

x = layers.Concatenate()(list(numeric_inputs.values()))
norm = layers.Normalization()
norm.adapt(np.array(titanic[numeric_inputs.keys()]))
all_numeric_inputs = norm(x)

all_numeric_inputs
<KerasTensor: shape=(None, 4) dtype=float32 (created by layer 'normalization_1')>

Kumpulkan semua hasil prapemrosesan simbolis, untuk digabungkan nanti.

preprocessed_inputs = [all_numeric_inputs]

Untuk input string gunakan fungsi tf.keras.layers.StringLookup untuk memetakan dari string ke indeks integer dalam kosakata. Selanjutnya, gunakan tf.keras.layers.CategoryEncoding untuk mengubah indeks menjadi data float32 yang sesuai dengan model.

Pengaturan default untuk lapisan tf.keras.layers.CategoryEncoding membuat vektor one-hot untuk setiap input. Lapisan. layers.Embedding juga akan berfungsi. Lihat panduan dan tutorial lapisan prapemrosesan untuk lebih lanjut tentang topik ini.

for name, input in inputs.items():
  if input.dtype == tf.float32:
    continue

  lookup = layers.StringLookup(vocabulary=np.unique(titanic_features[name]))
  one_hot = layers.CategoryEncoding(max_tokens=lookup.vocab_size())

  x = lookup(input)
  x = one_hot(x)
  preprocessed_inputs.append(x)
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.

Dengan koleksi inputs dan processing_inputs , Anda dapat menggabungkan semua input yang telah processed_inputs sebelumnya, dan membangun model yang menangani pra-pemrosesan:

preprocessed_inputs_cat = layers.Concatenate()(preprocessed_inputs)

titanic_preprocessing = tf.keras.Model(inputs, preprocessed_inputs_cat)

tf.keras.utils.plot_model(model = titanic_preprocessing , rankdir="LR", dpi=72, show_shapes=True)

png

model ini hanya berisi input preprocessing. Anda dapat menjalankannya untuk melihat apa yang dilakukannya pada data Anda. Model Keras tidak secara otomatis mengonversi Pandas DataFrames karena tidak jelas apakah harus dikonversi ke satu tensor atau ke kamus tensor. Jadi konversikan ke kamus tensor:

titanic_features_dict = {name: np.array(value) 
                         for name, value in titanic_features.items()}

Potong contoh pelatihan pertama dan berikan ke model pra-pemrosesan ini, Anda melihat fitur numerik dan string one-hots semua digabungkan menjadi satu:

features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}
titanic_preprocessing(features_dict)
<tf.Tensor: shape=(1, 28), dtype=float32, numpy=
array([[-0.61 ,  0.395, -0.479, -0.497,  0.   ,  0.   ,  1.   ,  0.   ,

         0.   ,  0.   ,  1.   ,  0.   ,  0.   ,  0.   ,  0.   ,  0.   ,
         0.   ,  0.   ,  0.   ,  1.   ,  0.   ,  0.   ,  0.   ,  1.   ,
         0.   ,  0.   ,  1.   ,  0.   ]], dtype=float32)>

Sekarang buat model di atas ini:

def titanic_model(preprocessing_head, inputs):
  body = tf.keras.Sequential([
    layers.Dense(64),
    layers.Dense(1)
  ])

  preprocessed_inputs = preprocessing_head(inputs)
  result = body(preprocessed_inputs)
  model = tf.keras.Model(inputs, result)

  model.compile(loss=tf.losses.BinaryCrossentropy(from_logits=True),
                optimizer=tf.optimizers.Adam())
  return model

titanic_model = titanic_model(titanic_preprocessing, inputs)

Saat Anda melatih model, berikan kamus fitur sebagai x , dan label sebagai y .

titanic_model.fit(x=titanic_features_dict, y=titanic_labels, epochs=10)
Epoch 1/10
20/20 [==============================] - 1s 4ms/step - loss: 0.8017
Epoch 2/10
20/20 [==============================] - 0s 4ms/step - loss: 0.5913
Epoch 3/10
20/20 [==============================] - 0s 5ms/step - loss: 0.5212
Epoch 4/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4841
Epoch 5/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4615
Epoch 6/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4470
Epoch 7/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4367
Epoch 8/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4304
Epoch 9/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4265
Epoch 10/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4239
<keras.callbacks.History at 0x7f70b1f82a50>

Karena prapemrosesan adalah bagian dari model, Anda dapat menyimpan model dan memuatnya kembali di tempat lain dan mendapatkan hasil yang sama:

titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
2022-01-26 06:36:18.822459: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: test/assets
features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}

before = titanic_model(features_dict)
after = reloaded(features_dict)
assert (before-after)<1e-3
print(before)
print(after)
tf.Tensor([[-1.791]], shape=(1, 1), dtype=float32)
tf.Tensor([[-1.791]], shape=(1, 1), dtype=float32)

Menggunakan tf.data

Di bagian sebelumnya, Anda mengandalkan pengacakan dan pengelompokan data bawaan model saat melatih model.

Jika Anda memerlukan lebih banyak kontrol atas jalur data input atau perlu menggunakan data yang tidak mudah masuk ke memori: gunakan tf.data .

Untuk lebih banyak contoh lihat panduan tf.data .

Di dalam data memori

Sebagai contoh pertama penerapan tf.data ke data CSV, pertimbangkan kode berikut untuk secara manual mengiris kamus fitur dari bagian sebelumnya. Untuk setiap indeks, diperlukan indeks itu untuk setiap fitur:

import itertools

def slices(features):
  for i in itertools.count():
    # For each feature take index `i`
    example = {name:values[i] for name, values in features.items()}
    yield example

Jalankan ini dan cetak contoh pertama:

for example in slices(titanic_features_dict):
  for name, value in example.items():
    print(f"{name:19s}: {value}")
  break
sex                : male
age                : 22.0
n_siblings_spouses : 1
parch              : 0
fare               : 7.25
class              : Third
deck               : unknown
embark_town        : Southampton
alone              : n

tf.data.Dataset paling dasar dalam pemuat data memori adalah konstruktor Dataset.from_tensor_slices . Ini mengembalikan tf.data.Dataset yang mengimplementasikan versi umum dari fungsi slices di atas, di TensorFlow.

features_ds = tf.data.Dataset.from_tensor_slices(titanic_features_dict)

Anda dapat mengulangi tf.data.Dataset seperti iterable python lainnya:

for example in features_ds:
  for name, value in example.items():
    print(f"{name:19s}: {value}")
  break
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'

Fungsi from_tensor_slices dapat menangani struktur kamus atau tupel bersarang apa pun. Kode berikut membuat kumpulan data dari pasangan (features_dict, labels) :

titanic_ds = tf.data.Dataset.from_tensor_slices((titanic_features_dict, titanic_labels))

Untuk melatih model menggunakan Dataset ini, Anda harus setidaknya shuffle dan batch data.

titanic_batches = titanic_ds.shuffle(len(titanic_labels)).batch(32)

Alih-alih meneruskan features dan labels ke Model.fit , Anda meneruskan kumpulan data:

titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4230
Epoch 2/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4216
Epoch 3/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4203
Epoch 4/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4198
Epoch 5/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4194
<keras.callbacks.History at 0x7f70b12485d0>

Dari satu file

Sejauh ini tutorial ini telah bekerja dengan data dalam memori. tf.data adalah toolkit yang sangat skalabel untuk membangun jalur pipa data, dan menyediakan beberapa fungsi untuk menangani pemuatan file CSV.

titanic_file_path = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv
32768/30874 [===============================] - 0s 0us/step
40960/30874 [=======================================] - 0s 0us/step

Sekarang baca data CSV dari file dan buat tf.data.Dataset .

(Untuk dokumentasi lengkap, lihat tf.data.experimental.make_csv_dataset )

titanic_csv_ds = tf.data.experimental.make_csv_dataset(
    titanic_file_path,
    batch_size=5, # Artificially small to make examples easier to show.
    label_name='survived',
    num_epochs=1,
    ignore_errors=True,)

Fungsi ini mencakup banyak fitur yang mudah digunakan sehingga data mudah digunakan. Ini termasuk:

  • Menggunakan header kolom sebagai kunci kamus.
  • Secara otomatis menentukan jenis setiap kolom.
for batch, label in titanic_csv_ds.take(1):
  for key, value in batch.items():
    print(f"{key:20s}: {value}")
  print()
  print(f"{'label':20s}: {label}")
sex                 : [b'male' b'male' b'female' b'male' b'male']
age                 : [27. 18. 15. 46. 50.]
n_siblings_spouses  : [0 0 0 1 0]
parch               : [0 0 0 0 0]
fare                : [ 7.896  7.796  7.225 61.175 13.   ]
class               : [b'Third' b'Third' b'Third' b'First' b'Second']
deck                : [b'unknown' b'unknown' b'unknown' b'E' b'unknown']
embark_town         : [b'Southampton' b'Southampton' b'Cherbourg' b'Southampton' b'Southampton']
alone               : [b'y' b'y' b'y' b'n' b'y']

label               : [0 0 1 0 0]

Itu juga dapat mendekompresi data dengan cepat. Berikut adalah file CSV yang di-gzip yang berisi kumpulan data lalu lintas antarnegara bagian metro

Macet.

Gambar dari Wikimedia

traffic_volume_csv_gz = tf.keras.utils.get_file(
    'Metro_Interstate_Traffic_Volume.csv.gz', 
    "https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz",
    cache_dir='.', cache_subdir='traffic')
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz
409600/405373 [==============================] - 1s 1us/step
417792/405373 [==============================] - 1s 1us/step

Setel argumen compression_type untuk membaca langsung dari file terkompresi:

traffic_volume_csv_gz_ds = tf.data.experimental.make_csv_dataset(
    traffic_volume_csv_gz,
    batch_size=256,
    label_name='traffic_volume',
    num_epochs=1,
    compression_type="GZIP")

for batch, label in traffic_volume_csv_gz_ds.take(1):
  for key, value in batch.items():
    print(f"{key:20s}: {value[:5]}")
  print()
  print(f"{'label':20s}: {label[:5]}")
holiday             : [b'None' b'None' b'None' b'None' b'None']
temp                : [280.56 266.79 281.64 292.71 270.48]
rain_1h             : [0. 0. 0. 0. 0.]
snow_1h             : [0. 0. 0. 0. 0.]
clouds_all          : [46 90 90  0 64]
weather_main        : [b'Clear' b'Clouds' b'Mist' b'Clear' b'Clouds']
weather_description : [b'sky is clear' b'overcast clouds' b'mist' b'Sky is Clear'
 b'broken clouds']
date_time           : [b'2012-11-05 20:00:00' b'2012-12-17 23:00:00' b'2013-10-06 19:00:00'
 b'2013-08-23 22:00:00' b'2013-11-11 05:00:00']

label               : [2415  966 3459 2633 2576]

Cache

Ada beberapa overhead untuk menguraikan data csv. Untuk model kecil ini bisa menjadi hambatan dalam pelatihan.

Tergantung pada kasus penggunaan Anda, mungkin ide yang baik untuk menggunakan Dataset.cache atau data.experimental.snapshot sehingga data csv hanya diuraikan pada zaman pertama.

Perbedaan utama antara metode cache dan snapshot adalah bahwa file cache hanya dapat digunakan oleh proses TensorFlow yang membuatnya, tetapi file snapshot dapat dibaca oleh proses lain.

Misalnya, mengulangi traffic_volume_csv_gz_ds 20 kali, membutuhkan waktu ~15 detik tanpa cache, atau ~2 detik dengan cache.

%%time
for i, (batch, label) in enumerate(traffic_volume_csv_gz_ds.repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 14.9 s, sys: 3.7 s, total: 18.6 s
Wall time: 11.2 s
%%time
caching = traffic_volume_csv_gz_ds.cache().shuffle(1000)

for i, (batch, label) in enumerate(caching.shuffle(1000).repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 1.43 s, sys: 173 ms, total: 1.6 s
Wall time: 1.28 s
%%time
snapshot = tf.data.experimental.snapshot('titanic.tfsnap')
snapshotting = traffic_volume_csv_gz_ds.apply(snapshot).shuffle(1000)

for i, (batch, label) in enumerate(snapshotting.shuffle(1000).repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
WARNING:tensorflow:From <timed exec>:1: snapshot (from tensorflow.python.data.experimental.ops.snapshot) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.snapshot(...)`.
...............................................................................................
CPU times: user 2.17 s, sys: 460 ms, total: 2.63 s
Wall time: 1.6 s

Jika pemuatan data Anda diperlambat dengan memuat file csv, dan cache serta snapshot tidak mencukupi untuk kasus penggunaan Anda, pertimbangkan untuk mengenkode ulang data Anda ke dalam format yang lebih ramping.

Banyak file

Semua contoh sejauh ini di bagian ini dapat dengan mudah dilakukan tanpa tf.data . Satu tempat di mana tf.data benar-benar dapat menyederhanakan banyak hal adalah ketika berhadapan dengan kumpulan file.

Misalnya, kumpulan data gambar font karakter didistribusikan sebagai kumpulan file csv, satu per font.

font

Gambar oleh Willi Heidelbach dari Pixabay

Unduh dataset, dan lihat file di dalamnya:

fonts_zip = tf.keras.utils.get_file(
    'fonts.zip',  "https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip",
    cache_dir='.', cache_subdir='fonts',
    extract=True)
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip
160317440/160313983 [==============================] - 8s 0us/step
160325632/160313983 [==============================] - 8s 0us/step
import pathlib
font_csvs =  sorted(str(p) for p in pathlib.Path('fonts').glob("*.csv"))

font_csvs[:10]
['fonts/AGENCY.csv',
 'fonts/ARIAL.csv',
 'fonts/BAITI.csv',
 'fonts/BANKGOTHIC.csv',
 'fonts/BASKERVILLE.csv',
 'fonts/BAUHAUS.csv',
 'fonts/BELL.csv',
 'fonts/BERLIN.csv',
 'fonts/BERNARD.csv',
 'fonts/BITSTREAMVERA.csv']
len(font_csvs)
153

Saat menangani sekumpulan file, Anda dapat meneruskan file_pattern gaya glob ke fungsi experimental.make_csv_dataset . Urutan file dikocok setiap iterasi.

Gunakan argumen num_parallel_reads untuk mengatur berapa banyak file yang dibaca secara paralel dan disisipkan bersama-sama.

fonts_ds = tf.data.experimental.make_csv_dataset(
    file_pattern = "fonts/*.csv",
    batch_size=10, num_epochs=1,
    num_parallel_reads=20,
    shuffle_buffer_size=10000)

File csv ini memiliki gambar yang diratakan menjadi satu baris. Nama kolom diformat r{row}c{column} . Ini angkatan pertama:

for features in fonts_ds.take(1):
  for i, (name, value) in enumerate(features.items()):
    if i>15:
      break
    print(f"{name:20s}: {value}")
print('...')
print(f"[total: {len(features)} features]")
font                : [b'HANDPRINT' b'NIAGARA' b'EUROROMAN' b'NIAGARA' b'CENTAUR' b'NINA'
 b'GOUDY' b'SITKA' b'BELL' b'SITKA']
fontVariant         : [b'scanned' b'NIAGARA SOLID' b'EUROROMAN' b'NIAGARA SOLID' b'CENTAUR'
 b'NINA' b'GOUDY STOUT' b'SITKA TEXT' b'BELL MT' b'SITKA TEXT']
m_label             : [  49 8482  245   88  174 9643   77  974  117  339]
strength            : [0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4]
italic              : [0 0 0 1 0 0 1 0 1 0]
orientation         : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
m_top               : [ 0 32 24 32 28 57 38 48 51 64]
m_left              : [ 0 20 24 20 22 24 27 23 25 23]
originalH           : [20 27 55 47 50 15 51 50 27 34]
originalW           : [  4  33  25  33  50  15 116  43  28  53]
h                   : [20 20 20 20 20 20 20 20 20 20]
w                   : [20 20 20 20 20 20 20 20 20 20]
r0c0                : [  1 255 255   1   1 255   1   1   1   1]
r0c1                : [  1 255 255   1   1 255   1   1   1   1]
r0c2                : [  1 217 255   1   1 255  54   1   1   1]
r0c3                : [  1 213 255   1   1 255 255   1   1  64]
...
[total: 412 features]

Opsional: Bidang pengepakan

Anda mungkin tidak ingin bekerja dengan setiap piksel dalam kolom terpisah seperti ini. Sebelum mencoba menggunakan kumpulan data ini, pastikan untuk mengemas piksel ke dalam tensor gambar.

Berikut adalah kode yang mem-parsing nama kolom untuk membuat gambar untuk setiap contoh:

import re

def make_images(features):
  image = [None]*400
  new_feats = {}

  for name, value in features.items():
    match = re.match('r(\d+)c(\d+)', name)
    if match:
      image[int(match.group(1))*20+int(match.group(2))] = value
    else:
      new_feats[name] = value

  image = tf.stack(image, axis=0)
  image = tf.reshape(image, [20, 20, -1])
  new_feats['image'] = image

  return new_feats

Terapkan fungsi itu ke setiap batch dalam kumpulan data:

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

Plot gambar yang dihasilkan:

from matplotlib import pyplot as plt

plt.figure(figsize=(6,6), dpi=120)

for n in range(9):
  plt.subplot(3,3,n+1)
  plt.imshow(features['image'][..., n])
  plt.title(chr(features['m_label'][n]))
  plt.axis('off')

png

Fungsi tingkat yang lebih rendah

Sejauh ini tutorial ini berfokus pada utilitas tingkat tertinggi untuk membaca data csv. Ada dua API lain yang mungkin berguna bagi pengguna tingkat lanjut jika kasus penggunaan Anda tidak sesuai dengan pola dasar.

Bagian ini membuat ulang fungsionalitas yang disediakan oleh make_csv_dataset , untuk mendemonstrasikan bagaimana fungsionalitas tingkat yang lebih rendah ini dapat digunakan.

tf.io.decode_csv

Fungsi ini menerjemahkan string, atau daftar string ke dalam daftar kolom.

Tidak seperti make_csv_dataset , fungsi ini tidak mencoba menebak tipe data kolom. Anda menentukan tipe kolom dengan memberikan daftar record_defaults yang berisi nilai tipe yang benar, untuk setiap kolom.

Untuk membaca data Titanic sebagai string menggunakan decode_csv Anda akan mengatakan:

text = pathlib.Path(titanic_file_path).read_text()
lines = text.split('\n')[1:-1]

all_strings = [str()]*10
all_strings
['', '', '', '', '', '', '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=all_strings) 

for f in features:
  print(f"type: {f.dtype.name}, shape: {f.shape}")
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)

Untuk menguraikannya dengan tipe sebenarnya, buat daftar record_defaults dari tipe yang sesuai:

print(lines[0])
0,male,22.0,1,0,7.25,Third,unknown,Southampton,n
titanic_types = [int(), str(), float(), int(), int(), float(), str(), str(), str(), str()]
titanic_types
[0, '', 0.0, 0, 0, 0.0, '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=titanic_types) 

for f in features:
  print(f"type: {f.dtype.name}, shape: {f.shape}")
type: int32, shape: (627,)
type: string, shape: (627,)
type: float32, shape: (627,)
type: int32, shape: (627,)
type: int32, shape: (627,)
type: float32, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)
type: string, shape: (627,)

tf.data.experimental.CsvDataset

Kelas tf.data.experimental.CsvDataset menyediakan antarmuka Dataset CSV minimal tanpa fitur kenyamanan fungsi make_csv_dataset : penguraian header kolom, inferensi tipe kolom, pengacakan otomatis, penyisipan file.

Konstruktor berikut ini menggunakan record_defaults dengan cara yang sama seperti io.parse_csv :

simple_titanic = tf.data.experimental.CsvDataset(titanic_file_path, record_defaults=titanic_types, header=True)

for example in simple_titanic.take(1):
  print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']

Kode di atas pada dasarnya setara dengan:

def decode_titanic_line(line):
  return tf.io.decode_csv(line, titanic_types)

manual_titanic = (
    # Load the lines of text
    tf.data.TextLineDataset(titanic_file_path)
    # Skip the header row.
    .skip(1)
    # Decode the line.
    .map(decode_titanic_line)
)

for example in manual_titanic.take(1):
  print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']

Banyak file

Untuk mengurai kumpulan data font menggunakan experimental.CsvDataset , Anda harus terlebih dahulu menentukan jenis kolom untuk record_defaults . Mulailah dengan memeriksa baris pertama dari satu file:

font_line = pathlib.Path(font_csvs[0]).read_text().splitlines()[1]
print(font_line)
AGENCY,AGENCY FB,64258,0.400000,0,0.000000,35,21,51,22,20,20,1,1,1,21,101,210,255,255,255,255,255,255,255,255,255,255,255,255,255,255,1,1,1,93,255,255,255,176,146,146,146,146,146,146,146,146,216,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,141,141,141,182,255,255,255,172,141,141,141,115,1,1,1,1,163,255,255,255,255,255,255,255,255,255,255,255,255,255,255,209,1,1,1,1,163,255,255,255,6,6,6,96,255,255,255,74,6,6,6,5,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255

Hanya dua bidang pertama yang berupa string, sisanya adalah int atau float, dan Anda bisa mendapatkan jumlah total fitur dengan menghitung koma:

num_font_features = font_line.count(',')+1
font_column_types = [str(), str()] + [float()]*(num_font_features-2)

Konstruktor CsvDatasaet dapat mengambil daftar file input, tetapi membacanya secara berurutan. File pertama dalam daftar CSV adalah AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

Jadi ketika Anda meneruskan daftar file ke CsvDataaset , catatan dari AGENCY.csv dibaca terlebih dahulu:

simple_font_ds = tf.data.experimental.CsvDataset(
    font_csvs, 
    record_defaults=font_column_types, 
    header=True)
for row in simple_font_ds.take(10):
  print(row[0].numpy())
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'
b'AGENCY'

Untuk menyisipkan beberapa file, gunakan Dataset.interleave .

Berikut adalah kumpulan data awal yang berisi nama file csv:

font_files = tf.data.Dataset.list_files("fonts/*.csv")

Ini mengacak nama file setiap zaman:

print('Epoch 1:')
for f in list(font_files)[:5]:
  print("    ", f.numpy())
print('    ...')
print()

print('Epoch 2:')
for f in list(font_files)[:5]:
  print("    ", f.numpy())
print('    ...')
Epoch 1:
     b'fonts/CORBEL.csv'
     b'fonts/GLOUCESTER.csv'
     b'fonts/GABRIOLA.csv'
     b'fonts/FORTE.csv'
     b'fonts/GILL.csv'
    ...

Epoch 2:
     b'fonts/MONEY.csv'
     b'fonts/ISOC.csv'
     b'fonts/DUTCH801.csv'
     b'fonts/CALIBRI.csv'
     b'fonts/ROMANTIC.csv'
    ...

Metode interleave mengambil map_func yang membuat kumpulan Dataset anak untuk setiap elemen kumpulan data Dataset .

Di sini, Anda ingin membuat CsvDataset dari setiap elemen dataset file:

def make_font_csv_ds(path):
  return tf.data.experimental.CsvDataset(
    path, 
    record_defaults=font_column_types, 
    header=True)

Dataset yang dikembalikan oleh interleave mengembalikan elemen dengan bersepeda di atas sejumlah child- Dataset s. Perhatikan, di bawah, bagaimana siklus dataset melalui cycle_length=3 tiga file font:

font_rows = font_files.interleave(make_font_csv_ds,
                                  cycle_length=3)
fonts_dict = {'font_name':[], 'character':[]}

for row in font_rows.take(10):
  fonts_dict['font_name'].append(row[0].numpy().decode())
  fonts_dict['character'].append(chr(row[2].numpy()))

pd.DataFrame(fonts_dict)

Pertunjukan

Sebelumnya, telah dicatat bahwa io.decode_csv lebih efisien ketika dijalankan pada sekumpulan string.

Hal ini dimungkinkan untuk memanfaatkan fakta ini, saat menggunakan ukuran batch yang besar, untuk meningkatkan kinerja pemuatan CSV (tetapi coba caching terlebih dahulu).

Dengan pemuat bawaan 20, 2048 contoh batch membutuhkan waktu sekitar 17 detik.

BATCH_SIZE=2048
fonts_ds = tf.data.experimental.make_csv_dataset(
    file_pattern = "fonts/*.csv",
    batch_size=BATCH_SIZE, num_epochs=1,
    num_parallel_reads=100)
%%time
for i,batch in enumerate(fonts_ds.take(20)):
  print('.',end='')

print()
....................
CPU times: user 24.3 s, sys: 1.46 s, total: 25.7 s
Wall time: 10.9 s

Melewati kumpulan baris teks ke decode_csv berjalan lebih cepat, dalam waktu sekitar 5 detik:

fonts_files = tf.data.Dataset.list_files("fonts/*.csv")
fonts_lines = fonts_files.interleave(
    lambda fname:tf.data.TextLineDataset(fname).skip(1), 
    cycle_length=100).batch(BATCH_SIZE)

fonts_fast = fonts_lines.map(lambda x: tf.io.decode_csv(x, record_defaults=font_column_types))
%%time
for i,batch in enumerate(fonts_fast.take(20)):
  print('.',end='')

print()
....................
CPU times: user 8.77 s, sys: 0 ns, total: 8.77 s
Wall time: 1.57 s

Untuk contoh lain meningkatkan kinerja csv dengan menggunakan batch besar, lihat tutorial overfit dan underfit .

Pendekatan semacam ini mungkin berhasil, tetapi pertimbangkan opsi lain seperti cache dan snapshot , atau enkode ulang data Anda ke dalam format yang lebih ramping.