Introduction
Préambule
Voici le premier article de mon blog.
Je m’appelle Sébastien. De 2017 à 2021, j’ai été tech lead et spécialiste en deep learning dans une équipe de proof of concept, au sein d’un pôle innovation.
Mon rôle était de faire de notre équipe l’une des pionnières de l’IA dans l’entreprise.
En parallèle, j’étais responsable d’une documentation technique destinée à enseigner les bases du deep learning, et je présentais nos avancées lors d’événements internes, de journées portes ouvertes ou d’interventions dans des écoles.
Cet article, que j’ai écrit petit à petit sur mon temps libre au fil des années, résume ce que j’ai enseigné à des ingénieurs et à des étudiants pendant tout ce temps.
On y part des toutes premières bases du deep learning, en se concentrant sur deux types de réseaux de neurones : les MLP et les CNN.
À l’heure où les IA basées sur le deep learning ont envahi le monde, pourquoi ne pas revenir aux fondamentaux ?
Que contient cette documentation ?
Cette documentation est une introduction au deep learning.
Elle s’adresse à celles et ceux qui veulent découvrir les réseaux de neurones et tout l’univers qui gravite autour.
Ce n’est pas un tutoriel, c’est plutôt un cours. Le but est de vous donner les connaissances nécessaires pour attaquer n’importe quel tutoriel de framework de deep learning.
L’idée est de se familiariser avec l’environnement, les concepts et le vocabulaire des réseaux de neurones.
Vous trouverez ici une description du deep learning, comment construire le réseau de neurones qui correspond à vos besoins, ainsi que quelques exemples de code écrits en Python avec Keras.
Keras est une librairie open source de réseaux de neurones qui tourne par-dessus d’autres librairies de réseaux de neurones. Ici, nous utiliserons Keras par-dessus Tensorflow.
Qu’est-ce que le deep learning ?
Avant de parler de deep learning, parlons un peu d’intelligence artificielle et de machine learning.
Personnellement, je définirais l’intelligence artificielle comme l’ensemble des théories et des techniques mathématiques et algorithmiques mises en œuvre pour simuler l’intelligence humaine.
On n’est pas très loin de la définition du Cambridge Dictionary : « L’étude de la manière de concevoir des machines dotées de certaines des qualités de l’esprit humain, comme la capacité à comprendre le langage, à reconnaître des images, à résoudre des problèmes et à apprendre ».
On trouve des tas de définitions de l’IA, mais elles décrivent toutes le même concept : la capacité d’une machine à imiter un comportement humain intelligent.
Le but est de rendre la machine capable d’atteindre ses objectifs comme le ferait un humain.
Il existe plusieurs façons d’y arriver. L’une d’elles consiste à permettre à une machine ou à un système d’apprendre et de s’améliorer par l’expérience, sans avoir été explicitement programmé pour ça : c’est le machine learning.
Le machine learning est donc une application de l’IA.
Et il existe à son tour plusieurs façons de faire du machine learning. L’une d’elles, une sous-catégorie du machine learning, s’inspire du cerveau humain et de sa façon de penser.
Directement inspirée des réseaux de neurones biologiques, la méthode de machine learning dont nous allons parler est celle qui utilise des réseaux de neurones artificiels. C’est ce qu’on appelle le deep learning.
Là encore, on n’est pas loin de la définition du Cambridge Dictionary : « Un type d’intelligence artificielle qui utilise des algorithmes (= des ensembles d’instructions ou de règles mathématiques) basés sur le fonctionnement du cerveau humain ».
En réalité, le deep learning est assez difficile à définir, parce que la notion a évolué décennie après décennie depuis les années 80.
On peut le résumer comme du machine learning qui utilise des réseaux de neurones avec un grand nombre de paramètres et de couches, et qui appartiennent à l’une des architectures de réseau fondamentales :
- Multilayer Perceptrons (MLP)
- Convolutional Neural Networks (CNN)
- Recurrent Neural Networks (RNN)
Historiquement, le premier modèle de perceptron a été inventé par Frank Rosenblatt en 1957, inspiré par les théories cognitives de Friedrich Hayek et de Donald Hebb.
C’est en 1974 que Paul Werbos propose le premier modèle de réseau de neurones utilisant des Multilayer Perceptrons, modèle ensuite développé et perfectionné par David Rumelhart en 1986.
C’est justement le premier type de modèle dont nous allons parler.
À quoi ça sert ?
Avant d’expliquer comment ça marche et comment s’en servir, c’est toujours bien de savoir à quel besoin ça répond.
D’après mon expérience et ce que j’en ai fait, je résumerais l’intérêt des réseaux de neurones en une phrase : « prédire une sortie à partir d’une entrée, en se basant sur un dataset d’entraînement ». C’est ce qu’on appelle l’apprentissage supervisé.
Prenons un exemple simple pour illustrer cette phrase. Vous marchez dans la rue et vous croisez un chat. Ce qui est intéressant, c’est que vous savez évidemment que c’est un chat, alors que vous ne l’avez jamais vu de votre vie. S’il s’agissait d’un animal, ou de n’importe quoi d’autre que vous aviez déjà vu, ça paraîtrait normal que vous soyez capable de le reconnaître. Mais quand vous voyez quelque chose qui ressemble à autre chose que vous connaissez déjà, vous ne le reconnaissez pas : vous le devinez. Vous êtes-vous déjà demandé comment vous arrivez à deviner ce que sont tant de choses que vous voyez chaque jour, alors que vous ne les avez jamais vues auparavant ?
La réponse est simple : votre cerveau est entraîné. Quand vous voyez quelque chose, l’image captée par vos yeux est envoyée à votre cerveau, et votre réseau de neurones traite l’information pour deviner ce que vous êtes en train de voir. Vous apprenez, puis vous êtes entraîné. Le fonctionnement biologique réel est bien sûr beaucoup plus compliqué que ça, mais cette explication suffit pour comprendre ce dont se sont inspirés les premiers chercheurs en deep learning.
On peut dire que l’image reçue par votre cerveau est l’entrée (input) envoyée à votre réseau de neurones, et que ce que vous devinez en est la sortie (output).
En suivant cette logique, si on arrive à créer un réseau de neurones artificiel, il devrait être capable de reconnaître quelque chose qu’il n’a jamais vu, en se basant sur le dataset d’entraînement qu’on lui a donné, exactement comme le fait votre cerveau.
On imagine alors facilement que les possibilités sont infinies.
Vous voulez reconnaître des lettres manuscrites ? Construisez correctement votre modèle de réseau de neurones, donnez-lui une énorme quantité de lettres manuscrites, puis essayez de lui faire reconnaître une lettre qu’il n’a jamais vue. Si votre modèle est bien construit et que vous avez suffisamment de données pour l’entraînement, ça marchera.
C’est grâce à cette méthode que les robots arrivent aujourd’hui à lire facilement des textes imprimés. En donnant à un réseau de neurones autant de polices et de lettres manuscrites que possible, ça devient facile de déchiffrer le texte contenu dans une image.
Et c’est la même chose si vous voulez reconnaître des visages, des voitures, ou n’importe quoi d’autre.
Cette méthode peut bien sûr s’appliquer à n’importe quel type de données, mais il est intéressant de savoir qu’aujourd’hui, pour reconnaître des patterns dans des images, on obtient de bien meilleurs résultats avec des réseaux de neurones qu’avec du traitement d’image classique.
Et si on va plus loin, la prédiction de données n’est pas si éloignée de la reconnaissance. Si je vous dis « 2, 4, 6, 8, et ? », vous allez me répondre « 10 ». C’est aussi grâce à votre cerveau entraîné que vous avez pu deviner la suite de cette séquence logique.
Okay, mais c’est quoi, un réseau de neurones ? À quoi ça ressemble ? Est-ce que c’est facile à implémenter ?
Multilayer Perceptrons (MLP)
Construire son modèle : la reconnaissance de formes
Commençons par le type de modèle le plus classique et le plus simple à comprendre : le Multilayer Perceptron.
Voici à quoi ressemble un MLP :
Il s’agit ici d’un Multilayer Perceptron à 5 couches. Il a 25 neurones dans sa couche d’entrée, 5 neurones dans sa couche de sortie, et 3 couches intermédiaires. Ces couches intermédiaires sont appelées des couches cachées (hidden layers).
Ce modèle est donc conçu pour trouver une réponse parmi 5 possibilités, à partir d’une donnée que l’on peut décrire en 25 parties.
Par exemple, on va utiliser ce modèle pour reconnaître des formes simples de 5x5 parmi 5 formes différentes.
Voici à quoi ressembleraient ces formes :
Construire ce modèle avec Keras
|
|
On y voit la déclaration des 5 couches, chacune avec son nombre de neurones. Les notions de fonction d’activation, de dropout, de loss, de cross entropy, d’optimizer et d’accuracy seront expliquées plus loin, dans la partie consacrée au fonctionnement réel des réseaux de neurones.
Voyons maintenant comment utiliser ces formes pour entraîner notre modèle, afin que le réseau de neurones soit ensuite capable de les reconnaître.
Entraîner son modèle
Évidemment, notre réseau de neurones ne va rien comprendre aux symboles qu’on a utilisés pour dessiner nos formes : il faut lui donner des valeurs numériques.
Si on convertit ces formes en données numériques, on obtient ceci :
Pour pouvoir les faire entrer dans le réseau de neurones, on va les vectoriser :
Enfin, comme il s’agit d’un dataset d’entraînement, il faut indiquer le résultat attendu. Pour l’écrire dans un format adapté à un réseau de neurones, on utilise le one-hot encoding.
Ça veut simplement dire qu’on écrit 5 bits, un par sortie possible. La sortie attendue pour la ligne d’entrée vaut 1, et les autres valent 0. Ainsi, « 1 0 0 0 0 » signifie « le premier neurone de sortie », et « 0 0 0 1 0 » signifie « le quatrième neurone de sortie ».
Si on écrit maintenant un fichier d’entraînement avec nos formes vectorisées, ça ressemble à ça :
Une fois le réseau de neurones entraîné avec ce dataset, si on lui donne l’une de nos formes, comme le losange, il va la reconnaître.
Mais c’est de la triche : ce vecteur exact qui décrit le losange, le réseau de neurones l’a déjà vu. Il fait partie du dataset d’entraînement, donc forcément, le modèle le reconnaît.
Mais comme vous vous en doutez sûrement, ce modèle entraîné est maintenant capable de deviner, face à des formes qu’il n’a jamais vues, à quelle forme elles le font penser.
Entraîner ce modèle avec Keras
|
|
Tester son modèle
Essayons maintenant avec ce carré cassé :
On passe le vecteur dans la première couche :
Et le réseau de neurones a trouvé, grâce à son entraînement.
Pour être clair, cet exemple est volontairement très simple, pour être le plus compréhensible possible. Le modèle est très léger, et le fichier d’entraînement très pauvre.
On n’avait ici qu’une seule ligne d’entraînement par forme. Pour un vrai cas d’usage, ce serait loin d’être suffisant.
Tester ce modèle avec Keras
|
|
Sortie
|
|
La reconnaissance de caractères
Passons maintenant à un exemple réel, qui est la suite logique du précédent : la reconnaissance de caractères. C’est un cas d’usage beaucoup plus courant, et de nombreux modèles de réseaux de neurones ont été créés pour y répondre. Beaucoup d’entre eux tournent aujourd’hui en production.
Avec l’exemple qu’on vient de voir, celui-ci ne devrait pas être bien difficile à comprendre :
Ce modèle prend 2 500 neurones en entrée : on va donc maintenant lui donner des images de 50x50 pour chaque caractère.
Il a toujours 5 couches, et cette fois 26 neurones en sortie, un par lettre de l’alphabet.
On va aussi utiliser un dataset un peu plus fourni, avec 25 images par caractère :
Comme on l’a vu juste avant, ces images seront converties en un seul fichier d’entraînement afin d’entraîner le modèle.
Pour être plus précis, au lieu de passer des données binaires, on va convertir les images en vecteurs normalisés. La première étape consiste à récupérer la valeur en niveaux de gris de chaque pixel (entre 0 et 255), puis à normaliser ces valeurs pour qu’elles soient toutes comprises entre 0 et 1.
Une fois qu’on a nos 26x25 vecteurs (25 images pour chaque lettre de l’alphabet), avec le one-hot encoding pour chaque ligne, notre fichier d’entraînement est prêt.
Si on dessine maintenant une autre lettre et qu’on lui applique automatiquement les mêmes opérations (vectorisation + normalisation) avant de la donner en entrée au réseau de neurones, il sera capable de la reconnaître.
Okay, mais comment ça marche vraiment ? Un réseau de neurones n’a évidemment rien de magique, alors voyons ce qui se passe sous le capot.
La partie suivante est plus théorique.
Elle a deux objectifs :
- Vous expliquer comment un réseau de neurones fonctionne réellement sous le capot
- Vous présenter le vocabulaire et les concepts courants des réseaux de neurones
Si la partie mathématique ne vous intéresse pas, vous pouvez la lire en diagonale et passer directement aux Convolutional Neural Networks (un autre type de réseau de neurones). Mais pour comprendre l’intérêt de ce type de réseau, vous aurez besoin de quelques notions de base comme l’accuracy, la cross-entropy loss ou l’overfitting.
Code Keras
Le code source complet de l’exemple est disponible ici :
https://github.com/sebferrer/keras-template/tree/main/mlp
Comment fonctionne vraiment un réseau de neurones
Degrés de liberté et fonction d’activation
Quand on passe le vecteur en entrée du réseau de neurones, un neurone fait toujours la même chose :
- Il calcule la somme pondérée de toutes ses entrées
- Il y ajoute une valeur supplémentaire (appelée « biais »)
- Puis il passe le résultat dans une fonction mathématique appelée « fonction d’activation »
Ce qui nous donne la formule suivante pour la sortie d’un neurone : L = activation(X.W + b)
- activation() : la fonction d’activation
- X : le vecteur des entrées
- W : le vecteur des poids
- b : le biais
On a donc, pour chaque neurone, 2 degrés de liberté :
- Le biais
- Les poids de la somme pondérée
On peut utiliser beaucoup de fonctions différentes comme fonction d’activation, mais elles doivent respecter 2 conditions :
- Ne pas être linéaire
- Être continue entre 0 et 1
Historiquement, la fonction d’activation a presque toujours été la sigmoïde, mais on a découvert ces dernières années que l’activation d’un neurone biologique ressemble davantage à une fonction ReLU qu’à une sigmoïde. Après de nombreux tests, des chercheurs en biologie ont découvert qu’à partir d’un certain seuil, les neurones passent à une activation proportionnelle à l’intensité des signaux d’entrée.
C’est pour ça que la ReLU est aujourd’hui la fonction la plus utilisée : elle permet au modèle de converger vers une sortie plus précise.
Fonction sigmoïde
Fonction ReLU
C’est ce que fait chaque neurone, couche après couche.
Il y a toutefois une exception pour la couche de sortie : pour celle-ci, on utilise presque toujours la fonction softmax comme fonction d’activation, à la place de la ReLU. La softmax, c’est simplement une fonction exponentielle : une fois que les neurones de sortie ont calculé leur somme pondérée, ils passent le résultat à l’exponentielle, puis on normalise le tout pour n’avoir que des valeurs comprises entre 0 et 1.
Le but est d’améliorer la convergence du réseau de neurones : la softmax creuse les écarts entre tous les résultats de sortie. L’idée est d’obtenir des valeurs proches de 0 (± 0,1) pour tous les neurones sauf un, proche de 1 (± 0,9). On peut alors l’interpréter comme une probabilité : le neurone dont la valeur de sortie est la plus proche de 1 correspond au résultat donné par le réseau de neurones.
C’est pour ça qu’on l’appelle « softmax » : elle fait ressortir le maximum, mais sans détruire l’information. Elle reste « douce » (soft).
Dans notre exemple précédent de reconnaissance de caractères, si tous les neurones de sortie ont une valeur proche de 0, sauf le deuxième qui est proche de 1, ça signifie que le réseau de neurones a déterminé que l’entrée qu’on lui a donnée correspond à la lettre « B ».
La première couche intermédiaire calcule la somme pondérée des entrées, la couche suivante calcule la somme pondérée des sommes pondérées précédentes (autrement dit, des sorties de la couche précédente), et ainsi de suite.
Si on arrive à ce résultat, c’est parce que le réseau de neurones a réussi à converger vers la bonne sortie. Et s’il a pu le faire, c’est parce que les poids et les biais étaient bien choisis.
D’accord, mais comment on les choisit ? Comment orienter les poids et les biais dans la bonne direction ?
Comment orienter les degrés de liberté
Accuracy et cross-entropy loss
C’est sur ce point que repose toute la capacité d’un réseau de neurones à faire de bonnes prédictions.
Comme on l’a vu plus tôt, pour qu’un réseau de neurones soit capable de faire une prédiction à partir d’une entrée, il faut l’entraîner.
L’entraînement consiste à faire passer un maximum d’entrées dans notre réseau de neurones (comme les formes ou les caractères des exemples précédents) et à définir une fonction d’erreur, puisqu’on connaît déjà les résultats. Chaque entrée est passée avec sa propre sortie attendue, encodée en one-hot. Pour chaque entrée, on fixe arbitrairement un vecteur de poids et un biais pour chaque neurone. Le résultat obtenu correspond alors à la sortie de la dernière couche qui a la plus forte probabilité. On compare ensuite ce résultat au résultat attendu, ce qui nous permet de savoir si les poids et les biais étaient suffisamment bons pour que le réseau de neurones fasse une bonne prédiction. C’est comme ça qu’on établit la fonction d’erreur, et tout l’enjeu sera de la minimiser. Cette fonction d’erreur, on l’appelle la cross-entropy loss.
Elle est définie par la distance entre le vecteur de sortie (les valeurs de tous les neurones de sortie) et le vecteur de sortie attendu, encodé en one-hot. On pourrait utiliser n’importe quelle méthode de calcul de distance, comme la distance euclidienne, mais celle qui fonctionne le mieux aujourd’hui, c’est la cross-entropy. D’où le nom de cross-entropy loss.
La cross-entropy consiste à multiplier, valeur par valeur, les sorties attendues (encodées en one-hot) par le logarithme des sorties calculées par le réseau de neurones, puis à faire la somme du tout.
Voici la formule de la cross-entropy :
C’est ce qui nous permet de calculer l’accuracy du réseau de neurones. C’est ce qu’on appelle l’évaluation.
Au fil de l’entraînement, la cross-entropy loss diminue et l’accuracy augmente.
Les poids et les biais évoluent dans la direction qui fait diminuer la fonction d’erreur.
Pour orienter les valeurs dans la bonne direction, on a donc besoin d’un optimizer.
Les optimizers
L’un des plus utilisés est le Gradient Descent Optimizer (la fameuse descente de gradient).
L’optimizer prend la fonction d’erreur et calcule ses dérivées partielles par rapport à tous les poids et à tous les biais du système.
Ça fait énormément de dérivées partielles à calculer.
Une fois les calculs terminés, on obtient un vecteur de dérivées partielles.
C’est ce vecteur qu’on appelle le gradient.
Ce gradient pointe vers l’extremum. Comme on veut minimiser la cross-entropy, on prend son opposé, qui nous indique alors dans quelle direction orienter les valeurs. En clair, il nous dit quel petit delta ajouter aux poids et aux biais pour se rapprocher d’une valeur où la fonction d’erreur sera plus faible.
Enfin, quand on entraîne un réseau de neurones, on ne fait pas passer le dataset une seule fois. On recommence l’entraînement encore et encore, de nombreuses fois.
Quand on a entraîné le réseau de neurones une fois sur tout le dataset, on dit qu’il s’est entraîné sur une epoch. Le nombre d’epochs, c’est le nombre de fois où l’on continue à l’entraîner avec le même dataset.
C’est donc à nous, à coups de calculs et d’expérimentations, de trouver le meilleur nombre de couches pour le réseau de neurones, ainsi que le bon nombre d’epochs.
Ces chiffres varient d’un besoin à l’autre.
Pour un usage générique, on est en général entre 3 et 5 couches pour la plupart des besoins, mais certains réseaux de neurones complexes peuvent dépasser les 80 couches.
Le nombre de couches comme le nombre d’epochs sont souvent trouvés de manière empirique.
Overfitting
Mais attention, il faut être très prudent au moment de les choisir. Ce serait une grosse erreur de se dire « plus j’utilise de couches et d’epochs, plus mon modèle sera précis. De toute façon, ça ne peut pas être pire », parce que c’est totalement faux. Si vous utilisez trop de couches, ou si vous entraînez trop votre réseau de neurones (sur trop d’epochs), vous perdrez la convergence, et l’accuracy avec.
C’est un phénomène courant, qu’on appelle l’overfitting.
Vanishing gradient
Maintenant qu’on a expliqué ce qu’est le gradient, on peut comprendre le problème de la sigmoïde : elle a deux zones plates sur les bords, et donc, à ces endroits, des dérivées qui tendent vers 0. Comme la direction qu’on donne aux poids et aux biais vient du gradient, lui-même composé de dérivées partielles, si ces dérivées sont nulles, on n’a plus aucune direction vers laquelle orienter nos degrés de liberté.
C’est ce qu’on appelle le vanishing gradient.
C’est un phénomène qui apparaît quand on commence à empiler les couches. Et c’est embêtant, parce qu’empiler des couches fait partie intégrante du concept de machine learning.
La ReLU a justement permis de régler ce problème.
Mais on n’a pas encore tout dit sur l’entraînement. Grâce à ces méthodes, on a réussi à faire converger le réseau de neurones, mais il reste encore à améliorer l’accuracy.
Learning rate decay
La première idée, c’est de réduire le pas (le learning rate), c’est-à-dire le delta qu’on utilise pour faire progresser les poids et les biais dans la bonne direction. Le réseau de neurones gagnera en précision, mais l’entraînement sera aussi plus lent.
Pour éviter cet inconvénient, on va utiliser le learning rate decay.
Le principe : on démarre très vite, puis on ralentit progressivement.
Ça évite que les deltas continuent de trop varier d’une direction à l’autre.
Ensuite, il faut s’occuper de l’overfitting et trouver un moyen de le réduire.
Pour ça, on va faire ce qu’on appelle de la régularisation, afin de corriger les parties de nos calculs qui peuvent nous induire en erreur.
Régularisation et dropout
Une méthode de régularisation très connue est le dropout, et elle est très simple à comprendre : avant l’entraînement, on va tuer aléatoirement une certaine proportion des neurones du réseau (25% par exemple). Ensuite, on remet tous les neurones dans le modèle et on procède à l’évaluation. Et on recommence, encore et encore. À chaque fois, on repart du modèle complet.
Par « tuer un neurone », on entend mettre sa valeur à 0 et augmenter légèrement celles des autres neurones, pour rééquilibrer la moyenne des vecteurs.
Si on fait ça pour régulariser le réseau de neurones, c’est parce qu’un réseau de neurones a trop de degrés de liberté. Il arrive que certains poids et biais évoluent dans la mauvaise direction, mais s’ils sont largement minoritaires, les autres neurones les corrigent et maintiennent la convergence. On pourrait se dire que ce n’est pas grave tant qu’il y a assez de neurones pour corriger, mais ça veut aussi dire qu’on pourrait obtenir une meilleure accuracy. Le problème, c’est qu’on ne peut localiser ni les mauvais neurones, ni ceux qui les corrigent.
Le dropout est une méthode simple et très efficace : à chaque entraînement, il y a une chance pour que les neurones qui corrigeaient les mauvais neurones ne soient plus là, et les mauvais neurones ont donc moins de chances d’être corrigés.
On a maintenant optimisé notre accuracy et notre cross-entropy loss.
Dans certains cas, on aurait atteint (à peu près) l’optimisation maximale, mais pour notre exemple (la reconnaissance de formes ou de caractères), on en est clairement loin.
Pourquoi ? Parce qu’on a fait une opération très brutale dès le départ : on avait des images/matrices en 2D, et on les a aplaties. Pas très malin, car on a perdu une information capitale : la forme 2D de la matrice.
Mais on n’avait pas le choix : notre MLP ne peut prendre que des vecteurs en entrée. Alors, comment faire ?
La réponse, c’est que notre type de modèle n’est tout simplement pas adapté. Il nous faut un modèle capable de prendre une matrice 2D en entrée.
C’est exactement pour ça que les Convolutional Neural Networks ont été créés.
Convolutional Neural Networks (CNN)
De la 1D à la 2D
Cette fois, voici à quoi ressemble notre modèle (toujours pour la reconnaissance de caractères, avec des images de 50x50) :
Le principe de base reste le même, à quelques différences près :
- On conserve l’information 2D de l’entrée
- Un neurone ne calcule plus la somme pondérée de toute la couche précédente
- On introduit la notion de canal (channel)
Patchs et canaux
Dans un MLP, si vous vous souvenez bien, chaque neurone prend en entrée le vecteur complet des sorties de la couche précédente. Ce n’est plus le cas ici.
Cette fois, chaque neurone ne prend en entrée qu’un patch de la couche précédente (de 4x4 par exemple). On calcule toujours la somme pondérée de ces entrées et on ajoute toujours un biais, mais uniquement sur ce patch.
Et on continue comme ça, patch après patch, pour construire une nouvelle couche de neurones en 2D.
Très important : on garde les mêmes poids et le même biais pour tous les patchs d’une même couche.
Avec cette méthode, on conserve l’information 2D, mais on n’a plus assez de degrés de liberté. C’est pour ça qu’on construit une autre couche au même niveau que celle-ci, mais avec d’autres poids et un autre biais. C’est comme « une autre version » de cette couche.
C’est le principe du canal : les couches cachées ne sont plus seules, chacune d’elles a un certain nombre d’autres versions d’elle-même.
Voici une représentation des entrées de chaque neurone, patch par patch (ici, on dessine les 2 canaux au lieu d’écrire « X2 ») :
Si on zoome un peu, ça donne ça :
Dans cet exemple, la même matrice de poids 4x4 et le même biais sont utilisés pour toute la couche, puis on utilise une autre matrice de poids et un autre biais pour le deuxième canal.
Utiliser des couches 2D et des patchs nous permet de conserver toutes les informations importantes, tous les détails de la forme contenue dans l’image/matrice : des morceaux de courbes, des lignes, etc.
C’est bien sûr à nous de définir la taille du patch (4x4, 5x5, 6x6…).
Si on s’arrête là, comme on peut le voir ci-dessus, peu importe le nombre de canaux utilisés, on génère des couches de sortie de la même taille que les couches d’entrée (logique).
Mais le but d’un réseau de neurones, c’est de converger. Pour ça, on va simplement déplacer le patch non plus à chaque pixel, mais tous les deux pixels (le pas du patch, ou stride, passe de 1 à 2) : on obtient alors des couches de sortie deux fois plus petites que la couche d’entrée.
On continue à réduire la taille de nos couches et à augmenter le nombre de canaux, jusqu’à être prêt à générer la dernière couche cachée, non convolutive et fully connected. On peut alors générer la couche de sortie finale, dont le résultat nous donnera notre prédiction (cf. notre premier schéma de CNN).
La dernière couche cachée est fully connected : ça signifie que la sortie de chaque neurone de cette couche sera une entrée de chaque neurone de la couche de sortie. Comme dans un MLP, chaque neurone a donc son propre vecteur de poids et son propre biais.
Si le réseau de neurones a du mal à converger (overfitting), ça signifie probablement qu’il n’a pas assez de degrés de liberté. La solution : utiliser plus de canaux pour chaque couche.
Et comme pour les MLP, ajouter du dropout aidera beaucoup, mais uniquement dans la couche fully connected. Il n’y a pas assez de degrés de liberté dans les couches convolutives pour le faire, ce serait trop risqué.
Et voilà ! On est maintenant capables de faire des prédictions à partir d’entrées 2D, avec l’accuracy théorique maximale et une cross-entropy loss minimale.
Avant d’en finir avec les CNN, sachez qu’une nouvelle méthode de régularisation a été découverte récemment : la batch normalization.
Batch normalization
Elle consiste à découper le dataset d’entraînement en batches, et à s’assurer que les valeurs sont à la même échelle, et que les courbes sont centrées et décorrélées.
On peut ainsi calculer des statistiques pour chaque batch.
Pour chaque batch, on prend la sortie d’une couche du réseau de neurones. Pour un batch de 100 matrices, on a donc 100 valeurs de sortie.
Sur l’ensemble de ces valeurs, on calcule la moyenne et l’écart-type, puis on modifie les sorties de chaque couche avant d’appliquer la fonction d’activation.
La modification consiste à soustraire la moyenne, puis à diviser par l’écart-type.
Avec x la sortie calculée initialement et x̂ la nouvelle sortie normalisée, on obtient la formule suivante :
Pour être sûr de ne pas abîmer les sorties calculées par la couche, on ajoute 2 paramètres à notre nouvelle sortie. Voici donc la batch normalization, avant l’application de la fonction d’activation : BN(x) = αx̂ + β
Si le réseau de neurones détermine que ce serait une mauvaise idée de transformer la valeur de sortie en soustrayant la moyenne et en divisant par l’écart-type, les paramètres α et β nous permettent d’annuler ce calcul. Ça ajoute 2 degrés de liberté par neurone à notre réseau.
Comme on peut annuler chaque batch normalization, on a la garantie que notre réseau de neurones sera au moins aussi bon avec la batch normalization que sans.
Code Keras
La façon de construire ce modèle avec Keras ressemble à celle des MLP, mais cette fois, on prend en compte la forme 2D des couches, ainsi que les canaux.
Construire ce modèle avec Keras
|
|
Comme les CNN sont conçus pour prendre des matrices en entrée plutôt que des vecteurs, les entrées sont souvent des images.
Pour pouvoir entraîner et utiliser le réseau de neurones avec des images, voici quelques fonctions qui permettent de convertir les images en matrices à donner au réseau de neurones :
Fonctions de préparation en Python
|
|
Entraîner ce modèle avec Keras
|
|
Tester ce modèle avec Keras
|
|
Sortie
|
|
Ici, c’est donc la classe ‘0001’ qui a été reconnue, ce qui correspond à la lettre ‘A’.
Le code source complet de l’exemple est disponible ici :
https://github.com/sebferrer/keras-template/tree/main/cnn
Au-delà de cet article
Il existe bien d’autres types de réseaux de neurones, comme les Recurrent Neural Networks (RNN / LSTM) ou les réseaux de neurones génératifs. De quoi rendre cet article cent fois plus long, mais j’espère qu’avec ce tour d’horizon des réseaux de neurones à travers les MLP et les CNN, vous avez maintenant quelques bases pour explorer le reste !
Tous les schémas de réseaux de neurones ont été réalisés avec ma propre librairie graphique : BYONND
J’ai également créé un studio de réseaux de neurones basé sur cette librairie : Deep Learning studio
Merci d’avoir lu jusqu’au bout !