Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Carica i dati CSV

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza sorgente su GitHubScarica notebook

Questo tutorial fornisce esempi di come utilizzare i dati CSV con TensorFlow.

Ci sono due parti principali di questo:

  1. Caricamento dei dati dal disco
  2. Pre-elaborarlo in una forma adatta per la formazione.

Questo tutorial si concentra sul caricamento e fornisce alcuni rapidi esempi di pre-elaborazione. Per un tutorial che si concentra sull'aspetto della pre-elaborazione, vedere la guida e il tutorial sui livelli di pre - elaborazione .

Impostare

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 memoria dati

Per qualsiasi set di dati CSV di piccole dimensioni, il modo più semplice per addestrare un modello TensorFlow su di esso è caricarlo in memoria come un Dataframe panda o un array NumPy.

Un esempio relativamente semplice è il set di dati abalone .

  • Il set di dati è piccolo.
  • Tutte le funzionalità di input sono tutte valori in virgola mobile a intervallo limitato.

Ecco come scaricare i dati in un 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()

Il set di dati contiene una serie di misurazioni dell'abalone , un tipo di lumaca di mare.

una conchiglia di abalone

"Abalone shell" (di Nicki Dugan Pogue , CC BY-SA 2.0)

Il compito nominale per questo set di dati è prevedere l'età dalle altre misurazioni, quindi separa le caratteristiche e le etichette per l'addestramento:

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

Per questo set di dati tratterai tutte le funzionalità in modo identico. Comprimi le funzionalità in un singolo array NumPy .:

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

Quindi crea un modello di regressione predire l'età. Poiché esiste un solo tensore di input, qui è sufficiente un modello keras.Sequential .

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

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

Per addestrare quel modello, passa le caratteristiche e le etichette a 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>

Hai appena visto il modo più semplice per addestrare un modello utilizzando i dati CSV. Successivamente, imparerai come applicare la preelaborazione per normalizzare le colonne numeriche.

Pre-elaborazione di base

È buona norma normalizzare gli input per il modello. I livelli experimental.preprocessing forniscono un modo conveniente per costruire questa normalizzazione nel tuo modello.

Il livello calcolerà in anticipo la media e la varianza di ciascuna colonna e le utilizzerà per normalizzare i dati.

Per prima cosa crei il livello:

normalize = preprocessing.Normalization()

Quindi si utilizza il metodo Normalization.adapt() per adattare il livello di normalizzazione ai dati.

normalize.adapt(abalone_features)

Quindi usa il livello di normalizzazione nel tuo modello:

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>

Tipi di dati misti

Il set di dati "Titanic" contiene informazioni sui passeggeri del Titanic. Il compito nominale su questo set di dati è prevedere chi è sopravvissuto.

Il Titanic

Immagine da Wikimedia

I dati grezzi possono essere facilmente caricati come Pandas DataFrame , ma non sono immediatamente utilizzabili come input per un modello 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')

A causa dei diversi tipi di dati e intervalli non è possibile semplicemente impilare le funzionalità in un array NumPy e passarle a un modello keras.Sequential . Ogni colonna deve essere gestita individualmente.

Come opzione, puoi preelaborare i tuoi dati offline (utilizzando qualsiasi strumento che ti piace) per convertire le colonne categoriali in colonne numeriche, quindi passare l'output elaborato al tuo modello TensorFlow. Lo svantaggio di questo approccio è che se si salva ed esporta il modello, la preelaborazione non viene salvata con esso. I livelli experimental.preprocessing evitano questo problema perché fanno parte del modello.

In questo esempio, creerai un modello che implementa la logica di pre-elaborazione utilizzando l' API funzionale di Keras . Puoi anche farlo creando delle sottoclassi .

L'API funzionale opera su tensori "simbolici". I normali tensori "desiderosi" hanno un valore. Al contrario, questi tensori "simbolici" non lo fanno. Invece, tengono traccia di quali operazioni vengono eseguite su di essi e creano una rappresentazione del calcolo che è possibile eseguire in seguito. Ecco un rapido esempio:

# 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

Per costruire il modello di pre-elaborazione, inizia creando un insieme di oggetti keras.Input simbolici, corrispondenti ai nomi e ai tipi di dati delle colonne 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': <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>}

Il primo passaggio nella logica di pre-elaborazione consiste nel concatenare gli input numerici insieme ed eseguirli attraverso un livello di normalizzazione:

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>

Raccogliere tutti i risultati simbolici della preelaborazione, per concatenarli in seguito.

preprocessed_inputs = [all_numeric_inputs]

Per gli input di stringa, utilizzare la funzione preprocessing.StringLookup per mappare da stringhe a indici interi in un vocabolario. Successivamente, utilizza preprocessing.CategoryEncoding per convertire gli indici in dati float32 appropriati per il modello.

Le impostazioni predefinite per il livello preprocessing.CategoryEncoding creano un vettore caldo per ogni input. Anche l' layers.Embedding funzionerebbe. Per ulteriori informazioni su questo argomento, vedere la guida e il tutorial sulla pre - elaborazione dei livelli .

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)

Con la raccolta di inputs e processed_inputs , puoi concatenare tutti gli input preelaborati insieme e costruire un modello che gestisce la preelaborazione:

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

Questo model contiene solo la preelaborazione dell'input. Puoi eseguirlo per vedere cosa fa ai tuoi dati. I modelli Keras non convertono automaticamente i Pandas DataFrames perché non è chiaro se debba essere convertito in un tensore o in un dizionario di tensori. Quindi convertilo in un dizionario di tensori:

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

Taglia il primo esempio di addestramento e passalo a questo modello di preelaborazione, vedrai le caratteristiche numeriche e le stringhe one-hots tutte concatenate insieme:

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

Ora costruisci il modello sopra questo:

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)

Quando addestrate il modello, passate il dizionario delle caratteristiche come x e l'etichetta come 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>

Poiché la preelaborazione fa parte del modello, è possibile salvare il modello e ricaricarlo da qualche altra parte e ottenere risultati identici:

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)

Utilizzando tf.data

Nella sezione precedente ti sei affidato allo shuffling e all'invio in batch dei dati incorporati nel modello durante l'addestramento del modello.

Se è necessario un maggiore controllo sulla pipeline dei dati di input o è necessario utilizzare dati che non si adattano facilmente alla memoria: utilizzare tf.data .

Per ulteriori esempi vedere la guida tf.data .

Acceso in memoria dati

Come primo esempio di applicazione di tf.data ai dati CSV, considera il codice seguente per tf.data manualmente il dizionario delle funzionalità dalla sezione precedente. Per ogni indice, prende quell'indice per ogni caratteristica:

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

Esegui questo e stampa il primo esempio:

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

Iltf.data.Dataset più semplice nel caricatore di dati di memoria è il costruttore Dataset.from_tensor_slices . Ciò restituisce untf.data.Dataset che implementa una versione generalizzata della funzione slices sopra, in TensorFlow.

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

Puoi iterare su untf.data.Dataset come qualsiasi altro iterabile Python:

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'

La funzione from_tensor_slices può gestire qualsiasi struttura di dizionari annidati o tuple. Il codice seguente crea un set di dati di coppie (features_dict, labels) :

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

Per addestrare un modello utilizzando questo Dataset , dovrai almeno shuffle e batch i dati.

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

Invece di passare features ed labels a Model.fit , passi il set di dati:

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>

Da un unico file

Finora questo tutorial ha funzionato con i dati in memoria. tf.data è un toolkit altamente scalabile per la creazione di pipeline di dati e fornisce alcune funzioni per gestire il caricamento dei 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

Ora leggi i dati CSV dal file e crea untf.data.Dataset .

(Per la documentazione completa, vedere 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,)

Questa funzione include molte funzioni utili in modo che i dati siano facili da lavorare. Ciò comprende:

  • Utilizzo delle intestazioni di colonna come chiavi del dizionario.
  • Determinazione automatica del tipo di ogni colonna.
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]

Può anche decomprimere i dati al volo. Ecco un file CSV compresso con gzip contenente il set di dati sul traffico interstatale della metropolitana

Un ingorgo.

Immagine da 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

Imposta l'argomento compression_type per leggere direttamente dal file compresso:

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

Esiste un sovraccarico nell'analisi dei dati csv. Per i modelli piccoli questo può essere il collo di bottiglia nell'addestramento.

A seconda del tuo caso d'uso, potrebbe essere una buona idea usare Dataset.cache o data.experimental.snapshot modo che i dati csv vengano analizzati solo nella prima epoca.

La differenza principale tra i metodi cache e snapshot è che cache file cache possono essere utilizzati solo dal processo TensorFlow che li ha creati, ma i file snapshot possono essere letti da altri processi.

Ad esempio, l'iterazione su traffic_volume_csv_gz_ds 20 volte, richiede ~ 15 secondi senza memorizzazione nella cache o ~ 2 secondi con memorizzazione nella 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 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

Se il caricamento dei dati viene rallentato dal caricamento dei file CSV e la cache e l' snapshot non sono sufficienti per il tuo caso d'uso, valuta la possibilità di ricodificare i dati in un formato più semplificato.

Più file

Tutti gli esempi finora in questa sezione potrebbero essere facilmente fatti senza tf.data . Un luogo in cui tf.data può davvero semplificare le cose è quando si tratta di raccolte di file.

Ad esempio, il set di dati delle immagini dei caratteri dei caratteri è distribuito come una raccolta di file CSV, uno per carattere.

Caratteri

Foto di Willi Heidelbach da Pixabay

Scarica il set di dati e dai un'occhiata ai file all'interno:

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

Quando si ha a che fare con un mucchio di file, è possibile passare un file_pattern stile file_pattern alla funzione experimental.make_csv_dataset . L'ordine dei file viene mischiato a ogni iterazione.

Utilizza l'argomento num_parallel_reads per impostare il numero di file letti in parallelo e intercalati insieme.

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)

Questi file CSV hanno le immagini appiattite in una singola riga. I nomi delle colonne sono formattati come r{row}c{column} . Ecco il primo lotto:

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]

Opzionale: campi di imballaggio

Probabilmente non vuoi lavorare con ogni pixel in colonne separate come questa. Prima di provare a utilizzare questo set di dati assicurati di comprimere i pixel in un tensore di immagini.

Ecco il codice che analizza i nomi delle colonne per creare immagini per ogni esempio:

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

Applica quella funzione a ogni batch nel set di dati:

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

Traccia le immagini risultanti:

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

Funzioni di livello inferiore

Finora questo tutorial si è concentrato sulle utilità di livello più alto per la lettura dei dati csv. Esistono altre due API che possono essere utili per utenti avanzati se il tuo caso d'uso non si adatta ai modelli di base.

Questa sezione ricrea la funzionalità fornita da make_csv_dataset , per dimostrare come è possibile utilizzare questa funzionalità di livello inferiore.

tf.io.decode_csv

Questa funzione decodifica una stringa o un elenco di stringhe in un elenco di colonne.

A differenza di make_csv_dataset questa funzione non cerca di indovinare i tipi di dati delle colonne. Puoi specificare i tipi di colonna fornendo un elenco di record_defaults contenente un valore del tipo corretto, per ogni colonna.

Per leggere i dati del Titanic come stringhe usando decode_csv diresti:

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

Per analizzarli con i loro tipi effettivi, crea un elenco di record_defaults dei tipi corrispondenti:

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

La classe tf.data.experimental.CsvDataset fornisce un'interfaccia CSV Dataset minimale senza le caratteristiche make_csv_dataset funzione make_csv_dataset : analisi dell'intestazione di colonna, inferenza del tipo di colonna, mescolamento automatico, interleaving di file.

Questo costruttore segue utilizza record_defaults allo stesso modo di 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']

Il codice sopra è fondamentalmente equivalente a:

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

Più file

Per analizzare il set di dati dei font utilizzando experimental.CsvDataset , devi prima determinare i tipi di colonna per record_defaults . Inizia ispezionando la prima riga di un 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

Solo i primi due campi sono stringhe, gli altri sono interi o float e puoi ottenere il numero totale di caratteristiche contando le virgole:

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

Il costruttore CsvDatasaet può prendere un elenco di file di input, ma li legge in sequenza. Il primo file nell'elenco dei CSV è AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

Quindi, quando si passa l'elenco dei file a CsvDataaset i record da AGENCY.csv vengono letti per primi:

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'

Per intercalare più file, utilizzare Dataset.interleave .

Ecco un set di dati iniziale che contiene i nomi dei file CSV:

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

Questo mescola i nomi dei file a ogni epoca:

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

Il metodo interleave accetta map_func che crea un Dataset map_func per ogni elemento del Dataset genitore.

Qui, vuoi creare un CsvDataset da ogni elemento del set di dati dei file:

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

Il Dataset restituito da interleave restituisce elementi ciclicamente su un numero di set di dati Dataset . Nota, di seguito, come il set di dati cycle_length)=3 su cycle_length)=3 tre file di 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)

Prestazione

In precedenza, è stato notato che io.decode_csv è più efficiente se eseguito su un batch di stringhe.

È possibile trarre vantaggio da questo fatto, quando si utilizzano batch di grandi dimensioni, per migliorare le prestazioni di caricamento CSV (ma provare prima a memorizzare nella cache ).

Con il caricatore integrato 20, i lotti di 2048 esempi richiedono circa 17 secondi.

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

Il passaggio di batch di righe di testo a decode_csv viene eseguito più velocemente, in circa 5 decode_csv :

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

Per un altro esempio di aumento delle prestazioni CSV utilizzando batch di grandi dimensioni, vedere il tutorial su overfit e underfit .

Questo tipo di approccio può funzionare, ma considera altre opzioni come cache e snapshot o reincodifica i tuoi dati in un formato più semplificato.