7.3 Introduction à l'apprentissage profond
Objectifs d'apprentissage
À la fin de cette section, vous devriez être en mesure de :
- 7.3.1 Discuter du rôle des couches cachées dans un réseau de neurones.
- 7.3.2 Décrire les fonctions de perte/d'erreur et leur rôle dans l'entraînement et l'évaluation d'un réseau de neurones.
- 7.3.3 Configurer, tester et entraîner un réseau de neurones d'apprentissage profond capable de classifier des données réelles.
L'apprentissage profond (deep learning) est un terme général désignant l'entraînement et l'implémentation de réseaux de neurones comportant de nombreuses couches afin d'apprendre des représentations structurées des relations présentes dans les données. Sous-domaine de l'apprentissage automatique, l'apprentissage profond a révolutionné de nombreux domaines, allant de la vision par ordinateur et du traitement du langage naturel à la robotique et aux soins de santé. Sous l'impulsion des recherches menées par des organisations telles que Google DeepMind, Meta, IBM, MosaicML, Databricks, OpenAI et bien d'autres, l'apprentissage profond a permis d'obtenir des résultats remarquables, à l'image de la maîtrise sans précédent par AlphaGo Zero du jeu de Go, à la fois ancien et complexe. Grâce à son aptitude à apprendre automatiquement des représentations hiérarchiques à partir de données brutes, l'apprentissage profond a repoussé les frontières de la reconnaissance de motifs, de la prise de décision et de la créativité. Ce chapitre a déjà exposé les fondements des réseaux de neurones dans les sections précédentes, et la section suivante mettra l'accent sur le développement de modèles de réseaux de neurones d'apprentissage profond capables de classifier des chiffres manuscrits et des images simples au moyen de la bibliothèque TensorFlow en langage Python.
Réseaux de neurones à plusieurs couches
Les couches cachées d'un réseau de neurones jouent un rôle déterminant dans la transformation des données d'entrée en représentations utiles, qui permettent ensuite de formuler des prédictions ou de prendre des décisions. Supposons que nous cherchions à entraîner un réseau de neurones pour reconnaître des chiffres manuscrits (comme à la Figure 7.2). Idéalement, chaque couche cachée devrait extraire des caractéristiques identifiables — par exemple, si un chiffre comporte une boucle ou un cercle (comme 0, 6, 8 et 9) ou non (comme 1, 2, 3, 4, 5, 7). D'autres couches cachées peuvent ensuite détecter la présence de traits verticaux ou horizontaux afin de faciliter la classification du chiffre. Cependant, les couches cachées ne repèrent pas nécessairement les mêmes types de motifs que l'œil humain. L'un des axes de recherche récents en apprentissage profond porte sur la conception de modèles capables d'extraire des caractéristiques de plus haut niveau (telles que des boucles, des contours, des textures) et d'exposer les raisons qui sous-tendent leurs décisions. Des modèles sophistiqués de classification d'images parviennent ainsi à localiser des motifs, des portions d'images et même des relations spatiales entre différentes parties d'une image, le tout au sein des couches les plus profondes du réseau.
Qu'est-ce qui rend l'apprentissage profond véritablement profond ? La profondeur (depth) d'un réseau de neurones correspond au nombre de couches cachées qu'il renferme et constitue une caractéristique fondamentale de l'apprentissage profond. Des réseaux plus profonds disposent d'une capacité accrue à modéliser des motifs et des relations complexes au sein des données. En empilant plusieurs couches d'unités cachées, les modèles d'apprentissage profond peuvent apprendre à extraire des caractéristiques à plusieurs niveaux de granularité, de façon hiérarchique (structurée), ce qui aboutit à des représentations plus puissantes et plus expressives des données d'entrée. Cet apprentissage hiérarchique des caractéristiques est l'élément central du succès de l'apprentissage profond dans de multiples domaines, tels que la vision par ordinateur, le traitement du langage naturel et la reconnaissance vocale, où des motifs et des relations complexes doivent être extraits de données en très grande dimension — c'est-à-dire de jeux de données comportant de très nombreuses variables explicatives.
Fonctions de perte et leur rôle dans l'entraînement des réseaux de neurones
Vous avez découvert les fonctions de perte les plus courantes dans Introduction aux réseaux de neurones et Rétropropagation. Cette section examine les situations dans lesquelles une fonction de perte s'avère préférable à une autre. Par exemple, l'erreur quadratique moyenne (MSE) est fréquemment employée lorsque le réseau doit prédire des valeurs continues, comme dans l'analyse de régression. Songez par exemple à un réseau de neurones entraîné à estimer la probabilité qu'un patient présentant un profil de symptômes donné soit atteint d'une cirrhose. Comme la MSE calcule le carré des écarts entre valeurs prédites et valeurs réelles, elle pénalise fortement les erreurs de grande amplitude et se montre particulièrement sensible aux valeurs aberrantes. En outre, la MSE n'est pas indépendante de l'échelle, ce qui signifie que ses unités ne correspondent pas aux unités initiales des données. L'exemple suivant illustre cette dernière caractéristique.
Exemple 7.4
Problème
Un réseau de neurones est entraîné pour prédire la hauteur maximale d'une variété de plant de maïs à partir de plusieurs caractéristiques, telles que l'acidité du sol, les températures enregistrées pendant la période de croissance, etc. Un échantillon de valeurs prédites ainsi que les valeurs réelles associées figurent dans le Tableau 7.2, exprimées en mètres et en centimètres. Remarque : les données sont strictement identiques sur les deux lignes, mais présentées dans des unités différentes. Déterminez la MSE pour chacune des deux lignes de prédictions. Par quel facteur la MSE est-elle modifiée ?
| Prédit | Réel (Cible) | |
|---|---|---|
| Unités : m | ||
| Unités : cm |
Solution
Ici, le nombre de termes est . Pour les données exprimées en m :
Pour les données exprimées en cm :
Nous constatons que la MSE varie d'un facteur de , alors que les valeurs numériques des données n'ont été multipliées que par un facteur de 100. Dans la mesure où le jeu de données représente très exactement les mêmes mesures physiques, cet exemple met en évidence l'une des principales limites de l'utilisation de la MSE comme fonction de perte.
D'autres fonctions de perte couramment utilisées comprennent l'entropie croisée binaire, la perte charnière et l'entropie croisée catégorielle (creuse).
L'entropie croisée binaire convient particulièrement aux tâches de classification binaire — c'est-à-dire décider entre Vrai ou Faux à partir du vecteur d'entrée fourni. Plus généralement, lorsque la sortie d'un réseau de neurones correspond à la probabilité d'appartenir à l'une de deux classes (par exemple 0 et 1), l'entropie croisée binaire constitue généralement une fonction de perte plus efficace pour l'entraînement du réseau. Cela s'explique principalement par le fait qu'elle pénalise sévèrement les erreurs de classification, qu'elle conserve de très bonnes performances même en présence de données déséquilibrées (imbalanced data) (c'est-à-dire des jeux de données comptant une classe nettement plus représentée qu'une autre) et qu'elle s'avère parfaitement adaptée au traitement de sorties s'interprétant sur une échelle de probabilité allant de 0 à 1.
La perte charnière est une autre fonction de perte fréquemment adoptée dans les tâches de classification binaire. Elle convient aux contextes où l'on cherche à maximiser la marge entre les classes tout en réduisant au minimum les erreurs de classification. Nous définissons la marge (margin) entre classes comme une mesure de l'écartement des points de données appartenant à des catégories distinctes. Des marges plus étendues sont généralement le signe de classes plus nettement différenciées. Par exemple, si l'ensemble des résultats obtenus à une épreuve est (71, 73, 78, 80, 81, 89, 90, 92), une séparation plus naturelle semble apparaître entre les sous-ensembles (71, 73), (78, 80, 81) et (89, 90, 92), plutôt que selon le découpage classique par dizaines en (71, 73, 78), (80, 81, 89) et (90, 92). Dans la première situation, il existe une marge1 de 5 unités entre (71, 73) et (78, 80, 81), ainsi qu'une marge de 8 unités entre (78, 80, 81) et (89, 90, 92), alors que les marges ne sont que de 2 et 1 entre les sous-ensembles (71, 73, 78), (80, 81, 89) et (80, 81, 89), (90, 92) respectivement. Dès lors, il apparaît plus logique (au vu des scores et de leurs marges) d'attribuer la note A aux étudiants appartenant au sous-ensemble (89, 90, 92), la note B à ceux du groupe (78, 80, 81) et la note C à ceux du groupe (71, 73).
L'entropie croisée catégorielle creuse est une généralisation de l'entropie croisée binaire utile pour les tâches de classification multiclasse, dans lesquelles le réseau prédit la distribution de probabilité sur l'ensemble des classes. Elle quantifie la similarité entre les probabilités de classe prédites et les étiquettes de classe réelles. Le terme creuse fait référence au fait que la cible est représentée sous forme d'entiers plutôt que de vecteurs encodés en one-hot (voir la notion d'encodage one-hot dans Autres techniques d'apprentissage automatique). L'entropie croisée catégorielle (creuse) est fréquemment associée à une fonction d'activation softmax au niveau de la couche de sortie afin de garantir que la somme des probabilités prédites soit égale à 1 et forme une distribution de probabilité cohérente.
Exemple : Utilisation de l'apprentissage profond pour classifier des chiffres manuscrits
TensorFlow offre des fonctionnalités particulièrement complètes pour concevoir des réseaux de neurones incorporant des couches cachées dotées de diverses fonctions d'activation. L'ajout de couches cachées s'effectue très simplement, comme l'illustre le code ci-dessous. Comparez l'utilisation de la fonction tf.keras.Sequential avec l'exemple analogue présenté dans Rétropropagation. Le modèle commence toujours par une couche d'entrée d'une dimension égale au nombre de variables explicatives. Les neurones d'entrée sont connectés de façon dense à une couche cachée de 128 neurones munie de la fonction d'activation ReLU. La couche cachée suivante comprend 64 neurones, utilisant de nouveau ReLU comme fonction d'activation. Enfin, la couche de sortie est composée de 10 neurones (un par chiffre) avec une activation softmax. Il est d'usage d'employer une fonction d'activation élémentaire (comme ReLU) dans les couches cachées comportant de nombreuses interconnexions, tout en réservant des fonctions d'activation plus élaborées à la couche de sortie.
On remarquera que l'exactitude de ce modèle progresse pour atteindre 0,967, contre 0,919 pour notre modèle précédent dépourvu de couches cachées.
Code Python
# import the libraries
import tensorflow as tf
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Load MNIST Digits dataset
mnist = fetch_openml('mnist_784', version=1)
# Split the dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(mnist.data, mnist.target, test_size=0.2, random_state=42)
# Standardize features by removing the mean and scaling to unit variance
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Cast the target variable as an integer (int32)
y_train = y_train.astype('int32')
y_test = y_test.astype('int32')
# Define the neural network architecture
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu', input_shape=(X_train_scaled.shape[1],)),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
# Compile the model
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
# Train the model
history = model.fit(X_train_scaled, y_train, epochs=10, batch_size=32, validation_split=0.2)
# Evaluate the model on test data
test_loss, test_accuracy = model.evaluate(X_test_scaled, y_test)
print("Test Loss:", test_loss)
print("Test Accuracy:", test_accuracy)
Le résultat obtenu ressemble à ceci :
Test Loss: 0.20183834433555603
Test Accuracy: 0.968999981880188