TensorFlow 2 – tutoriel #1

TensorFlow est en version 2 Alpha depuis mars 2019.

The 2.0 Alpha release is available now. Users can use this today and get started with all that TensorFlow 2.0 has to offer. 

https://www.tensorflow.org/community/roadmap

Les changements sont importants, notamment en ce qui concerne la simplification d’usage. C’est ce que nous allons voir avec ce tutoriel.

Contrairement à nos tutoriels précédents (qui utilisaient Jupyter sur Mac), celui-ci, et les suivants, sont sur GCP (Google Colaboratory Platform).

Le logiciel est ici et le document ici.

L’exercice consiste à classifier des images d’habits (un peu du type de MNIST).

On utilise la sur-couche Keras à TensorFlow.

Installation

On commence par installer la bonne version de TensorFlow (si ce n’est pas déjà fait). Dans GCP c’est inutile !

!pip install -q tensorflow==2.0.0-alpha0

Imports

from __future__ import absolute_import, division, print_function
# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__)

Le from __future__ est là pour la compatibilité avec les versions précédentes de Python.

On importe les librairies de base (TensorFlow, Keras, numpy et pyplot). Pour ceux qui ne connaissent pas Keras, c’est ici (pour résumer, Keras est une API en Python au dessus de TensorFlow et CNTK), on importe numpy (indispensable pour les calculs scientifiques en Python) et pyplot pour les affichages.

Données

Les données à classer sont celles de Fashion-MNIST.

Fashion-MNIST is a dataset of Zalando‘s article images—consisting of a training set of 60,000 examples and a test set of 10,000 examples. Each example is a 28×28 grayscale image, associated with a label from 10 classes. We intend Fashion-MNIST to serve as a direct drop-in replacement for the original MNIST dataset for benchmarking machine learning algorithms. It shares the same image size and structure of training and testing splits.

https://github.com/zalandoresearch/fashion-mnist

Ci-dessous un échantillon de ces images.

Lecture des données (téléchargement)

fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step

On utilise Keras pour le téléchargement des données.

Notez que l’import des données peut aussi se faire de cette façon (méthode que je préfère) :

from keras.datasets import fashion_mnist
(train_images, train_labels), (test_images, y_t test_labels est) = fashion_mnist.load_data()

A la suite de ces lignes, on dispose de données pour l’apprentissage (train_images) et pour le test (test_images). Le label (étiquettes, noms, catégories) de ces images est dans (train_labels) et (test_labels).

Il est important de distinguer le validation set du test set. Lire ici.

To reiterate the findings from researching the experts above, this section provides unambiguous definitions of the three terms.
Training Dataset: The sample of data used to fit the model.
Validation Dataset: The sample of data used to provide an unbiased evaluation of a model fit on the training dataset while tuning model hyperparameters. The evaluation becomes more biased as skill on the validation dataset is incorporated into the model configuration.
Test Dataset: The sample of data used to provide an unbiased evaluation of a final model fit on the training dataset.

https://machinelearningmastery.com/difference-test-validation-datasets/

fashion_mnist.load_data() return 2 tuples:

x_train, x_test: uint8 array of grayscale image data with shape (num_samples, 28, 28).
y_train, y_test: uint8 array of labels (integers in range 0-9) with shape (num_samples,).

https://keras.io/datasets/

Les étiquettes des données sont des catégories numériques, dont les noms sont :

LabelDescription
0T-shirt/top
1Trouser
2Pullover
3Dress
4Coat
5Sandal
6Shirt
7Sneaker
8Bag
9Ankle boot
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

Etudes des données

train_images.shape

affiche : le nombre de données d’apprentissage et leur taille

(60000, 28, 28)

soit 60 000 images carrées de 28 pixels de côté.

Le nombre d’étiquettes est donc aussi de 60 000

len(train_labels)
60000

chaque étiquette étant un nombre entre 0 et 9

train_labels
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

quant aux images de test, elles sont au même format et au nombre de 10 000

test_images.shape
(10000, 28, 28)

et le nombre de labels pour la validation est aussi de 10 000

len(test_labels)
10000

Préparation des données

Les images sont en niveaux de gris. Elles ont des valeurs entre 0 et 255, comme on peut le voir avec cet exemple :

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()

On veut que les valeurs soient entre 0 et 1. C’est ce qu’impose TensorFlow.

train_images = train_images / 255.0
test_images = test_images / 255.0

affichage des 25 premières images (apprentissage)

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

Construction du modèle (architecture du réseau)

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])

Keras permet de décrire séquentiellement les couches du réseau de neurones.

Commençons par la 1ère ligne, qui décrit la 1ère couche :

keras.layers.Flatten(input_shape=(28, 28))

The model needs to know what input shape it should expect. For this reason, the first layer in a Sequential model (and only the first, because following layers can do automatic shape inference) needs to receive information about its input shape.

https://keras.io/getting-started/sequential-model-guide/

On a bien un input_shape=(28, 28) de 28*28 puisque nos images ont cette taille. On commence par transformer notre matrice (image) 28*28 en un vecteur de dimension 28*28=784.

Ensuite, on est dans le cas d’un Dense dont la fonction d’activation est un ReLU (Rectified Linear Unit). On a ici une couche de 128 neurones. Cette couche est connectée à la couche précédente de 784 neurones.

keras.layers.Dense(128, activation='relu')

Dense implements the operation: output = activation(dot(input, kernel) + bias) where activation is the element-wise activation function passed as the activation argument, kernelis a weights matrix created by the layer, and bias is a bias vector created by the layer (only applicable if use_bias is True)

https://keras.io/layers/core/#dense

A dense layer is a regular DNN (Deep Neural Network) layer that connects every neuron in the defined layer to every neuron in the previous layer. For instance, if Layer 1 has 5 neurons and Layer 2 (dense layer) has 3 neurons, the total number of connections between Layer 1 and Layer 2 would be 15 (5 × 3). Since it accommodates every possible connection between the layers, it is called a “dense” layer.

Learn Keras for Deep Neural Networks – Jojo Moolayil – apress

On termine avec une couche de 10 neurones (normal puisqu’on a 10 catégories).

La fonction d’activation est un softmax.

keras.layers.Dense(10, activation='softmax')

Notre réseau de neurones a cette architecture. Ne pas oublier que c’est pour 60 000 images.

compilation du modèle

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Before training a model, you need to configure the learning process, which is done via the compilemethod. It receives three arguments:
An optimizer. This could be the string identifier of an existing optimizer (such as rmsprop or adagrad), or an instance of the Optimizer class. See: optimizers.
A loss function. This is the objective that the model will try to minimize. It can be the string identifier of an existing loss function (such as categorical_crossentropy or mse), or it can be an objective function. See: losses.
A list of metrics. For any classification problem you will want to set this to metrics=['accuracy']. A metric could be the string identifier of an existing metric or a custom metric function.

https://keras.io/getting-started/sequential-model-guide/#compilation

3 paramètres sont nécessaires à la compilation : l’optimizer, la fonction de coût et la métrique.

optimizer

The optimizer function is a mathematical algorithm that uses derivatives, partial derivatives, and the chain rule
in calculus to understand how much change the network will see in the loss function by making a small change in the weight of the neurons. The change in the loss function, which would be an increase or decrease, helps in determining the direction of the change required in the weight of the connection. 

Learn Keras for Deep Neural Networks – Jojo Moolayil – apress

L’optimizer retenu ici est Adam.

Adam, which stands for Adaptive Moment Estimation, is by far the most popular and widely used optimizer in DL. In most cases, you can blindly choose the Adam optimizer and forget about the optimization alternatives.

Learn Keras for Deep Neural Networks – Jojo Moolayil – apress

Il n’y a pas de choix par défaut pour l’optimizer mais le plus courant est Adam.

fonction de coût

La fonction de coût est sparse_categorical_crossentropy.

C’est la fonction qu’on utilise dans les cas comme le nôtre :

Categorical crossentropy with integer targets.

https://keras.io/backend/#sparse_categorical_crossentropy

métrique

La métrique est accuracy.

metrics: List of metrics to be evaluated by the model during training and testing. Typically you will use metrics=['accuracy']

https://keras.io/models/model/#compile

Similar to the loss function, we also define metrics for the model in Keras. In a simple way, metrics can be understood as the function used to judge the performance of the model on a different unseen dataset, also called the validation dataset. The only difference between metrics and the loss function is that the results from metrics are not used in training the model with respect to optimization. They are only used to validate the test results

Learn Keras for Deep Neural Networks – Jojo Moolayil – apress

Notez bien que la métrique ne sert pas au calcul mais seulement à valider les résultats sur les données de test.

apprentissage

On indique au modèle d’utiliser les image train_images avec leur étiquettes train_labels sur 5 epochs (parcours complet du jeu de données).

model.fit(train_images, train_labels, epochs=5)
Epoch 1/5
60000/60000 [==============================] - 7s 118us/sample - loss: 1.0872 - accuracy: 0.6613
Epoch 2/5
60000/60000 [==============================] - 6s 93us/sample - loss: 0.6397 - accuracy: 0.7683
Epoch 3/5
60000/60000 [==============================] - 6s 92us/sample - loss: 0.5615 - accuracy: 0.8006
Epoch 4/5
60000/60000 [==============================] - 6s 108us/sample - loss: 0.5179 - accuracy: 0.8164
Epoch 5/5
60000/60000 [==============================] - 5s 88us/sample - loss: 0.4901 - accuracy: 0.8268
<tensorflow.python.keras.callbacks.History at 0x7fd9c719b7b8>

Après 5 epochs la perte est de 0.4901 pour une exactitude de 0.8268

validation

On étudie comment se comporte le modèle sur les données de validation.

test_loss, test_acc = model.evaluate(test_images, test_labels)
print('\nTest accuracy:', test_acc)

Le résultat :

10000/10000 [==============================] - 1s 57us/sample - loss: 0.5076 - accuracy: 0.8156
Test accuracy: 0.8156

Le résultat 0.8156 est légèrement inférieur à 0.8268 calculé sur les données d’apprentissage, signe d’une léger sur-apprentissage (overfitting)

Prédictions

Testons quelques prédictions.

predictions = model.predict(test_images)

Si on s’intéresse à la première des prédictions :

predictions[0]

on obtient :

array([2.6798305e-06, 1.8942366e-07, 1.6576356e-05, 8.9628738e-06,
       4.6992078e-05, 1.5858281e-01, 1.3067870e-05, 3.1452715e-01,
       6.4298431e-03, 5.2037174e-01], dtype=float32)

La prédiction est 9 comme le montre les lignes suivantes :

np.argmax(predictions[0])
9

ce qui est une bonne prédiction, puisque le label est aussi 9

test_labels[0]
9

Conclusion

La suite du code n’est pas présentée car elle n’a que peu d’intérêt en ce qui concerne le Deep Learning. Il s’agit uniquement d’affichage des prédictions.

En résumé, on a construit un DNN avec seulement quelques lignes de code et on obtient un résultat honorable. Avec un réseau un peu plus complexe, du type CNN, on obtient plus de 93% d’accuracy.

Pour un exemple de CNN en analogie avec l’exemple MNIST Keras.

from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
batch_size = 128
num_classes = 10
epochs = 12
# input image dimensions
img_rows, img_cols = 28, 28
fashion_mnist = keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

On obtient les résultats :

Train on 60000 samples, validate on 10000 samples
Epoch 1/12
60000/60000 [==============================] - 9s 152us/step - loss: 0.5690 - acc: 0.7981 - val_loss: 0.3662 - val_acc: 0.8690
Epoch 2/12
60000/60000 [==============================] - 9s 143us/step - loss: 0.3641 - acc: 0.8697 - val_loss: 0.3044 - val_acc: 0.8874
Epoch 3/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.3165 - acc: 0.8867 - val_loss: 0.2774 - val_acc: 0.9003
Epoch 4/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.2846 - acc: 0.8973 - val_loss: 0.2563 - val_acc: 0.9075
Epoch 5/12
60000/60000 [==============================] - 9s 143us/step - loss: 0.2593 - acc: 0.9073 - val_loss: 0.2508 - val_acc: 0.9076
Epoch 6/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.2391 - acc: 0.9137 - val_loss: 0.2508 - val_acc: 0.9098
Epoch 7/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.2259 - acc: 0.9181 - val_loss: 0.2417 - val_acc: 0.9126
Epoch 8/12
60000/60000 [==============================] - 9s 143us/step - loss: 0.2133 - acc: 0.9230 - val_loss: 0.2257 - val_acc: 0.9181
Epoch 9/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.1999 - acc: 0.9282 - val_loss: 0.2212 - val_acc: 0.9217
Epoch 10/12
60000/60000 [==============================] - 9s 143us/step - loss: 0.1909 - acc: 0.9305 - val_loss: 0.2216 - val_acc: 0.9189
Epoch 11/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.1813 - acc: 0.9341 - val_loss: 0.2183 - val_acc: 0.9211
Epoch 12/12
60000/60000 [==============================] - 9s 144us/step - loss: 0.1723 - acc: 0.9375 - val_loss: 0.2087 - val_acc: 0.9255
Test loss: 0.2087470836341381
Test accuracy: 0.9255

Dans cet exemple, seul les lignes

fashion_mnist = keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()

ont été modifiées pour remplacer MNIST par Fashion MNIST

Si vous remplacez keras.optimizers.Adadelta par keras.optimizers.Adam vous obtiendrez un encore meilleur résultat.
Epoch 12/12
60000/60000 [==============================] - 8s 140us/step - loss: 0.1471 - acc: 0.9451 - val_loss: 0.2175 - val_acc: 0.9267
Test loss: 0.2175135334253311
Test accuracy: 0.9267

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.