Deep Learning démystifié : Petit guide.
Katia Meziani
Préface
Ce document fait suite à la présentation donnée au séminaire du jeudi 25 janvier 2024. L’objectif ici n’est ni de présenter la théorie ni de plonger dans le monde complexe du code, mais plutôt de fournir une vue d’ensemble accessible et vulgarisée sur le vaste domaine de l’intelligence artificielle (IA) avec un accent particulier sur l’apprentissage profond (DL: Deep Learning). Ce document peut se voir comme un petit guide pour acquérir une compréhension minimale mais étendue du domaine, fournissant les outils nécessaires pour explorer des sujets plus spécifiques, que ce soit de manière autonome ou lors de ce séminaire spécialisé. Aucune connaissance théorique ni expérience en programmation préalables ne sont requises.
Nous débuterons par une présentation des concepts fondamentaux de l’IA. Qu’est-ce que l’IA, et plus précisement le DL ? nous aborderons des notions cruciales telles que la Donnée, l’Apprentissage et ses différentes composantes, les tâches réalisables par l’IA, les architectures principales des réseaux de neurones, et leur utilité dans diverses applications.
Au travers d’une architecture de réseaux de neurones simple, nous mettrons en lumière le vocabulaire essentiel en DL, discuterons du pré-processing des données, des principes d’entraînement des réseaux, examinerons le choix des fonctions d’activation, exposerons des problématiques courantes telles que le surajustement overfitting , le vanishing gradient, et bien d’autres.
J’espère fournir ici une base solide de manière pédagogique, permettant à chacun.e de s’engager de manière éclairée dans le monde fascinant de l’IA et servir de tremplin pour des explorations plus approfondies dans ce séminaire qui je le sais sera certainement aussi enrichissant que passionnant !
PARTIE 1 : Les bases
1 Introduction
1.1 L’IA, c’est quoi ?
L’intelligence, c’est quoi ? Une formulation pourrait être celle donnée par Sharma, Radha R. (2008) : “It can be described as the ability to perceive or infer information; and to retain it as knowledge to be applied to adaptive behaviors within an environment or context.”
L’intelligence Artificielle (IA), c’est quoi ? L’IA vise à construire des systèmes qui simulent un comportement intelligent. En d’autres termes, c’est la création de programmes informatiques capables d’effectuer des tâches (résolution de problèmes, l’apprentissage, la compréhension du langage naturel, la reconnaissance de motifs, la perception visuelle,…) qui, traditionnellement, nécessitent l’intelligence humaine.
Historiquement, l’IA était un domaine de recherche axé sur la résolution de problèmes par des machines, impliquant la rédaction de codes et de séquences d’instructions à suivre par la machine. Mais s’il l’on veut qu’un système soit “capable de percevoir ou d’inférer l’information, et de la conserver comme une connaissance à appliquer à des comportements adaptatifs dans un environnement ou un contexte donné”, ce système doit avoir la capacité d’apprendre. De nos jours, c’est cette approche dite data-driven, guidée par les données, que l’on appelle apprentissage automatique ou plus communément le Machine Learning (ML).
Plutôt que de programmer explicitement un ordinateur pour effectuer une tâche, le ML consiste à développer des techniques et des modèles permettant aux ordinateurs d’apprendre. Plus précisément, des algorithmes permettent à l’ordinateur d’apprendre par lui-même à partir de l’expérience. Cette “expérience” se manifeste par l’apprentissage de modèles mathématiques à partir de données. Ce domaine a connu une croissance explosive et est maintenant, à tort, presque synonyme du terme IA!
En ML, l’apprentissage peut être grossièrement divisées en 4 domaines d’apprentissage.
- Apprentissage supervisé (Supervised learning).
- Apprentissage non supervisé (Unsupervised learning). .
- Apprentissage par renforcement (Reinforcement learning).
- Apprentissage par transfert (Tranfert learning).
Le Deep Learning (DL) ou en français apprentissage profond est une sous-catégorie de ML. Le DL désigne l’ensemble des techniques d’apprentissage automatique basées sur l’utilisation de réseaux de neurones artificiels profonds, Deep Neural Network (DNN). Ces modèles d’apprentissage, DNN, peuvent à partir d’un ensemble de données d’entraînement apprendre des représentations hiérarchiques complexes, voir très très complexes !
1.2 Quelques dates
Au cours des dernières décennies, le domaine de l’apprentissage profond a connu des avancées spectaculaires, transformant radicalement la façon dont nous abordons les problèmes complexes en IA. De ses débuts modestes, à l’émergence de jalons importants comme ChatGPT, l’évolution du Deep Learning a été jalonnée de découvertes cruciales.
1943 : Le Modèle du neurone McCulloch-Pitts. Le neurophysiologiste McCulloch et le logicien Pitts proposent un modèle mathématique d’un neurone formel, jetant les bases théoriques des réseaux neuronaux.
1958 : Rosenblatt’s Perceptron. Frank Rosenblatt invente le perceptron, un modèle de réseau neuronal simple, ouvrant la voie à des idées fondamentales du deep learning.
1969 : Résolution du problème XOR par Minsky et Papert. Le problème XOR est un problème de classification non linéaire. Minsky et Papert ont démontré que les perceptrons simples étaient limités dans leur capacité à résoudre des problèmes non linéaires.
1986 : Backpropagation/RNN Rumelhart, Hinton et Williams décrivent les bases de l’algorithme de rétropropagation Backpropagation qui est fondamental pour l’entraînement des réseaux de neurones. On leurs attribue l’introduction des réseaux de neurones récurrents (RNN) conçus pour traiter des données séquentielles en maintenant une mémoire interne.
1990 &98 : LeNet-5 (a, b). LeCun, et al. introduisent l’un des premiers modèles de réseaux de neurones convolutionnel, appelés aussi CNN ou ConvNet. LeNet-5 a été introduit pour la reconnaissance de caractères manuscrits, en particulier pour lire des codes postaux sur des enveloppes. Les CNN sont extrêmement efficace dans le domaine de la vision par ordinateur et du traitement d’images.
1997 : Long Short-Term Memory (LSTM) Networks introduit par Hochreiter et Schmidhuber comme une solution au problème de la disparition du gradient, qui affecte la capacité des réseaux de neurones récurrents (RNN) traditionnels à apprendre des dépendances à long terme.
2006: Autoencoder Hinton,G. & Salakhutdinov,R. publie un article fondateur sur les autoencodeurs. Ces réseaux de neurones permettent d’apprendre une représentation compacte des données d’entrée, puis de reconstruire ces données à partir de cette représentation.
2012 : ImageNet Challenge remportée par AlexNet (CNN) [Krizhevsky, Sutskever et Hinton] un réseau neuronal convolutif Convolutional Neural Network, a remporté la compétition ImageNet Large Scale Visual Recognition Challenge en 2012, marquant le début de la domination des réseaux de neurones profonds dans la vision par ordinateur. L’écart de précision était tellement important que cela a attiré l’attention de la communauté de la recherche et de l’industrie.
2014 : Generative Adversarial Networks (GANs) introduit par Goodfellow et al. a été une avancée majeure. Les GANs ont été conçus pour générer des données réalistes en entraînant deux réseaux neuronaux, un générateur et un discriminateur, qui sont en compétition. Le générateur crée des données, tandis que le discriminateur évalue à quel point elles sont réalistes. Ce processus d’adversité pousse le générateur à créer des données indiscernables des données réelles.
2015 : ResNet (Residual Networks) He et al introduisent un type spécifique de CNN: ResNet avec l’introduction de connexions résiduelles pour résoudre le problème de la disparition du gradient lors de l’entraînement de réseaux très profonds. ResNet permet aux informations de contourner certaines couches. Cela ouvrira la voie à des architectures encore plus profondes, en particulier dans le domaine de la vision par ordinateur, où des architectures profondes sont souvent nécessaires pour capturer des caractéristiques complexes.
2015 : AlphaGo bat un Champion du Monde de Go [Silver et al. : Google] DeepMind, filiale de Google, développe AlphaGo, combinaison de réseaux de neurones convolutifs (CNN), qui bat le champion du monde Lee Sedol au jeu de Go.
2017 : Transformer pour le Traitement du Langage Naturel [Attention is All You Need] Le modèle Transformer, introduit par Vaswani et al., RÉVOLUTIONNE le traitement du langage naturel et devient la base de nombreux modèles de pointe tels que BERT, GPT-2, etc.
2018-23: Avancées dans la Génération de Modèles GPT (Generative Pre-trained Transformer). BERT,GPT-1 à GPT-4, ChatGPT : des modèles de langage à grande échelle.
- BERT (Bidirectional Encoder Representations from Transformers) : une percée majeure dans le traitement du langage naturel, montrant comment les modèles de deep learning pouvaient comprendre le contexte bidirectionnel dans le langage.
- Les modèles GPT (sigle de Generative Pre-trained Transformer : modèle de langage génératif), développés par OpenAI, utilisent l’architecture Transformer, basée sur le mécanisme d’attention, qui permet au modèle de prendre en compte des parties spécifiques de la séquence d’entrée lors du traitement de chaque élément. GPT-1 (2018) avec 117 millions de paramètres, GPT-2 (2019) avec 1,5 milliard, GPT-3 (2020) avec 175 milliards, GPT-4 (2023) avec plus de 1000 milliards.
- ChatGPT est un robot conversationnel commercialisé par OpenAI. LA base de ChatGPT est GPT-3.5 pour la version gratuite) et GPT-4 pour la version payante.
2 Apprentissage par les données
2.1 Qu’est-ce qu’un modèle en ML?
Modèle en ML En machine learning, un modèle mathématique désigne une représentation formelle f créée dans le but de résoudre une tâche spécifique. À l’aide d’un algorithme, le modèle est appris ou entraîné pour ajuster ses paramètres en se basant sur des données. [Katia]
Dans cette présentation, nous nous placerons dans le cadre paramétrique où f est connue à un paramètre près, c’est-à-dire que l’on suppose que f appartient à \mathcal{F}_{\theta}, une classe de fonctions paramétrées par le paramètre \theta : \mathcal{F}_{\theta} = \{ f_{\theta} : \ \theta \in \Theta \subseteq \mathbb{R}^d \} \equiv \text{modèle de taille } d \text{ paramètres}
En apprentissage profond (Deep Learning), un modèle est un réseau de neurones et sa taille est égale au nombre de paramètres d à apprendre. Il peut être très grand, par exemple, d=175 milliards pour la version gratuite de ChatGPT. On dit que les réseaux de neurones sont des modèles sur-paramétrés.
Si le modèle est la structure mathématique qui représente la relation entre les données, l’algorithme est la procédure où la méthode qui est utilisée pour entraîner ce modèle, c’est-à-dire pour ajuster ses d paramètres en minimisant une fonction de perte ou d’erreur (Loss function), c’est la phase d’apprentissage.
- Un modèle plus expressif a la capacité de capturer des motifs plus subtils et des structures plus sophistiquées, ce qui peut conduire à de meilleures performances sur des tâches complexes.
- Cependant, il est crucial de trouver un équilibre, car des modèles excessivement complexes (d trop grand) peuvent également être sujets au surajustement (Overfitting), où ils s’adaptent trop étroitement aux données d’entraînement et ne généralisent pas bien sur de nouvelles données.
Ainsi, le choix du modèle doit être guidé par la nature de la tâche et la complexité inhérente des données, en cherchant à obtenir une expressivité appropriée pour une performance optimale. Ce qu’il faut surtout retenir c’est qu’il n’y a pas de modèle universel qui excelle dans toutes les situations!
No free lunch Théorème Wolpert,D.H, & Macready,W.G. (1997). “simplifié” Aucune méthode (modèle/algorithme) ne peut surpasser toutes les autres sur tous les types de problèmes.
Qu’est-ce qu’un modèle performant ? Un modèle performant est un modèle capable de tirer des conclusions précises et utiles à partir de données qu’il n’a pas encore rencontrées, c’est-à-dire celles qui ne faisaient pas partie de l’ensemble d’entraînement. On dit que le modèle généralise à des données non vues, démontrant ainsi de bonnes performances de généralisation. La Généralisation est donc un problème central en ML qui fera l’objet d’une section.
L’apprentissage peut être grossièrement divisées en 4 domaines. Chaque type d’apprentissage est associé à des tâches spécifiques qui lui sont définies. Le schéma récapitulatif ci-dessous illustre pour chaque catégorie d’apprentissage les types particuliers de tâches associées.
- Apprentissage supervisé/Supervised learning.
- Apprentissage non supervisé/Unsupervised learning.
- Apprentissage par renforcement/Reinforcement learning.
- Tranfert learning.
2.2 Apprentissage supervisé
En apprentissage supervisé (Supervised learning), le modèle est entraîné sur un ensemble d’apprentissage \mathcal D=\Big\{(x_1,y_1),\ldots,(x_n,y_n) \ : \ (x_i,y_i)\in\mathcal X\times\mathcal Y \big\} où \mathcal{X} est l’espace des exemples x_i, les données d’entrée, et \mathcal{Y} est l’espace des étiquettes y_i associées aux données d’entrée x_i. En apprentissage supervisée, l’ensemble d’apprentissage \mathcal{D} est un ensemble d’exemples appairés à étiquettes.
On distingue deux tâches principales: la régression, lorsque y est de nature quantitative et la classification, lorsque y est de nature quanlitative.
L’objectif principal est que le modèle f_\theta soit capable de généraliser ses prédictions \widehat y=f_{\theta}(x) des étiquettes y sur de nouveaux exemples x en dehors de l’ensemble d’entraînement.
2.3 Apprentissage non supervisé
En apprentissage non supervisé (Unsupervised learning), le modèle est entraîné sur un ensemble de données non étiquetées. \mathcal D=\Big\{x_1,\ldots,x_n \ : \ x_i\in\mathcal X\big\}.
L’apprentissage étant non guidé par les étiquettes, la tâche est de trouver un modèle capable de découvrir des structures intrinsèques, de réduire la dimension, de segmenter les données ou de créer des groupes homogènes. Le modèle f_\theta mappe les données vers un espace latent, souvent de dimension réduite où les données d’origines sont représentées de manière comprimée tout en conservant les informations importantes
La généralisation est interprétée ici comme la capacité du modèle à découvrir un espace latent où des structures sous-jacentes sont plus claires, permettant ainsi une application efficace des connaissances acquises à de nouvelles données similaires x en dehors de l’ensemble d’entraînement.
2.4 Apprentissage par renforcement
L’agent prend selon les informations/observations qu’il percoit, une décision/action et reçoit une récompense ou une pénalité en fonction de la qualité de ses actions \mathcal D=\Big\{(s_1,a_1,r_1),(s_2,a_2,r_2),\ldots,(s_t,a_t,r_t),\ldots\big\},
- Séquence de perceptions: s_t. L’environnement répond aux actions de l’agent et fournit des retours d’information.
- Actions: a_t. Les actions prises par l’agent peuvent être discrètes (ensemble fini d’options possibles) ou continues (valeurs numériques).
- Récompenses: r_t . L’agent reçoit une récompense ou pénalité reçue en fonction de la qualité de ses actions, et ajuste sa stratégie pour maximiser la récompense cumulée au fil du temps
L’objectif de l’agent est de maximiser la somme cumulée des récompenses sur le long terme. L’agent apprend par essais et de ses erreurs à partir des interactions avec l’environnement! La généralisation, dans ce contexte, est la capacité de l’agent à généraliser ses connaissances sur la manière de prendre des actions optimales dans des situations similaires mais non identiques à celles rencontrées pendant l’entraînement.
2.5 Apprentissage par transfert
Un modèle pré-entraîné, sur une grande quantité de données pour effectuer une tâche spécifique, est ajusté (fine-tuné) sur un ensemble de données plus restreint spécifique à la tâche cible. Les couches du modèle peuvent être adaptées pour mieux s’adapter aux caractéristiques de la nouvelle tâche, tout en préservant les connaissances générales apprises lors de la phase de pré-entraînement.
Le transfert d’apprentissage est particulièrement utile lorsque les données pour la tâche cible sont limitées, car le modèle peut généraliser à partir de l’information préalablement apprise sur la tâche source.
3 Les Données
Donnée /Data: [Centre National de Ressources Textuelles et Lexicales - CNRTL] : “Ce qui est connu et admis, et qui sert de base, à un raisonnement, à un examen ou à une recherche.
Les données, qu’elles soient observées sur le terrain ou générées de manière artificielle, présentent une grande diversité.
3.1 Niveaux de traitement des données
Les données sont le carburant essentiel des IA! Elles exigent un prétraitement minutieux. Cette étape est cruciale pour préparer les données brutes afin qu’elles soient adaptées à l’entraînement de modèles.
Prétraitement. Les données, à l’état brut, manquent de sens et de contexte. L’objectif de cette phase préliminaire est de les nettoyer et de les structurer, transformant ainsi les données brutes en une information exploitable en préparation pour la prochaine étape du traitement des données, la transformant en connaissance.
Traitement des données Les données sont maintenant porteuses d’information. Leur traitement s’effectue par des méthodes de machine learning pour être transformées connaissance. Plusieurs scénarios peuvent se présenter:
Soit nous en obtenons un simple aperçu, ce qui signifie avoir une idée générale sans une compréhension approfondie de ce qui se passe.
Soit nous parvenons à une compréhension approfondie. Dans ce cas, les données sont totalement comprises, permettant d’expliquer les observations de manière détaillée, d’interpréter.
⚠️ Cependant, la compréhension ou l’interprétation qui en découle peuvent être erronées. Cela peut résulter d’erreurs diverses : erreur d’interprétation volontaire ou non, présence de données erronées, incomplètes, inadaptées, ou polluées…
3.2 Prétraitement des données
Les données jouent un rôle central dans les algorithmes de Machine Learning. Une préparation minutieuse de ces données est essentielle pour garantir des performances optimales. Les étapes du prétraitement des données comprennent les suivantes :
1. Profilage des données.
Le profilage des données représente une phase exploratoire essentielle visant à comprendre la nature des données, ce qui nécessite une solide connaissance du domaine d’application. Cette étape sert de point de départ pour évaluer la qualité des données d’une manière macroscopique, en identifiant notamment les données erronées et les anomalies. Au cours de cette phase, les données provenant de sources hétérogènes sont agrégées dans un processus appelé Data Integration (intégration de données), visant à créer un ensemble de données unifié.
2. Nettoyage des données.
Le nettoyage des données est essentiel pour détecter et corriger les erreurs, incohérences et problèmes de qualité dans les données brutes.
- Gestion des types de données: Conversion des types pour rendre les données compatibles avec les algorithmes d’apprentissage automatique.
- Traitement des valeurs manquantes (Ex: mode, moyenne,..) , traitement des valeurs aberrantes (correction, suppression, remplaçement), correction des erreurs de saisie.
- Normalisation des données (Feature Scaling) pour mettre toutes les variables à la même échelle (Ex: Standardisation, Normalisation,…)
- Le lissage des données pour traiter le bruit (Ex: binning, clustering))
- Gestion des duplicatas (suppression),
- …
Gestion des types de données: Exemples.
Dans des situations simples, cette conversion est relativement directe. Par exemple, une image peut être transformée en une matrice de pixels, un signal audio peut être échantillonné en séries, et un texte peut être converti en embeddings.
Cependant, des défis se présentent avec des données plus complexes, telles que celles qui sont structurées, comme les molécules ou les réseaux sociaux. Souvent, ces données peuvent être converties en représentations sous forme de graphes, qui à leur tour peuvent être transformées en matrice (matrice d’adjacence).
Des données encore plus complexes, telles que les objets en 3D. Les maillages 3D, les graphes 3D et les nuages de points 3D sont trois des principales méthodes de représentation utilisées et peuvent être représenter par des tenseurs.
Les données vidéo, parmi les plus complexes, peuvent désormais être traitées en tant que tenseurs, ajoutant ainsi une dimension supplémentaire à la complexité du traitement des données.
Les données vidéo, parmi les plus complexes, peuvent désormais être traitées en tant que séquence de tenseurs (volumes vidéo ), c’est-à-dire une séquence de matrices (ou tenseurs si image en couleur) où chaque matrice représente une image individuelle de la vidéo à un moment donné.
Données textuelles.
À titre d’illustration [Source: FIDLE-CNRS], considérons différents descripteurs pour convertir en format numérique les données textuelles.
Letter occurence: Un vecteur où chaque composante correspond à l’occurrence de chaque lettre dans le texte. C’est un descripteur de base, mais il permet de classer les textes par langue.
One-Hot Encoding: Transforme chaque mot en un vecteur creux de la taille du dictionnaire disponible (comportant plusieurs milliers d’entrées) :
0indique l’absence du mot,1indique la présence du mot. Chaque mot a sa propre dimension, offrant une signification globale, mais ces matrices creuses de grande taille occupent beaucoup d’espace!Word Embeddings: Chaque mot est représenté par un vecteur de nombres réels dans un espace vectoriel continu de dimension choisi à l’avance (\propto 10^5. Les embeddings capturent la sémantique des mots en analysant le contexte dans lequel ils apparaissent : deux mots ayant un sens similaire seront représentés par des vecteurs proches dans l’espace latent. Ces embeddings sont obtenus grâce à l’apprentissage sur de larges corpus de texte à l’aide de modèles tels que les DNN Word2Vec, GloVe, FastText, BERT, etc. L’objectif de l’apprentissage est de placer les vecteurs de mots de telle sorte que les mots ayant des significations similaires soient plus proches les uns des autres dans l’espace vectoriel.
Arrêt sur Image.
📜 Une image est composée de pixels. On distingue les images en couleur de celles en niveau de gris, dites en noir et blanc.
- Une image en niveaux de gris (grayscale) est représentée sous forme matricielle, où chaque pixel est lié à une valeur numérique représentant son niveau de luminosité. Ces valeurs varient généralement de 0 (pour le noir) à 255 (pour le blanc) dans une image codée sur 8 bits.
- Une image en couleur est représentée par un tenseur tridimensionnel, ce qui implique que chaque pixel est généralement décrit par trois valeurs numériques correspondant à l’intensité de sa couleur dans les composantes rouge, verte et bleue (RVB). Chaque canal, à savoir le rouge (R), le vert (V) et le bleu (B), possède une plage de valeurs allant généralement de 0 à 255 dans une image 8 bits par canal.
🔵Py. Gestion sous Python.
# Importation des librairies nécessaires
import torch
from PIL import Image
import torchvision. transforms as transforms
# lire l'image et la convertir en tenseur
image = Image. open ('mnist_image.jpg')
transform = transforms. ToTensor()
img_tensor = transform(image)
# Afficher l'image
plt.imshow(img_tensor.permute(1, 2, 0)) # Permutation des dimensions pour matplotlib differnte des dimensions PyTorch
plt.title('MNIST Image')
plt.axis('off') # Désactiver les axes
plt.show()
# Visualisation du tensor
print (img_tensor)
# Visualisation de la taille du tenseur
print("Taille du tenseur:", img_tensor.size())3. Réduction de la dimensionnalité.
La réduction de la dimensionnalité est cruciale pour éviter des problèmes tels que le surajustement et améliorer les performances des modèles. elle peut se faire par exemple :
- Analyse en composantes principales (ACP): Utilisation de combinaisons linéaires pour représenter efficacement les données.
- Sélection de variables/caractéristiques: Utilisation de modèles de ML (Ex: Random Forest,…) pour extraire un sous-ensemble pertinent.
- …
4. Transformation des données.
La combinaison de variables, également connue sous le nom de Feature Engineering, constitue une étape cruciale dans le processus de prétraitement des données. Un data scientist ou un expert peut intervenir manuellement en créant de nouvelles caractéristiques à partir de l’application de transformations sur les variables , telles que l’addition, la soustraction, la multiplication, transformation logarithmique,… Cette approche traditionnelle est efficace et permet une compréhension approfondie du jeu de données.
Cependant, avec l’avènement du Deep Learning, une révolution s’opère. La machine peut désormais automatiser ce processus complexe en identifiant de manière autonome des relations profondes entre les variables.
5. Augmentation/Réduction des données
- Sampling : Sous-échantillonnage pour éviter les coûts excessifs avec un nombre d’observations trop grand.
- Data augmentation : Utilisation de transformations aléatoires (Ex: pour les images: rotation, le zoom ou la variation des couleurs) pour élargir et diversifier l’ensemble de données sans augmenter physiquement sa taille.
6. Validation des données
L’évaluation de la qualité des données prétraitées et des performances des modèles sur des données distinctes est cruciale. Elle permet notamment d’identifier d’éventuels problèmes dans les hypothèses formulées lors des étapes précédentes du processus. .
Étapes clés
- Division aléatoires des données: en ensembles d’entraînement (Train), de validation (Validation) et de test (Test) .
- Apprentissage sur le Train, Evaluation de la performance sur le Validation, ajustements du modèl en fonction des résultats.
- Itérations et ajustement: Itérations et ajustements en fonction des résultats de la validation.
Remarque. Pour les ensembles de données plus petits, la validation croisée peut être utilisée pour augmenter la robustesse de l’évaluation en divisant les données en plusieurs sous-ensembles, permettant ainsi d’entraîner et de tester le modèle plusieurs fois.
Comparaison de modèles Le Test est exclusivement réservé pour comparer différents modèles.
4 Généralisation: un enjeu central
Lors de la modélisation, il est essentiel de trouver le juste équilibre entre un modèle trop simpliste qui ne capture pas toutes les nuances des données (sous-ajustement, underfitting) et un modèle trop complexe qui s’adapte trop précisément aux données d’entraînement mais perd en généralité sur de nouvelles données (sur-ajustement, , overfitting). Trouver cet équilibre, garantit que le modèle généralise bien sur des données inconnues, assurant ainsi des performances robustes
Pour résoudre ce problème, une solution est la quantité de données disponible, mais surtout, d’appliquer une régularisation en pénalisant les modèles de grande taille. C’est par le biais de cette
Régularisation implicite [ Foundations of Deep Learning NYU Tandon, Spring (2022)] La régularisation peut être implicite, cachée. Elle fait référence à des mécanismes ou des caractéristiques inhérents aux algorithmes d’apprentissage ou aux architectures de modèles qui contribuent à prévenir le surapprentissage sans qu’une technique de régularisation explicite ne soit appliquée. On peut citer l’utilisation de
Ces mécanismes agissent souvent de manière implicite pour régulariser le modèle et prévenir le overfitting, même si aucune technique de régularisation explicite n’est utilisée. Mais en combinant ces mécanismes avec des techniques de régularisation explicites, il est possible d’améliorer la capacité du modèle à généraliser sur de nouvelles données.
Métriques
Métriques les plus courantes.
Les métriques sont des mesures quantitatives utilisées pour évaluer la performance de Généralisation d’un modèle d’apprentissage automatique. Elles fournissent une évaluation objective de la qualité des prédictions d’un modèle sur un ensemble de données spécifique.
Métriques les plus courantes
Précision (Accuracy) : C’est le rapport du nombre de prédictions correctes sur le nombre total d’échantillons. \text{Précision} = \frac{\text{Nombre de prédictions correctes}}{\text{Nombre total d'échantillons}}
Rappel (Recall) : C’est la capacité du modèle à identifier tous les exemples positifs. \text{Rappel} = \frac{\text{Nombre de vrais positifs}}{\text{Nombre de vrais positifs} + \text{Nombre de faux négatifs}}
F1 Score : C’est la moyenne harmonique de la précision et du rappel. Il est souvent utilisé lorsque les classes sont déséquilibrées. \text{F1 Score} = 2 \times \frac{\text{Précision} \times \text{Rappel}}{\text{Précision} + \text{Rappel}}
Aire sous la courbe ROC (AUC-ROC) : Mesure la capacité d’un modèle à discriminer entre les classes. Plus l’AUC-ROC est proche de 1, meilleure est la performance du modèle.
Log Loss (perte logarithmique) : Mesure la performance d’un modèle de classification en comparant les prédictions de probabilité avec les vraies étiquettes.
Erreur quadratique moyenne (MSE - Mean Squared Error) : Mesure la moyenne des carrés des erreurs entre les prédictions d’un modèle et les vraies valeurs dans le cas de la régression.
Coefficient de détermination (R²) : Mesure la proportion de la variance dans la variable dépendante qui est prévisible à partir des variables indépendantes dans le cas de la régression.
Précision binaire et rappel binaire : Utilisés dans des tâches de classification binaire, ils sont similaires à la précision et au rappel, mais se concentrent spécifiquement sur la classe positive ou négative.
Métriques de classification multiclasse : Incluent des mesures telles que la précision macro/micro/pondérée et le rappel macro/micro/pondéré pour évaluer la performance dans des tâches de classification multiclasse.
Lors de l’évaluation d’un modèle, le choix des métriques dépend du type de problème (classification, régression, etc.) et des objectifs spécifiques du projet. Il est important de sélectionner les métriques appropriées en fonction du contexte et des exigences de la tâche à accomplir.
5 Du Perceptron au MultiLayer Perceptron
Le concept de réseau de neurones artificiels a été inspiré par la structure et le fonctionnement des neurones dans le cerveau biologique. L’idée de base est de modéliser le fonctionnement du cerveau pour créer des systèmes informatiques capables d’apprendre et de résoudre des problèmes complexes de manière autonome.
5.1 Le perceptron
Le perceptron, ou neurone, est un modèle (paramétrique) de réseau neuronal simple introduit par Frank Rosenblatt (1958) [2] pour résoudre un problème de classification binaire simple.
Neurone biologique vs Neurone artificiel. Les neurones dans le cerveau sont des cellules nerveuses qui communiquent entre elles par l’intermédiaire de connexions appelées synapses. Les réseaux de neurones artificiels cherchent à reproduire cette structure en utilisant des unités de traitement artificielles appelées neurones et des connexions entre ces neurones. Comme le neurone biologique (NB), le perceptron reçoit des signaux en entrée via des synapses. Ces signaux sont traités et transformés en un signal de sortie. La sortie du neurone, peut être interprétée comme l’activation ou la désactivation du neurone qui elle dépend de ses signaux d’entrée.
Classification binaire simple. Afin de mieux comprendre, considérons la tâche de classification binaire où les examples x_i\in\mathcal X\subseteq\mathbb R^p et les étiquettes associées y_i\in\{0,1\}. Dans cette exemple simple où p=2 : le classifieur qui sépare les étiquettes 1 et 0 est simple. Graphiquement les exemples classés 1 sont les ceux situés sur le demi plan supérieur w_1\boldsymbol {x}_{1}+w_2\boldsymbol {x}_{2}+b > 0.
Formalisme.
- En entrée, le perceptron récupère un signal, ici les caractéristiques de l’exemple x_i=(x_{i1},\ldots,x_{ip})^\top\in\mathcal X\subseteq\mathbb R^p, i=1,\ldots,n
- Puis le signal x_i passe dans les différentes synpases. Chaque synapse j applique un poids synaptique w_j: w_j\times x_{ij}, \ \forall i=1,\ldots,p. Si le poids synaptique w_j>0, on dit que la synapse est activatrice, dans le cas contraire elle est inhibitrice.
- Puis, on somme et on ajoute un terme de biais \ b : z_j=\sum_{j=1}^pw_jx_{ij}+b.
- Ensuite, on applique une fonction d’activation pour produire une sortie: a_i=\sigma(z_j)=\widehat y_i.
Algorithme du perceptron avec la fonction d’activation \sigma(u)=\text{sign}(u)=\1_{u\geq 0}.
- Initialisation. des paramètres du modèle à 0 ou à de petites valeurs aléatoires.
- Mise à jour des paramètres Pour (x_i,y_i)\in\mathcal X\subseteq\mathbb R^p\times\{0,1\} \left|\left|\begin{array}{ll} \text{Calculer la sortie:}&\widehat y_i=\sigma\Big(\sum_{j=1}^pw_j(t)x_{ij}+b(t)\Big)\\ \text{Mettre à jour:}&\left\{\begin{array}{ll} b_j(t+1)&=b_j(t)+\eta\times(y_i-\widehat y_i)\\ w_j(t+1)&=w_j(t)+\eta\times(y_i-\widehat y_i) x_{ij} \end{array}\right.\\ \end{array}\right.\right.
- (y_i-\widehat y_i) est l’erreur de classification
- \eta est le taux d’apprentissage (learning rate), c’est-à-dire la taille du pas de l’algorithme lors de la mise à jour.
- Itération/Répétition. Itérations sur , i=1,\ldots,n + plusieurs epoch.
Remarques.
Si (y_i-\widehat y_i)=(1-0)=1, on veut augmenter les poids des input positifs, et diminuer ceux des input négatifs.
Si (y_i-\widehat y_i)=(0-1)=-1, on veut augmenter les poids des input négatifs, et diminuer ceux des input positifs.
Le learning rate est un hyperparamètre crucial dans l’entraînement des modèles d’apprentissage machine. Il contrôle la vitesse à laquelle un modèle apprend. Un learning rate trop élevé peut entraîner une divergence du modèle (non convergence vers une solution optimale). Un learning rate trop faible peut conduire à une convergence très lente, voire à ce que le modèle ne converge pas du tout. Il est souvent nécessaire d’ajuster expérimentalement le learning rate en fonction du problème spécifique que l’on cherche à résoudre.
La règle de mise à jour des poids décrite ci-dessus, n’utilise donc pas de méthodes basées sur la descente de gradient. Pour cela, d’autres fonctions d’activation, telles que la sigmoïde ou la fonction tangente hyperbolique, qui introduisent une non-linéarité plus douce dans le modèle, peuvent être utilisées.
Cette règle de mise à jour peut nécessiter le plusieurs passes. En ML, on dit époques ou epoch en anglais. Une epoch est le passage de tous les exemples du dataset. Ainsi, plusieurs epoch peuvent être nécessaires pour que le perceptron converge vers une solution adaptée. en pratique soit on s’est fixé une erreur minimale à atteindre ( ex: \sqrt{\sum_{i=1}^n(y_i-\widehat y_i)^2/n}< err_{\min}), soit un nombre d’itérations maximum.
😕 Le perceptron est limité dans sa capacité à modéliser des relations complexes (comme le XOR), il est efficace dans des scénarios où la séparation linéaire des classes est possible. Dans la pratique, pour des tâches de classification plus complexes, on utilise des réseaux de neurones profond ou non.
5.2 MultiLayer Perceptron (MLP)
Le problème XOR est un problème de classification non linéaire dans lequel un perceptron simple ne peut pas apprendre à séparer les classes de manière satisfaisante en ajustant uniquement les poids. Plus, précisément, le XOR ( ou exclusif logique) est une opération binaire qui renvoie vrai 1 si les bits de ses deux opérandes sont différents, et faux 0 si les bits sont identiques. Voici la table de vérité pour l’opérateur XOR :
🙂 Là où le perceptron échoue à résoudre ce problème, un modèle non linéaire comme le Perceptron Multicouches MultiLayer Perceptron (MLP) peut le faire! Le MLP est une architecture de réseau de neurones artificiels composé de plusieurs couches de neurones,noeuds, nodes (ou perceptrons) complètement connectés et orientés. Un MLP est composé de :
MLP à L+1 couches:
- Une couche d’entrée/Input Layer C_0 de largeur p, constituée des inputs, caractéristiques (x_{11},\ldots,x_{ip}) de l’exemple x_i\mathbb R^p.
- Une couche de Sortie/Output Layer C_{L} de largeur q, constituée de q outputs \widehat y_i: la prédiction de y_i\in\mathbb R^{q}.
- Couche(s) cachées/Hidden Layers \Big\{C_\ell\Big\}_{\ell=1,\ldots,L-1} de largeur N_{\ell}, c’est-à-dire chacune composée de N_{\ell} neurones.
Formalisme. On notera
- Les poids des synapses d’entrée et les biais ajoutés dans la couche C_\ell, \ \ell=1,\ldots, L \left\{\begin{array}{l} \boldsymbol W^{(\ell)} =\big[W_{jk}^{(\ell)} \big]_{j,k}\in\mathbb R^{N_{\ell-1}}\times\mathbb R^{N_{\ell}}\\ \\ \boldsymbol b^{(\ell)} = \big[b_{k}^{(\ell)}\big]_{k}\in\mathbb R^{N_{\ell}} \end{array}\right.
- L’ensemble de fonctions d’activation \big\{\sigma_{\ell} \ : \ \ell=1, \ldots, L\big\}.
Ce type de réseaux de neurones peut, grâce à ses couches de neurones interconnectées, capturer des relations non linéaires plus complexes que le perceptron: des représentations hiérarchiques complexes. Mais pour cela, il faudra l’entraîner!
Théorème approximation universelle de Cybenko G. (1989) “simplifié” Un réseau de neurones à une seule couche cachée avec une fonction d’activation sigmoïde (ou d’autres non linéaires) peut approximer n’importe quelle fonction continue sur un sous-ensemble borné et fermé de l’espace euclidien.
Remarques
- L’énoncé de ce théorème varie suivant les auteurs et les sources. Je “vulgarise” ici l’une des premières versions.
- Attention. Si ces conditions garantissent que le réseau peut approximer de manière universelle des fonctions continues, cela ne garantit pas sa facilité à apprendre ni sa capacité à généraliser sur des données non vues.
- Pour des tâches beaucoup beaucoup plus complexes (image, chatbox,..), il faut des modèles dont l’expressivité est beaucoup beaucoup plus grande. C’est là qu’interviennent des modèles plus complexes, tels que les réseaux de neurones profonds et même là, le type de réseaux doit être bien choisit, nous verrons dans cette présentation les différentes architectures de réseaux et quand il convient de les utiliser.
Le MLP dans le cas du XOR. Dans le cas du XOR, 2 couches cachées avec 2 neurones par couches suffisent combinées avec la fonction d’activation \tanh. Pour déterminer le nombre de couches cachées et de neurones dans chaque couche cachée, le plus souvent, c’est une question d’expérience et d’essais-erreurs, même si des heuristiques existent.
Comment entraîner un MLP L’algorithme du perceptron vu précédemment pour ajuster les poids ne peut pas être utilisée ici. Dans la pratique, on utilise des algorithmes d’optimisation basés sur la descente de gradient, tels que l’algorithme de Backpropagation. Mais avant cela discutons du choix des fonctions d’activation.
6 Fonctions d’activation
Les fonctions d’activation sont des composants essentiels des réseaux de neurones. Elles introduisent une non-linéarité dans le modèle, permettant au réseau de modéliser des relations complexes et de réaliser des tâches plus sophistiquées. Nous allons distinguer les fonctions d’activation utilisées sur la couche de sortie (Output layer) de celles utilisées sur les autres couches.
6.1 Fonctions d’activation sur la dernière couche
La fonction d’activation sur la dernière couche d’un Deep Neural Network (DNN) dépend de la nature de la tâche que le réseau doit accomplir.
Sigmoïde (classification binaire). Dans le cadre de la classification binaire ( Ex: le XOR), la Sigmoïde, à valeurs dans (0,1), elle est principalement utilisé dans la couche de sortie et est interprétée comme la probabilité de la sortie y_i d’appartenir à la classe +1, étant donné le vecteur de caractéristiques d’entrée x_i \sigma(x_i)=P(y_i=1|x_i)=(1+e^{-W^\top z_i})^{-1}, où z_i, est le vecteur des sorties de la dernière couche cachée pour l’example (x_i,y_i).
Softmax (classification multiclasse). Lorsque l’on travaille dans le cadre de la classification multiclasse, la couche de sortie du réseau de neurones doit fournir la probabilité de chaque classe envisagée. Dans ce cadre, la fonction d’activation Softmax est importante: c’est la probabilité de la sortie y_i d’appartenir à une classe particulière c_\ell parmi un total de K classes, étant donné le vecteur de caractéristiques d’entrée x_i \text{Softmax}(x_i)=P(y_i=c_\ell|x_i)=\frac{e^{W_\ell^\top z_i}}{\sum_{k=1}^Ke^{W_k^\top z_i}}, où z_i, est le vecteur des sorties de la dernière couche cachée pour l’example (x_i,y_i) et W_k les points pour la classe k. La fonction d’activation Softmax est à valeurs dans [0, 1], les valeurs étant normalisées de manière à former une distribution de probabilité sur les classes.
⚠️ Pour cette tâche de classification multiclasse, il est courant d’ajouter la fonction \arg\max pour déterminer pour extraire l’indice de la classe ayant la probabilité la plus élevée et ainsi déterminer la classe prédite.
Identity (regression). Pour les problèmes de classification, les activations Sigmoïde et Softmax peuvent être utilisées, car elles fournissent une séparation non linéaire abrupte entre les classes. Mais, pour les problèmes de régression (prédiction d’une variable continue en sortie), une activation linéaire peut être nécessaire. L’activation identité appartient à la classe des activations linéaires et est utilisée dans l’apprentissage profond comme activation pour la dernière couche dans un problème de régression visant à prédire une sortie continue. \text{Identity}(u)=u
6.2 Fonctions d’activation sur les autres couches
Dans cette section, je vais présenter les trois fonctions d’activation les plus connues et couramment utilisées pour les couches intermédiaires.
6.2.1 Tangente Hyperbolique & Sigmoïde
Dans les réseaux neuronaux artificiels traditionnels, des activations telles que la Sigmoïde et la Tangente hyperbolique étaient couramment utilisées pour l’activation des couches d’entrée et cachées. \tanh(u)=\frac{e^{2 u}-1}{e^{2 u}+1} La tangente Hyperbolique est à valeurs dans (-1,1), elle a une utilité similaire à la sigmoïde mais avec une sortie centrée autour de zéro.
😕 Vanishing Gradient. Lors de la retropropagation du gradient vers les couches les plus profondes du réseau, un problème majeur peut survenir: Le Vanishing Gradient. Il se produit lorsque les gradients deviennent extrêmement petits à mesure qu’ils sont rétropropagés, ils diminuent exponentiellement à mesure qu’ils traversent les couches du réseau, et cela peut entraîner des poids qui ne sont pas correctement mis à jour, conduisant à une convergence lente ou même à un arrêt de l’apprentissage.
Ce phénomène est souvent observé avec des fonctions d’activation telles que la fonction sigmoïde ou tanh. Lorsque la sortie d’une couche est proche des extrémités de la fonction sigmoïde (0 ou 1), la dérivée de la fonction est proche de zéro, et donc les gradients qui sont rétropropagés peuvent devenir négligeables.
🙂 Relu!
6.2.2 ReLU (Rectified Linear Unit).
Je finirai cette section avec la fonction d’activation la plus populaire: ReLU
\text{ReLU}(u)=\max(0,u)
Même si elle n’est pas dérivable en 0, en pratique, lors de l’entraînement d’un réseau de neurones, on définit simplement la dérivée en x=0 comme 0 ou 1, car le point x=0 a une probabilité nulle d’être atteint avec des valeurs réelles dans les calculs numériques.
Pourquoi une telle popularité ?
Elle a contribué à résoudre le problème du Vanishing gradient pour les activations positives: sa dérivée étant constante (
1pour les valeurs positives).Elle produit une sortie de
0pour toutes les entrées négatives. Cela encourage la sparsité dans l’activation du réseau, ce qui signifie que seuls certains neurones sont activés, rendant le modèle plus efficace en termes de calcul.
😕 Mais ReLU peut conduire à des problèmes de dying ReLU, c’est-à-dire où certains neurones peuvent rester inactifs pendant l’entraînement, ne contribuant pas à l’apprentissage.
🙂 C’est pourquoi des variantes, à valeurs dans \mathbb R, de ReLU, telles que Leaky ReLU et Parametric ReLU, ont été proposées pour atténuer ces problèmes spécifiques. \left\{\begin{array}{l} \text{Leaky ReLU}(u)= u\boldsymbol 1_{u>0}+ \alpha u\boldsymbol 1_{u\leq0}\\ \\ \text{PReLU}(u)=\max(\alpha u,u) \end{array}\right. Elles permettent un flux d’informations même pour les valeurs négatives de l’entrée avec un paramètre \alpha qui peut être appris pour éviter le problème de dying ReLU.
- La Leakey Relu ajoute un hyperparmètre \alpha pour les nombres négatifs, ainsi les neurones ne meurent jamais. Ils entrent en “sommeil” mais ont la possibilité d’être réativés.
*PReLU définit \alpha comme paramètre même du modèle et non plus comme un hyperparmètre**. Il sera calibrer lors de l’apprentissage. La PRelu est déconseillé sur les petits jeux de données, le risque étant un éventuel sur ajustement (underfitting).
😕 Explosing Gradient
Le phénomène du “gradient qui explose” se produit lorsque les gradients deviennent très grands au fil des itérations, cela peut entraîner des problèmes lors de la mise à jour des poids. Les valeurs des poids peuvent devenir extrêmement grandes, et cela peut rendre le modèle instable, entraînant des divergences numériques, des erreurs de calculs, ou simplement un échec de la convergence de l’apprentissage.
Cela peut être exacerbé par l’utilisation de fonctions d’activation qui amplifient les entrées, comme la fonction d’activation ReLU (Rectified Linear Unit).
🙂 Pour atténuer le problème du gradient qui explose, des techniques telles que la normalisation des lot (batch normalization) ou l’initialisation appropriée des poids (comme l’initialisation He ou Glorot) peuvent être utilisées. Ces approches aident à stabiliser l’apprentissage en maintenant des valeurs de gradient raisonnables à chaque étape du processus.
7 Fonction de perte (Loss function)
La fonction de perte (loss function) est essentielle lors de l’entraînement d’un réseau de neurones, car elle mesure l’erreur entre les prédictions \widehat y_i=f_\theta(x_i) du modèle et les étiquettes réelles y_i. L’objectif principal lors de l’entraînement d’un réseau de neurones est de minimiser cette fonction de perte. Son choix dépend de la tâche à résoudre.
Par exemple:
Régression Lorsque la sortie attendue \widehat{y}_i est une valeur continue, on utilise la perte quadratique, Quadratic loss L(y_i,\widehat y_i) = \frac{1}{2}||y_i - \widehat{y}_i||_2^2=\sum_{j=1}^q (y_{ij}-\widehat{y}_{ij})^2
Classification Binaire Lorsque la sortie attendue \widehat{y}_i est à valeur dans \{0,1\}, on utilise la perte logistique Logistic/Binary Cross-Entropy loss L(y_i,\widehat y_i) = -[y_i \log(\widehat y_i) + (1 - y_i) \log(1 - \widehat y_i)]
où \widehat{y}_{i} est la probabilité prédite par le modèle que l’exemple x_i appartienne à la classe 1.
Classification Multiclasse Lorsque la sortie attendue \widehat{y}_i est à valeur dans \{1,\ldots,K\}, on utilise l’ entropie croisée catégorielle Categorical Cross-Entropy). L(y_i,\widehat y_i) = -\sum_{k=1}^K y_{ik} \log(\widehat{y}_{ik})
où \widehat{y}_{ik} est la probabilité prédite par le modèle que l’exemple x_i appartienne à la classe k.
⚠️ En apprentissage non supervisé, il n’y a pas de fonction de coût classique. Par exemple, les autoencodeurs, des réseaux de neurones spéciaux qui apprennent à reconstruire les données en passant par une représentation interne (espace latent) de dimension réduite. L’objectif est de minimiser la différence entre les données d’entrée et leur reconstruction.
8 Formalisation de l’algorithme de Backpropagation
La Rétropropagation, Backpropagation, est l’algorithme d’optimisation clé pour entraîner des réseaux de neurones.
Mise à jour des paramètres
1. Propagation Avant (Feed forward Propagation). L’algorithme commence par la propagation avant, où les données d’entrée sont introduites dans le réseau et passent à travers chaque couche jusqu’à atteindre la couche de sortie. Les activations de chaque neurone sont calculées à l’aide des poids et biais actuels du réseau et des fonctions d’activation. \left|\left|\begin{array}{ll} \underline{\textbf{Input layer:}}\ \ C_0&\\ \text{Input=Output:} &\boldsymbol a^{(0)}=x_i\in\mathbb R^{N_{0}}, \ N_0=p\\ &\\ \underline{\textbf{Hidden layer:}}&C_\ell, \ \ \ell=1,\ldots, L-1\\ \text{Input:} &\boldsymbol a^{(\ell-1)}=\Big(a^{(\ell-1)}_1,\ldots,a^{(\ell-1)}_{N_{\ell-1}}\Big)^\top\in\mathbb R^{N_{\ell-1}} \\ \text{Sum+Bias:}&z_k^{(\ell)}=\sum_{j=1}^{N_{\ell-1}} W^{(\ell)}_{jk}a_j^{(\ell-1)}+ b_k^{(\ell)},\ k=1,\ldots,N_{\ell} \\ \text{Activation:} &a_k^{(\ell)}= \sigma_{\ell}\big( z_k^{(\ell)}\big),\ k=1,\ldots,N_{\ell}\\ \text{Output:} &\boldsymbol a^{(\ell)}=\Big(a^{(\ell-1)}_1,\ldots,a^{(\ell)}_{N_{\ell}}\Big)^\top\in\mathbb R^{N_{\ell}}\\ &\\ \underline{\textbf{Output layer:}}\ \ C_L&\\ \text{Input:} &\boldsymbol a^{(L-1)}\in\mathbb R^{N_{L-1}}&\\ \text{Sum+Bias:}&z_k^{(L)}=\sum_{j=1}^{N_{L-1}} W^{(L)}_{jk}a_j^{(L-1)}+ b_k^{(L)},\ k=1,\ldots,N_{L} \\ \text{Activation:} &a_k^{(L)}= \sigma_{L}\big( z_k^{(L)}\big),\ k=1,\ldots,N_{L}\\ \text{Output:} &\boldsymbol a^{(L)}=\widehat y_i\in\mathbb R^{N_{L}},\ N_{L}=q.\\ \end{array} \right.\right.
2. Calcul de la valeur de la Loss.
\left|\left|\begin{array}{ll} L(y_i,\widehat y_i)& \text{ Son choix dépend de la tâche.}\\ \end{array} \right.\right.
3. Retropropagation du Gradient (Backward Propagation). À partir de la couche de sortie, l’algorithme effectue la rétropropagation du gradient en calculant les dérivées partielles de la fonction de perte par rapport aux poids de chaque neurone. Ces dérivées indiquent comment la fonction de perte change par rapport à chaque poids.
\left|\left| \begin{array}{l} \underline{\textbf{Output layer:}}\ C_L\\ \color{blue}{\bullet \text{ As }\widehat y_{ik}=\sigma_{L}\big(z_k^{(L)}\big)}\\ \\ \delta_k^{(L)}:=\frac{\partial L}{\partial z_k^{(L)}}=\frac{\partial L}{\partial\widehat y_{ik}}\times\frac{\partial\widehat y_{ik}}{\partial z_k^{(L)}}=\frac{\partial L}{\partial\widehat y_{ik}}\times\sigma_{L}'\big(z_k^{(L)}\big) \\ \\ \color{blue}{\bullet \text{ As } z_k^{(L)}=\sum_{j=1}^{N_{L-1}}W_{jk}^{(L)}a_j^{(L-1)} +b_k^{(L)} }\\ \\ \frac{\partial L}{\partial W_{jk}^{(L)}}=\frac{\partial L}{\partial z_k^{(L)}}\times\frac{\partial z_k^{(L)}}{\partial W_{jk}^{(L)}}=\delta_k^{(L)}\times a_j^{(L-1)}\\ \\ \frac{\partial L}{\partial b_k^{(L)}}=\frac{\partial L}{\partial z_k^{(L)}}\times\frac{\partial z_k^{(L)}}{\partial b_k^{(L)}}=\delta_k^{(L)}\\ \\ \underline{\textbf{Layer:}}\ C_{\ell},\,\ \ell=L-1,\ldots, 1\\ \color{blue}{\bullet \text{ As }z_j^{(\ell+1)}=\sum_{k=1}^{N_{\ell}}W_{kj}^{(\ell+1)}\sigma_{\ell}\big(z_k^{(\ell)}\big) +b_j^{(\ell+1)}}\\ \\ \delta_k^{(\ell)}:=\frac{\partial L}{\partial z_k^{(\ell)}}=\sum_{j=1}^{N_{\ell+1}}\frac{\partial L}{\partial z_j^{(\ell+1)}}\times\frac{\partial z_j^{(\ell+1)}}{\partial z_k^{(\ell)}}=\sum_{j=1}^{N_{\ell+1}}\delta_j^{(\ell+1)}\times W_{kj}^{(\ell+1)}\times\sigma_{\ell}'\big(z_k^{(\ell)}\big)\\ \\ \color{blue}{\bullet \text{ As } z_k^{(\ell)}=\sum_{j=1}^{N_{\ell-1}}W_{jk}^{(\ell)}a_j^{(\ell-1)} +b_k^{(\ell)}}\\ \\ \frac{\partial L}{\partial W_{jk}^{(\ell)}}=\frac{\partial L}{\partial z_k^{(\ell)}}\times\frac{\partial z_k^{(\ell)}}{\partial W_{jk}^{(\ell)}}=\delta_k^{(\ell)}\times a_j^{(\ell-1)} \\ \frac{\partial L}{\partial b_k^{(\ell)}}=\frac{\partial L}{\partial z_k^{(\ell)}}\times\frac{\partial z_k^{(\ell)}}{\partial b_k^{(\ell)}}=\delta_k^{(\ell)}\\ \end{array}\right.\right.
4. Mise à Jour des Poids et Biais (Gradient descent). En utilisant les gradients calculés, les poids du réseau sont ajustés dans la direction qui minimise la fonction de perte. Cela se fait généralement à l’aide d’un optimiseur.
\left|\left|\begin{array}{l} \\ W_{jk}^{(l)}(t+1) =W_{jk}^{(l)}(t) - \eta \frac{\partial L}{\partial W_{jk}^{(l)}}(t) \\ b_k^{(l)}(t+1) = b_k^{(l)}(t) - \eta \frac{\partial L}{\partial b_i^{(l)}}(t)\\ \\ \end{array} \right.\right.
5. Répétition du processus Les étapes de propagation avant, calcul de la fonction de perte, rétropropagation du gradient et mise à jour des poids sont répétées pour chaque lot
9 Pratique: quelques astuces
Pour en savoir plus: Efficient BackProp (2012).
Largeur et profondeur du DNN
Le choix de la profondeur L et de la largeur N_\ell des couches cachées dépend souvent de la tâche spécifique et des caractéristiques des données.
Nombre de couches:
- Plus la non-linéarité et la complexité des données sont grandes, plus le nombre de couches L devrait être élevé.
- Si l’ensemble de données est relativement petit, il est judicieux de limiter la profondeur du réseau pour éviter le surapprentissage.
- Un réseau à 2 couches cachées (comme pour le XOR) est capable de représenter une frontière de décision arbitraire à condition de choisir des fonctions d’activation appropriés.
Nombre de neurones de la couche cachée N_\ell. \,\, Plusieurs heuristiques existent, parmi elles:
- Règle des 8 : Pour une tâche de classification, le nombre de neurones dans la couche cachée devrait être environ 8 fois le nombre d’entrée.
- \min(p,q)\leq N_{\ell}\leq \max(p,q) pour un apprentissage de représentations utiles sans être trop complexe,
- N_{\ell}\approx 2p/3+q pour offrir une dimensionnalité appropriée pour les transformations de données,
- N_{\ell}\leq 2p pour éviter le surajustement du modèle,
- N_{\ell}\approx \sqrt{pq}, N_{\ell}\approx (p+q)/2, N_{\ell}\approx \log{p}…
Initialisation des poids/biais
Pour initialiser un modèle en Deep Learning, voici quelques astuces courantes :
- Initialisation des poids de la couche C_ell. Il existe deux méthodes populaires
\bullet L’initialisation Glorot et Bengio L’objectif de cette méthode est de mieux gérer la propagation des gradients lors de l’entraînement des réseaux de neurones en ajustant les poids de manière à maintenir la variance constante à travers les différentes couches du réseau: \forall j=1,\cdots,N_{\ell-1},\forall k=1,\cdots,N_{\ell} \begin{array}{l} W_{jk}\sim\mathcal N\left(0,\sqrt{\frac{2}{{N_{\ell-1}} + N_{\ell}}}\right),\\ \qquad \qquad \text{ou}\quad\\ W_{jk}\sim \text{Uniforme} \left( -\sqrt{\frac{6}{N_{\ell-1} + N_{\ell}} }, \sqrt{\frac{6}{N_{\ell-1} + N_{\ell}} } \right) \end{array} \bullet L’initialisation He Elle est conçue spécifiquement pour les couches activées par la fonction ReLU, cette méthode ajuste les poids pour compenser la non-linéarité de ReLU. \forall j=1,\cdots,N_{\ell-1},\forall k=1,\cdots,N_{\ell} \begin{array}{l} W_{jk}\sim\mathcal N\left(0,\sqrt{\frac{2}{ N_{\ell}}}\right),\\ \qquad \qquad \text{ou}\quad\\ W_{jk}\sim \text{Uniforme} \left( -\sqrt{\frac{6}{ N_{\ell}} }, \sqrt{\frac{6}{ N_{\ell}} } \right) \end{array}
- Initialisation des biais. Il est recommandé de mettre à zéro ou avec une petite valeur constante, en fonction du problème.
Mini-Batch
Le Mini-Batch est une technique utilisée dans l’apprentissage automatique. Plutôt que d’ajuster un modèle avec l’ensemble des données d’entraînement d’un coup, on divise les données en petits lots (Mini-Batch). Cela rend l’entraînement plus efficace en permettant au modèle de s’ajuster progressivement. La taille du Mini-Batch influence la vitesse d’entraînement et l’utilisation de la mémoire.
Petits Mini-Batchs::
Avantages::
- Régularisation: Ils introduisent un bruit stochastique qui peut agir comme une forme de régularisation, aidant à prévenir le surapprentissage. (Chaque mini-batch représente une sous-section aléatoire des données totales, prévient la mémorisation des motifs spécifiques à chaque exemple)
- Adaptabilité: Des mises à jour plus fréquentes des poids permettent au modèle de s’ajuster plus rapidement aux changements dans les données.
Inconvénients:
- Calculs plus lents: Les petits mini-batchs peuvent entraîner une utilisation inefficace des ressources matérielles, car les calculs par lot sont généralement moins parallélisables.
- Moins de stabilité: Les petites tailles de mini-batch peuvent rendre l’entraînement plus sujet aux variations, car chaque mini-batch peut introduire un biais significatif (le mini-batch ne représente peut-être pas bien la distribution globale des données).
Grands Mini-Batchs::
Avantages::
- Calculs plus rapides: Les opérations sur des mini-batchs plus grands peuvent être effectuées de manière plus efficace, profitant de la parallélisation sur les GPU.
- Stabilité accrue: Les grands mini-batchs peuvent fournir une estimation plus stable du gradient, réduisant les variations d’une mise à jour à l’autre.
Inconvénients:
- Moins de régularisation: Moins de bruit stochastique peut rendre le modèle plus enclin au surapprentissage, car il a moins d’occasions de généraliser à partir de variations dans les données.
- Utilisation de mémoire accrue: Les grands mini-batchs peuvent nécessiter plus de mémoire, limitant la taille des modèles qui peuvent être entraînés sur certains matériels.
Batch Normalization
Learning rate
Par exemple:
- Commencer par des valeurs classiques
0.1,0.01,0.001et ajustez en conséquence. - Utiliser des optimiseurs adaptatifs tels que Adam, RMSprop, ou AdaGrad qui ajustent automatiquement le learning rate pendant l’entraînement.
- Annealing learning rate. Expérimenter avec des schémas d’abaissement du learning rate au fil du temps, comme la décroissance linéaire ou exponentielle. Cela peut aider à stabiliser l’entraînement à mesure que le modèle se rapproche d’une solution optimale.
Régularisation
La régularisation est une technique essentielle en apprentissage profond pour prévenir le surajustement (Overfitting). L’utilisation combinée de plusieurs méthodes de régularisation peut souvent conduire à de meilleurs résultats. Expérimentez avec différentes approches pour trouver celles qui fonctionnent le mieux pour votre problème spécifique.
Par exemple:
Dropout. Pendant l’entraînement, les neurones sont désactivés de manière aléatoire avec une probabilité donnée à chaque itération. Commencer avec des valeurs de probabilité de désactivation telles que 0,2 à 0,5 et ajustez en fonction des performances du modèle. Cela force le réseau à ne pas dépendre excessivement de certains neurones, ce qui contribue à améliorer sa capacité à généraliser.
DropConnect. Le fonctionnement est similaire à Dropout, mais il désactive de manière aléatoire les connexions entre les neurones au lieu des neurones eux-mêmes.
Augmentation des données. Appliquer des transformations appropriées en fonction de la nature de vos données, telles que la rotation, le recadrage et la translation.
Weight Decay. Ajout d’une pénalité à la fonction de perte (Loss) du modèle L + \frac{\lambda}{2} \sum_{\ell} \|W^{(\ell)}\|_F^2\quad\text{Choix empirique de } \ \lambda.
Optimiseurs
Recommandations générales:
- Adaptive Moment Estimation: Adam (\beta_1, \beta_2). Adam est souvent un bon choix de départ en raison de sa performance robuste dans une variété de tâches. Il offre un bon équilibre entre vitesse de convergence et stabilité. Il combine les idées de la descente de gradient stochastique avec momentum pour accélérer la convergence, et il adapte automatiquement le learning rate pour chaque paramètre du modèle.
Plus précisément, Adam
- Utilise une notion de “momentum” (\beta_1=0.9) pour accélérer la convergence. Cela signifie qu’il prend en compte le gradient actuel ainsi qu’une moyenne pondérée exponentielle des gradients passés pour mettre à jour les poids. Cela aide à atténuer les oscillations et à accélérer la convergence, en particulier dans les zones de faible courbure.
- Adapte automatiquement le learning rate (\beta_2=0.99) pour chaque paramètre du modèle. Il maintient deux moyennes mobiles exponentielles : une pour les gradients et une pour les carrés des gradients. Ces moyennes sont utilisées pour ajuster le learning rate de manière adaptative, accordant une plus grande importance aux paramètres avec des gradients plus importants.
- Effectue une correction du biais pour compenser les effets des moyennes mobiles sur les premières itérations de l’entraînement. Cela permet d’obtenir des estimations de moyenne plus précises.
- RMSprop (Root Mean Square Propagation): RMSprop est un autre optimiseur largement utilisé en apprentissage profond. Il est particulièrement efficace dans les situations où l’adaptation du learning rate est cruciale, telles que les problèmes non stationnaires, bruités, ou lorsque l’échelle des gradients varie considérablement d’un paramètre à l’autre.
Plus précisément, RMSprop
- Adapte le learning rate pour chaque paramètre du modèle en fonction de son historique de gradients.
- Utilise une moyenne mobile de la décroissance quadratique des gradients récents pour ajuster le learning rate. Il divise le learning rate par la racine carrée de cette moyenne, ce qui atténue les changements brusques dans la direction du gradient et permet une convergence plus stable.
- Cette approche permet à RMSprop de converger plus rapidement vers le minimum local tout en évitant les oscillations indésirables dans l’espace des paramètres.
- SGD (Descente de Gradient Stochastique). Simple et souvent utilisé, résilient au bruit. C’est l’optimiseur de base. Mise à jour des paramètres du modèle en se déplaçant dans la direction opposée du gradient de la fonction de perte. Option fiable avec des ajustements de learning rate.
En plus d’Adam, RMSprop et SGD, on peut citer d’autres optimiseurs (liste non exhaustive):
SGD avec Momentum (Descente de Gradient Stochastique avec Momentum) utilise une notion de “momentum” pour accélérer la convergence en prenant en compte les gradients passés lors de la mise à jour des poids. Le momentum aide à atténuer les oscillations et à accélérer la convergence, en particulier dans les zones de faible courbure.
Nadam (Nesterov Adam) combine les idées d’Adam avec la mise à jour du moment Nesterov, ce qui peut aider à éviter les oscillations dans certaines situations.
Adagrad (Adaptive Gradient Algorithm) adapte le learning rate pour chaque paramètre en utilisant une mise à l’échelle basée sur l’historique des gradients. Il est utile pour des problèmes où certaines variables ont des gradients très différents.
Adadelta est une extension d’Adagrad qui tente de remédier à son inconvénient de diminution constante du learning rate au fil du temps en ne conservant qu’une fenêtre fixe de la somme des carrés des gradients.
FTRL (Follow-the-Regularized-Leader) est utilisé dans des tâches de classification en ligne, notamment pour les problèmes de grande échelle. Il tente de minimiser une fonction de perte en suivant un leader régularisé qui est mis à jour à chaque itération.
Pour résumé : Chaque optimiseur a ses propres forces et faiblesses, et le choix dépend souvent de l’expérience empirique et des spécificités du problème que vous essayez de résoudre. Il est courant de tester plusieurs optimiseurs et hyperparamètres pour trouver la combinaison la plus efficace pour votre tâche spécifique.
Clipping des gradients
Le clipping des gradients est une technique couramment utilisée en conjonction avec d’autres méthodes de régularisation et d’optimisation, telles que les algorithmes d’optimisation comme Adam, RMSprop, SGD, ainsi que les méthodes de régularisation comme le dropout ou la régularisation L2. Cette technique vise à limiter la magnitude des gradients pendant la rétropropagation, ce qui peut aider à prévenir les explosions de gradients.
Il existe deux types de clipping des gradients :
Clipping des gradients par norme (norm clipping) : Dans cette approche, la norme totale du gradient est calculée. Si elle dépasse un seuil prédéfini, tous les gradients sont réduits proportionnellement pour ramener la norme à ce seuil.
Clipping des gradients par valeur (value clipping) : Dans cette approche, chaque composante du gradient est vérifiée individuellement. Si la valeur d’une composante de gradient dépasse un seuil prédéfini, elle est tronquée ou réduite à ce seuil.
Arrêt de l’apprentissage
L’arrêt de l’apprentissage, ou le moment où l’on décide de terminer le processus d’entraînement d’un modèle d’apprentissage profond, peut être déterminé de plusieurs manières.
Quelques pistes.
Early Stopping vise à arrêter l’entraînement prématurément pour éviter le surajustement.
Plateau de performances vise à arrêter l’entraînement lorsque les performances du modèle sur l’ensemble de validation atteignent un plateau, indiquant que le modèle a atteint son optimum.
Nombre fixe d’époques. Certains modèles sont entraînés pour un nombre fixe d’époques prédéterminé. Cette approche peut être utilisée si l’on a une idée approximative du nombre d’époques nécessaires pour atteindre la convergence.
Il est recommandé de surveiller régulièrement les performances du modèle sur des ensembles de validation indépendants et d’expérimenter avec différentes stratégies d’arrêt pour trouver celle qui fonctionne le mieux pour votre tâche particulière.
Limitations des perceptrons multicouches (MLP)
Les perceptrons multicouches sont redoutablement efficaces pour résoudre plusieurs catégories de problème : classification, estimation, approximation, reconnaissance etc. Cependant, ces derniers peuvent rapidement s’avérer limités.
Ils ne sont pas adaptés aux données séquentielles. Les MLP ne captent pas les relations séquentielles dans les données, ce qui les rend moins efficaces pour les tâches impliquant des séquences, telles que la modélisation du langage naturel ou la prédiction de séries temporelles. Pour ces tâches, d’autres architectures comme les réseaux de neurones récurrents (RNN) ou les réseaux de neurones récurrents à mémoire à court terme (LSTM) sont souvent préférés.
Le nombre de paramètres peut très vite explosé. Par exemple pour la classification d’image, même en niveau de gris, de taille
512x512=262144pixels. Si on prend 128 neurones dans la première couche cachée, cela nous donne262144*128= 33 554 432poids, auxquels s’ajoutent finalement 128 biais. Notre tout petit modèle devra donc apprendre plus de 33 millions de paramètres. Cela pour seulement 128 neurones et une couche intermédiaire. Si on veut un modèle performant, il faudra plusieurs couches supplémentaires et plus de neurones par couche. Le nombre de paramètres explose!! Cela pose différents problèmes: temps, puissance de calcul, ressources …Dégradation spatiale de l’image. Entrainer un perceptron multicouches requiert de lui fournir les données sous forme de vecteur autrement dit, une colonne. Si la donnée d’entrée est une image matricielle, alors il faudra l’aplatir de sorte à obtenir une colonne. Seulement, dans une image, il existe des liens très forts entre pixels voisins; en aplatissant l’image, on perd ces informations, il devient plus difficile pour le réseau d’interpréter les données pendant la phase d’entrainement.
En résumé, bien que les MLP soient des outils puissants pour de nombreuses tâches d’apprentissage automatique, il est important de reconnaître leurs limites et d’explorer d’autres architectures de réseau neuronal lorsque les MLP ne répondent pas aux exigences de la tâche spécifique. Choisir l’architecture appropriée dépend de la nature spécifique de la tâche que vous souhaitez accomplir et des caractéristiques de vos données.
Quelques architectures importantes.
Voici quelques architectures de réseaux couramment utilisées avec des indications générales sur leurs applications :
Réseaux de neurones feedforward (FNN) : Adaptés aux tâches de classification et de régression simples lorsque les données n’ont pas de dépendance temporelle. Souvent utilisés comme couches dans des architectures plus complexes.
Réseaux de neurones convolutifs (CNN) : Excellents pour la vision par ordinateur et la reconnaissance d’images grâce à leur capacité à capturer des motifs spatiaux locaux. Ils sont également utilisés pour des tâches de traitement de séquences où la localité des données est importante.
Réseaux de neurones récurrents (RNN) : Idéaux pour les tâches avec des données séquentielles, telles que le traitement du langage naturel, la génération de texte et les prévisions temporelles. Les architectures plus avancées comme LSTM et GRU sont souvent préférées pour éviter les problèmes de disparition/exploration de gradients.
Réseaux de neurones récurrents à mémoire à court terme (LSTM) : Similaires aux RNN, mais avec une meilleure capacité à gérer les dépendances à long terme dans les données séquentielles. Utiles pour des tâches où une mémoire à long terme est cruciale.
Réseaux de neurones récurrents à unités récurrentes à portes (GRU) : Alternatives aux LSTM, plus légères en termes de paramètres. Convient à des tâches similaires nécessitant une gestion efficace des dépendances temporelles.
Réseaux de neurones autoencodeurs (AE) : Excellents pour la réduction de dimensionnalité, la reconstruction de données, le débruitage et la génération de données similaires à celles d’un ensemble d’entraînement.
Réseaux de neurones adversariaux (GAN) : Principalement utilisés pour générer des données réalistes. Appliqués dans des domaines tels que la génération d’images, la synthèse de texte et la création de contenu multimédia.
Réseaux de neurones transformer : Excellents pour le traitement de séquences, en particulier dans le domaine du traitement du langage naturel. Sont devenus la base de nombreux modèles performants tels que BERT, GPT et T5.
PARTIE 2. Réseaux de neurones convolutifs (CNN, ConvNet)
Les réseaux de neurones convolutionnels (CNN) sont conçus pour la vision par ordinateur et le traitement d’images. Ils sont particulièrement adaptés à des tâches telles que la classification d’images, la détection d’objets, la segmentation sémantique, la reconnaissance faciale, la reconnaissance d’émotions, et bien plus encore.
Un aspect fondamental de l’architecture des Convolutional Neural Networks (CNN) réside dans la nature de leur entrée. En général, un CNN reçoit en entrée un tenseur de taille (h, w, d), représentant respectivement la hauteur, la largeur (ou width), et le nombre de canaux du volume d’entrée. Cette structure flexible permet aux CNN de traiter efficacement diverses données, telles que des vidéos (d canaux pour les images, plus une dimension temporelle t), des données génomiques (avec un nombre très grand de canaux), ou encore des données structurées en général.
Un CNN est composé de différentes couches, notamment des couches de convolution, des couches de pooling (ou de sous-échantillonnage) et des couches entièrement connectées. Les couches de convolution sont chargées d’appliquer des filtres pour extraire des caractéristiques importantes des images, telles que les bords, les textures, ou des motifs plus complexes. Les couches de pooling servent à réduire la dimensionnalité des données en sélectionnant les valeurs les plus importantes. Les couches entièrement connectées sont utilisées pour combiner les caractéristiques extraites par les couches précédentes afin de prendre des décisions finales, comme la classification des images.
10 Couches de convolution
Les couches de convolution appliquent des filtres (noyaux de convolution) pour extraire des caractéristiques importantes des données en entrée à différentes niveaux d’abstraction et générer des cartes de convolutions, appelées également plans convolutionnels ou feature maps.
Dans une couche de convolution , on applique K\in\mathbb N^* filtre pour extraire des caractéristiques importantes. Puis un biais est ajouté à chaque élément avant l’application de la fonction d’activation.
Les filtres de la première couche d’un CNN sont spécialisés dans la détection de motifs simples tels que des bords, des lignes et des combinaisons de couleurs élémentaires.[Lee et al.], Image issue d’un site
10.1 Couche de convolution d=1 et K=1 filtre
Prenons le cas d’une image en greysacle. L’entrée I est une matrice de taille I_h\times I_w. Mathématiquement, la convolution de l’entrée I avec un filtre de convolution F de taille F_h \times F_w est réalisée en faisant glisser le filtre sur l’image avec un pas de déplacement S=1 et en appliquant la formule suivante à chaque position : Conv(x, y) = \sum_{h=0}^{F_h-1}\sum_{w=0}^{F_w-1} I(x+h, y+w)\cdot F(h,w) où I(x, y) est la valeur du pixel de l’image d’entrée à la position (x, y), et F(h,w) la valeur du filtre à la position (h,w).
[Url du Gif: Convolution avec un noyau 3*3]
La sortie d’une couche de convolution est une carte des caractéristiques C (feature maps), résultat de la convolution auquel un biais b est ajouté de l’ajout avant l’application de la fonction d’activation \sigma. Formellement, la valeur du pixel à la position (x, y) dans la carte des caractéristiques C vaut C(x, y) = \sigma\left(Conv(x, y)+ b\right)=\sigma\left(\sum_{h=0}^{F_h-1}\sum_{w=0}^{F_w-1} I(x+h, y+w) \times F(h,w) + b\right)
Avant l’avènement du Deep Learning, les filtres étaient prédéfinis et sélectionnés par les data scientists, tels que les filtres de détection de contours comme le filtre de Sobel ou le filtre de Prewitt, qui mettaient en évidence les variations abruptes d’intensité entre les pixels adjacents, souvent associées aux contours des objets dans une image. Aujourd’hui, grâce au DL, les paramètres \{F(h,w)\}_{h,w} du filtre et le bias b sont appris par le réseau pendant l’entraînement à l’aide de l’algorithme de rétropropagation. Leur initialisation et leur taille sont donc d’une grande importance.
On peut schématiser une couche de convolution appliquée à une entrée de volume d=1 avec K=1 filtre.
10.2 Padding et Strides
L’opération principale dans une couche de convolution est la convolution elle-même. Cependant, la manière dont la convolution est réalisée peut être ajustée en utilisant deux paramètres : les strides et le padding. (cf: Padding et strides comment les coder en Python.)
Strides.
Les strides S déterminent le déplacement du noyau de convolution à travers es données d’entrée. Ils contrôlent le nombre de pas de déplacement effectués à chaque étape de la convolution, ce qui influence la taille spatiale de la sortie. En augmentant le stride (S>1), la taille de la sortie est réduite. Le paramètre S est un hyperparamètre, ce qui signifie qu’il doit être fixé avant l’entraînement.
📜Choix de S: un compromis.
- L’utilisation de strides plus grands réduit le nombre d’opérations de convolution nécessaires, ce qui peut améliorer les performances lors de l’entraînement et de l’inférence, surtout pour les données d’entrée de grande taille (Ex: une grande image). Cependant, cela peut entraîner une perte d’informations fines car des parties (Ex: de l’image) peuvent être ignorées lors de la convolution.
- Les strides plus grands sont souvent utilisés pour extraire des caractéristiques globales des données d’entrée, tandis que des strides plus petits sont préférables pour capturer des détails fins et des caractéristiques locales.
Padding.
Lors de la convolution, les informations situées aux bords des données d’entrée sont moins prises en compte que celles au centre. Le padding P vise à améliorer le traitement des données d’entrée près des bords en ajoutant des valeurs (généralement des zéros) autour de l’entrée. Le padding peut également permettre de conserver ou d’augmenter la taille spatiale de la sortie de la convolution, surtout lorsque la taille du noyau et les strides réduisent la taille des données d’entrée.
📜On distingue principalement trois approches de padding P.
- Zero-padding : Il s’agit du type de padding par défaut, largement utilisé dans les opérations de convolution, où des zéros sont ajoutés autour des bords de l’entrée pour fournir une zone tampon.
- Custom padding : Ce type de padding offre une flexibilité supplémentaire par rapport au zero-padding, car il permet à l’utilisateur d’ajouter des valeurs spécifiques aux bords selon les besoins de l’application.
- No-padding : Dans ce cas, aucune couche de padding n’est ajoutée, ce qui conduit généralement à une réduction de la taille de la sortie par rapport à l’entrée.
Deux objectifs principaux guident l’utilisation du padding
- Same Padding : L’objectif est de produire une sortie de même taille que l’entrée, ce qui permet une analyse plus précise des bords et une meilleure préservation de l’information spatiale.
- Full Padding : L’objectif est de garantir que chaque pixel de l’entrée ait la même influence sur la sortie, assurant ainsi une égalité de traitement pour tous les pixels de l’image.
Exemple [(No padding,S=2) - (Same padding, S=1)- (Same padding, S=2) - (Full padding, S=1): Gif de Dumoulin & Visin (2016)]
10.3 Couche de convolution avec d canaux et K filtres
Une couche de convolution a pour objectif d’extraire des caractéristiques spécifiques de l’entrée et de produire en sortie PLUSIEURS cartes de caractéristiques (feature maps). Chaque couche utilise généralement un nombre K > 1 de filtres pour effectuer cette opération. Les entrées sont généralement de dimensions I_h \times I_w \times d, où d représente le nombre de canaux.
Les filtres F^{(k)} pour chaque feature maps ont des dimensions F^{(k)}_h \times F^{(k)}_w \times d. Les sorties, les feature maps, C^{(k)} d’une couche de convolution sont calculées selon l’équation suivante : \begin{array}{ll} C^{(k)}(x, y) &= \sigma^{(k)}\left(Conv^{(k)}(x, y)+ b^{(k)}\right)\\ &\\ &=\sigma^{(k)}\left(\sum_{v=0}^{d-1}\sum_{h=0}^{F_h-1}\sum_{w=0}^{F_w-1} I(x+h, y+w,v) \cdot F^{(k)}(h,w,v) + b^{(k)}\right) \end{array} où
- Conv^{(k)}(x, y) est la convolution avec le Filtre k
- I(x+h, y+w,v) l’entrée à la position (x+h, y+w, v),
- \sigma^{(k)} représente la fonction d’activation. La plus courante est ReLU (Rectified Linear Unit), mais d’autres fonctions comme Tanh ou Sigmoid peuvent également être utilisées.
- b^{(k)} est le terme de biais associé au filtre k. Chaque filtre a son propre terme de biais.
Exemple [Exemple: K=2, d=3 avant l’activation: Source du Gif ]
⚠️ Soulignons que K filtres correspondent à K cartes des caractéristiques, features maps.
10.4 Enchaînement d’opérations de convolution
Il est possible d’enchaîner des opérations de convolution avant d’appliquer le pooling. En retardant l’application du pooling, le réseau capture et apprend des informations plus fines et plus détaillées avant de réduire la dimensionnalité de la sortie. Réduire la dimensionnalité trop tôt peut entraîner une perte d’informations précieuses.
La taille de la sortie d’une opération de convolution dépend de plusieurs facteurs, notamment la taille de l’entrée (H_{\text{in}},W_{\text{in}}), la taille des filtres de convolution (F_h^{(k)},F_w^{(k)}), le stride S et le padding P. La taille (H_{\text{out}},W_{\text{out}}) de la sortie vaut \Big(H_{\text{out}},W_{\text{out}}\big) = \left( \left\lfloor \frac{H_{\text{in}} - F^{(k)}_h + 2P}{S} \right\rfloor + 1,\left\lfloor \frac{W_{\text{in}} - F^{(k)}_w + 2P}{S} \right\rfloor + 1\right), La profondeur de la sortie sera égale au nombre de filtres de convolution: D_{out}=K.11 Couche de pooling
Dans les architectures de réseaux de neurones convolutionnels (CNN), il est courant d’ajouter une couche de pooling (ou d’agrégation) après les couches de convolution. Cette étape vise principalement à réduire la dimensionnalité des cartes de caractéristiques (feature maps) extraites par les couches convolutionnelles. En général, le pooling est suivi de couches de convolution supplémentaires dans une architecture typique de CNN.
Le pooling conserve uniquement les caractéristiques les plus significatives à travers une fenêtre spécifique, favorisant ainsi l’extraction des caractéristiques les plus dominantes des données en entrée. On obtient en sortie d’une couche de pooling, le même nombre de feature maps qu’en entrée mais considérablement compressées.
Formellement, une fenêtre de taille prédéfinie se déplace à travers l’image en appliquant une opération spécifique dans chaque région de la fenêtre. Contrairement aux filtres de convolution, le pooling n’utilise pas de paramètres à apprendre. Le pooling peut être associé à un stride et même parfois à du padding. Le stride contrôle la distance entre les déplacements de la fenêtre lors de l’opération de pooling. Le padding peut être utile pour conserver certaines informations aux bords de l’image, surtout si le stride est grand et risque de perdre des informations sur les bords de l’image.
Les opérations de pooling les plus courantes sont le max pooling et l’average pooling, qui sélectionnent respectivement le maximum ou la moyenne des valeurs dans une région donnée de la carte caractéristique. Le max pooling est souvent préféré pour sa capacité à capturer les caractéristiques les plus importantes et à maintenir l’invariance aux translations, tandis que l’average pooling peut aider à réduire le bruit et à conserver une certaine information statistique sur les caractéristiques.[Exemple Max/average pooling avec une fenêtre 2x2 et S=2]
🙂 La réduction de la dimensionnalité aide à réduire le nombre de paramètres dans le modèle, ce qui diminue la charge de calcul nécessaire pour l’entraînement et l’inférence. Cela contribue à prévenir l’overfitting et favorise une généralisation efficace du modèle. De plus, l’utilisation du pooling permet au CNN d’être moins sensible aux petites variations spatiales, car il extrait les caractéristiques les plus importantes de manière indépendante de leur position précise dans les données en entrée.
12 Couches entièrement connectées (MLP)
Généralement, l’architecture d’un Convolutional Neural Network est sensiblement la même. Les K cartes de caractéristiques (feature maps) obtenues à la sortie des couches convolutionnelles et de pooling sont transformées en un vecteur unidimensionnel, c’est-à-dire qu’elles sont “aplaties” ou “mise à plat” par une couche entièrement connectée. Puis d’autres couches entièrement connectées prennent en entrée ce vecteur pour le combiner et prendre des décisions finales, telles que la classification des images.
Le MLP (Multilayer Perceptron) est constitué de couches entièrement connectées. Les K cartes de caractéristiques de taille (H,W) sont d’abord transformées en un vecteur unidimensionnel par un processus souvent appelé “mis à plat”. Ensuite, la première couche du MLP prend ce vecteur de valeurs pour le transformer en un autre vecteur unidimensionnel \text{Out} de taille D. Cette transformation est réalisée par l’ajout d’une couche entièrement connectée. Chaque neurone \text{Out}_j, où j = 1, \dots, J, dans ce vecteur est une combinaison des caractéristiques extraites des K cartes de caractéristiques en entrée, pondérées par des poids correspondants, et en ajoutant le biais avant d’appliquer la fonction d’activation \sigma :
\text{Out}_j = \sigma\left( \sum_{k=1}^{K} \sum_{h=1}^{H} \sum_{w=1}^{W} W^{(j)}_{h,w,k} \cdot\text{maps}^{(k)}(h,w) + b^{(j)} \right)
Où : - \text{maps}^{(k)}(h,w) représente la valeur à la position (h,w) de la carte de caractéristiques k en entrée. - W^{(j)}_{h,w,k} sont les poids associés à la connexion entre la position (h,w) de la carte de caractéristiques k et du neurone \text{Out}_j du vecteur. - b^{(j)} est le biais du neuron e\text{Out}_j du vecteur. - \sigma est une fonction d’activation appliquée à la somme pondérée et au biais.
Mise à plat. 🔵Py. Gestion sous Python. En PyTorch, les dimensions des tenseurs x pour les features maps en entrée ou en sortie d’une couche sont organisées de la manière suivante :
x.size(0): Correspond à la taille du batch.x.size(1): Indique le nombre de canaux.x.size(2): Représente la hauteur des features maps.x.size(3): Correspond à la largeur des features maps.
Pour convertir un tensor x en un vecteur unidimensionnel afin qu’il puisse être utilisé en entrée de la première couche entièrement connectée, on utilise la méthode view() de PyTorch. Cette méthode permet de remodeler les tenseurs selon des dimensions spécifiées.
Lorsque vous utilisez view(-1, N), PyTorch ajuste automatiquement la taille de la première dimension de sorte que le nombre total d’éléments (déterminé par le produit des dimensions restantes) reste inchangé après la transformation. Cela facilite la manipulation des données, notamment lors de la construction des couches entièrement connectées dans les réseaux de neurones. Pour un exemple : Python.
Les couches suivantes entièrement connectées permettent d’effectuer des tâches telles que la classification ou la régression.
[Premier CNN: LeNet-5]
Architecture LeNet-5
Entrée. Image en niveaux de gris de taille 32x32x1.
C1. Couche de convolution avec 6 filtres de taille 5x5, sans padding, un stride de 1 et activation par la fonction \tanh. Résultat : 6 feature maps de taille 28x28.
S2. Couche de pooling (sous-échantillonnage) avec une fenêtre de 2x2, sans padding, un stride de 2 et activation par la fonction \tanh. Résultat : 6 feature maps de taille 14x14.
C3. Couche de convolution avec 16 filtres de taille 5x5, sans padding, un stride de 1 et activation par la fonction \tanh. Résultat : 16 feature maps de taille 10x10.
S4. Couche de pooling avec une fenêtre de 2x2, sans padding, un stride de 2 et activation par la fonction \tanh. Résultat : 16 feature maps de taille 5x5.
C5. Couche entièrement connectée (flattened layer) avec 120 neurones et activation par la fonction \tanh.
F6. Couche entièrement connectée avec 84 neurones et activation par la fonction \tanh.
Sortie. Couche entièrement connectée avec 10 neurones correspondant aux probabilités d’avoir les nombres de 0 à 9, activées par la fonction \text{Softmax}
13 CNN : quelques pistes pour la pratique
13.1 Taille des Filtres
Les tailles habituelles des noyaux pour les réseaux de neurones convolutifs (CNN) varient en fonction de l’architecture du réseau et de la tâche spécifique à laquelle le réseau est destiné. Cependant, certaines tailles de noyaux sont plus courantes que d’autres et sont souvent utilisées en raison de leur efficacité dans différents scénarios. En général, les noyaux plus petits sont privilégiés car ils sont plus efficaces en termes de calculs et peuvent capturer des motifs plus fins. Cependant, les noyaux plus grands peuvent être nécessaires dans certaines situations pour capturer des caractéristiques plus globales.
Tailles courantes.
Taille 3x3. C’est l’une des tailles de filtres les plus courantes dans de nombreuses architectures de CNN en raison de leur capacité à capturer des motifs fins tout en étant suffisamment petits pour maintenir des calculs efficaces.
Taille 5x5. Ces filtres sont utilisés pour capturer des motifs plus grands dans les images. Ils sont utiles pour extraire des caractéristiques plus globales à partir des images.
Taille 7x7 et plus grands. Pour des architectures profondes et des tâches spécifiques nécessitant une analyse plus large des caractéristiques, des filtres plus grands comme 7x7 ou même plus grands peuvent être utilisés.
Taille 1x1. Bien que cela puisse sembler contre-intuitif, les filtres 1x1 sont parfois utilisés dans les CNN pour effectuer des opérations de réduction de dimension et de non-linéarité. Ils sont souvent utilisés dans les couches de convolution de bottleneck pour réduire la dimension de l’espace des caractéristiques.
La taille du filtre est un hyperparamètre, il n’est pas appris! Pour un exemple : Python.
13.2 Initialisation des filtres
Les poids des filtres sont des paramètres appris lors de l’entraînement. Leur initialisation est cruciale. Une initialisation adéquate peut accélérer la convergence de l’algorithme d’optimisation et aider à éviter les problèmes tels que le gradient explosif ou le gradient qui s’éteint. Pour un exemple : Python.
Méthodes courantes d’initialisation.
Une initialisation aléatoire des filtres favorise la diversité des filtres dans les couches initiales du réseau, ce qui permet au réseau d’apprendre une gamme variée de caractéristiques visuelles dès le début de l’entraînement.
\bullet Initialisation Glorot et Bengio. Cette initialisation est conçue pour maintenir les gradients stables pendant la rétropropagation, ce qui favorise une convergence plus rapide et plus efficace de l’apprentissage.
Si la couche précédente à K_{\text{in}} cartes de caractéristiques (feature maps ou canaux pour l’entrée), alors les poids des K_{\text{out}} filtres de taille F_{h} \times F_{w}\times K_{\text{in}} de la couche de convolution suivante sont initialisés de la façon suivante pour chaque filtre k=1,\cdots,K_{\text{out}}
\begin{array}{l}
F^{(k)}(h,w)\sim \text{Uniforme}\left( -\sqrt{\frac{6}{K_{\text{in}} + K_{\text{out}}}}, \sqrt{\frac{6}{K_{\text{in}} + K_{\text{out}}}} \right),\\
\qquad\qquad \qquad \qquad \text{ou}\quad\\
F^{(k)}(h,w)\sim\text{Normale}\left(0, {\frac{2}{K_{\text{in}} + K_{\text{out}}}} \right)
\end{array}
La distribution normale est souvent préférée pour l’initialisation des poids dans les CNN en raison de ses propriétés d’initialisation plus douces.
\bullet Initialisation He. Pour chaque filtre k=1,\cdots,K_{\text{out}} \begin{array}{l} F^{(k)}(h,w)\sim \text{Uniforme}\left( -\sqrt{\frac{6}{K_{\text{in}} }}, \sqrt{\frac{6}{K_{\text{in}}}} \right),\\ \qquad\qquad \qquad \qquad \text{ou}\quad\\ F^{(k)}(h,w)\sim\text{Normale}\left(0, {\frac{2}{K_{\text{in}} }} \right) \end{array}
Pour les tâches de transfert d’apprentissage (Transfert learning), les filtres peuvent être initialisés à partir d’un modèle pré-entraîné sur un ensemble de données similaire. Par exemple, utiliser un modèle pré-entraîné sur ImageNet comme point de départ pour l’entraînement sur une tâche spécifique.
13.3 Taille des Fenêtres dans le pooling
En pratique, le choix de la taille du pooling dans les CNN est souvent guidé par plusieurs considérations empiriques et expérimentales. Le choix de la taille du pooling dépend d’un équilibre entre la réduction de la dimensionnalité, la préservation des informations importantes et les considérations architecturales du réseau. Il est souvent déterminé par une combinaison d’expérimentations, d’observations empiriques et de bonnes pratiques de conception de réseau.
Quelques éléments pistes.
Les tailles de pooling les plus couramment utilisées sont généralement de 2x2 ou 3x3 avec un stride de 2 pour réduire la résolution spatiale de manière significative tout en préservant les informations importantes.
La taille du pooling est parfois choisie en fonction de la taille des filtres de convolution utilisés dans le réseau. Par exemple : si les filtres de convolution sont relativement petits, par exemple 3x3, alors un pooling 2x2 peut être utilisé pour réduire la résolution spatiale de manière significative.
Dans les architectures profondes, des tailles de pooling plus importantes peuvent être utilisées dans les couches initiales pour réduire la résolution spatiale, tandis que des tailles de pooling plus petites peuvent être utilisées dans les couches plus profondes pour maintenir la précision des informations.
Pour un exemple : Python.
🔵Py. CODE PYTHON
Initialisation Gloriot
# Importation des modules
import torch
import torch.nn as nn
def init_weights_glorot(m):
if isinstance(m, nn.Conv2d):
nn.init.xavier_uniform_(m.weight.data)
if m.bias is not None:
nn.init.zeros_(m.bias.data)
elif isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight.data)
if m.bias is not None:
nn.init.zeros_(m.bias.data)La fonction def init_weights_glorot(m) est une fonction utilisée pour initialiser les poids de manière à maintenir des gradients stables lors de la rétropropagation dans les réseaux de neurones. m représente un module de réseau de neurones.
if isinstance(m, nn.Conv2d)vérifie si le modulemest une couche de convolution.elif isinstance(m, nn.Linear)vérifie si le modulemest une couche linéaire.if m.bias is not Nonevérifie si la couche a un biais.nn.init.xavier_uniform_(m.weight.data)initialise les poids du modulemen utilisant la fonctionxavier_uniform_du modulenn.initde PyTorch. Les poids sont accessibles viam.weight.data.nn.init.zeros_(m.bias.data)initialise les biais à zéro en utilisant la fonctionzeros_du modulenn.initde PyTorch. Les biais sont accessibles viam.bias.data.
Initialisation He
# Importation des modules
import torch
import torch.nn as nn
def init_weights_he(m):
if isinstance(m, nn.Conv2d):
nn.init.kaiming_uniform_(m.weight.data, nonlinearity='relu')
if m.bias is not None:
nn.init.zeros_(m.bias.data)
elif isinstance(m, nn.Linear):
nn.init.kaiming_uniform_(m.weight.data, nonlinearity='relu')
if m.bias is not None:
nn.init.zeros_(m.bias.data)Initialisation HE. La fonction def init_weights_he(m) est une fonction utilisée pour initialiser les poids avec la méthode He. m représente un module de réseau de neurones.
if isinstance(m, nn.Conv2d)vérifie si le modulemest une couche de convolution.elif isinstance(m, nn.Linear)vérifie si le modulemest une couche linéaire.if m.bias is not Nonevérifie si la couche a un biais.nn.init.kaiming_uniform_(m.weight.data, nonlinearity='relu')initialise les poids du modulemen utilisant la fonctionnn.init.kaiming_uniform_du modulenn.initde PyTorch. Cette fonction prend en compte la non-linéarité de la fonction d’activation. Les poids sont accessibles viam.weight.data.nn.init.zeros_(m.bias.data)initialise les biais à zéro en utilisant la fonctionzeros_du modulenn.initde PyTorch. Les biais sont accessibles viam.bias.data.
Le Padding et le stride
Voilà comme sont codés le stride et le padding sous Python avec torch.
import torch
import torch.nn as nn
# Fixer la seed pour la reproductibilité
torch.manual_seed(0)
# Supposons une entrée avec une taille de 5x5x4 et 64 filtres de taille 3x3x4
input_size = 5
kernel_size = 3
in_channels = 4 # spécifier le nombre de canaux d'entrée
out_channels = 64 # spécifier le nombre de filtres distincts
stride_value = 2 # spécifier la valeur du stride
# Créer des données d'entrée (exemple Jouet random)
batch_size = 1
input_data = torch.randn(batch_size, in_channels, input_size, input_size)
# Créer une couche de convolution avec un padding "same padding"
# Il faut calculer le padding manuellement pour maintenir la taille de l'entrée
same_padding = (kernel_size - 1) // 2
conv_same_padding = nn.Conv2d(in_channels, out_channels, kernel_size, padding=same_padding, stride=1)
# Créer une couche de convolution avec "Custom padding" et un stride de 2
padding_value = 2 # padding personnalisé
conv_custom_padding = nn.Conv2d(in_channels, out_channels, kernel_size, padding=padding_value, stride=stride_value)
# Créer une couche de convolution sans padding et un stride de 2
conv_no_padding = nn.Conv2d(in_channels, out_channels, kernel_size, padding=0, stride=stride_value)
# Créer une couche de convolution avec un padding "full padding" et un stride de 2
full_padding = kernel_size - 1
conv_full_padding = nn.Conv2d(in_channels, out_channels, kernel_size, padding=full_padding, stride=stride_value)
# Passer les données à travers les couches de convolution
output_same_padding = conv_same_padding(input_data)
output_custom_padding = conv_custom_padding(input_data)
output_no_padding = conv_no_padding(input_data)
output_full_padding = conv_full_padding(input_data)
# Vérifier les formes des sorties
print("Output shape (same padding):", output_same_padding.shape)
print("Output shape (custom padding):", output_custom_padding.shape)
print("Output shape (no padding):", output_no_padding.shape)
print("Output shape (full padding):", output_full_padding.shape)
# Accéder aux valeurs de la carte des caractéristiques (feature maps) par exemple pour le filtre 8 et les afficher
filter_8_output = output_same_padding[:, 8, :, :]
print("Filter 8 Feature Map Output:")
print(filter_8_output)13.4 Création de la classe CNN
Construisons notre modèle CNN avec deux couches de convolution (conv1 et conv2) suivies de deux couches entièrement connectées (fc1 et fc2) à l’aide d’une classe .
Le code ci-dessous définit une classe CNN en utilisant le module nn.Module de PyTorch. Cette classe représente un réseau de neurones convolutionnel (CNN) simple avec deux couches de convolution et deux couches entièrement connectées où
class CNN(nn.Module)définit une nouvelle classe appeléeCNNqui hérite denn.Module.def __init__(self)définit la méthode d’initialisation de la classeCNN.super(CNN, self).__init__()appelle la méthode d’initialisation de la classe parentenn.Module.def forward(self, x)définit la méthodeforwardqui spécifie comment les données sont propagées à travers le réseau.return xrenvoie la sortie finale du réseau.
import torch.nn.functional as F
class CNN(nn.Module):
def __init__(self, h, w, c,out):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(in_channels=c, out_channels=16, kernel_size=3, stride=1, padding=1)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
self.fc1 = nn.Linear(32 * (h//4) * (w//4), 512) # Utilisation de la taille de sortie après deux max pooling
self.fc2 = nn.Linear(512, out)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 32 * (x.size(2)//4) * (x.size(3)//4)) # Calcul de la taille de l'entrée pour la couche linéaire
x = F.relu(self.fc1(x))
x = self.fc2(x)
x = F.softmax(x, dim=1) # Appliquer le softmax sur les logits
return xPrécision sur le code
Plus précisément, dans ce code, les commandes sont exécutées dans l’ordre ci-dessous
- Convolution et Pooling avec
self.conv1:self.conv1 = nn.Conv2d(in_channels=c, out_channels=16, kernel_size=3, stride=1, padding=1)Définition de la couche de convolutionself.conv1.x = F.relu(self.conv1(x))Application de la convolution à l’entréexen utilisantself.conv1(x)et activation avec ReLU à la sortie de la convolution.x = F.max_pool2d(x, kernel_size=2, stride=2)Application d’ un max pooling 2x2 et un stride de 2.
- Convolution et Pooling avec
self.conv2:self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)Définition de la couche de convolutionself.conv2.x = F.relu(self.conv2(x))Application de la convolution à l’entréexen utilisantself.conv2(x)et activation avec ReLU à la sortie de la convolution.x = F.max_pool2d(x, kernel_size=2, stride=2)Application d un max pooling 2x2 et un stride de 2.
- Mise à plat des caractéristiques :
x = x.view(-1, 32 * (x.size(2)//4) * (x.size(3)//4))Utilisation dex.view()pour aplatir les caractéristiques en ajustant la taille de la sortie précédente pour être compatible avec les couches entièrement connectées.
- Couches entièrement connectées :
self.fc1 = nn.Linear(32 * (h//4) * (w//4), 512)Définition de la couche entièrement connectéeself.fc1avec la taille de l’entrée spécifiée.x = F.relu(self.fc1(x))Application de la couche entièrement connectée à l’entréexen utilisantself.fc1(x)et activation avec ReLU à la sortie de la convolution.self.fc2 = nn.Linear(512, out)Définition de la couche entièrement connectéeself.fc2avec la taille de l’entrée et la taille de sortie spécifiée pour la classification finale.
x = self.fc2(x)Application de la couche entièrement connectée à l’entréexen utilisantself.fc2(x).x = F.softmax(x, dim=1)Application d’un Softmax.
Définition d’une classe en Python
En programmation orientée objet (POO), une classe est une structure fondamentale qui regroupe des données et des méthodes agissant sur ces données. La classe CNN représente une architecture spécifique de réseau de neurones utilisée pour des tâches telles que la classification d’images.
Les données associées à une classe sont appelées des attributs. Dans notre classe CNN, les attributs incluent différentes couches du réseau, telles que les couches de convolution (self.conv1, self.conv2), les couches entièrement connectées (self.fc1, self.fc2), etc. Ces attributs contiennent les paramètres (poids et biais) appris lors de l’entraînement du réseau.
Les classes contiennent des méthodes qui opèrent sur les données de la classe. Dans la classe CNN, la méthode principale est forward(self, x). Cette méthode définit comment les données sont propagées à travers le réseau. Notre classe spécifie également les opérations à effectuer sur les données à chaque couche du réseau, y compris les opérations de convolution, de pooling et les opérations linéaires.
Les classes offrent un mécanisme d’encapsulation, permettant de regrouper les données et les méthodes qui agissent sur ces données dans une seule entité, la classe. Dans notre exemple, les différentes couches, les fonctions d’activation et les opérations sont encapsulées dans la classe CNN, facilitant ainsi la gestion et la manipulation du réseau.
Une classe peut hériter des attributs et des méthodes d’une autre classe, ce qui permet d’étendre et de spécialiser les fonctionnalités. Le mécanisme d’héritage favorise la réutilisabilité du code et la hiérarchie de classes. Notre classe CNN hérite de la classe nn.Module, une classe de base dans la bibliothèque PyTorch. La classe nn.Module est essentielle pour la construction de réseaux de neurones et fournit de nombreuses fonctionnalités utiles pour la gestion des paramètres, la propagation avant, et d’autres aspects de l’entraînement et de l’évaluation des modèles.
Création d’une instance du modèle CNN
Création d’une instance du modèle CNN et application de l’initialisation Glorot (Xavier) à ses poids à l’aide de la méthode apply().
# Par exemple pour une entrée de taille
h, w, c, out = 32, 32, 3, 10
model_glorot = CNN(h,w,c, out)
model_glorot.apply(init_weights_glorot)Création d’une autre instance du modèle CNN et application de l’initialisation He à ses poids à l’aide de la méthode apply().
# Par exemple pour une entrée de taille
h, w, c, out = 32, 32, 1, 3
model_he = CNN(h,w,c,out)
model_he.apply(init_weights_he)Entraînement d’un CNN : Exemple MNIST
Ceci est un exemple simple, non optimisé, où des techniques telles que l’arrêt anticipé (early stopping) et la séparation des données en ensembles d’entraînement, de validation et de test sont utilisées, bien qu’elles auraient pu être évitées. Ce code est simplement destiné à illustrer différentes façons de coder en Python.
Importation des modules nécessaires
import torch
import random
import numpy as np
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader, random_split
import torch.nn.functional as F
# Fixer la seed pour la reproductibilité
seed = 42
torch.manual_seed(seed)
random.seed(seed)
np.random.seed(seed)Définition de la transformation des données
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])Chargement des données MNIST
mnist_dataset = MNIST(root='./data', train=True, download=True, transform=transform)Division des données en ensembles d’entraînement, de validation et de test
train_size = int(0.8 * len(mnist_dataset))
val_size = int(0.1 * len(mnist_dataset))
test_size = len(mnist_dataset) - train_size - val_size
train_dataset, val_dataset, test_dataset = torch.utils.data.random_split(
mnist_dataset, [train_size, val_size, test_size])Création des DataLoader pour charger les données en Batch pendant l’entraînement et spécification de la taille du batch, le mélange aléatoire des données,..
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)Définition de la classe CNN pour notre exemple
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1, padding=1)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
self.fc1 = nn.Linear(32 * 7 * 7, 512)
self.fc2 = nn.Linear(512, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 32 * 7 * 7) # Aplatir les caractéristiques pour les couches entièrement connectées
x = F.relu(self.fc1(x))
x = self.fc2(x)
return xInitialisation Glorot et Bengio avec distribution normale
def init_weights_glorot_normal(m):
if isinstance(m, nn.Conv2d):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
nn.init.zeros_(m.bias.data)
elif isinstance(m, nn.Linear):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
nn.init.zeros_(m.bias.data)Création du modèle et initialisation des poids
model = CNN()
model.apply(init_weights_glorot_normal)Définition de la fonction de perte et de l’optimiseur pour la tâche de Multi-classification
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)Entraînement du modèle avec arrêt anticipé sur l’ensemble de validation
num_epochs = 10
patience = 3 # Nombre d'époques de patience avant l'arrêt anticipé
best_val_loss = float('inf')
count = 0 # Compteur pour le critère d'arrêt anticipé
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for images, labels in train_loader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
## Clipping des gradients (mais pas utile ici)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
running_loss += loss.item() * images.size(0)
epoch_loss = running_loss / len(train_loader.dataset)
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}')
# Évaluation du modèle sur l'ensemble de validation
model.eval()
val_loss = 0.0
with torch.no_grad():
for images, labels in val_loader:
outputs = model(images)
loss = criterion(outputs, labels)
val_loss += loss.item() * images.size(0)
val_loss /= len(val_loader.dataset)
print(f'Validation Loss: {val_loss:.4f}')
# Vérification si l'arrêt anticipé est nécessaire
if val_loss < best_val_loss:
best_val_loss = val_loss
count = 0
else:
count += 1
if count >= patience:
print("Early stopping déclenché. Entraînement arrêté.")
breakEvaluation du modèle et affichage des 10 premières prédictions du jeu de données de test et des 10 vrais labels associés sur le test
model.eval()
correct_test = 0
total_test = 0
predictions = []
# Affichage des 10 premières prédictions du jeu de données de test
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total_test += labels.size(0)
correct_test += (predicted == labels).sum().item()
# Ajout des prédictions à la liste
predictions.extend(predicted.numpy())
# Sortir de la boucle après avoir traité les 10 premiers lots
if total_test >= 10:
break
# Convertir les prédictions en un tableau NumPy
predictions = np.array(predictions)
# Récupérer les vraies étiquettes du jeu de données de test
actual_number = []
for images, labels in test_loader:
actual_number.extend(labels.numpy())
if len(actual_number) >= 10:
break
# Convertir les vraies étiquettes en un tableau NumPy
actual_number = np.array(actual_number)
# Afficher les 10 premières prédictions et leurs vraies étiquettes
print("10 premières predictions:")
print(predictions[:10])
print("10 vrais labels associé:")
print(actual_number[:10])
# Calculer et afficher la précision du modèle sur l'ensemble de test
accuracy_test = correct_test / total_test
print(f'Accuracy on test set: {accuracy_test:.4f}')Pour aller plus loin: liens
Le papier de la débrouille!! Efficient BackProp (2012). LeCun Y.A., Bottou L., Orr G.B. & Müller K-R. (2012). Psychological Review, 65(6), 386–408. pdf
Super formation en ligne et gratuite :Formation Fidle.