Aiuto proteggere la Grande Barriera Corallina con tensorflow sul Kaggle Join Sfida

Carica dati CSV

Visualizza su TensorFlow.org Visualizza la fonte su GitHubScarica taccuino

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

Ci sono due parti principali in questo:

  1. Caricamento dei dati dal disco
  2. Pre-elaborazione in una forma adatta per l'allenamento.

Questo tutorial si concentra sul caricamento e fornisce alcuni rapidi esempi di pre-elaborazione. Per un'esercitazione che focalizza l'aspetto pre-elaborazione consulta la strati guida pre-elaborazione e esercitazione .

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

In memoria dati

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

Un relativamente semplice esempio è l' insieme di dati abalone .

  • Il set di dati è piccolo.
  • Tutte le funzioni di input sono tutti valori a 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()

L'insieme di dati contiene una serie di misure di abalone , un tipo di lumaca di mare.

un guscio di abalone

“Abalone shell” (da Nicki Dugan Pogue , CC BY-SA 2.0)

L'attività nominale per questo set di dati è prevedere l'età dalle altre misurazioni, quindi separare le caratteristiche e le etichette per l'allenamento:

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

Per questo set di dati tratterai tutte le funzionalità in modo identico. Raggruppa 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 fai un modello di regressione per prevedere l'età. Poiché v'è un solo tensore ingresso, un keras.Sequential modello è sufficiente qui.

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, passare le caratteristiche e le etichette a Model.fit :

abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 1s 2ms/step - loss: 57.8799
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 11.6617
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 8.5956
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 8.0663
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 7.6160
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 7.2284
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 6.9368
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 6.7287
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 6.5694
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 6.4730
<keras.callbacks.History at 0x7fd1a0579ed0>

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 al modello. I livelli di pre-elaborazione di Keras forniscono un modo conveniente per costruire questa normalizzazione nel tuo modello.

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

Per prima cosa crei il livello:

normalize = layers.Normalization()

Poi si utilizza il Normalization.adapt() metodo 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: 92.6760
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 54.4503
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 17.1807
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 5.9306
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 5.0489
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9627
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9511
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9162
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9172
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9027
<keras.callbacks.History at 0x7fd1a0286f90>

Tipi di dati misti

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

Il Titanic

Immagine da Wikimedia

I dati grezzi può essere facilmente caricato come Pandas DataFrame , ma non è immediatamente utilizzabile 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 gli intervalli non si può semplicemente impilare le caratteristiche in matrice NumPy e passare ad una keras.Sequential modello. Ogni colonna deve essere gestita singolarmente.

Come opzione, puoi preelaborare i tuoi dati offline (usando 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 salvi ed esporti il ​​tuo modello, la pre-elaborazione non viene salvata con esso. I livelli di pre-elaborazione Keras evitano questo problema perché fanno parte del modello.

In questo esempio, si costruisce un modello che implementa la logica pre-elaborazione utilizzando Keras API funzionale . Si potrebbe anche fare da sottoclasse .

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

# 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

Per costruire il modello di pre-elaborazione, iniziare con la costruzione di una serie di simbolico keras.Input oggetti, abbinando i nomi ei 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': <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')>}

Il primo passaggio nella logica di pre-elaborazione consiste nel concatenare insieme gli input numerici 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 = 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')>

Raccogliere tutti i risultati della preelaborazione simbolica per concatenarli in un secondo momento.

preprocessed_inputs = [all_numeric_inputs]

Per la stringa ingressi usa il tf.keras.layers.StringLookup funzione per mappare da stringhe indici interi in un vocabolario. Successivamente, l'uso tf.keras.layers.CategoryEncoding per convertire gli indici in float32 dati appropriati per il modello.

Le impostazioni di default per il tf.keras.layers.CategoryEncoding strato creare un vettore di un caldo per ogni ingresso. Un layers.Embedding potrebbe anche funzionare. Vedere il layer guida pre-elaborazione e dimostrativi per più su questo argomento.

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.

Con la raccolta di inputs e processed_inputs , è possibile concatenare tutti gli ingressi preelaborate insieme e costruire un modello che gestisce la pre-elaborazione:

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 pre-elaborazione di ingresso. Puoi eseguirlo per vedere cosa fa ai tuoi dati. Modelli Keras non convertono automaticamente Pandas DataFrames perché non è chiaro se dovesse essere convertito in un tensore o 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()}

Separa il primo esempio di addestramento e passalo a questo modello di pre-elaborazione, vedrai le caratteristiche numeriche e le stringhe one-hot tutte concatenate insieme:

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

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 ci si allena il modello, passare il dizionario di 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 [==============================] - 1s 5ms/step - loss: 0.5580
Epoch 2/10
20/20 [==============================] - 0s 4ms/step - loss: 0.5019
Epoch 3/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4702
Epoch 4/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4495
Epoch 5/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4394
Epoch 6/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4340
Epoch 7/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4272
Epoch 8/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4254
Epoch 9/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4244
Epoch 10/10
20/20 [==============================] - 0s 5ms/step - loss: 0.4222
<keras.callbacks.History at 0x7fd2212c7fd0>

Poiché la pre-elaborazione fa parte del modello, puoi salvare il modello e ricaricarlo da qualche altra parte e ottenere risultati identici:

titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
2021-11-20 02:24:26.384348: 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.918]], shape=(1, 1), dtype=float32)
tf.Tensor([[-1.918]], shape=(1, 1), dtype=float32)

Utilizzo di tf.data

Nella sezione precedente ci si è affidati allo shuffle e al batch dei dati incorporati nel modello durante l'addestramento del modello.

Se avete bisogno di un maggiore controllo sulla pipeline dei dati di ingresso o di necessità di utilizzare dati che non si adattano facilmente nella memoria: l'uso tf.data .

Per ulteriori esempi consultare la guida tf.data .

On in memoria dati

Come primo esempio di applicazione tf.data ai dati CSV considerare il seguente codice fetta manualmente il dizionario di caratteristiche 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

Il più fondamentale tf.data.Dataset nel loader dati della memoria è il Dataset.from_tensor_slices costruttore. Ciò restituisce un tf.data.Dataset che implementa una versione generalizzata di quanto sopra slices di funzione, in tensorflow.

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

È possibile iterare su una tf.data.Dataset come qualsiasi altro pitone iterabile:

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 from_tensor_slices funzione può gestire qualsiasi struttura di dizionari nidificati o tuple. Il codice seguente rende un set di dati di (features_dict, labels) accoppiamenti:

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

Per addestrare un modello di utilizzo di questo Dataset , è necessario almeno shuffle e batch dei dati.

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

Invece di passare features e labels per Model.fit , si passa il set di dati:

titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4215
Epoch 2/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4208
Epoch 3/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4205
Epoch 4/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4204
Epoch 5/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4185
<keras.callbacks.History at 0x7fd22046cf50>

Da un singolo file

Finora questo tutorial ha funzionato con i dati in memoria. tf.data è un toolkit altamente scalabile per la costruzione di pipeline di dati, e fornisce alcune funzioni per la gestione dei file CSV di carico.

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

Ora leggere i dati dal file CSV e creare un tf.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, quindi è facile lavorare con i dati. 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'male' b'male' b'female']
age                 : [28. 70. 19.  6. 24.]
n_siblings_spouses  : [0 1 0 0 0]
parch               : [0 1 0 1 0]
fare                : [ 7.75  71.     7.775 12.475 13.   ]
class               : [b'Third' b'First' b'Third' b'Third' b'Second']
deck                : [b'unknown' b'B' b'unknown' b'E' b'unknown']
embark_town         : [b'Queenstown' b'Southampton' b'Southampton' b'Southampton' b'Southampton']
alone               : [b'y' b'n' b'y' b'n' b'y']

label               : [0 0 0 1 0]

Può anche decomprimere i dati al volo. Ecco un file CSV gzip contenente la metropolitana insieme di dati di traffico interstatale

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 1us/step
417792/405373 [==============================] - 1s 1us/step

Impostare il compression_type argomento 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                : [284.77 257.79 260.01 286.13 290.47]
rain_1h             : [0.   0.   0.   0.   0.25]
snow_1h             : [0. 0. 0. 0. 0.]
clouds_all          : [90 20 75  1 92]
weather_main        : [b'Mist' b'Clouds' b'Clouds' b'Clear' b'Rain']
weather_description : [b'mist' b'few clouds' b'broken clouds' b'sky is clear' b'light rain']
date_time           : [b'2012-10-25 17:00:00' b'2013-01-15 06:00:00' b'2012-12-24 08:00:00'
 b'2013-05-08 02:00:00' b'2013-08-12 06:00:00']

label               : [6488 5434 2174  298 5939]

memorizzazione nella cache

C'è un sovraccarico per l'analisi dei dati CSV. Per i modelli piccoli questo può essere il collo di bottiglia nell'allenamento.

A seconda del caso d'uso può essere una buona idea di utilizzare Dataset.cache o data.experimental.snapshot in modo che i dati csv viene analizzato solo sulla prima epoca.

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

Ad esempio, scorrere il traffic_volume_csv_gz_ds 20 volte, richiede ~ 15 secondi senza cache, o ~ 2s con 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 16.8 s, sys: 4.07 s, total: 20.8 s
Wall time: 12.7 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.46 s, sys: 243 ms, total: 1.7 s
Wall time: 1.34 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.26 s, sys: 490 ms, total: 2.75 s
Wall time: 1.65 s

Se il caricamento dei dati è rallentato dalla caricamento dei file CSV e cache e snapshot sono insufficienti per il vostro caso d'uso, in considerazione ri-codifica i dati in un formato più snella.

File multipli

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

Ad esempio, le immagini dei font di caratteri set di dati è distribuito come un insieme di file csv, uno per ogni tipo di carattere.

caratteri

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

Quando si tratta di un gruppo di file è possibile passare un glob-style file_pattern al experimental.make_csv_dataset funzioni. L'ordine dei file viene mischiato ad ogni iterazione.

Utilizzare il num_parallel_reads argomento per impostare il numero di file vengono 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 un'unica riga. I nomi delle colonne sono formattati 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'NINA' b'FORTE' b'CALIFORNIAN' b'JAVANESE' b'FORTE' b'BAITI' b'HARLOW'
 b'NIRMALA' b'JAVANESE' b'NINA']
fontVariant         : [b'NINA' b'FORTE' b'CALIFORNIAN FB' b'JAVANESE TEXT' b'FORTE'
 b'MONGOLIAN BAITI' b'HARLOW SOLID ITALIC' b'NIRMALA UI SEMILIGHT'
 b'JAVANESE TEXT' b'NINA']
m_label             : [ 932  172   55  376  215 6156   65 7286   59  302]
strength            : [0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4]
italic              : [0 1 1 1 1 0 0 0 0 0]
orientation         : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
m_top               : [38 45 36 45 40 41 34 44 76 38]
m_left              : [20 33 24 33 28 23 20 23 25 22]
originalH           : [49 23 47 59 34 33 60 49 40 62]
originalW           : [34 40 25 49 43 33 72 38 11 20]
h                   : [20 20 20 20 20 20 20 20 20 20]
w                   : [20 20 20 20 20 20 20 20 20 20]
r0c0                : [255   1  82   1   1   1   1   1   1 255]
r0c1                : [255 255 255   1   1   1   1   1   1 255]
r0c2                : [255 255 255   1   1   1   1  42   1 255]
r0c3                : [255 255 255   1   1   1   1 135  47 255]
...
[total: 412 features]

Opzionale: Campi di imballaggio

Probabilmente non vorrai lavorare con ogni pixel in colonne separate come questa. Prima di provare a utilizzare questo set di dati, assicurati di impacchettare i pixel in un tensore di immagine.

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')
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 64422 (\N{ARABIC LETTER HEH GOAL ISOLATED FORM}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)

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 gli utenti avanzati se il tuo caso d'uso non si adatta ai modelli di base.

Questa funzionalità sezione ricrea fornito da make_csv_dataset , per dimostrare come questa funzionalità di livello inferiore può essere utilizzata.

tf.io.decode_csv

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

A differenza make_csv_dataset questa funzione non cerca di indovinare colonne tipi di dati. È possibile specificare i tipi di colonna, fornendo un elenco di record_defaults che contengono un valore del tipo corretto, per ogni colonna.

Per leggere i dati del Titanic come stringhe utilizzando decode_csv si potrebbe dire:

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 analizzare con i loro tipi effettivi, creare 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

Il tf.data.experimental.CsvDataset classe fornisce un minimo CSV Dataset interfaccia senza le caratteristiche di comfort della make_csv_dataset funzione: colonna di intestazione di analisi, colonna tipo inferenza, mescolamento automatico, file di interleaving.

Questo costruttore segue usi 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 è sostanzialmente 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']

File multipli

Per analizzare i caratteri set di dati utilizzando experimental.CsvDataset , è necessario prima di determinare i tipi di colonna per i 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, il resto sono interi o float e puoi ottenere il numero totale di funzionalità contando le virgole:

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

Il CsvDatasaet costruttore può richiedere un elenco dei file di input, ma li legge in modo sequenziale. Il primo file nella lista dei CSV è AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

Così, quando si passa l'elenco dei file per CsvDataaset i record AGENCY.csv vengono letti prima:

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 interleave 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 in 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/CENTAUR.csv'
     b'fonts/JUICE.csv'
     b'fonts/COMIC.csv'
     b'fonts/SKETCHFLOW.csv'
     b'fonts/NIAGARA.csv'
    ...

Epoch 2:
     b'fonts/EUROROMAN.csv'
     b'fonts/YI BAITI.csv'
     b'fonts/LEELAWADEE.csv'
     b'fonts/GOUDY.csv'
     b'fonts/COMPLEX.csv'
    ...

Il interleave metodo prende un map_func che crea un bambino- Dataset per ciascun elemento del genitore Dataset .

Qui, si vuole creare un CsvDataset da ogni elemento del set di dati di file:

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

Il Dataset restituito da elementi restituisce interleave dal ciclismo su un numero del bambino- Dataset s. Si noti, di seguito, come i cicli di set di dati oltre 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 osservato che io.decode_csv è più efficiente quando viene eseguito su un lotto di stringhe.

È possibile trarre vantaggio da questo fatto, quando si usano grandi lotti, per migliorare le prestazioni di caricamento CSV (ma provate cache prima).

Con il caricatore integrato 20, 2048 lotti di esempio 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 27.4 s, sys: 1.74 s, total: 29.1 s
Wall time: 11.8 s

Passando lotti di linee di testo per decode_csv corre più veloce, in circa 5 secondi:

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

Per un altro esempio di aumentare le prestazioni csv utilizzando grandi lotti vedere il sovradattamento e esercitazione underfit .

Questo tipo di approccio può funzionare, ma prendere in considerazione altre opzioni, come cache e snapshot , o ri-codifica i dati in un formato più snella.