Académique Documents
Professionnel Documents
Culture Documents
218
Pour le moment, nous avons le choix entre trois bibliothèques open source de Deep
Learning répandues : TensorFlow, Microsoft Cognitive Toolkit (CNTK) et Theano.
2585
Afin d’éviter toute confusion, nous désignerons cette implémentation de référence
sous le terme Keras multibackend.
:168
Depuis la fin 2016, d’autres implémentations sont apparues. Nous pouvons à pré-
8.10
sent exécuter Keras sur Apache MXNet, Core ML d’Apple, Javascript ou Typescript
(pour exécuter du code Keras dans un navigateur web) et PlaidML (qui peut fonc-
52.4
tionner sur toutes sortes de cartes graphiques, pas uniquement celles de Nvidia).
TensorFlow fournit sa propre implémentation de Keras, tf.keras. Dans ce cas, le seul
193.
backend pris en charge est TensorFlow, mais elle a l’avantage d’offrir des possibilités
supplémentaires très utiles (voir la figure 2.10). Par exemple, elle reconnaît l’API
681:
Keras (tout au moins dans Python), avec seulement quelques changements mineurs,
comme la modification des importations.
9533
propres à TF
Keras
© Dunod – Toute reproduction non autorisée est un délit.
et tf.keras (à droite)
niver
partie parce que ces deux API se sont inspirées de Scikit-Learn et de Chainer, https://
chainer.org/) et, si vous maîtrisez Keras, vous n’aurez pas de difficultés à basculer sur
PyTorch en cas de besoin. La popularité de PyTorch a considérablement augmenté
en 2018, essentiellement grâce à sa simplicité et à son excellente documentation, ce
qui n’était pas véritablement les points forts de TensorFlow 1.x. Toutefois, certains
estiment que TensorFlow 2 est aussi simple que PyTorch, puisque Keras est devenue
son API de haut niveau officielle et que le reste de l’API a également été largement
simplifié et nettoyé. La documentation a également été totalement réorganisée et il
est beaucoup plus facile d’y trouver ce que l’on cherche. De façon comparable, les
principales faiblesses de PyTorch (par exemple une portabilité limitée et aucune ana-
lyse du graphe de calcul) ont été grandement comblées dans PyTorch 1.0. Une saine
218
compétition est bénéfique pour tout le monde.
À présent, du code ! Puisque tf.keras vient avec TensorFlow, commençons par
2585
installer ce dernier.
:168
2.2.1 Installer TensorFlow 2
8.10
Si vous avez bien suivi les instructions d’installation du chapitre 1, TensorFlow est
déjà installé sur votre ordinateur. Pour tester votre installation, ouvrez un terminal,
52.4
activez l’environnement conda tf2, démarrez un shell Python, importez tensorflow
et keras, et affichez leurs versions : 193.
$ conda activate tf2
$ python
681:
>>> tf.__version__
'2.1.0'
51:8
>>> keras.__version__
'2.2.4-tf'
9533
La deuxième version est celle de l’API Keras mise en œuvre par tf.keras. Vous
remarquerez qu’elle se termine par -tf, ce qui indique que tf.keras implémente non
2110
détecté :
Breta
>>> tf.test.is_gpu_available()
True
Nous devons tout d’abord charger un jeu de données. Nous choisissons Fashion MNIST,
qui est un équivalent exact de MNIST41. Leur format est identique (70 000 images en
om:U
c
41. Voir le chapitre 3 de l’ouvrage Machine Learning avec Scikit-Learn, A. Géron, Dunod (2e édition, 2019).
rvox.
la
scho
2.2 Implémenter des MPC avec Keras 67
niveaux de gris de 28×28 pixels chacune, avec 10 classes), mais les images de Fashion
MNIST représentent des articles de mode à la place de chiffres manuscrits. Chaque
classe est donc plus variée et le problème se révèle plus compliqué. Par exemple, un
modèle linéaire simple donne une précision de 92 % avec MNIST, mais seulement
de 83 % avec Fashion MNIST.
218
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()
2585
Le chargement de MNIST ou de Fashion MNIST avec Keras, à la place de
Scikit-Learn, présente une différence importante : chaque image est représentée
:168
non pas sous forme d’un tableau à une dimension de 784 éléments, mais sous
forme d’un tableau 28×28. Par ailleurs, les intensités des pixels sont représentées
8.10
par des entiers (de 0 à 255) plutôt que par des nombres à virgule flottante (de 0,0
à 255,0). Jetons un coup d’œil à la forme et au type de données du jeu d’entraî-
52.4
nement :
>>> X_train_full.shape
193.
(60000, 28, 28)
>>> X_train_full.dtype
681:
dtype('uint8)
jeu de test, mais qu’il n’y a pas de jeu de validation. Nous allons donc en créer
un. De plus, puisque nous allons entraîner le réseau de neurones avec la descente
51:8
une question de simplicité, nous allons réduire les intensités de pixels à la plage
0‑1 en les divisant par 255,0 (cela les convertit également en nombres à virgule
2110
flottante) :
X_valid, X_train = X_train_full[:5000] / 255.0, X_train_full[5000:] / 255.0
Sud:
Avec MNIST, lorsque l’étiquette est égale à 5, cela signifie que l’image représente
gne
le chiffre manuscrit 5. Facile. En revanche, avec Fashion MNIST, nous avons besoin
de la liste des noms de classes pour savoir ce que nous manipulons :
Breta
>>> class_names[y_train[0]]
'Coat'
niver
om:U
42. Ce jeu de données, aussi utilisé dans l’ouvrage Machine Learning avec Scikit-Learn, A. Géron, Dunod
(2e édition, 2019), a été originellement présenté par R. Kelley Pace et Ronald Barry (1997), dans « Sparse
c
218
2585
:168
Figure 2.11 – Quelques exemples tirés de Fashion MNIST
8.10
Créer le modèle en utilisant l’API Sequential
52.4
Construisons à présent le réseau de neurones ! Voici un MPC de classification avec
deux couches cachées : 193.
model = keras.models.Sequential()
model.add(keras.layers.Flatten(input_shape=[28, 28]))
681:
model.add(keras.layers.Dense(300, activation="relu"))
model.add(keras.layers.Dense(100, activation="relu"))
8902
model.add(keras.layers.Dense(10, activation="softmax"))
Détaillons ce code :
51:8
d’une couche Flatten dont le rôle est de convertir chaque image d’entrée en
un tableau à une dimension : si elle reçoit une donnée d’entrée X, elle calcule
Sud:
• Puis, nous ajoutons une couche cachée Dense constituée de 300 neurones.
Elle utilise la fonction d’activation ReLU. Chaque couche Dense gère sa
niver
propre matrice de poids, qui contient tous les poids des connexions entre les
neurones et leurs entrées. Elle gère également un vecteur de termes constants
om:U
(un par neurone). Lorsqu’elle reçoit des données d’entrée, elle calcule
l’équation 2.2.
c
rvox.
la
scho
2.2 Implémenter des MPC avec Keras 69
• Une deuxième couche cachée Dense de 100 neurones est ensuite ajoutée, elle
aussi avec la fonction d’activation ReLU.
• Enfin, nous ajoutons une couche de sortie Dense avec 10 neurones (un
par classe) en utilisant la fonction d’activation softmax (car les classes sont
exclusives).
218
Au lieu d’ajouter les couches une par une comme nous l’avons fait, nous pouvons
passer une liste de couches au moment de la création du modèle Sequential :
2585
model = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
:168
keras.layers.Dense(300, activation="relu"),
keras.layers.Dense(100, activation="relu"),
8.10
keras.layers.Dense(10, activation="softmax")
])
52.4
193.
Utiliser les exemples de code provenant de keras.io
Les exemples de code documentés sur keras.io fonctionneront parfaitement avec
681:
tf.keras, mais il vous faudra modifier des importations. Examinons, par exemple, le
code keras.io suivant :
8902
output_layer = Dense(10)
Ou, si vous le préférez, utilisez simplement des chemins complets :
2110
Même si cette solution est plus verbeuse, nous l’avons retenue dans cet ouvrage
© Dunod – Toute reproduction non autorisée est un délit.
car elle permet de voir plus facilement les packages à employer et d’éviter toute
gne
confusion entre les classes standard et les classes personnalisées. Dans un code de
production, nous préférons l’approche précédente. Nombreux sont également ceux
Breta
création de la couche), la forme de leur sortie (None signifie que la taille du lot peut
être quelconque) et leur nombre de paramètres. Le résumé se termine par le nombre
om:U
c
43. Vous pouvez utiliser keras.utils.plot_model() pour générer une image du modèle.
rvox.
la
scho
70 Chapitre 2. Introduction aux réseaux de neurones artificiels avec Keras
total de paramètres, qu’ils soient entraînables ou non. Dans notre exemple, nous
avons uniquement des paramètres entraînables (nous verrons des exemples de para-
mètres non entraînables au chapitre 3) :
>>> model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten (Flatten) (None, 784) 0
_________________________________________________________________
dense (Dense) (None, 300) 235500
_________________________________________________________________
218
dense_1 (Dense) (None, 100) 30100
_________________________________________________________________
2585
dense_2 (Dense) (None, 10) 1010
=================================================================
Total params: 266,610
:168
Trainable params: 266,610
Non-trainable params: 0
8.10
_________________________________________________________________
tement est accru, en particulier lorsque les données d’entraînement sont peu nom-
breuses. Nous y reviendrons ultérieurement.
8902
Nous pouvons aisément obtenir une liste des couches du modèle, pour ensuite
retrouver une couche par son indice ou son nom :
51:8
>>> model.layers
9533
[<tensorflow.python.keras.layers.core.Flatten at 0x132414e48>,
<tensorflow.python.keras.layers.core.Dense at 0x1324149b0>,
<tensorflow.python.keras.layers.core.Dense at 0x1356ba8d0>,
2110
<tensorflow.python.keras.layers.core.Dense at 0x13240d240>]
>>> hidden1 = model.layers[1]
Sud:
>>> hidden1.name
'dense'
>>> model.get_layer('dense') is hidden1
gne
True
Breta
Tous les paramètres d’une couche sont accessibles à l’aide des méthodes get_
weights() et set_weights(). Dans le cas d’une couche Dense, cela comprend
à la fois les poids des connexions et les termes constants :
e
sité d
>>> weights.shape
rvox.
la
scho
2.2 Implémenter des MPC avec Keras 71
(784, 300)
>>> biases
array([0., 0., 0., 0., 0., 0., 0., 0., 0., ..., 0., 0., 0.], dtype=float32)
>>> biases.shape
(300,)
La couche Dense initialise les poids des connexions de façon aléatoire (indispen-
sable pour briser la symétrie, comme nous l’avons expliqué) et les termes constants à
zéro, ce qui convient parfaitement. Pour employer une méthode d’initialisation diffé-
rente, il suffit de fixer kernel_initializer (kernel, ou noyau, est un autre nom
pour la matrice des poids des connexions) ou bias_initializer au moment de
la création de la couche. Nous reviendrons sur les initialiseurs au chapitre 3, mais
vous en trouverez la liste complète à l’adresse https://keras.io/initializers/.
218
La forme de la matrice des poids dépend du nombre d’entrées. C’est pour-
2585
quoi il est conseillé de préciser le input_shape lors de la création de la
première couche dans un modèle Sequential. Vous pouvez très bien ne
:168
pas le faire, auquel cas Keras attendra simplement de connaître la forme de
l’entrée pour construire réellement le modèle. Cela se produira lorsque vous
8.10
lui fournirez des données réelles (par exemple, au cours de l’entraînement)
ou lorsque vous appellerez sa méthode build(). Tant que le modèle n’est
52.4
pas véritablement construit, les couches n’auront aucun poids et certaines
opérations ne seront pas possibles (comme sauvegarder le modèle ou affi-
193.
cher son résumé). Par conséquent, si vous connaissez la forme de l’entrée
au moment de la création du modèle, il est préférable de l’indiquer.
681:
Compiler le modèle
8902
Après qu’un modèle a été créé, nous devons invoquer sa méthode compile() de
manière à préciser la fonction de perte et l’optimiseur. Nous pouvons également indi-
51:8
quer une liste d’indicateurs supplémentaires qui seront mesurés au cours de l’entraî-
nement et de l’évaluation :
9533
model.compile(loss="sparse_categorical_crossentropy",
optimizer="sgd",
2110
metrics=["accuracy"])
Sud:
dans ce cas) et les classes sont exclusives. Si, à la place, nous avions une probabilité
rvox.
la
scho
72 Chapitre 2. Introduction aux réseaux de neurones artificiels avec Keras
cible par classe pour chaque instance (comme des vecteurs one-hot, par exemple [0.,
0., 0., 1., 0., 0., 0., 0., 0., 0.] pour représenter une classe 3),
nous opterions pour la fonction de perte "categorical_crossentropy". Si
nous réalisions une classification binaire (avec une ou plusieurs étiquettes binaires),
nous choisirions alors la fonction d’activation "sigmoid" (c’est-à-dire logistique)
dans la couche de sortie à la place de la fonction d’activation "softmax", ainsi que
la fonction de perte "binary_crossentropy".
218
2585
Quant à l’optimiseur, "sgd" signifie que nous entraînerons le modèle à l’aide
d’une descente de gradient stochastique simple. Autrement dit, Keras mettra en
:168
œuvre l’algorithme de rétropropagation décrit précédemment (c’est-à-dire une dif-
férentiation automatique en mode inverse plus une descente de gradient). Nous
8.10
verrons des optimiseurs plus efficaces au chapitre 3 (ils améliorent la descente de
52.4
gradient, non la différentiation automatique).
193.
Lorsqu’on utilise l’optimiseur SGD, le réglage du taux d’apprentissage est im-
portant. Par conséquent, pour fixer le taux d’apprentissage, nous utiliserons
681:
Epoch 2/30
sité d
0.8480
[...]
Epoch 30/30
om:U
0.8926
rvox.
la
scho
2.2 Implémenter des MPC avec Keras 73
Nous lui fournissons les caractéristiques d’entrée (X_train) et les classes cibles
(y_train), ainsi que le nombre d’époques d’entraînement (dans le cas contraire,
il n’y en aurait qu’une, ce qui serait clairement insuffisant pour converger vers une
bonne solution). Nous passons également un jeu de validation (facultatif). Keras
mesurera la perte et les indicateurs supplémentaires sur ce jeu à la fin de chaque
époque, ce qui se révélera très utile pour déterminer les performances réelles du
modèle. Si les performances sur le jeu d’entraînement sont bien meilleures que sur le
jeu de validation, il est probable que le modèle surajuste le jeu d’entraînement (ou
qu’il y ait une erreur, comme une différence entre les données du jeu d’entraînement
et celles du jeu de validation).
Et voilà, le réseau de neurones est entraîné44. Au cours de chaque époque de l’en-
218
traînement, Keras affiche le nombre d’instances traitées (ainsi qu’une barre de pro-
gression), le temps moyen d’entraînement par échantillon, ainsi que la perte et la
2585
précision (ou tout autre indicateur supplémentaire demandé) à la fois sur le jeu d’en-
traînement et sur celui de validation. Vous constatez que la perte dans l’entraînement
:168
a diminué, ce qui est bon signe, et que la précision de la validation a atteint 89,26 %
8.10
après 30 époques, ce qui n’est pas très loin de la précision de l’entraînement. Nous
pouvons donc en conclure que s’il y a surajustement, il n’est pas trop important.
52.4
Au lieu de passer un jeu de validation avec l’argument validation_
193.
data, vous pouvez indiquer dans validation_split la portion du
jeu d’entraînement que Keras doit utiliser pour la validation. Par exemple,
681:
lors de l’appel à la méthode fit() afin de donner un poids plus important aux
classes sous-représentées et un poids plus faible à celle surreprésentées. Ils seront uti-
9533
lisés par Keras dans le calcul de la perte. Si des poids sont nécessaires pour chaque
instance, nous pouvons utiliser l’argument sample_weight (si class_weight
2110
et sample_weight sont tous deux précisés, Keras les multiplie). Les poids par ins-
tance peuvent être utiles lorsque certaines instances ont été libellées par des experts
Sud:
tandis que d’autres l’ont été au travers d’une collaboration participative : les pre-
© Dunod – Toute reproduction non autorisée est un délit.
mières peuvent avoir un poids plus important. Nous pouvons également fournir des
gne
poids d’instance (mais pas de classe) pour le jeu de validation en les ajoutant en
Breta
44. Si les données d’entraînement ou de validation n’ont pas la forme attendue, une exception est générée.
Cette erreur étant probablement la plus fréquente, vous devez vous familiariser avec le message d’erreur. Il
niver
est relativement clair. Par exemple, si vous tentez d’entraîner ce modèle avec un tableau qui contient des
images applaties (X_train.reshape(-1, 784)), vous recevez l’exception suivante : « ValueError:
om:U
Error when checking input: expected flatten_input to have 3 dimensions, but got array with shape (60000,
784) ». Le message explique que flatten_input doit avoir trois dimensions alors que l’entrée fournie
c
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
218
plt.gca().set_ylim(0, 1) # Régler la plage verticale sur [0‑1]
plt.show()
2585
1,0
:168
4
8.10
0,8
52.4
2
193.
0,6
1
681:
3
0,4
8902
51:8
0,2
9533
2110
0,0
0 5 10 15 20 25
Sud:
validation décroissent. Parfait ! Par ailleurs, les courbes de validation sont proches
des courbes d’entraînement, ce qui signifie que le surentraînement n’est pas très
niver
ment. Mais ce n’est pas le cas : l’erreur de validation est calculée à la fin de chaque
époque, tandis que l’erreur d’entraînement est calculée à l’aide d’une moyenne glis-
c
sante pendant chaque époque. La courbe d’entraînement doit donc être décalée d’une
rvox.
la
scho
2.2 Implémenter des MPC avec Keras 75
moitié d’époque vers la gauche. Avec ce décalage, nous constatons que les courbes
d’entraînement et de validation se chevauchent presque parfaitement au début de
l’entraînement.
218
long. Nous pouvons voir que le modèle n’a pas encore assez convergé, car la perte
de validation continue à diminuer. Il serait donc préférable de poursuivre l’entraî-
2585
nement. Il suffit d’invoquer une nouvelle fois la méthode fit(), car Keras reprend
l’entraînement là où il s’était arrêté (une précision de validation proche de 89 %
:168
devrait pouvoir être atteinte).
8.10
Si vous n’êtes pas satisfait des performances de votre modèle, revenez en arrière
et ajustez les hyperparamètres. Le premier à examiner est le taux d’apprentissage. Si
52.4
cela ne change rien, essayez un autre optimiseur (réajustez toujours le taux d’appren-
tissage après chaque changement d’un hyperparamètre). Si les performances sont 193.
toujours mauvaises, essayez d’ajuster les hyperparamètres du modèle, par exemple le
nombre de couches, le nombre de neurones par couche et le type des fonctions d’ac-
681:
tivation attribuées à chaque couche cachée. Vous pouvez également affiner d’autres
hyperparamètres, comme la taille du lot (fixée dans la méthode fit() à l’aide de
8902
l’argument batch_size, dont la valeur par défaut est 32). Nous reviendrons sur
le réglage des hyperparamètres à la fin de ce chapitre. Dès que vous êtes satisfait
51:8
[0.3339798209667206, 0.8851]
gne
Il est fréquent d’obtenir des performances légèrement moins bonnes sur le jeu
Breta
de test que sur le jeu de validation45. En effet, les hyperparamètres sont ajustés non
pas sur le jeu de test mais sur le jeu de validation (cependant, dans cet exemple,
puisque les hyperparamètres n’ont pas été affinés, la précision inférieure est juste
e
sité d
45. Voir le chapitre 2 de l’ouvrage Machine Learning avec Scikit-Learn, A. Géron, Dunod (2e édition, 2019).
rvox.
la
scho
76 Chapitre 2. Introduction aux réseaux de neurones artificiels avec Keras
Pour chaque instance, le modèle estime une probabilité par classe, de la classe 0
218
à la classe 9. Par exemple, pour la première image, il estime que la probabilité de
2585
la classe 9 (bottine) est de 96 %, que celle de la classe 5 (sandale) est de 3 %, que
celle de la classe 7 (basket) est de 1 %, et que celles des autres classes sont négli-
:168
geables. Autrement dit, il « croit » que la première image est une chaussure, pro-
bablement une bottine, mais éventuellement une sandale ou une basket. Si nous
8.10
nous intéressons uniquement à la classe dont la probabilité estimée est la plus élevée
(même si celle-ci est relativement faible), nous pouvons utiliser à la place la méthode
52.4
predict_classes() : 193.
>>> y_pred = model.predict_classes(X_new)
>>> y_pred
681:
array([9, 2, 1])
>>> np.array(class_names)[y_pred]
array(['Ankle boot', 'Pullover', 'Trouser'], dtype='<U11')
8902
Le classificateur réalise une classification correcte des trois images (elles sont
51:8
>>> y_new
array([9, 2, 1])
2110
Sud:
gne
Breta
e
sité d
niver
Vous savez à présent utiliser l’API Sequential pour construire, entraîner, évaluer et
c
housing = fetch_california_housing()
218
X_train_full, X_test, y_train_full, y_test = train_test_split(
2585
housing.data, housing.target)
X_train, X_valid, y_train, y_valid = train_test_split(
X_train_full, y_train_full)
:168
scaler = StandardScaler()
8.10
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
52.4
X_test = scaler.transform(X_test)
voulons prédire une seule valeur) et aucune fonction d’activation, et que la fonction
de perte est l’erreur quadratique moyenne. Puisque le jeu de données contient beau-
51:8
coup de bruit, nous utilisons simplement une seule couche cachée avec moins de
neurones que précédemment. Cela permet d’éviter le surajustement :
9533
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
2110
keras.layers.Dense(1)
])
model.compile(loss="mean_squared_error", optimizer="sgd")
Sud:
validation_data=(X_valid, y_valid))
gne
y_pred = model.predict(X_new)
Cependant, bien que les modèles séquentiels soient extrêmement courants, il est
sité d
46. Ce jeu de données est plus simple que California Housing, utilisé au chapitre 5, car il contient uni-
quement des caractéristiques numériques (la caractéristique ocean_proximity est absente) et il ne
c