Il y a 2 ans, une éternité, un tutoriel de classification d’iris, à l’aide de TensorFlow 1.x avait déjà été présenté sur un de nos autres sites.
Depuis cette date, TensorFlow a fondamentalement changé. La version 2.x est très différente de la 1.x. Raison pour laquelle, l’article précédent est obsolète.
Ce nouveau tutoriel de classification d’iris, disponible sur le site de TensorFlow, est repris ici et commenté. Le cours est divisé en quatre parties : le dataset, le modèle, l’apprentissage, les prédictions.
Le problème
Classer des iris, en 3 catégories (Iris setosa, Iris virginica et Iris versicolor), à partir des dimensions (largeur et longueur) des sépales et pétales est un problème classique du Machine Learning. Puisqu’on peut le traiter facilement avec les outils de la statistique, pourquoi ne pas essayer de le faire, de façon plus compliquée avec un réseau de neurones ! Tout cela, bien sûr, dans un but pédagogique.
Références
- Custom training: walkthrough
- Premade Estimators
- TensorFlow – tutoriel #1
- Tutorial Classification
- Scatter Matrices using pandas
- How to use Pandas Scatter Matrix
- Dataset 5 fleurs
- How does the Softmax activation function work?
Code
Le notebook Jupyter, en Python, dans l’environnement GCP est disponible ici.
Gist
Imports
import os
import matplotlib.pyplot as plt
import tensorflow as tf
print("TensorFlow version: {}".format(tf.__version__))
print("Eager execution: {}".format(tf.executing_eagerly()))
TensorFlow version: 2.2.0-rc2
Eager execution: True
Le test pour savoir si on est en mode eager est inutile car depuis la version 2.x de Tf on est obligatoirement en mode eager.
Dataset
Le fichier d’origine est à cette adresse : https://archive.ics.uci.edu/ml/datasets/iris
C’est un fichier csv, organisé de la façon suivante : tout d’abord une ligne de description (nombre d’enregistrements, de catégories et le nom des catégories).
Puis pour chaque enregistrement la longueur du sépale, sa largeur, la longueur du pétale, sa largeur, sa classe (0, 1, 2) pour Iris Setosa, Iris Versicolour; Iris Virginica
Download the dataset
train_dataset_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv"
train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),
origin=train_dataset_url)
print("Local copy of the dataset file: {}".format(train_dataset_fp))
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv
8192/2194 [================================================================================================================] - 0s 0us/step
Local copy of the dataset file: /root/.keras/datasets/iris_training.csv
Inspection des données
!head -n5 {train_dataset_fp}
120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0
! permet d’exécuter une commande système
Création d’un tf.data.Dataset
On choisit de créer un dataset en fournissant le nom des colonnes. Ce n’est pas obligatoire. Seuls les file_pattern, batch_size doivent être précisés.
tf.data.experimental.make_csv_dataset
# column order in CSV file
column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
feature_names = column_names[:-1]
label_name = column_names[-1]
print("Features: {}".format(feature_names))
print("Label: {}".format(label_name))
Features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
Label: species
batch_size = 32
train_dataset = tf.data.experimental.make_csv_dataset(
train_dataset_fp,
batch_size,
column_names=column_names,
label_name=label_name,
num_epochs=1)
On crée aussi une variable class_names qui nous servira pour plus tard, uniquement pour de l’affichage.
class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']
Le dataset est itérable. Deux façons de voir le contenu :
features, labels = next(iter(train_dataset))
print(features)
OrderedDict([('sepal_length', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([7.7, 4.8, 6.1, 6.4, 6.7, 5.7, 6.7, 5.7, 5.8, 6. , 7.3, 6.2, 6.1,
4.6, 4.8, 5.1, 5.6, 6.5, 7.2, 5.4, 5.8, 6.1, 6.4, 5.9, 5.5, 5.3,
5.1, 6.3, 4.4, 5.7, 4.9, 6.3], dtype=float32)>), ('sepal_width', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([2.6, 3.4, 2.6, 2.8, 3.1, 2.8, 3. , 4.4, 2.8, 2.2, 2.9, 2.8, 2.8,
3.6, 3. , 3.8, 2.7, 3. , 3.6, 3.9, 2.7, 3. , 3.2, 3. , 3.5, 3.7,
3.7, 2.5, 3. , 2.9, 3.1, 2.7], dtype=float32)>), ('petal_length', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([6.9, 1.6, 5.6, 5.6, 5.6, 4.1, 5. , 1.5, 5.1, 5. , 6.3, 4.8, 4.7,
1. , 1.4, 1.6, 4.2, 5.8, 6.1, 1.3, 4.1, 4.9, 5.3, 5.1, 1.3, 1.5,
1.5, 5. , 1.3, 4.2, 1.5, 4.9], dtype=float32)>), ('petal_width', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([2.3, 0.2, 1.4, 2.2, 2.4, 1.3, 1.7, 0.4, 2.4, 1.5, 1.8, 1.8, 1.2,
0.2, 0.1, 0.2, 1.3, 2.2, 2.5, 0.4, 1. , 1.8, 2.3, 1.8, 0.2, 0.2,
0.4, 1.9, 0.2, 1.3, 0.1, 1.8], dtype=float32)>)])
print(labels)
tf.Tensor([2 0 2 2 2 1 1 0 2 2 2 2 1 0 0 0 1 2 2 0 1 2 2 2 0 0 0 2 0 1 0 2], shape=(32,), dtype=int32)
takeone = train_dataset.take(1)
for data in takeone:
print(data)
(OrderedDict([('sepal_length', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([6.9, 6.6, 6.2, 6.4, 4.6, 6.4, 6.5, 4.9, 4.4, 6.7, 7.3, 7.9, 5.6,
4.6, 6.8, 6.3, 6.1, 4.4, 6.3, 5. , 5.8, 4.9, 7.7, 4.8, 4.6, 6.8,
5.5, 6.4, 5.8, 6.4, 5.4, 5. ], dtype=float32)>), ('sepal_width', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([3.1, 3. , 2.2, 2.7, 3.4, 3.1, 3.2, 3. , 3. , 3.1, 2.9, 3.8, 2.7,
3.1, 3.2, 3.3, 3. , 3.2, 2.7, 3. , 2.8, 3.1, 2.6, 3.4, 3.6, 3. ,
2.6, 3.2, 2.7, 2.8, 3. , 3.4], dtype=float32)>), ('petal_length', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([4.9, 4.4, 4.5, 5.3, 1.4, 5.5, 5.1, 1.4, 1.3, 5.6, 6.3, 6.4, 4.2,
1.5, 5.9, 4.7, 4.9, 1.3, 4.9, 1.6, 5.1, 1.5, 6.9, 1.6, 1. , 5.5,
4.4, 4.5, 5.1, 5.6, 4.5, 1.5], dtype=float32)>), ('petal_width', <tf.Tensor: shape=(32,), dtype=float32, numpy=
array([1.5, 1.4, 1.5, 1.9, 0.3, 1.8, 2. , 0.2, 0.2, 2.4, 1.8, 2. , 1.3,
0.2, 2.3, 1.6, 1.8, 0.2, 1.8, 0.2, 2.4, 0.1, 2.3, 0.2, 0.2, 2.1,
1.2, 1.5, 1.9, 2.2, 1.5, 0.2], dtype=float32)>)]), <tf.Tensor: shape=(32,), dtype=int32, numpy=
array([1, 1, 1, 2, 0, 2, 2, 0, 0, 2, 2, 2, 1, 0, 2, 1, 2, 0, 2, 0, 2, 0,
2, 0, 0, 2, 1, 1, 2, 2, 1, 0], dtype=int32)>)
data[0]['sepal_length']
<tf.Tensor: shape=(32,), dtype=float32, numpy=
array([6.9, 6.6, 6.2, 6.4, 4.6, 6.4, 6.5, 4.9, 4.4, 6.7, 7.3, 7.9, 5.6,
4.6, 6.8, 6.3, 6.1, 4.4, 6.3, 5. , 5.8, 4.9, 7.7, 4.8, 4.6, 6.8,
5.5, 6.4, 5.8, 6.4, 5.4, 5. ], dtype=float32)>
Généralement les données ne sont pas organisées de cette façon dans le dataset. On a habituellement les valeurs de chaque enregistrement et un vecteur pour les variables à expliquer. C’est ce qu’on va faire.
tf.stack
def pack_features_vector(features, labels):
"""Pack the features into a single array."""
features = tf.stack(list(features.values()), axis=1)
return features, labels
train_dataset = train_dataset.map(pack_features_vector)
features, labels = next(iter(train_dataset))
print(features[:5])
tf.Tensor(
[[7.2 3.2 6. 1.8]
[4.9 3.1 1.5 0.1]
[5.4 3.7 1.5 0.2]
[5. 3.5 1.3 0.3]
[5.5 2.4 3.8 1.1]], shape=(5, 4), dtype=float32)
Au sujet de tf.stack
tf.stack stacks a list of rank-R tensors into one rank-(R+1) tensor.
x = tf.constant([1, 4])
y = tf.constant([2, 5])
z = tf.constant([3, 6])
tf.stack([x, y, z])
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[1, 4],
[2, 5],
[3, 6]], dtype=int32)>
Given a list of length N of tensors of shape (A, B, C); if axis == 0 then the output tensor will have the shape (N, A, B, C). if axis == 1 then the output tensor will have the shape (A, N, B, C)
tf.stack([x, y, z], axis=1)
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 3],
[4, 5, 6]], dtype=int32)>
Suite du tutoriel ici.