Cet article est la suite de TensorFlow – tutoriel #1

Le précédent tutoriel s’appuyait sur Getting Started for ML Beginners sur le site officiel de TensorFlow alors que celui-ci s’appuie sur Getting Started with TensorFlow

Le programme décrit est le même dans les deux tutoriels. Il s’agit de celui que j’ai légèrement modifié afin qu’il soit accessible sous Jupyter en un seul fichier pour faciliter la lecture.

Getting Started with TensorFlow n’apporte pas beaucoup plus que Getting Started for ML Beginners mais c’est l’occasion de revenir sur quelques concepts et spécificités de TensorFlow (TF).

Datasets

TensorFlow recommande (fortement) d’utiliser les API Datasets et Estimators.

Datasets, which build a data input pipeline. The Dataset API has methods to load and manipulate data, and feed it into your model.

Lecture des données

Prenons de nouveau l’exemple Iris. La lecture des données se fait avec 2 fonctions : maybe_download() et load_data().

load_data() appelle maybe_download()

maybe_download() stocke en cache les fichiers Iris  (en utilisant l’API Keras) et load_data() lit les fichiers (format cdv). Les données qui sont récupérées sont celles présentes sur le site de TensorFlow et non celles stockées par sklearn.

def maybe_download():
 
# https://www.tensorflow.org/api_docs/python/tf/keras/utils/get_file
# Downloads a file from a URL if it not already in the cache.
 train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)
 
return train_path, test_path
def load_data(y_name='Species'):
    """Returns the iris dataset as (train_x, train_y), (test_x, test_y)."""
    train_path, test_path = maybe_download()
 
    train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)

    # Return item and drop from frame. 
    train_x, train_y = train, train.pop(y_name) 
    test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)

    # Return item and drop from frame. 
    test_x, test_y = test, test.pop(y_name)
 
    return (train_x, train_y), (test_x, test_y)

Les données Iris (voir The Iris Dataset) forment un jeu de 150 éléments qui a été séparé en 2 lots par TF pour les besoins du tutoriel. Le 1 er lot de 120 éléments sert à l’apprentissage. Le 2nd sert à l’estimation (au test) du modèle.

On a plus l’habitude de lire ce données de la façon suivante.

from sklearn import datasets
iris = datasets.load_iris()

Le code suivant n’est pas intuitif pour un béotien en Python.

train, test = load_data()
features, labels = train

Il faut comprendre que load_data() retourne 2 jeux de données (train) pour l’apprentissage et (test) pour l’estimation du modèle. Ces jeux de données sont structurés en features (les données) et labels (les étiquettes associées aux items).

Conversion des données en Dataset

# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
print(dataset)

Un Dataset contient des éléments structurés. Shapes et Types du Dataset font partie de la structure.

TensorSliceDataset shapes: { SepalLength: (), PetalWidth: (), PetalLength: (), SepalWidth: ()}, types: { SepalLength: tf.float64, PetalWidth: tf.float64, PetalLength: tf.float64, SepalWidth: tf.float64} ;

Le Dataset créé par train_input_fn contient en plus le labels (de type int64)

# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
print(dataset)

Notez (en plus à l’exemple précédent) le labels de type int64

TensorSliceDataset
    shapes: (
        {
          SepalLength: (), PetalWidth: (),
          PetalLength: (), SepalWidth: ()},
        ()),

    types: (
        {
          SepalLength: tf.float64, PetalWidth: tf.float64,
          PetalLength: tf.float64, SepalWidth: tf.float64
        },
        strong;tf.int64;/strong;);
TensorSliceDataset
    shapes: (
        {
          SepalLength: (), PetalWidth: (),
          PetalLength: (), SepalWidth: ()},
        ()),

    types: (
        {
          SepalLength: tf.float64, PetalWidth: tf.float64,
          PetalLength: tf.float64, SepalWidth: tf.float64
        },
        strong;tf.int64;/strong;);

Manipulation des données

A un moment, dans le programme, on fait un shuffle des données. (to shuffle : mélanger)? Pourquoi fait-on ça ?

# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)

Je renvoie à différents articles sur le sujet Does the order of training data matter when training neural networks? qui montrent que l’ordre d’apprentissage des données a une importance élevée sur l’apprentissage – surtout lorsque les données sont enregistrées selon une logique.

In cases such as these where the order of the dataset holds some significance, it is necessary to shuffle the examples before selecting minibatches. For very large datasets, for example datasets containing billions of examples in a data center, it can be impractical to sample examples truly uniformly at random every time we want to construct a minibatch. Fortunately, in practice it is usually sufficient to shuffle the order of the dataset once and then store it in shuffled fashion.

Deep Learning – Ian Goodfellow Yoshua Bengio Aaron Courville

 

The shuffle method uses a fixed-size buffer to shuffle the items as they pass through. Setting a buffer_size greater than the number of examples in the Dataset ensures that the data is completely shuffled. The Iris data set only contains 150 examples.

The repeat method has the Dataset restart when it reaches the end. To limit the number of epochss, set the count argument.

The batch method collects a number of examples and stacks them, to create batches. This adds a dimension to their shape. The new dimension is added as the first dimension. The following code uses the batch method on the MNIST Dataset, from earlier. This results in a Dataset containing 3D arrays representing stacks of (28,28) images:

Estimateurs

Dans la terminologie de TensorFlow, un estimateur est la représentation complète d’un modèle. L’API estimateur est l’élément central de TensorFlow. C’est avec l’estimateur qu’on effectue l’apprentissage et qu’on qualifie sa qualité.

An Estimator is TensorFlow’s high level representation of a complete model. It handles the details of initialization, logging, saving and restoring, and many other features so you can concentrate on your model.

Etant donné un modèle (spécifié par un model_fn), avec ses inputs (et d’autres paramètres) l’Estimator va fournir les opérations nécessaires à l’apprentissage, à l’évaluation et aux prédictions.

Ce qui fait la force de Tensorflow (pour les débutants) c’est le nombre d’Estimator pré-définis.

Pour utiliser les Estimators pré-définis, il faut appliquer la séquence suivante :

  • Créer une ou plusieurs input functions.
  • Définir le modèle de feature columns
  • Instanter un Estimator
  • Appeler les méthodes sur l’Estimator

Input function

Une input function est une fonction qui retourne un  the 2-tuple:

  • « features » – (dictionary)
    le key est le nom du feature.
    et les features valeurs.
  • « label » – (un tableau)
    les valeurs du label

 

Supposons le tableau de données suivant :

SepalLength SepalWidth PetalLength PetalWidth labels
6.4 2.8 5.6 2.2 2
5.0 2.3 3.3 1.0 1

L’input function peut être définie de la façon suivante :

def input_evaluation_set():
    features = {'SepalLength': np.array([6.4, 5.0]),
                'SepalWidth':  np.array([2.8, 2.3]),
                'PetalLength': np.array([5.6, 3.3]),
                'PetalWidth':  np.array([2.2, 1.0])}
    labels = np.array([2, 1])
    return features, labels

l’appel

f, l = my_input_evaluation_set()
print(f)
print(l)

affichera :

{'PetalLength': array([5.6, 3.3]), 'SepalLength': array([6.4, 5. ]), 'PetalWidth': array([2.2, 1. ]), 'SepalWidth': array([2.8, 2.3])}
[2 1]

Feature columns

Il faut indiquer à TF comment interpréter les données brutes du dictionnaire features.
Dans le cas Iris, c’est simple car les valeurs sont de type numérique. On choisit de les interpréter comment des flottants.

# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))
print(my_feature_columns)

L’affichage de my_feature_columns :

[_NumericColumn(key='SepalLength', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None), _NumericColumn(key='SepalWidth', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None), _NumericColumn(key='PetalLength', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None), _NumericColumn(key='PetalWidth', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None)]

Evidemment, ça peut être beaucoup plus compliqué. Nous y reviendrons.

Instanter un Estimator

TF propose une variété importante d’estimateurs. Pour les problèmes de classification, tels que celui qui nous concerne, nous avons le choix :

(note : DNN Deep Neural Network)

Celui, reconnu le plus pertinent, dans ce cas, est tf.estimator.DNNClassifier.

# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # Two hidden layers of 10 nodes each.
    hidden_units=[10, 10],
    # The model must choose between 3 classes.
    n_classes=3)

Appeler les méthodes sur l’Estimator : Apprentissage, Evaluation et Prediction

Apprentissage

On a notre input fonction, on a défini le feature_columns et on a défini l’estimateur (classifier). On peut procéder à l’apprentissage.

# Train the Model.
classifier.train(
    input_fn=lambda:iris_data.train_input_fn(train_x, train_y, args.batch_size),
    steps=args.train_steps)

On a aussi un hyperparamètre (training steps)

Evaluation

L’évaluation est similaire à l’apprentissage. Les données sont celles du test.

# Evaluate the model.
eval_result = classifier.evaluate(
    input_fn=lambda:iris_data.eval_input_fn(test_x, test_y, args.batch_size))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))

Prédiction

Idem pour les prédictions. Sauf que nous n’avons pas de labels.

# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
}

predictions = classifier.predict(
    input_fn=lambda:iris_data.eval_input_fn(predict_x,
                                           batch_size=args.batch_size))

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *