Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Laden Sie CSV-Daten

Ansicht auf TensorFlow.org Quelle auf GitHub anzeigenNotizbuch herunterladen

Dieses Tutorial enthält Beispiele für die Verwendung von CSV-Daten mit TensorFlow.

Dies besteht aus zwei Hauptteilen:

  1. Laden der Daten von der Festplatte
  2. Vorverarbeitung in eine für das Training geeignete Form.

Dieses Tutorial konzentriert sich auf das Laden und enthält einige kurze Beispiele für die Vorverarbeitung. Ein Tutorial, das sich auf den Vorverarbeitungsaspekt konzentriert, finden Sie in der Anleitung und im Tutorial für Vorverarbeitungsebenen .

Konfiguration

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
from tensorflow.keras.layers.experimental import preprocessing

In Speicherdaten

Für jedes kleine CSV-Dataset besteht die einfachste Möglichkeit, ein TensorFlow-Modell darauf zu trainieren, darin, es als Pandas-Datenrahmen oder NumPy-Array in den Speicher zu laden.

Ein relativ einfaches Beispiel ist der Abalone-Datensatz .

  • Der Datensatz ist klein.
  • Alle Eingabefunktionen sind Gleitkommawerte mit begrenztem Bereich.

So laden Sie die Daten in einen 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()

Der Datensatz enthält eine Reihe von Messungen von Abalone , einer Art Meeresschnecke.

eine Abalone-Muschel

"Abalone Shell" (von Nicki Dugan Pogue , CC BY-SA 2.0)

Die nominelle Aufgabe für diesen Datensatz besteht darin, das Alter von den anderen Messungen vorherzusagen. Trennen Sie daher die Merkmale und Bezeichnungen für das Training:

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

Für diesen Datensatz werden alle Funktionen gleich behandelt. Packen Sie die Funktionen in ein einziges NumPy-Array:

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

Lassen Sie als nächstes ein Regressionsmodell das Alter vorhersagen. Da es nur einen einzigen Eingangstensor gibt, ist hier ein keras.Sequential ausreichend.

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

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

Um dieses Modell zu trainieren, übergeben Sie die Funktionen und Beschriftungen an Model.fit :

abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 0s 1ms/step - loss: 60.2584
Epoch 2/10
104/104 [==============================] - 0s 1ms/step - loss: 11.4741
Epoch 3/10
104/104 [==============================] - 0s 1ms/step - loss: 8.5354
Epoch 4/10
104/104 [==============================] - 0s 1ms/step - loss: 8.0559
Epoch 5/10
104/104 [==============================] - 0s 1ms/step - loss: 7.6156
Epoch 6/10
104/104 [==============================] - 0s 1ms/step - loss: 7.2644
Epoch 7/10
104/104 [==============================] - 0s 1ms/step - loss: 6.9853
Epoch 8/10
104/104 [==============================] - 0s 1ms/step - loss: 6.7824
Epoch 9/10
104/104 [==============================] - 0s 1ms/step - loss: 6.6324
Epoch 10/10
104/104 [==============================] - 0s 1ms/step - loss: 6.5348

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

Sie haben gerade die grundlegendste Methode zum Trainieren eines Modells mithilfe von CSV-Daten gesehen. Als Nächstes lernen Sie, wie Sie die Vorverarbeitung anwenden, um numerische Spalten zu normalisieren.

Grundlegende Vorverarbeitung

Es wird empfohlen, die Eingaben in Ihr Modell zu normalisieren. Die experimental.preprocessing Vorverarbeitungsebenen bieten eine bequeme Möglichkeit, diese Normalisierung in Ihr Modell zu integrieren.

Die Ebene berechnet den Mittelwert und die Varianz jeder Spalte vor und normalisiert damit die Daten.

Zuerst erstellen Sie die Ebene:

normalize = preprocessing.Normalization()

Anschließend verwenden Sie die Methode Normalization.adapt() , um die Normalisierungsebene an Ihre Daten anzupassen.

normalize.adapt(abalone_features)

Verwenden Sie dann die Normalisierungsebene in Ihrem Modell:

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: 93.4577
Epoch 2/10
104/104 [==============================] - 0s 1ms/step - loss: 54.7412
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 16.7861
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 5.8002
Epoch 5/10
104/104 [==============================] - 0s 1ms/step - loss: 4.9725
Epoch 6/10
104/104 [==============================] - 0s 1ms/step - loss: 4.9332
Epoch 7/10
104/104 [==============================] - 0s 1ms/step - loss: 4.9074
Epoch 8/10
104/104 [==============================] - 0s 1ms/step - loss: 4.9035
Epoch 9/10
104/104 [==============================] - 0s 1ms/step - loss: 4.9028
Epoch 10/10
104/104 [==============================] - 0s 1ms/step - loss: 4.8998

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

Gemischte Datentypen

Der Datensatz "Titanic" enthält Informationen zu den Passagieren auf der Titanic. Die nominelle Aufgabe dieses Datensatzes besteht darin, vorherzusagen, wer überlebt hat.

Die Titanic

Bild aus Wikimedia

Die Rohdaten können problemlos als Pandas DataFrame geladen werden, können jedoch nicht sofort als Eingabe für ein TensorFlow-Modell verwendet werden.

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

Aufgrund der unterschiedlichen Datentypen und Bereiche können Sie die Features nicht einfach in ein NumPy-Array stapeln und an ein keras.Sequential Modell übergeben. Jede Spalte muss einzeln behandelt werden.

Als eine Option können Sie Ihre Daten offline (mit einem beliebigen Tool) vorverarbeiten, um kategoriale Spalten in numerische Spalten zu konvertieren, und dann die verarbeitete Ausgabe an Ihr TensorFlow-Modell übergeben. Der Nachteil dieses Ansatzes besteht darin, dass beim Speichern und Exportieren Ihres Modells die Vorverarbeitung nicht damit gespeichert wird. Die experimental.preprocessing Vorverarbeitungsebenen vermeiden dieses Problem, da sie Teil des Modells sind.

In diesem Beispiel erstellen Sie ein Modell, das die Vorverarbeitungslogik mithilfe der Keras-Funktions-API implementiert. Sie können dies auch durch Unterklassen tun.

Die funktionale API arbeitet mit "symbolischen" Tensoren. Normale "eifrige" Tensoren haben einen Wert. Im Gegensatz dazu tun dies diese "symbolischen" Tensoren nicht. Stattdessen verfolgen sie, welche Vorgänge auf ihnen ausgeführt werden, und erstellen eine Darstellung der Berechnung, die Sie später ausführen können. Hier ist ein kurzes Beispiel:

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

# Do a calculation using is
result = 2*input + 1

# the result doesn't have a value
result
<tf.Tensor 'AddV2:0' shape=(None,) dtype=float32>
calc = tf.keras.Model(inputs=input, outputs=result)
print(calc(1).numpy())
print(calc(2).numpy())
3.0
5.0

Um das Vorverarbeitungsmodell zu keras.Input , keras.Input eine Reihe symbolischer keras.Input Objekte, die mit den Namen und Datentypen der CSV-Spalten übereinstimmen.

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': <tf.Tensor 'sex:0' shape=(None, 1) dtype=string>,
 'age': <tf.Tensor 'age:0' shape=(None, 1) dtype=float32>,
 'n_siblings_spouses': <tf.Tensor 'n_siblings_spouses:0' shape=(None, 1) dtype=float32>,
 'parch': <tf.Tensor 'parch:0' shape=(None, 1) dtype=float32>,
 'fare': <tf.Tensor 'fare:0' shape=(None, 1) dtype=float32>,
 'class': <tf.Tensor 'class:0' shape=(None, 1) dtype=string>,
 'deck': <tf.Tensor 'deck:0' shape=(None, 1) dtype=string>,
 'embark_town': <tf.Tensor 'embark_town:0' shape=(None, 1) dtype=string>,
 'alone': <tf.Tensor 'alone:0' shape=(None, 1) dtype=string>}

Der erste Schritt in Ihrer Vorverarbeitungslogik besteht darin, die numerischen Eingaben miteinander zu verketten und sie durch eine Normalisierungsschicht zu führen:

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

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

all_numeric_inputs
<tf.Tensor 'normalization_1/truediv:0' shape=(None, 4) dtype=float32>

Sammeln Sie alle symbolischen Vorverarbeitungsergebnisse, um sie später zu verketten.

preprocessed_inputs = [all_numeric_inputs]

Verwenden Sie für die Zeichenfolgeneingaben die Funktion preprocessing.StringLookup , um Zeichenfolgen auf ganzzahlige Indizes in einem Vokabular abzubilden. Als nächste Verwendung preprocessing.CategoryEncoding die Indizes in konvertieren float32 Daten angemessen für das Modell.

Die Standardeinstellungen für die Ebene " preprocessing.CategoryEncoding erstellen für jede Eingabe einen One-Hot-Vektor. Ein layers.Embedding würde auch funktionieren. Weitere Informationen zu diesem Thema finden Sie im Handbuch und im Lernprogramm für Vorverarbeitungsebenen .

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

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

  x = lookup(input)
  x = one_hot(x)
  preprocessed_inputs.append(x)

Mit der Sammlung von inputs und processed_inputs inputs processed_inputs Sie alle vorverarbeiteten Eingaben miteinander verketten und ein Modell erstellen, das die Vorverarbeitung übernimmt:

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

Dieses model enthält nur die Eingabevorverarbeitung. Sie können es ausführen, um zu sehen, was es mit Ihren Daten macht. Keras-Modelle konvertieren Pandas DataFrames nicht automatisch, da nicht klar ist, ob sie in einen Tensor oder in ein DataFrames konvertiert werden sollen. Konvertieren Sie es also in ein Tensorwörterbuch:

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

Schneiden Sie das erste Trainingsbeispiel aus und übergeben Sie es an dieses Vorverarbeitungsmodell. Sie sehen die numerischen Merkmale und die Zeichenfolge One-Hots, die alle miteinander verknüpft sind:

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

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

Bauen Sie nun das Modell darauf auf:

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)

Wenn Sie das Modell trainieren, übergeben Sie das Wörterbuch der Features als x und die Bezeichnung als y .

titanic_model.fit(x=titanic_features_dict, y=titanic_labels, epochs=10)
Epoch 1/10
20/20 [==============================] - 0s 3ms/step - loss: 0.6595
Epoch 2/10
20/20 [==============================] - 0s 3ms/step - loss: 0.5576
Epoch 3/10
20/20 [==============================] - 0s 3ms/step - loss: 0.5002
Epoch 4/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4719
Epoch 5/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4533
Epoch 6/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4415
Epoch 7/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4346
Epoch 8/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4299
Epoch 9/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4285
Epoch 10/10
20/20 [==============================] - 0s 3ms/step - loss: 0.4261

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

Da die Vorverarbeitung Teil des Modells ist, können Sie das Modell speichern und an einer anderen Stelle neu laden, um identische Ergebnisse zu erhalten:

titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: test/assets
WARNING:tensorflow:5 out of the last 5 calls to <function recreate_function.<locals>.restored_function_body at 0x7f3f60030950> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.

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.903]], shape=(1, 1), dtype=float32)
tf.Tensor([[-1.903]], shape=(1, 1), dtype=float32)

Verwenden von tf.data

Im vorherigen Abschnitt haben Sie sich beim Training des Modells auf das integrierte Mischen und Stapeln von Daten des Modells verlassen.

Wenn Sie mehr Kontrolle über die Eingabedaten-Pipeline benötigen oder Daten verwenden müssen, die nicht einfach in den Speicher passen: Verwenden Sie tf.data .

Weitere Beispiele finden Sie im tf.data-Handbuch .

Ein in Speicherdaten

Betrachten Sie als erstes Beispiel für die Anwendung von tf.data auf CSV-Daten den folgenden Code, um das Wörterbuch der Funktionen aus dem vorherigen Abschnitt manuell tf.data . Für jeden Index wird dieser Index für jedes Feature verwendet:

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

Führen Sie dies aus und drucken Sie das erste Beispiel:

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

Das grundlegendstetf.data.Dataset im Speicherdatenlader ist der Konstruktor Dataset.from_tensor_slices . Dies gibt eintf.data.Dataset , das eine verallgemeinerte Version der obigen slices Funktion in TensorFlow implementiert.

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

Sie können eintf.data.Dataset wie jedes andere iterierbare Python durchlaufen:

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'

Die Funktion from_tensor_slices kann jede Struktur verschachtelter Wörterbücher oder Tupel verarbeiten. Der folgende Code erstellt einen Datensatz aus (features_dict, labels) Paaren:

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

Um ein Modell zu trainieren , dies mit Dataset , müssen Sie mindestens shuffle und batch - Daten.

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

Anstatt features und labels an Model.fit zu Model.fit , übergeben Sie den Datensatz:

titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4233
Epoch 2/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4223
Epoch 3/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4225
Epoch 4/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4213
Epoch 5/5
20/20 [==============================] - 0s 4ms/step - loss: 0.4209

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

Aus einer einzigen Datei

Bisher hat dieses Tutorial mit In-Memory-Daten gearbeitet. tf.data ist ein hoch skalierbares Toolkit zum tf.data und bietet einige Funktionen zum Laden von CSV-Dateien.

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

Lesen Sie nun die CSV-Daten aus der Datei und erstellen Sie eintf.data.Dataset .

(Die vollständige Dokumentation finden Sie unter 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,)

Diese Funktion enthält viele praktische Funktionen, mit denen die Daten einfach verarbeitet werden können. Das beinhaltet:

  • Verwenden der Spaltenüberschriften als Wörterbuchschlüssel.
  • Automatische Ermittlung des Typs jeder Spalte.
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'female']
age                 : [28. 11. 29. 28. 34.]
n_siblings_spouses  : [1 0 1 0 0]
parch               : [0 0 0 0 0]
fare                : [ 24.15   18.788  26.    221.779  10.5  ]
class               : [b'Third' b'Third' b'Second' b'First' b'Second']
deck                : [b'unknown' b'unknown' b'unknown' b'C' b'F']
embark_town         : [b'Queenstown' b'Cherbourg' b'Southampton' b'Southampton' b'Southampton']
alone               : [b'n' b'y' b'n' b'y' b'y']

label               : [0 0 1 0 1]

Es kann auch die Daten im laufenden Betrieb dekomprimieren. Hier ist eine komprimierte CSV-Datei, die den U-Bahn-Verkehrsdatensatz enthält

Ein Stau.

Bild aus 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 2us/step

Stellen Sie das Argument compression_type , dass es direkt aus der komprimierten Datei liest:

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.66 283.22 284.23 280.7  287.79]
rain_1h             : [0. 0. 0. 0. 0.]
snow_1h             : [0. 0. 0. 0. 0.]
clouds_all          : [92 90 90 64 75]
weather_main        : [b'Mist' b'Drizzle' b'Rain' b'Clouds' b'Clouds']
weather_description : [b'mist' b'light intensity drizzle' b'light rain' b'broken clouds'
 b'broken clouds']
date_time           : [b'2012-10-19 21:00:00' b'2012-10-25 21:00:00' b'2013-05-23 16:00:00'
 b'2013-11-19 14:00:00' b'2013-05-16 08:00:00']

label               : [2942 2587 6305 5242 6404]

Caching

Das Parsen der CSV-Daten ist mit einem gewissen Aufwand verbunden. Bei kleinen Modellen kann dies der Engpass im Training sein.

Abhängig von Ihrem Anwendungsfall ist es möglicherweise eine gute Idee, Dataset.cache oder data.experimental.snapshot damit die CSV-Daten nur in der ersten Epoche analysiert werden.

Der Hauptunterschied zwischen der cache und der snapshot Methode besteht darin, dass cache Dateien nur von dem TensorFlow-Prozess verwendet werden können, der sie erstellt hat, snapshot Dateien jedoch von anderen Prozessen gelesen werden können.

Zum Beispiel traffic_volume_csv_gz_ds 20- traffic_volume_csv_gz_ds des traffic_volume_csv_gz_ds ~ 15 Sekunden ohne Caching oder ~ 2 Sekunden mit Caching.

%%time
for i, (batch, label) in enumerate(traffic_volume_csv_gz_ds.repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 15.4 s, sys: 3.98 s, total: 19.4 s
Wall time: 12.4 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.48 s, sys: 179 ms, total: 1.65 s
Wall time: 1.32 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()
...............................................................................................
CPU times: user 2.09 s, sys: 449 ms, total: 2.54 s
Wall time: 1.64 s

Wenn das Laden Ihrer Daten durch das Laden von CSV-Dateien verlangsamt wird und cache und snapshot für Ihren Anwendungsfall nicht ausreichen, sollten Sie Ihre Daten in ein optimierteres Format umcodieren.

Mehrere Dateien

Alle bisherigen Beispiele in diesem Abschnitt könnten problemlos ohne tf.data . Ein Ort, an dem tf.data die Dinge wirklich vereinfachen kann, ist der Umgang mit tf.data .

Beispielsweise wird der Datensatz für Zeichenschriftbilder als Sammlung von CSV-Dateien verteilt, eine pro Schriftart.

Schriftarten

Bild von Willi Heidelbach aus Pixabay

Laden Sie den Datensatz herunter und sehen Sie sich die darin enthaltenen Dateien an:

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

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

Wenn Sie mit einer Reihe von Dateien arbeiten, können Sie ein file_pattern an die Funktion experimental.make_csv_dataset . Die Reihenfolge der Dateien wird bei jeder Iteration gemischt.

Verwenden Sie das Argument num_parallel_reads , um num_parallel_reads , wie viele Dateien parallel gelesen und verschachtelt werden.

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)

Bei diesen CSV-Dateien werden die Bilder in einer einzigen Zeile abgeflacht. Die Spaltennamen sind mit r{row}c{column} formatiert. Hier ist die erste Charge:

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'HARRINGTON' b'ISOC' b'GEORGIA' b'CAMBRIA' b'PROXY' b'SNAP'
 b'COUNTRYBLUEPRINT' b'PROXY' b'VIVALDI' b'GILL']
fontVariant         : [b'HARRINGTON' b'ISOCTEUR' b'GEORGIA' b'CAMBRIA' b'PROXY 9' b'SNAP ITC'
 b'COUNTRYBLUEPRINT' b'PROXY 9' b'VIVALDI'
 b'GILL SANS ULTRA BOLD CONDENSED']
m_label             : [   94  8800  8539 10659   305  8223  8217   728   170   115]
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 0 1 0 0 1 0 0]
orientation         : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
m_top               : [49 55 35 46 52 41 38 36 36 46]
m_left              : [22 33 24 22 27 23 24 40 27 21]
originalH           : [17 30 48 37 34 59 54  9 25 41]
originalW           : [30 21 62 39 12 34 25 24 27 26]
h                   : [20 20 20 20 20 20 20 20 20 20]
w                   : [20 20 20 20 20 20 20 20 20 20]
r0c0                : [  1   1   1   1   1 255 255 213   1   1]
r0c1                : [  1   1   3   1   1 255 255   1   1   1]
r0c2                : [  1   1  88   1   1 255 255   1   1   1]
r0c3                : [  1   1 232   1   1 255 255   1   1   1]
...
[total: 412 features]

Optional: Felder packen

Sie möchten wahrscheinlich nicht mit jedem Pixel in separaten Spalten wie diesen arbeiten. Bevor Sie versuchen, diesen Datensatz zu verwenden, müssen Sie die Pixel in einen Bildtensor packen.

Hier ist Code, der die Spaltennamen analysiert, um Bilder für jedes Beispiel zu erstellen:

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

Wenden Sie diese Funktion auf jede Charge im Datensatz an:

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

Zeichnen Sie die resultierenden Bilder:

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

Funktionen der unteren Ebene

Bisher konzentrierte sich dieses Tutorial auf die Dienstprogramme auf höchster Ebene zum Lesen von CSV-Daten. Es gibt zwei weitere APIs, die für fortgeschrittene Benutzer hilfreich sein können, wenn Ihr Anwendungsfall nicht den Grundmustern entspricht.

In diesem Abschnitt werden die von make_csv_dataset bereitgestellten Funktionen neu make_csv_dataset , um zu demonstrieren, wie diese Funktionen auf niedrigerer Ebene verwendet werden können.

tf.io.decode_csv

Diese Funktion decodiert eine Zeichenfolge oder eine Liste von Zeichenfolgen in eine Liste von Spalten.

Im Gegensatz zu make_csv_dataset diese Funktion nicht, make_csv_dataset zu erraten. Sie geben die record_defaults , indem Sie für jede Spalte eine Liste von record_defaults die einen Wert des richtigen Typs enthält.

Um die Titanic-Daten mit decode_csv als Strings zu decode_csv , würden Sie sagen:

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

Erstellen Sie eine Liste der record_defaults der entsprechenden Typen, um sie mit ihren tatsächlichen Typen zu analysieren:

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

Die Klasse tf.data.experimental.CsvDataset bietet eine minimale CSV- Dataset Schnittstelle ohne die praktischen Funktionen der Funktion make_csv_dataset : Spaltenüberschriftenanalyse, Inferenz von make_csv_dataset , automatisches Mischen, Verschachteln von Dateien.

Dieser folgende Konstruktor verwendet record_defaults dieselbe Weise wie 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']

Der obige Code entspricht im Wesentlichen:

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

Mehrere Dateien

Um das Fonts-Dataset mit experimental.CsvDataset zu analysieren, müssen Sie zunächst die record_defaults für die record_defaults . Überprüfen Sie zunächst die erste Zeile einer Datei:

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

Nur die ersten beiden Felder sind Zeichenfolgen, der Rest sind Ints oder Floats, und Sie können die Gesamtzahl der Features durch Zählen der Kommas ermitteln:

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

Der CsvDatasaet Konstruktor kann eine Liste von Eingabedateien erstellen, diese jedoch nacheinander lesen. Die erste Datei in der Liste der CSVs ist AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

Wenn Sie also die Liste der Dateien an CsvDataaset AGENCY.csv werden zuerst die Datensätze von AGENCY.csv gelesen:

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'

Verwenden Sie Dataset.interleave , um mehrere Dateien zu verschachteln.

Hier ist ein erster Datensatz, der die CSV-Dateinamen enthält:

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

Dies mischt die Dateinamen jeder Epoche:

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/ELEPHANT.csv'
     b'fonts/NINA.csv'
     b'fonts/COPPERPLATE.csv'
     b'fonts/GOTHICE.csv'
     b'fonts/SWIS721.csv'
    ...

Epoch 2:
     b'fonts/PALACE.csv'
     b'fonts/GABRIOLA.csv'
     b'fonts/COURIER.csv'
     b'fonts/CONSTANTIA.csv'
     b'fonts/QUICKTYPE.csv'
    ...

Die interleave Methode nimmt einen map_func die einen Kind- schafft Dataset für jedes Element des Eltern- Dataset .

Hier möchten CsvDataset aus jedem Element des CsvDataset ein CsvDataset erstellen:

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

Der Dataset zurück durch Verschachtelung kehrt Elemente durch eine Anzahl der Kind- cycling über Dataset s. Beachten Sie unten, wie der Datensatz über cycle_length)=3 drei Schriftdateien wechselt:

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)

Performance

Zuvor wurde festgestellt, dass io.decode_csv effizienter ist, wenn es mit einem Stapel von Zeichenfolgen ausgeführt wird.

Es ist möglich, diese Tatsache bei Verwendung großer Stapelgrößen zu nutzen, um die CSV-Ladeleistung zu verbessern (versuchen Sie jedoch zuerst das Caching ).

Mit dem eingebauten Lader 20 dauern Chargen mit 2048 Beispielen etwa 17 Sekunden.

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 28.9 s, sys: 2.76 s, total: 31.7 s
Wall time: 11.7 s

Das Übergeben von Textzeilenstapeln an decode_csv wird in ca. 5 decode_csv schneller ausgeführt:

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 5.4 s, sys: 0 ns, total: 5.4 s
Wall time: 4.84 s

Ein weiteres Beispiel für die Steigerung der CSV- Leistung durch Verwendung großer Stapel finden Sie im Tutorial zu Über- und Unteranpassungen .

Diese Art von Ansatz mag funktionieren, Sie sollten jedoch andere Optionen wie cache und snapshot Betracht ziehen oder Ihre Daten in ein optimierteres Format umcodieren.