Introduction Au Deep Learning (Notes de Cours)
Introduction Au Deep Learning (Notes de Cours)
de cours)
Romain Tavenard
28 novembre 2023
TABLE DES MATIÈRES
1 Introduction 3
1.1 Un premier modèle : le perceptron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Optimisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Récapitulatif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2 Perceptrons multicouches 9
2.1 Empiler des couches pour une meilleure expressivité . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Décider de l’architecture d’un MLP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Fonctions d’activation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4 Déclarer un MLP en keras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3 Fonctions de coût 17
3.1 Erreur quadratique moyenne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2 Perte logistique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 Optimisation 19
4.1 Descente de gradient stochastique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2 Une note sur Adam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3 La malédiction de la profondeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4 Coder tout cela en keras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.5 Prétraitement des données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5 Régularisation 27
5.1 Early stopping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2 Pénalisation de la perte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.3 DropOut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
i
Bibliographie 47
ii
Introduction au Deep Learning (notes de cours)
INTRODUCTION
Dans ce chapitre d’introduction, nous allons présenter un premier réseau neuronal appelé le Perceptron. Ce modèle est un
réseau neuronal constitué d’un seul neurone, et nous l’utiliserons ici pour introduire des concepts-clés que nous détaillerons
plus tard dans le cours.
Dans la terminologie des réseaux de neurones, un neurone est une fonction paramétrée qui prend un vecteur x en entrée
et sort une valeur unique 𝑎 comme suit :
𝑎 = 𝜑(wx
⏟ + 𝑏),
𝑜
où les paramètres du neurone sont ses poids stockés dans w. et un terme de biais 𝑏, et 𝜑 est une fonction d’activation qui
est choisie a priori (nous y reviendrons plus en détail plus tard dans le cours) :
𝑥0
𝑤0
𝑥1 𝑤
1
𝑤2 𝜑
𝑥2 𝑜 𝑎
𝑤3
𝑥3
𝑏
+1
3
Introduction au Deep Learning (notes de cours)
1.2 Optimisation
Les modèles présentés dans ce document ont pour but de résoudre des problèmes de prédiction dans lesquels l’objectif est
de trouver des valeurs de paramètres « suffisamment bonnes » pour le modèle en jeu compte tenu de données observées.
Le problème de la recherche de telles valeurs de paramètres est appelé optimisation. L’apprentissage profond (ou deep
learning) fait un usage intensif d’une famille spécifique de stratégies d’optimisation appelée descente gradiente.
Pour se faire une idée de la descente de gradient, supposons que l’on nous donne le jeu de données suivant sur les prix de
l’immobilier :
import pandas as pd
RM PRICE
0 6.575 24.0
1 6.421 21.6
2 7.185 34.7
3 6.998 33.4
4 7.147 36.2
.. ... ...
501 6.593 22.4
502 6.120 20.6
503 6.976 23.9
504 6.794 22.0
505 6.030 11.9
Dans notre cas, nous essaierons (pour commencer) de prédire la valeur cible "PRICE" de ce jeu de données, qui est la
valeur médiane des maisons occupées par leur propriétaire en milliers de dollars en fonction du nombre moyen de pièces
par logement "RM" :
4 Chapitre 1. Introduction
Introduction au Deep Learning (notes de cours)
Supposons que nous ayons une approche naïve dans laquelle notre modèle de prédiction est linéaire sans biais, c’est-à-dire
que pour une entrée donnée 𝑥𝑖 la sortie prédite est calculée comme suit :
𝑦𝑖̂ = 𝑤𝑥𝑖
import numpy as np
x = boston["RM"]
y = boston["PRICE"]
plt.plot(w, loss(w, x, y), "r-");
1.2. Optimisation 5
Introduction au Deep Learning (notes de cours)
Ici, il semble qu’une valeur de 𝑤 autour de 4 devrait être un bon choix. Cette méthode (générer de nombreuses valeurs pour
le paramètre et calculer la perte pour chaque valeur) ne peut pas s’adapter aux modèles qui ont beaucoup de paramètres,
donc nous allons donc essayer autre chose.
Supposons que nous ayons accès, à chaque fois que nous choisissons une valeur candidate pour 𝑤, à la fois à la perte
ℒ et aux informations sur la façon dont ℒ varie, localement. Nous pourrions, dans ce cas, calculer une nouvelle valeur
candidate pour 𝑤 en nous déplaçant à partir de la valeur candidate précédente dans la direction de la descente la plus raide.
C’est l’idée de base de l’algorithme de descente du gradient qui, à partir d’un candidat initial 𝑤0 , calcule itérativement de
nouveaux candidats comme :
𝜕ℒ
𝑤𝑡+1 = 𝑤𝑡 − 𝜌 ∣
𝜕𝑤 𝑤=𝑤𝑡
où 𝜌 est un hyper-paramètre (appelé taux d’apprentissage) qui contrôle la taille des pas à effectuer, et 𝜕ℒ𝜕𝑤 ∣𝑤=𝑤𝑡 est le
gradient de ℒ par rapport à 𝑤, évalué en 𝑤 = 𝑤𝑡 . Comme vous pouvez le voir, la direction de la descente la plus raide
est l’opposé de la direction indiquée par le gradient (et cela vaut aussi pour les paramètres vectoriels).
Ce processus est répété jusqu’à la convergence, comme l’illustre la figure suivante :
rho = 1e-5
w = [0.]
for t in range(10):
w_update = w[t] - rho * grad_loss(w[t], x, y)
w.append(w_update)
6 Chapitre 1. Introduction
Introduction au Deep Learning (notes de cours)
rho = 1e-6
w = [0.]
for t in range(10):
w_update = w[t] - rho * grad_loss(w[t], x, y)
w.append(w_update)
Cela prendrait certainement plus de temps pour converger. Mais attention, un taux d’apprentissage plus élevé n’est pas
toujours une bonne idée :
1.2. Optimisation 7
Introduction au Deep Learning (notes de cours)
rho = 5e-5
w = [0.]
for t in range(10):
w_update = w[t] - rho * grad_loss(w[t], x, y)
w.append(w_update)
Vous voyez comment nous divergeons lentement parce que nos pas sont trop grands ?
1.3 Récapitulatif
8 Chapitre 1. Introduction
CHAPITRE 2
PERCEPTRONS MULTICOUCHES
Dans le chapitre précédent, nous avons vu un modèle très simple appelé le perceptron. Dans ce modèle, la sortie prédite
𝑦 ̂ est calculée comme une combinaison linéaire des caractéristiques d’entrée plus un biais :
𝑑
𝑦 ̂ = ∑ 𝑥𝑗 𝑤𝑗 + 𝑏
𝑗=1
En d’autres termes, nous optimisions parmi la famille des modèles linéaires, qui est une famille assez restreinte.
Afin de couvrir un plus large éventail de modèles, on peut empiler des neurones organisés en couches pour former un
modèle plus complexe, comme le modèle ci-dessous, qui est appelé modèle à une couche cachée, car une couche supplé-
mentaire de neurones est introduite entre les entrées et la sortie :
Couche d'entrée Couche cachée Couche de sortie
x h(1) ŷ
w(0) w(1)
9
Introduction au Deep Learning (notes de cours)
La question que l’on peut se poser maintenant est de savoir si cette couche cachée supplémentaire permet effectivement
de couvrir une plus grande famille de modèles. C’est à cela que sert le théorème d’approximation universelle ci-dessous.
En d’autres termes, en utilisant une couche cachée pour mettre en correspondance les entrées et les sorties, on peut
maintenant approximer n’importe quelle fonction continue, ce qui est une propriété très intéressante. Notez cependant
que le nombre de neurones cachés nécessaire pour obtenir une qualité d’approximation donnée n’est pas discuté ici. De
plus, il n’est pas suffisant qu’une telle bonne approximation existe, une autre question importante est de savoir si les
algorithmes d’optimisation que nous utiliserons convergeront in fine vers cette solution ou non, ce qui n’est pas garanti,
comme discuté plus en détail dans le chapitre dédié.
En pratique, nous observons empiriquement que pour atteindre une qualité d’approximation donnée, il est plus efficace
(en termes de nombre de paramètres requis) d’empiler plusieurs couches cachées plutôt que de s’appuyer sur une seule :
Première Seconde Couche de sortie
Couche d'entrée
couche cachée couche cachée ŷ
x
h(1) h(2)
(2) (2)
𝑦 ̂ = 𝜑out (∑ 𝑤𝑖 ℎ𝑖 + 𝑏(2) ) (2.1)
𝑖
(𝑙)
Pour être précis, les termes de biais 𝑏𝑖 ne sont pas représentés dans la représentation graphique ci-dessus.
De tels modèles avec une ou plusieurs couches cachées sont appelés Perceptrons multicouches (ou Multi-Layer Percep-
trons, MLP).
Lors de la conception d’un modèle de perceptron multicouche destiné à être utilisé pour un problème spécifique, certaines
quantités sont fixées par le problème en question et d’autres sont des hyper-paramètres du modèle.
Prenons l’exemple du célèbre jeu de données de classification d’iris :
import pandas as pd
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
.. ... ... ... ...
145 6.7 3.0 5.2 2.3
146 6.3 2.5 5.0 1.9
147 6.5 3.0 5.2 2.0
148 6.2 3.4 5.4 2.3
149 5.9 3.0 5.1 1.8
target
0 0
1 0
2 0
3 0
4 0
.. ...
145 2
146 2
147 2
148 2
149 2
L’objectif ici est d’apprendre à déduire l’attribut « cible » (3 classes différentes possibles) à partir des informations conte-
nues dans les 4 autres attributs.
La structure de ce jeu de données dicte :
— le nombre de neurones dans la couche d’entrée, qui est égal au nombre d’attributs descriptifs dans notre jeu de
données (ici, 4), et
— le nombre de neurones dans la couche de sortie, qui est ici égal à 3, puisque le modèle est censé produire une
probabilité par classe cible.
De manière plus générale, pour la couche de sortie, on peut être confronté à plusieurs situations :
— lorsqu’il s’agit de régression, le nombre de neurones de la couche de sortie est égal au nombre de caractéristiques
à prédire par le modèle,
— quand il s’agit de classification
— Dans le cas d’une classification binaire, le modèle aura un seul neurone de sortie qui indiquera la probabilité
de la classe positive,
— dans le cas d’une classification multi-classes, le modèle aura autant de neurones de sortie que le nombre de
classes du problème.
Une fois que ces nombres de neurones d’entrée / sortie sont fixés, le nombre de neurones cachés ainsi que le nombre de
neurones par couche cachée restent des hyper-paramètres du modèle.
Un autre hyper-paramètre important des réseaux neuronaux est le choix de la fonction d’activation 𝜑.
Il est important de noter que si nous utilisons la fonction identité comme fonction d’activation, quelle que soit la profondeur
de notre MLP, nous ne couvrirons plus que la famille des modèles linéaires. En pratique, nous utiliserons donc des fonctions
d’activation qui ont un certain régime linéaire mais qui ne se comportent pas comme une fonction linéaire sur toute la
gamme des valeurs d’entrée.
Historiquement, les fonctions d’activation suivantes ont été proposées :
2
tanh(𝑥) = −1
1 + 𝑒−2𝑥
1
sigmoid(𝑥) =
1 + 𝑒−𝑥
𝑥 si 𝑥 > 0
ReLU(𝑥) = {
0 sinon
En pratique, la fonction ReLU (et certaines de ses variantes) est la plus utilisée de nos jours, pour des raisons qui seront
discutées plus en détail dans notre chapitre consacré à l’optimisation.
Vous avez peut-être remarqué que dans la formulation du MLP fournie par l’équation (1), la couche de sortie possède sa
propre fonction d’activation, notée 𝜑out . Cela s’explique par le fait que le choix de la fonction d’activation pour la couche
de sortie d’un réseau neuronal est spécifique au problème à résoudre.
En effet, vous avez pu constater que les fonctions d’activation abordées dans la section précédente ne partagent pas la
même plage de valeurs de sortie. Il est donc primordial de choisir une fonction d’activation adéquate pour la couche de
sortie, de sorte que notre modèle produise des valeurs cohérentes avec les quantités qu’il est censé prédire.
Si, par exemple, notre modèle est censé être utilisé dans l’ensemble de données sur les logements de Boston dont nous
avons parlé dans le chapitre précédent, l’objectif est de prédire les prix des logements, qui sont censés être des quantités non
négatives. Il serait donc judicieux d’utiliser ReLU (qui peut produire toute valeur positive) comme fonction d’activation
pour la couche de sortie dans ce cas.
Comme indiqué précédemment, dans le cas de la classification binaire, le modèle aura un seul neurone de sortie et ce
neurone produira la probabilité associée à la classe positive. Cette quantité devra se situer dans l’intervalle [0, 1], et la
fonction d’activation sigmoïde est alors le choix par défaut dans ce cas.
Enfin, lorsque la classification multi-classes est en jeu, nous avons un neurone par classe de sortie et chaque neurone est
censé fournir la probabilité pour une classe donnée. Dans ce contexte, les valeurs de sortie doivent être comprises entre 0
et 1, et leur somme doit être égale à 1. À cette fin, nous utilisons la fonction d’activation softmax définie comme suit :
𝑒𝑜𝑖
∀𝑖, softmax(𝑜𝑖 ) =
∑𝑗 𝑒𝑜𝑗
où, pour tous les 𝑖, les 𝑜𝑖 sont les valeurs des neurones de sortie avant application de la fonction d’activation.
Pour définir un modèle MLP dans keras, il suffit d’empiler des couches. A titre d’exemple, si l’on veut coder un modèle
composé de :
— une couche d’entrée avec 10 neurones,
— d’une couche cachée de 20 neurones avec activation ReLU,
— une couche de sortie composée de 3 neurones avec activation softmax,
le code sera le suivant :
model = Sequential([
InputLayer(input_shape=(10, )),
Dense(units=20, activation="relu"),
Dense(units=3, activation="softmax")
])
model.summary()
=================================================================
Total params: 283 (1.11 KB)
Trainable params: 283 (1.11 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Notez que model.summary() fournit un aperçu intéressant d’un modèle défini et de ses paramètres.
Exercice #1
En vous basant sur ce que nous avons vu dans ce chapitre, pouvez-vous expliquer le nombre de paramètres retournés par
model.summary() ci-dessus ?
Solution
Notre couche d’entrée est composée de 10 neurones, et notre première couche est entièrement connectée, donc chacun de
ces neurones est connecté à un neurone de la couche cachée par un paramètre, ce qui fait déjà 10 × 20 = 200 paramètres.
De plus, chacun des neurones de la couche cachée possède son propre paramètre de biais, ce qui fait 20 paramètres
supplémentaires. Nous avons donc 220 paramètres, tels que sortis par model.summary() pour la couche "dense
(Dense)".
De la même manière, pour la connexion des neurones de la couche cachée à ceux de la couche de sortie, le nombre total
de paramètres est de 20 × 3 = 60 pour les poids plus 3 paramètres supplémentaires pour les biais.
Au total, nous avons 220 + 63 = 283 paramètres dans ce modèle.
Exercice #2
Déclarez, en keras, un MLP avec une couche cachée composée de 100 neurones et une activation ReLU pour le jeu de
données Iris présenté ci-dessus.
Solution
model = Sequential([
InputLayer(input_shape=(4, )),
Dense(units=100, activation="relu"),
Dense(units=3, activation="softmax")
])
Exercice #3
Même question pour le jeu de données sur le logement à Boston présenté ci-dessous (le but ici est de prédire l’attribut
PRICE en fonction des autres).
Solution
model = Sequential([
InputLayer(input_shape=(6, )),
Dense(units=100, activation="relu"),
Dense(units=1, activation="relu")
])
FONCTIONS DE COÛT
Nous avons maintenant présenté une première famille de modèles, qui est la famille MLP. Afin d’entraîner ces modèles
(i.e. d’ajuster leurs paramètres pour qu’ils s’adaptent aux données), nous devons définir une fonction de coût (aussi appelée
fonction de perte, ou loss function) à optimiser. Une fois cette fonction choisie, l’optimisation consistera à régler les
paramètres du modèle de manière à la minimiser.
Dans cette section, nous présenterons deux fonctions de pertes standard, à savoir l’erreur quadratique moyenne (princi-
palement utilisée pour la régression) et la fonction de perte logistique (utilisée en classification).
Dans ce qui suit, nous supposons connu un ensemble de données 𝒟 composé de 𝑛 échantillons annotés (𝑥𝑖 , 𝑦𝑖 ), et nous
désignons la sortie du modèle :
où 𝑚𝜃 est notre modèle et 𝜃 est l’ensemble de tous ses paramètres (poids et biais).
L’erreur quadratique moyenne (ou Mean Squared Error, MSE) est la fonction de perte la plus couramment utilisée dans
les contextes de régression. Elle est définie comme suit
1
ℒ(𝒟; 𝑚𝜃 ) = ∑ ‖𝑦𝑖̂ − 𝑦𝑖 ‖2
𝑛 𝑖
1
= ∑ ‖𝑚𝜃 (𝑥𝑖 ) − 𝑦𝑖 ‖2
𝑛 𝑖
17
Introduction au Deep Learning (notes de cours)
La perte logistique est la fonction de perte la plus largement utilisée pour entraîner des réseaux neuronaux dans des
contextes de classification. Elle est définie comme suit
1
ℒ(𝒟; 𝑚𝜃 ) = ∑ − log 𝑝(𝑦𝑖̂ = 𝑦𝑖 ; 𝑚𝜃 )
𝑛 𝑖
OPTIMISATION
Dans ce chapitre, nous présenterons des variantes de la stratégie d’optimisation de descente de gradient et montrerons
comment elles peuvent être utilisées pour optimiser les paramètres des réseaux de neurones.
Commençons par l’algorithme de base de la descente de gradient et ses limites.
où 𝜌 est un hyper-paramètre important de la méthode, appelé le taux d’apprentissage (ou learning rate). La descente de
gradient consiste à jour itérativement 𝜃 dans la direction de la plus forte diminution de la perte ℒ.
Comme on peut le voir dans l’algorithme précédent, lors d’un descente de gradient, les paramètres du modèle sont mis
à jour une fois par epoch, ce qui signifie qu’un passage complet sur l’ensemble des données est nécessaire avant la mise
à jour. Lorsque l’on traite de grands jeux de données, cela constitue une forte limitation, ce qui motive l’utilisation de
variantes stochastiques.
19
Introduction au Deep Learning (notes de cours)
L’idée derrière l’algorithme de descente de gradient stochastique (ou Stochastic Gradient Descent, SGD) est d’obtenir des
estimations bon marché (au sens de la quantité de calculs nécessaires) pour la quantité
1
∇𝜃 ℒ(𝒟; 𝑚𝜃 ) = ∑ ∇ ℒ(𝑥𝑖 , 𝑦𝑖 ; 𝑚𝜃 )
𝑛 (𝑥 ,𝑦 )∈𝒟 𝜃
𝑖 𝑖
où 𝒟 est l’ensemble d’apprentissage. Pour ce faire, on tire des sous-ensembles de données, appelés minibatchs, et
1
∇𝜃 ℒ(ℬ; 𝑚𝜃 ) = ∑ ∇ ℒ(𝑥𝑖 , 𝑦𝑖 ; 𝑚𝜃 )
𝑏 (𝑥 ,𝑦 )∈ℬ 𝜃
𝑖 𝑖
est utilisé comme estimateur de ∇𝜃 ℒ(𝒟; 𝑚𝜃 ). Il en résulte l’algorithme suivant dans lequel les mises à jour des paramètres
se produisent après chaque minibatch, c’est-à-dire plusieurs fois par epoch.
Par conséquent, lors de l’utilisation de SGD, les mises à jour des paramètres sont plus fréquentes, mais elles sont « brui-
tées » puisqu’elles sont basées sur une estimation du gradient par minibatch au lieu de s’appuyer sur le vrai gradient,
comme illustré ci-dessous :
20 Chapitre 4. Optimisation
Introduction au Deep Learning (notes de cours)
Outre le fait qu’elle implique des mises à jour plus fréquentes des paramètres, la SGD présente un avantage supplémentaire
en termes d’optimisation, qui est essentiel pour les réseaux de neurones. En effet, comme on peut le voir ci-dessous,
contrairement à ce que nous avions dans le cas du Perceptron, la perte MSE (et il en va de même pour la perte logistique)
n’est plus convexe en les paramètres du modèle dès que celui-ci possède au moins une couche cachée :
La descente de gradient est connue pour souffrir d’optima locaux, et de tels fonctions de pertes constituent un problème
sérieux pour la descente de gradient. D’un autre côté, la descente de gradient stochastique est susceptible de bénéficier
d’estimations de gradient bruitées pour s’échapper des minima locaux.
Adam [Kingma and Ba, 2015] est une variante de la méthode de descente de gradient stochastique. Elle diffère dans la
règle de mise à jour des paramètres.
Tout d’abord, elle utilise ce qu’on appelle le momentum, qui consiste essentiellement à s’appuyer sur les mises à jour
antérieures du gradient pour lisser la trajectoire dans l’espace des paramètres pendant l’optimisation. Une illustration
interactive du momentum peut être trouvée dans [Goh, 2017].
L’estimation du gradient est remplacée par la quantité :
1
m(𝑡+1) ← [𝛽 m(𝑡) + (1 − 𝛽1 )∇𝜃 ℒ]
1 − 𝛽1𝑡 1
Lorsque 𝛽1 est égal à zéro, nous avons m(𝑡+1) = ∇𝜃 ℒ et pour 𝛽1 ∈]0, 1[, m(𝑡+1) l’estimation courante du gradient utilise
l’information sur les estimations passées, stockée dans m(𝑡) .
Une autre différence importante entre SGD et la Adam consiste à utiliser un taux d’apprentissage adaptatif. En d’autres
termes, au lieu d’utiliser le même taux d’apprentissage 𝜌 pour tous les paramètres du modèle, le taux d’apprentissage pour
un paramètre donné 𝜃𝑖 est défini comme :
𝜌
𝜌(𝑡+1)
̂ (𝜃𝑖 ) =
√𝑠(𝑡+1) (𝜃𝑖 ) + 𝜖
Ici aussi, le terme 𝑠 utilise le momentum. Par conséquent, le taux d’apprentissage sera réduit pour les paramètres qui ont
subi de grandes mises à jour dans les itérations précédentes.
Globalement, la règle de mise à jour d’Adam est la suivante :
et rappelons que, pour une couche donnée (ℓ), la sortie de la couche est calculée comme suit
où 𝜑 est la fonction d’activation pour la couche donnée (nous ignorons les termes de biais dans cet exemple simplifié).
Afin d’effectuer une descente de gradient (stochastique), les gradients de la perte par rapport aux paramètres du modèle
doivent être calculés.
En utilisant la règle de la dérivation en chaîne, ces gradients peuvent être exprimés comme suit :
𝜕ℒ 𝜕ℒ 𝜕𝑎(3) 𝜕𝑜(3)
=
𝜕𝑤(2) 𝜕𝑎(3) 𝜕𝑜(3) 𝜕𝑤(2)
𝜕ℒ 𝜕ℒ 𝜕𝑎(3) 𝜕𝑜(3) 𝜕𝑎(2) 𝜕𝑜(2)
=
𝜕𝑤(1) 𝜕𝑎(3) 𝜕𝑜(3) 𝜕𝑎(2) 𝜕𝑜(2) 𝜕𝑤(1)
𝜕ℒ 𝜕ℒ 𝜕𝑎(3) 𝜕𝑜(3) 𝜕𝑎(2) 𝜕𝑜(2) 𝜕𝑎(1) 𝜕𝑜(1)
(0)
=
𝜕𝑤 𝜕𝑎(3) 𝜕𝑜(3) 𝜕𝑎(2) 𝜕𝑜(2) 𝜕𝑎(1) 𝜕𝑜(1) 𝜕𝑤(0)
Il y a des idées importantes à saisir ici.
Tout d’abord, il faut remarquer que les poids qui sont plus éloignés de la sortie du modèle héritent de règles de gradient
composées de plus de termes. Par conséquent, lorsque certains de ces termes deviennent de plus en plus petits, il y a un
risque plus élevé pour ces poids que leurs gradients tombent à 0. C’est ce qu’on appelle l’effet de gradient évanescent
(vanishing gradient), qui est un phénomène très courant dans les réseaux neuronaux profonds (c’est-à-dire les réseaux
composés de nombreuses couches).
22 Chapitre 4. Optimisation
Introduction au Deep Learning (notes de cours)
𝜕𝑎(ℓ) 𝜕𝑜(ℓ)
Deuxièmement, certains termes sont répétés dans ces formules, et en général, des termes de la forme 𝜕𝑜(ℓ)
et 𝜕𝑎(ℓ−1)
sont
présents à plusieurs endroits. Ces termes peuvent être développés comme suit :
𝜕𝑎(ℓ)
= 𝜑′ (𝑜(ℓ) )
𝜕𝑜(ℓ)
𝜕𝑜(ℓ)
= 𝑤(ℓ−1)
𝜕𝑎(ℓ−1)
Voyons à quoi ressemblent les dérivées des fonctions d’activation standard :
On peut constater que la dérivée de ReLU possède une plus grande plage de valeurs d’entrée pour lesquelles elle est non
nulle (typiquement toute la plage de valeurs d’entrée positives) que ses concurrentes, ce qui en fait une fonction d’activation
(ℓ)
très intéressante pour les réseaux neuronaux profonds, car nous avons vu que le terme 𝜕𝑎 𝜕𝑜(ℓ)
apparaît de manière répétée
dans les dérivations en chaîne.
Dans keras, les informations sur les pertes et l’optimiseur sont transmises au moment de la compilation :
model = Sequential([
InputLayer(input_shape=(10, )),
Dense(units=20, activation="relu"),
Dense(units=3, activation="softmax")
])
model.summary()
=================================================================
Total params: 283 (1.11 KB)
Trainable params: 283 (1.11 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
model.compile(loss="categorical_crossentropy", optimizer="adam")
En termes de pertes :
— "mse" est la perte d’erreur quadratique moyenne,
— "binary_crossentropy" est la perte logistique pour la classification binaire,
— "categorical_crossentropy" est la perte logistique pour la classification multi-classes.
Les optimiseurs définis dans cette section sont disponibles sous forme de "sgd" et "adam". Afin d’avoir le contrôle sur
les hyper-paramètres des optimiseurs, on peut alternativement utiliser la syntaxe suivante :
model.compile(loss="categorical_crossentropy", optimizer=adam_opt)
En pratique, pour que la phase d’ajustement du modèle se déroule correctement, il est important de mettre à l’échelle les
données d’entrée. Dans l’exemple suivant, nous allons comparer deux entraînements du même modèle, avec une initiali-
sation similaire et la seule différence entre les deux sera de savoir si les données d’entrée sont centrées-réduites ou laissées
telles quelles.
import pandas as pd
from keras.utils import to_categorical
24 Chapitre 4. Optimisation
Introduction au Deep Learning (notes de cours)
set_random_seed(0)
model = Sequential([
InputLayer(input_shape=(4, )),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=3, activation="softmax")
])
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h = model.fit(X, y, epochs=n_epochs, batch_size=30, verbose=0)
X -= X.mean(axis=0)
X /= X.std(axis=0)
set_random_seed(0)
model = Sequential([
InputLayer(input_shape=(4, )),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=3, activation="softmax")
])
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h_standardized = model.fit(X, y, epochs=n_epochs, batch_size=30, verbose=0)
26 Chapitre 4. Optimisation
CHAPITRE 5
RÉGULARISATION
Comme nous l’avons vu dans les chapitres précédents, l’une des forces des réseaux neuronaux est qu’ils peuvent approximer
n’importe quelle fonction continue lorsqu’un nombre suffisant de paramètres est utilisé. Lors de l’utilisation d’approxima-
teurs universels dans des contextes d’apprentissage automatique, un risque connexe important est celui du surajustement
(overfitting) aux données d’apprentissage. Plus formellement, étant donné un jeu de données d’apprentissage 𝒟𝑡 tiré d’une
distribution inconnue 𝒟, les paramètres du modèle sont optimisés de manière à minimiser le risque empirique :
1
ℛ𝑒 (𝜃) = ∑ ℒ(𝑥𝑖 , 𝑦𝑖 ; 𝑚𝜃 )
|𝒟𝑡 | (𝑥 ,𝑦 )∈𝒟
𝑖 𝑖 𝑡
Comme illustré ci-dessous, on peut observer que l’entraînement d’un réseau neuronal pendant un trop grand nombre
d”epochs peut conduire à un surajustement. Notez qu’ici, le risque réel est estimé grâce à l’utilisation d’un ensemble de
validation qui n’est pas vu pendant l’entraînement.
27
Introduction au Deep Learning (notes de cours)
set_random_seed(0)
model = Sequential([
InputLayer(input_shape=(4, )),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=256, activation="relu"),
Dense(units=3, activation="softmax")
])
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h = model.fit(X, y, validation_split=0.3, epochs=n_epochs, batch_size=30, verbose=0)
Ici, le meilleur modèle (en termes de capacités de généralisation) semble être le modèle à l”epoch 30. En d’autres termes,
si nous avions arrêté le processus d’apprentissage après l”epoch 30, nous aurions obtenu un meilleur modèle que si nous
utilisons le modèle entraîné pendant 70 epochs.
C’est toute l’idée derrière la stratégie d”early stopping, qui consiste à arrêter le processus d’apprentissage dès que la perte
de validation cesse de s’améliorer. Cependant, comme on peut le voir dans la visualisation ci-dessus, la perte de validation
a tendance à osciller, et on attend souvent plusieurs epochs avant de supposer que la perte a peu de chances de s’améliorer
dans le futur. Le nombre d”epochs à attendre est appelé le paramètre de patience.
Dans keras, l’arrêt anticipé peut être configuré via un callback, comme dans l’exemple suivant :
set_random_seed(0)
model = Sequential([
InputLayer(input_shape=(4, )),
(suite sur la page suivante)
28 Chapitre 5. Régularisation
Introduction au Deep Learning (notes de cours)
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h = model.fit(X, y,
validation_split=0.3, epochs=n_epochs, batch_size=30,
verbose=0, callbacks=[cb_es])
Et maintenant, même si le modèle était prévu pour être entraîné pendant 70 epochs, l’entraînement est arrêté dès qu’il
atteint 10 epochs consécutives sans amélioration de la perte de validation, et les paramètres du modèle sont restaurés
comme les paramètres du modèle à l”epoch 30.
Une autre façon importante d’appliquer la régularisation dans les réseaux neuronaux est la pénalisation des pertes. Un
exemple typique de cette stratégie de régularisation est la régularisation L2. Si nous désignons par ℒ𝑟 la perte régularisée
par L2, elle peut être exprimée comme suit :
λ = 0.01
set_random_seed(0)
model = Sequential([
InputLayer(input_shape=(4, )),
Dense(units=256, activation="relu", kernel_regularizer=L2(λ)),
Dense(units=256, activation="relu", kernel_regularizer=L2(λ)),
Dense(units=256, activation="relu", kernel_regularizer=L2(λ)),
Dense(units=3, activation="softmax")
])
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h = model.fit(X, y, validation_split=0.3, epochs=n_epochs, batch_size=30, verbose=0)
5.3 DropOut
Dans cette section, nous présentons la stratégie DropOut, qui a été introduite dans [Srivastava et al., 2014]. L’idée derrière
le DropOut est d’éteindre certains neurones pendant l’apprentissage. Les neurones désactivés changent à chaque minibatch
de sorte que, globalement, tous les neurones sont entraînés pendant tout le processus.
Le concept est très similaire dans l’esprit à une stratégie utilisée pour l’entraînement des forêts aléatoires, qui consiste
à sélectionner aléatoirement des variables candidates pour chaque division d’arbre à l’intérieur d’une forêt, ce qui est
connu pour conduire à de meilleures performances de généralisation pour les forêts aléatoires. La principale différence
ici est que l’on peut non seulement désactiver les neurones d’entrée mais aussi les neurones de la couche cachée pendant
l’apprentissage.
Dans keras, ceci est implémenté comme une couche, qui agit en désactivant les neurones de la couche précédente dans
le réseau :
30 Chapitre 5. Régularisation
Introduction au Deep Learning (notes de cours)
FIG. 5.1 – Illustration du mécanisme de DropOut. Afin d’entraîner un modèle donné (à gauche), à chaque minibatch, une
proportion donnée de neurones est choisie au hasard pour être « désactivée » et le sous-réseau résultant est utilisé pour
l’étape d’optimisation en cours (cf. figure de droite, dans laquelle 40% des neurones – colorés en gris – sont désactivés).
set_random_seed(0)
switchoff_proba = 0.3
model = Sequential([
InputLayer(input_shape=(4, )),
Dropout(rate=switchoff_proba),
Dense(units=256, activation="relu"),
Dropout(rate=switchoff_proba),
Dense(units=256, activation="relu"),
Dropout(rate=switchoff_proba),
Dense(units=256, activation="relu"),
Dropout(rate=switchoff_proba),
Dense(units=3, activation="softmax")
])
n_epochs = 100
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
h = model.fit(X, y, validation_split=0.3, epochs=n_epochs, batch_size=30, verbose=0)
5.3. DropOut 31
Introduction au Deep Learning (notes de cours)
Exercice #1
En observant les valeurs de perte dans la figure ci-dessus, pouvez-vous expliquer pourquoi la perte de validation est presque
systématiquement inférieure à celle calculée sur le jeu d’apprentissage ?
Solution
En fait, la perte d’apprentissage est calculée comme la perte moyenne sur tous les minibatchs d’apprentissage pendant une
epoch. Si nous nous rappelons que pendant l’apprentissage, à chaque minibatch, 30% des neurones sont désactivés, on
peut voir que seule une sous-partie du modèle complet est utilisée lors de l’évaluation de la perte d’apprentissage alors
que le modèle complet est utilisé lors de la prédiction sur l’ensemble de validation, ce qui explique pourquoi la perte de
validation mesurée est inférieure à celle de l’apprentissage.
32 Chapitre 5. Régularisation
CHAPITRE 6
Les réseaux de neurones convolutifs (aussi appelés ConvNets) sont conçus pour tirer parti de la structure des données.
Dans ce chapitre, nous aborderons deux types de réseaux convolutifs : nous commencerons par le cas monodimensionnel
et verrons comment les réseaux convolutifs à convolutions 1D peuvent être utiles pour traiter les séries temporelles. Nous
présenterons ensuite le cas 2D, particulièrement utile pour traiter les données d’image.
Les réseaux de neurones convolutifs pour les séries temporelles reposent sur l’opérateur de convolution 1D qui, étant
donné une série temporelle x et un filtre f, calcule une carte d’activation comme :
𝐿
(x ∗ f) (𝑡) = ∑ 𝑓𝑘 𝑥𝑡+𝑘 (6.1)
𝑘=−𝐿
plt.tight_layout()
/tmp/ipykernel_14514/1028966743.py:23: MatplotlibDeprecationWarning: Auto-removal␣
↪of overlapping axes is deprecated since 3.6 and will be removed two minor␣
fig2 = plt.subplot(2, 1, 2)
/tmp/ipykernel_14514/1028966743.py:32: UserWarning: The figure layout has changed␣
↪to tight
plt.tight_layout()
33
Introduction au Deep Learning (notes de cours)
<IPython.core.display.HTML object>
Les modèles convolutifs sont connus pour être très performants dans les applications de vision par ordinateur, utilisant des
quantités modérées de paramètres par rapport aux modèles entièrement connectés (bien sûr, des contre-exemples existent,
et le terme « modéré » est particulièrement vague).
La plupart des architectures standard de séries temporelles qui reposent sur des blocs convolutionnels sont des adaptations
directes de modèles de la communauté de la vision par ordinateur ([Le Guennec et al., 2016] s’appuie sur une alternance
entre couches de convolution et couches de pooling, tandis que des travaux plus récents s’appuient sur des connexions
résiduelles et des modules d”inception [Fawaz et al., 2020]). Ces blocs de base (convolution, pooling, couches résiduelles)
sont discutés plus en détail dans la section suivante.
Ces modèles de classification des séries temporelles (et bien d’autres) sont présentés et évalués dans [Fawaz et al., 2019]
que nous conseillons au lecteur intéressé.
Nous allons maintenant nous intéresser au cas 2D, dans lequel les filtres de convolution ne glisseront pas sur un seul axe
comme dans le cas des séries temporelles, mais plutôt sur les deux dimensions (largeur et hauteur) d’une image.
Comme on le voit ci-dessous, une image est une grille de pixels, et chaque pixel a une valeur d’intensité dans chacun des
canaux de l’image. Les images couleur sont typiquement composées de 3 canaux (ici Rouge, Vert et Bleu).
FIG. 6.1 – Une image et ses 3 canaux (intensités de Rouge, Vert et Bleu, de gauche à droite).
La sortie d’une convolution sur une image x est une nouvelle image, dont les valeurs des pixels peuvent être calculées
comme suit :
𝐾 𝐿 3
(x ∗ f) (𝑖, 𝑗) = ∑ ∑ ∑ 𝑓𝑘,𝑙,𝑐 𝑥𝑖+𝑘,𝑗+𝑙,𝑐 . (6.2)
𝑘=−𝐾 𝑙=−𝐿 𝑐=1
En d’autres termes, les pixels de l’image de sortie sont calculés comme le produit scalaire entre un filtre de convolution
(qui est un tenseur de forme (2𝐾 + 1, 2𝐿 + 1, 𝑐)) et un patch d’image centré à la position donnée.
Considérons, par exemple, le filtre de convolution 9x9 suivant :
Le résultat de la convolution de l’image de chat ci-dessus avec ce filtre est l’image suivante en niveaux de gris (c’est-à-dire
constituée d’un seul canal) :
On peut remarquer que cette image est une version floue de l’image originale. C’est parce que nous avons utilisé un filtre
Gaussien. Comme pour les séries temporelles, lors de l’utilisation d’opérations de convolution dans les réseaux neuronaux,
le contenu des filtres sera appris, plutôt que défini a priori.
Dans [LeCun et al., 1998], un empilement de couches de convolution, de pooling et de couches entièrement connectées
est introduit pour une tâche de classification d’images, plus spécifiquement une application de reconnaissance de chiffres.
Le réseau neuronal résultant, appelé LeNet, est représenté ci-dessous :
Couches de convolution
Une couche de convolution est constituée de plusieurs filtres de convolution (également appelés kernels) qui opèrent en
parallèle sur la même image d’entrée. Chaque filtre de convolution génère une carte d’activation en sortie et toutes ces
cartes sont empilées pour former la sortie de la couche de convolution. Tous les filtres d’une couche partagent la même
largeur et la même hauteur. Un terme de biais et une fonction d’activation peuvent être utilisés dans les couches de
convolution, comme dans d’autres couches de réseaux neuronaux. Dans l’ensemble, la sortie d’un filtre de convolution est
calculée comme suit :
𝐾 𝐿
𝑐
(x ∗ f) (𝑖, 𝑗, 𝑐) = 𝜑 ( ∑ ∑ ∑ 𝑓𝑘,𝑙,𝑐 ′ 𝑥𝑖+𝑘,𝑗+𝑙,𝑐′ + 𝑏𝑐 ) (6.3)
𝑘=−𝐾 𝑙=−𝐿 𝑐′
où 𝑐 désigne le canal de sortie (notez que chaque canal de sortie est associé à un filtre 𝑓 𝑐 ), 𝑏𝑐 est le terme de biais qui lui
est associé et 𝜑 est la fonction d’activation utilisée.
Astuce: En keras, une telle couche est implémentée à l’aide de la classe Conv2D :
Padding
FIG. 6.3 – Visualisation de l’effet du padding (source: V. Dumoulin, F. Visin - A guide to convolution arithmetic for deep
learning). Gauche: sans padding, droite: avec padding.
Lors du traitement d’une image d’entrée, il peut être utile de s’assurer que la carte de caractéristiques (ou carte d’activation)
de sortie a la même largeur et la même hauteur que l’image d’entrée. Cela peut être réalisé en agrandissant artificiellement
l’image d’entrée et en remplissant les zones ajoutées avec des zéros, comme illustré dans Fig. 6.3 dans lequel la zone de
padding est représentée en blanc.
Couches de pooling
Les couches de pooling effectuent une opération de sous-échantillonnage qui résume en quelque sorte les informations
contenues dans les cartes de caractéristiques dans des cartes à plus faible résolution.
L’idée est de calculer, pour chaque parcelle d’image, une caractéristique de sortie qui calcule un agrégat des pixels de
la parcelle. Les opérateurs d’agrégation typiques sont les opérateurs de moyenne (dans ce cas, la couche correspondante
est appelée average pooling) ou de maximum (pour les couches de max pooling). Afin de réduire la résolution des cartes
de sortie, ces agrégats sont généralement calculés sur des fenêtres glissantes qui ne se chevauchent pas, comme illustré
ci-dessous, pour un max pooling avec une taille de pooling de 2x2 :
max
Ces couches étaient largement utilisées historiquement dans les premiers modèles convolutifs et le sont de moins en moins
à mesure que la puissance de calcul disponible augmente.
Astuce: En keras, les couches de pooling sont implémentées à travers les classes MaxPool2D et AvgPool2D :
max_pooling_layer = MaxPool2D(pool_size=2)
average_pooling_layer = AvgPool2D(pool_size=2)
Un empilement de couches de convolution et de pooling produit une carte d’activation structurée (qui prend la forme d’une
grille 2d avec une dimension supplémentaire pour les différents canaux). Lorsque l’on vise une tâche de classification
d’images, l’objectif est de produire la classe la plus probable pour l’image d’entrée, ce qui est généralement réalisé par une
tête de classification (classification head) composée de couches entièrement connectées.
Pour que la tête de classification soit capable de traiter une carte d’activation, les informations de cette carte doivent être
transformées en un vecteur. Cette opération est appelée Flatten dans keras, et le modèle correspondant à Fig. 6.2 peut
être implémenté comme :
model = Sequential([
(suite sur la page suivante)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 28, 28, 6) 156
=================================================================
Total params: 61706 (241.04 KB)
Trainable params: 61706 (241.04 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Les réseaux neuronaux récurrents (RNN) traitent les éléments d’une série temporelle un par un. Typiquement, à l’instant
𝑡, un bloc récurrent prend en entrée :
— l’entrée courante 𝑥𝑡 et
— un état caché ℎ𝑡−1 qui a pour but de résumer les informations clés provenant de des entrées passées {𝑥0 , … , 𝑥𝑡−1 }
Ce bloc retourne un état caché mis à jour ℎ𝑡 :
ℎ𝑡−1 ℎ𝑡 ℎ𝑡+1
… …
𝑥𝑡−1 𝑥𝑡 𝑥𝑡+1
Il existe différentes couches récurrentes qui diffèrent principalement par la façon dont ℎ𝑡 est calculée.
41
Introduction au Deep Learning (notes de cours)
où 𝑊ℎ est une matrice de poids associée au traitement de l’état caché précédent, 𝑊𝑥 est une autre matrice de poids
associée au traitement de la l’entrée actuelle et 𝑏 est un terme de biais.
On notera ici que 𝑊ℎ , 𝑊𝑥 et 𝑏 ne sont pas indexés par 𝑡, ce qui signifie que qu’ils sont partagés entre tous les temps.
Une limitation importante de cette formule est qu’elle échoue à capturer les dépendances à long terme. Pour mieux
comprendre pourquoi, il faut se rappeler que les paramètres de ces réseaux sont optimisés par des algorithmes de descente
de gradient stochastique.
Pour simplifier les notations, considérons un cas simplifié dans lequel ℎ𝑡 et 𝑥𝑡 sont tous deux des valeurs scalaires, et
regardons ce que vaut le gradient de la sortie ℎ𝑡 par rapport à 𝑊ℎ (qui est alors aussi un scalaire) :
′ 𝜕𝑜𝑡
∇𝑊ℎ (ℎ𝑡 ) = tanh (𝑜𝑡 ) ⋅ (7.2)
𝜕𝑊ℎ
où 𝑜𝑡 = 𝑊ℎ ℎ𝑡−1 + 𝑊𝑥 𝑥𝑡 + 𝑏, donc:
𝜕𝑜𝑡 𝜕ℎ𝑡−1
= ℎ𝑡−1 + 𝑊ℎ ⋅ . (7.3)
𝜕𝑊ℎ 𝜕𝑊ℎ
𝜕ℎ𝑡−1
Ici, la forme de 𝜕𝑊ℎ sera similaire à celle de ∇𝑊ℎ (ℎ𝑡 ) ci-dessus, et, au final, on obtient :
′ 𝜕ℎ𝑡−1
∇𝑊ℎ (ℎ𝑡 ) = tanh (𝑜𝑡 ) ⋅ [ℎ𝑡−1 + 𝑊ℎ ⋅ ] (7.4)
𝜕𝑊ℎ
′ ′
= tanh (𝑜𝑡 ) ⋅ [ℎ𝑡−1 + 𝑊ℎ ⋅ tanh (𝑜𝑡−1 ) ⋅ [ℎ𝑡−2 + 𝑊ℎ ⋅ [… ]]] (7.5)
′ ′ ′
= ℎ𝑡−1 tanh (𝑜𝑡 ) + ℎ𝑡−2 𝑊ℎ tanh (𝑜𝑡 )tanh (𝑜𝑡−1 ) + … (7.6)
𝑡−1
′ ′ ′
= ∑ ℎ𝑡′ [𝑊ℎ𝑡−𝑡 −1 tanh (𝑜𝑡′ +1 ) ⋅ ⋯ ⋅ tanh (𝑜𝑡 )] (7.7)
𝑡′ =1
′ ′ ′
En d’autres termes, l’influence de ℎ𝑡′ sera atténuée par un facteur 𝑊ℎ𝑡−𝑡 −1 tanh (𝑜𝑡′ +1 ) ⋅ ⋯ ⋅ tanh (𝑜𝑡 ).
Rappelons maintenant à quoi ressemblent la fonction tanh et sa dérivée :
On peut voir à quel point les gradients se rapprochent rapidement de 0 pour des entrées plus grandes (en valeur absolue)
que 2, et avoir plusieurs termes de ce type dans une dérivation en chaîne fera tendre les termes correspondants vers 0.
En d’autres termes, le gradient de l’état caché au temps 𝑡 sera seulement influencé par quelques uns de ses prédécesseurs
{ℎ𝑡−1 , ℎ𝑡−2 , … } et les les dépendances à long terme seront ignorées lors de l’actualisation des paramètres du modèle par
descente de gradient. Il s’agit d’une occurrence d’un phénomène plus général connu sous le nom de vanishing gradient.
Les blocs Long Short Term Memory (LSTM, [Hochreiter and Schmidhuber, 1997]) ont été conçus comme une alternative
à aux blocs récurrents classiques. Ils visent à atténuer l’effet de vanishing gradient par l’utilisation de portes qui codent
explicitement quelle partie de l’information doit (resp. ne doit pas) être utilisée.
Dans ces blocs, un état supplémentaire est utilisé, appelé état de la cellule 𝐶𝑡 . Cet état est calculé comme suit :
𝐶𝑡 = 𝑓𝑡 ⊙ 𝐶𝑡−1 + 𝑖𝑡 ⊙ 𝐶𝑡̃ (7.8)
où 𝑓𝑡 est appelée forget gate (elle pousse le réseau à oublier les parties inutiles de l’état passé de la cellule), 𝑖𝑡 est l”input
gate et 𝐶𝑡̃ est une version actualisée de l’état de la cellule (qui, à son tour, peut être partiellement censurée par l”input
gate).
Laissons de côté pour l’instant les détails concernant le calcul de ces 3 termes et concentrons-nous plutôt sur la façon dont
la formule ci-dessus est est significativement différente de la règle de mise à jour de l’état caché dans le modèle classique.
En effet, dans ce cas, si le réseau l’apprend (par l’intermédiaire de 𝑓𝑡 ), l’information complète de l’état précédent de la
cellule 𝐶𝑡−1 peut être récupérée, ce qui permet aux gradients de se propager à rebours de l’axe du temps (et de ne plus
disparaître).
Alors, le lien entre l’état de la cellule et l’état caché est :
ℎ𝑡 = 𝑜𝑡 ⊙ tanh(𝐶𝑡 ) . (7.9)
En d’autres termes, l’état caché est la version transformée (par la fonction tanh) de l’état de la cellule, encore censuré par
une porte de sortie (output gate) 𝑜𝑡 .
Toutes les portes utilisées dans les formules ci-dessus sont définies de manière similaire :
où 𝜎 est la fonction d’activation sigmoïde (dont les valeurs sont comprises dans [0, 1]) et [ℎ𝑡−1 , 𝑥𝑡 ] la concaténation des
caractéristiques ℎ𝑡−1 et 𝑥𝑡 .
Enfin, l’état de cellule mis à jour 𝐶𝑡̃ est calculé comme suit :
Il existe dans la littérature de nombreuses variantes de ces blocs LSTM qui reposent toujours sur les mêmes principes de
base.
Une paramétrisation légèrement différente d’un bloc récurrent est utilisée dans les Gated Recurrent Units (GRU, [Cho et
al., 2014]).
Les GRUs reposent également sur l’utilisation de portes pour laisser (de manière adaptative) l’information circuler à
travers le temps. Une première différence significative entre les GRUs et les LSTMs est que les GRUs n’ont pas recours
à l’utilisation d’un état de cellule. Au lieu de cela, la règle de mise à jour de l’état caché est la suivante :
ℎ𝑡 = (1 − 𝑧𝑡 ) ⊙ ℎ𝑡−1 + 𝑧𝑡 ⊙ ℎ̃ 𝑡 (7.14)
où 𝑧𝑡 est une porte qui équilibre (par caractéristique) la quantité d’informations qui est conservée de l’état caché précédent
avec la quantité d’informations qui doit être mise à jour en utilisant le nouvel état caché candidat ℎ̃ 𝑡 , calculé comme suit :
où 𝑟𝑡 est une porte supplémentaire qui peut cacher une partie de l’état caché précédent.
Les formules pour les portes 𝑧𝑡 et 𝑟𝑡 sont similaires à celles fournies pour 𝑓𝑡 , 𝑖𝑡 et 𝑜𝑡 dans le cas des LSTMs.
Une étude graphique de la capacité de ces variantes de réseaux récurrents à apprendre des dépendances à long terme est
fournie dans [Madsen, 2019].
7.4 Conclusion
Dans ce chapitre et le précédent, nous avons passé en revue les architectures de réseaux de neurones qui sont utilisées
pour apprendre à partir de données temporelles ou séquentielles. En raison de contraintes de temps, nous n’avons pas
abordé les modèles basés sur l’attention dans ce cours. Nous avons présenté les modèles convolutifs qui visent à extraire
des formes locales discriminantes dans les séries et les modèles récurrents qui exploitent plutôt la notion de séquence.
Concernant ces derniers, des variantes visant à faire face à l’effet de gradient évanescent ont été introduites. Il est à noter
que les modèles récurrents sont connus pour nécessiter plus de données d’entraînement que leurs homologues convolutifs.
7.4. Conclusion 45
Introduction au Deep Learning (notes de cours)
[Goh17] Gabriel Goh. Why momentum really works. Distill, 2017. URL: http://distill.pub/2017/momentum.
[KB15] Diederik P. Kingma and Jimmy Ba. Adam: a method for stochastic optimization. In Yoshua Bengio and
Yann LeCun, editors, ICLR. 2015.
[SHK+14] Nitish Srivastava, Geoffrey Hinton, Alex Krizhevsky, Ilya Sutskever, and Ruslan Salakhutdinov. Dro-
pout: a simple way to prevent neural networks from overfitting. Journal of Machine Learning Research,
15(56) :1929–1958, 2014. URL: http://jmlr.org/papers/v15/srivastava14a.html.
[FFW+19] Hassan Ismail Fawaz, Germain Forestier, Jonathan Weber, Lhassane Idoumghar, and Pierre-Alain Muller.
Deep learning for time series classification: a review. Data Mining and Knowledge Discovery, 33(4) :917–
963, 2019.
[FLF+20] Hassan Ismail Fawaz, Benjamin Lucas, Germain Forestier, Charlotte Pelletier, Daniel F Schmidt, Jonathan
Weber, Geoffrey I Webb, Lhassane Idoumghar, Pierre-Alain Muller, and François Petitjean. Inceptiontime:
finding alexnet for time series classification. Data Mining and Knowledge Discovery, 34(6) :1936–1962,
2020.
[LGMT16] Arthur Le Guennec, Simon Malinowski, and Romain Tavenard. Data Augmentation for Time Series Clas-
sification using Convolutional Neural Networks. In ECML/PKDD Workshop on Advanced Analytics and
Learning on Temporal Data. Riva Del Garda, Italy, September 2016.
[LBBH98] Yann LeCun, Léon Bottou, Yoshua Bengio, and Patrick Haffner. Gradient-based learning applied to docu-
ment recognition. Proceedings of the IEEE, 86(11) :2278–2324, 1998.
[CVMerrienboerBB14] Kyunghyun Cho, Bart Van Merriënboer, Dzmitry Bahdanau, and Yoshua Bengio. On the pro-
perties of neural machine translation: encoder-decoder approaches. 2014. arXiv:1409.1259.
[HS97] Sepp Hochreiter and Jürgen Schmidhuber. Long short-term memory. Neural computation, 9(8) :1735–1780,
1997.
[Mad19] Andreas Madsen. Visualizing memorization in rnns. Distill, 2019. URL: https://distill.pub/2019/
memorization-in-rnns.
47