Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Charger les données CSV

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHubTélécharger le cahier

Ce didacticiel fournit des exemples d'utilisation des données CSV avec TensorFlow.

Il y a deux parties principales à cela :

  1. Chargement des données hors disque
  2. Le pré-traiter sous une forme adaptée à la formation.

Ce tutoriel se concentre sur le chargement et donne quelques exemples rapides de prétraitement. Pour un tutoriel qui se concentre sur l'aspect prétraiter voir le guide des couches prétraiter et tutoriel .

Installer

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

Données en mémoire

Pour tout petit ensemble de données CSV, le moyen le plus simple d'entraîner un modèle TensorFlow sur celui-ci consiste à le charger en mémoire en tant que Dataframe pandas ou tableau NumPy.

Un exemple relativement simple est le jeu de données de l' ormeau .

  • L'ensemble de données est petit.
  • Toutes les entités en entrée sont toutes des valeurs à virgule flottante à plage limitée.

Voici comment télécharger les données dans 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'ensemble de données contient un ensemble de mesures de l' ormeau , un type d'escargot de mer.

une coquille d'ormeau

« Shell Abalone » (par Nicki Dugan Pogue , CC BY-SA 2.0)

La tâche nominale de cet ensemble de données est de prédire l'âge à partir des autres mesures, donc séparez les caractéristiques et les étiquettes pour l'entraînement :

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

Pour cet ensemble de données, vous traiterez toutes les entités de la même manière. Emballez les fonctionnalités dans un seul tableau 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 ]])

Ensuite, créez un modèle de régression pour prédire l'âge. Comme il n'y a qu'un seul tenseur d'entrée, un keras.Sequential modèle suffit ici.

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

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

Pour former ce modèle, passer les caractéristiques et les étiquettes à 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>

Vous venez de voir le moyen le plus simple d'entraîner un modèle à l'aide de données CSV. Ensuite, vous apprendrez à appliquer un prétraitement pour normaliser les colonnes numériques.

Prétraitement de base

Il est recommandé de normaliser les entrées de votre modèle. Les couches de prétraitement Keras offrent un moyen pratique d'intégrer cette normalisation dans votre modèle.

La couche précalculera la moyenne et la variance de chaque colonne et les utilisera pour normaliser les données.

Vous créez d'abord le calque :

normalize = layers.Normalization()

Ensuite , vous utilisez la Normalization.adapt() méthode pour adapter la couche de normalisation à vos données.

normalize.adapt(abalone_features)

Utilisez ensuite la couche de normalisation dans votre modèle :

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>

Types de données mixtes

L'ensemble de données "Titanic" contient des informations sur les passagers du Titanic. La tâche nominale de cet ensemble de données est de prédire qui a survécu.

Le Titanic

Image de Wikimedia

Les données brutes peut facilement être chargé en tant que Pandas DataFrame , mais pas immédiatement utilisable comme entrée à un modèle 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')

En raison des différents types de données et les plages vous ne pouvez pas empiler simplement les fonctions dans un tableau NumPy et le transmettre à un keras.Sequential modèle. Chaque colonne doit être traitée individuellement.

Comme option, vous pouvez prétraiter vos données hors ligne (à l'aide de n'importe quel outil de votre choix) pour convertir les colonnes catégorielles en colonnes numériques, puis transmettre la sortie traitée à votre modèle TensorFlow. L'inconvénient de cette approche est que si vous enregistrez et exportez votre modèle, le prétraitement n'est pas enregistré avec celui-ci. Les couches de prétraitement Keras évitent ce problème car elles font partie du modèle.

Dans cet exemple, vous allez construire un modèle qui met en œuvre la logique pré - traitement utilisant l' API fonctionnelle Keras . Vous pouvez aussi le faire par le sous - classement .

L'API fonctionnelle opère sur des tenseurs "symboliques". Les tenseurs "avides" normaux ont une valeur. En revanche, ces tenseurs "symboliques" ne le font pas. Au lieu de cela, ils gardent une trace des opérations exécutées sur eux et créent une représentation du calcul que vous pouvez exécuter plus tard. Voici un exemple rapide :

# 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

Pour construire le modèle pré - traitement, commencez par la construction d' un ensemble de symboles keras.Input objets, correspondant aux noms et des types de données des colonnes 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')>}

La première étape de votre logique de prétraitement consiste à concaténer les entrées numériques et à les exécuter via une couche de normalisation :

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

Collectez tous les résultats du prétraitement symbolique, pour les concaténer ultérieurement.

preprocessed_inputs = [all_numeric_inputs]

Pour les entrées chaîne utilisent la tf.keras.layers.StringLookup fonction de la carte à partir de chaînes à des indices entiers dans un vocabulaire. Ensuite, utilisez tf.keras.layers.CategoryEncoding pour convertir les index en float32 des données appropriées pour le modèle.

Les paramètres par défaut pour la tf.keras.layers.CategoryEncoding couche créer un vecteur d' une chaude pour chaque entrée. Un layers.Embedding fonctionnerait aussi. Voir le guide des couches prétraiter et tutoriel pour en savoir plus sur ce sujet.

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.

Avec la collection d' inputs et processed_inputs , vous pouvez concaténer toutes les entrées prétraitées ensemble et construire un modèle qui gère les prétraiter:

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

Ce model ne contient que le pré - traitement d'entrée. Vous pouvez l'exécuter pour voir ce qu'il fait à vos données. Modèles KERAS ne se convertissent pas automatiquement Pandas DataFrames parce qu'il ne sait pas si elle doit être convertie en un tenseur ou à un dictionnaire de tenseurs. Convertissez-le donc en un dictionnaire de tenseurs :

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

Découpez le premier exemple d'entraînement et transmettez-le à ce modèle de prétraitement, vous voyez les caractéristiques numériques et les chaînes uniques toutes concaténées :

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

Construisez maintenant le modèle par-dessus :

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)

Lorsque vous formez le modèle, passer le dictionnaire de caractéristiques telles que x , et l'étiquette 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>

Étant donné que le prétraitement fait partie du modèle, vous pouvez enregistrer le modèle et le recharger ailleurs et obtenir des résultats identiques :

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)

Utilisation de tf.data

Dans la section précédente, vous vous êtes appuyé sur le brassage et le traitement par lots des données intégrés au modèle lors de l'entraînement du modèle.

Si vous avez besoin d'un plus grand contrôle sur le pipeline de données d'entrée ou le besoin d'utiliser des données qui ne correspondent pas facilement dans la mémoire: l' utilisation tf.data .

Pour plus d' exemples voir le guide tf.data .

Activé dans les données de la mémoire

Dans un premier exemple d'application tf.data aux données CSV examiner le code suivant tranche manuellement le dictionnaire des caractéristiques de la section précédente. Pour chaque index, il prend cet index pour chaque fonctionnalité :

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

Exécutez ceci et imprimez le premier exemple :

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

Le plus élémentaire tf.data.Dataset dans le chargeur de données mémoire est le Dataset.from_tensor_slices constructeur. Cette fonction renvoie une tf.data.Dataset qui implémente une version généralisée de ce qui précède slices fonction, tensorflow.

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

Vous pouvez itérer sur un tf.data.Dataset comme tout autre python itérables:

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 fonction peut gérer toute structure de dictionnaires imbriqués ou tuples. Le code suivant fait un ensemble de données de (features_dict, labels) paires:

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

Pour former un modèle en utilisant ce Dataset , vous devrez au moins shuffle et par batch les données.

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

Au lieu de passer des features et des labels à Model.fit , vous passez le jeu de données:

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>

A partir d'un seul fichier

Jusqu'à présent, ce didacticiel a fonctionné avec des données en mémoire. tf.data est une boîte à outils hautement évolutive pour la construction de pipelines de données, et fournit quelques fonctions pour traiter le chargement des fichiers CSV.

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

Maintenant , lisez les données CSV à partir du fichier et créer un tf.data.Dataset .

(Pour la documentation complète, voir 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,)

Cette fonction comprend de nombreuses fonctionnalités pratiques pour que les données soient faciles à utiliser. Ceci comprend:

  • Utilisation des en-têtes de colonnes comme clés de dictionnaire.
  • Déterminer automatiquement le type de chaque colonne.
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]

Il peut également décompresser les données à la volée. Voici un fichier CSV contenant gzip l' ensemble de données de trafic inter - métro

Un embouteillage.

Image de 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

Réglez le compression_type argument pour lire directement à partir du fichier compressé:

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]

Mise en cache

L'analyse des données csv nécessite une certaine surcharge. Pour les petits modèles, cela peut être le goulot d'étranglement de la formation.

En fonction de votre cas d'utilisation , il peut être une bonne idée d'utiliser Dataset.cache ou data.experimental.snapshot afin que les données csv n'analysée sur la première époque.

La principale différence entre les cache et snapshot des méthodes est que cache les fichiers ne peuvent être utilisés par le processus de tensorflow qui les a créés, mais snapshot des fichiers peuvent être lus par d' autres procédés.

Par exemple, itérer sur les traffic_volume_csv_gz_ds 20 fois, prend ~ 15 secondes sans mise en cache ou ~ 2 s avec la mise en 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

Si votre chargement de données est ralenti par le chargement des fichiers csv, et cache et snapshot ne sont pas suffisants pour votre cas d'utilisation, pensez à réencoder vos données dans un format plus simple.

Plusieurs fichiers

Tous les exemples jusqu'à présent dans cette section pourraient facilement se faire sans tf.data . Un endroit où tf.data peut vraiment simplifier les choses est en matière de collections de fichiers.

Par exemple, les images de police de caractères ensemble de données est distribué sous forme d' une collection de fichiers csv, un par la police.

Polices

Image par Willi Heidelbach de pixabay

Téléchargez l'ensemble de données et jetez un œil aux fichiers qu'il contient :

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

Lorsque vous traitez avec un tas de fichiers que vous pouvez passer un glob-style file_pattern à la experimental.make_csv_dataset fonction. L'ordre des fichiers est mélangé à chaque itération.

Utilisez le num_parallel_reads argument pour définir combien de fichiers sont lus en parallèle et entrecroisées.

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)

Ces fichiers csv ont les images aplaties en une seule ligne. Les noms de colonnes sont formatées r{row}c{column} . Voici le premier lot :

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]

Facultatif : champs d'emballage

Vous ne voulez probablement pas travailler avec chaque pixel dans des colonnes séparées comme celle-ci. Avant d'essayer d'utiliser cet ensemble de données, assurez-vous d'emballer les pixels dans un tenseur d'image.

Voici le code qui analyse les noms de colonnes pour créer des images pour chaque exemple :

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

Appliquez cette fonction à chaque lot de l'ensemble de données :

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

Tracez les images résultantes :

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

Fonctions de niveau inférieur

Jusqu'à présent, ce didacticiel s'est concentré sur les utilitaires de plus haut niveau pour la lecture de données csv. Il existe deux autres API qui peuvent être utiles pour les utilisateurs avancés si votre cas d'utilisation ne correspond pas aux modèles de base.

Cette section recrée fonctionnalité fournie par make_csv_dataset , pour démontrer comment cette fonctionnalité de niveau inférieur peut être utilisé.

tf.io.decode_csv

Cette fonction décode une chaîne ou une liste de chaînes en une liste de colonnes.

Contrairement à make_csv_dataset cette fonction ne cherche pas à deviner les types de données colonnes. Vous spécifiez les types de colonnes en fournissant une liste de record_defaults contenant une valeur du type correct, pour chaque colonne.

Pour lire les données du Titanic en tant que chaînes en utilisant decode_csv vous diriez:

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

Pour les analyser avec leurs types réels, créer une liste de record_defaults des types correspondants:

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 tf.data.experimental.CsvDataset classe fournit un minimum CSV Dataset l' interface sans les caractéristiques de commodité de la make_csv_dataset fonction: colonne parsing d' en- tête, type inférence de colonne, brouillant automatique, entrelaçant fichier.

Ce constructeur suit utilise record_defaults la même manière que 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']

Le code ci-dessus est fondamentalement équivalent à :

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

Plusieurs fichiers

Pour analyser les polices ensemble de données en utilisant experimental.CsvDataset , vous devez d'abord déterminer les types de colonnes pour les record_defaults . Commencez par inspecter la première ligne d'un fichier :

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

Seuls les deux premiers champs sont des chaînes, les autres sont des entiers ou des flottants, et vous pouvez obtenir le nombre total de fonctionnalités en comptant les virgules :

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

Le CsvDatasaet constructeur peut prendre une liste de fichiers d'entrée, mais les lit séquentiellement. Le premier fichier dans la liste des CSVs est AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

Ainsi , lorsque vous passez la liste des fichiers à CsvDataaset les enregistrements de AGENCY.csv sont lus en premier:

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'

Pour imbriquer plusieurs fichiers, utilisez Dataset.interleave .

Voici un ensemble de données initial qui contient les noms de fichiers csv :

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

Cela mélange les noms de fichiers à chaque époque :

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

L' interleave méthode prend un map_func qui crée un Child Dataset pour chaque élément du parent - Dataset de Dataset .

Ici, vous voulez créer un CsvDataset de chaque élément de l'ensemble de données de fichiers:

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

Le Dataset de Dataset Dataset renvoyé par des éléments de retours d' entrelacement par le vélo sur un certain nombre de Child - Dataset s. Remarque, ci - dessous, comment les cycles de jeu de données sur cycle_length=3 trois fichiers de police:

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

Auparavant, il a été noté que io.decode_csv est plus efficace lorsqu'il est exécuté sur un lot de chaînes.

Il est possible de tirer profit de ce fait, lors de l' utilisation de grandes tailles de lots, pour améliorer les performances de chargement CSV (mais essayez de la mise en cache en premier).

Avec le chargeur intégré 20, les lots de 2048 exemples prennent environ 17 s.

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

En passant des lots de lignes de texte à decode_csv fonctionne plus rapidement, dans environ 5 secondes:

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

Pour un autre exemple d'augmenter les performances de csv en utilisant de grands lots voir le surajustement et tutoriel underfit .

Ce type d'approche peut fonctionner, mais envisager d' autres options comme le cache et l' snapshot , ou réencoder vos données dans un format plus simple.