Skip to main content

Fonctionnement d’un réseau de neurones

Dans un monde où la technologie évolue très rapidement, nous essayons de rendre les tâches longues et fastidieuses réalisables par des machines. La découverte des réseaux de neurones a permis de réaliser une nouvelle approche dans la résolution de ces tâches. À partir de données collectées, nous avons réussi à faire apprendre à la machine des notions parfois complexes. Ainsi, nous allons essayer de comprendre et de détailler le fonctionnement d’un réseau de neurones profonds. 

Réseau de neurones

Inspirés du fonctionnement des neurones biologiques, les réseaux de neurones se modifient eux-mêmes en fonction des actions et des résultats qu’ils obtiennent permettant de réaliser un apprentissage par eux-mêmes. Cet apprentissage lui permet notamment de résoudre des problèmes complexes tels que la reconnaissance de forme ou bien le traitement du langage. Afin de mieux comprendre le fonctionnement d’un réseau de neurones, nous allons nous appuyer sur le cas d’un système de classification.

Architecture

À l’aide d’un réseau de neurones, nous allons chercher à classifier des objets. Pour ce faire, nous allons donner en entrée de notre système différentes caractéristiques. À partir de cela, nous aimerions que notre système nous dise à quelle catégorie appartient notre objet. Par exemple, nous pourrions imaginer que nous allons réaliser un système de classification pour des animaux. Ainsi, nous donnerons, en entrée de notre système, différentes caractéristiques comme la taille, le poids… De ce fait, notre système va, à partir de ces données, chercher la catégorie de l’animal qui correspond le mieux.

Les réseaux de neurones sont composés de plusieurs couches. Chacune d’entre elles est composée de neurones qui sont chacun connecté à tous les autres de la couche précédente. Nous pouvons visualiser cela à l’aide du schéma ci-dessous.

Illustration d’un réseau de neurones.

Ici, nous allons avoir en entrée, sur la couche bleue, différentes valeurs correspondant aux caractéristiques de notre objet que nous souhaitons classifier. En sortie, sur la couche verte, nous souhaitons obtenir un score pour chacune des catégories qui représente un animal que notre système aura appris. Ce score que nous allons transformer en probabilité, va nous indiquer la correspondance entre la catégorie et les données que nous avons donner en entrée de notre système. Ainsi, ces probabilités vont nous indiquer l’appartenance ou non de chaque catégorie par rapport à notre objet. Ainsi, il nous suffira de prendre la plus grande probabilité afin de déterminer, avec le plus de chance, la catégorie d’appartenance de notre objet.

Au tout début, nous avons, en entrée de notre réseau, différentes valeurs. Notre objectif, avec ce réseau, est d’obtenir des probabilités pour chaque catégorie possible. Pour ce faire, nous allons propager nos valeurs d’entrée sur toutes les autres couches de notre réseau.

Fonctionnement d’un neurone

Un neurone va prendre, en entrée, plusieurs valeurs. Nous allons, dans un premier temps, pour chacune de ces valeurs, les multiplier par un poids correspondant qui se situe sur les arêtes des connexions des neurones. Ces poids sont définis aléatoirement lorsque nous initialisons notre réseau pour la première fois. Puis, nous allons réaliser la somme de ces valeurs que nous allons passer dans une fonction d’activation. La plus connue et la plus utilisée est la fonction ReLU (*Rectified Linear Unit*). Cette fonction d’activation va nous renvoyer une nouvelle valeur que nous allons propager à la couche suivante. Vous pouvez visualiser cela à l’aide du schéma ci-dessous.

Fonctionnement d’un neurone.

Comme vous avez pu le constater, nous avons une valeur correspondant au biais. La valeur du biais de change pas, elle est égale à 1. Cependant, la valeur du poids change, lors de la phase d’apprentissage.

Ainsi, si nous écrivons cela d’un point de vue mathématique, nous avons :

x^{(n)}_{j} = g^{(n)}(\sum_{k=1}^{K} w^{(n)}_{jk}x^{(n-1)}_k + w^{(n)}_b)

Pour un neurone j présent sur la couche n, nous appliquons une fonction d’activation g présent sur la couche n. Cette fonction d’activation prend en paramètre la somme du produit des poids notée w pour le poids présent sur la couche k et le neurone j, par rapport à la valeur correspondante, notée x, de la couche précédente. Nous n’oublions pas d’ajouté à cette somme le biais présent sur la couche n.

Nous appliquons ce fonctionnement sur tous les neurones de notre réseau. Ainsi, nous avons propagé nos valeurs que nous avons eues en entrée jusqu’à la fin de notre réseau, ou nous allons obtenir un score pour chaque catégorie de fin que nous avons.

Fonction d’activation – ReLU (Rectified Linear Units)

Lorsque nous avons abordé les différentes étapes du fonctionnement de notre réseau, nous avons parlé de la fonction ReLU. Cette fonction retourne x si x est positive, sinon 0 :

g(x)=max(0,x)
gKA4kA9.jpg
Fonction d’activation – ReLU (Rectified Linear Unit).

Transformer notre score en probabilité – Fonction Softmax

Afin de pouvoir interprété le score que nous obtenons en sortie, nous allons transformer ce score en probabilité. Pour ce faire, nous allons utiliser la fonction softmax. Cette fonction prend en entrée un vecteur et va nous renvoyer en sortie un autre vecteur dont les éléments sont des nombres strictement positifs et dont la somme et égale à 1.

\sigma(z)_j=\frac{e^{(z_j)}}{\sum^{K}_{k=1}e^{(z_k)}},\forall j\in\{1,…,K\}

Ici, pour un score que nous notons z sur une sortie j, nous réalisons l’exponentielle de ce score que nous divisons par la somme des exponentielles des autres scores présents.

Déterminer l’erreur

Dans un premier temps, nous allons propager nos valeurs dans notre système afin de pouvoir déterminer un score. À partir de ce score, nous allons le comparer à ce que nous aimerions avoir. En effet, si notre objet de départ représenté, par exemple une chaise, nous aimerions que notre système nous dise que cet objet représente une chaise et non une table. Ainsi, nous allons comparer chacun des nœuds de sortie de notre réseau. Avec cette comparaison, nous allons pouvoir obtenir une erreur. Afin de la déterminer, nous appliquons la formule suivante :

erreur^{sortie}_{j} = g'({\sum^{K}_{k=0}w^{sortie}_{jk}x^{sortie-1}_k})[t_i-y_i]

L’erreur que nous avons en sortie est égale à la dérivée de la fonction d’activation qui prend en paramètre la somme des valeurs multipliés par la valeur correspondante. Nous allons multiplier cela par la différence entre la valeur désiré t et la valeur que nous avons obtenus avec notre réseau y.

Cette erreur, que nous avons obtenue sur la dernière couche, nous allons la rétro-propager, c’est-à-dire que nous allons chercher l’erreur des nœuds précédent à partir de celle-ci. Pour ce faire, nous appliquons la formule suivante :

erreur^{(n-1)}_j=g’^{(n-1)}(\sum^{K}_{k=0}w^{(n-1)}_{jk}x^{(n-2)}_k)\sum^{I}_{(i=0)}w^{(n)}_{ij}e^{(n)}_i

Ici, nous allons rétro-propager l’erreur en fonction de l’influence que le neurone a dans la détermination de la catégorie d’appartenance. C’est pour cela que nous multiplions la valeur de l’erreur de la couche suivante par le poids correspondant. Ensuite, il ne nous reste plus qu’a le multiplier par la dérivée de la fonction d’activation qui prend en paramètre la valeur obtenue sur le nœud.

À partir de cette erreur, nous allons pouvoir savoir la direction où nous devons aller afin de corriger au mieux les poids qui composent notre système.

Correction des poids

Une fois l’erreur déterminée, nous allons pouvoir, à partir de celle-ci, déterminer la nouvelle valeur du poids. Pour ce faire, nous allons appliquer la formule suivante :

\Delta w^{(n)}_{ij}=w^{(n)}_{ij}+\lambda e^{(n)}_i x^{(n-1)}_j

Ici, nous faisons intervenir λ, qui correspond au taux d’apprentissage. Ce taux d’apprentissage est généralement très petit 0.01 voir moins. Cela va nous permettre de converger vers une valeur d’un poids optimal. Si nous ne faisons pas intervenir de taux d’apprentissage, la valeur va directement s’adapter à notre donnée. Cependant, nous voulons que notre système soit général. Pour imager, nous pouvons prendre le cas d’un classificateur d’animaux. Si je prends l’image d’un chien, j’aimerais que mon système détermine des caractéristiques propres au chien (museau, 4 pattes …). En revanche, si j’ai un cas particulier d’un chien qui à, par exemple, des tâches sur son pelage, je ne veux pas que mon système prenne en compte ces tâches comme étant une caractéristique majeure. Ainsi, le taux d’apprentissage me permet de résoudre ce problème.

Une fois la nouvelle valeur du poids trouvée, il ne me reste plus qu’à l’appliquer sur mon système. L’une des possibilités que nous pouvons faire est de prendre un lot de données et d’enregistrer pour chacune de ces données le nouveau poids sans l’appliquer sur notre système. Ainsi, nous pouvons faire la moyenne de cet ensemble de poids ce qui permettra à notre système d’être général lors de l’apprentissage. De plus, afin d’avoir un système optimal, il nous faut l’entraîner sur énormément de données.

Problème de recherche

Lorsque nous cherchons la valeur du nouveau poids, nous avons parlé du taux d’apprentissage qui détermine le pas du déplacement. Ce que nous cherchons, c’est une valeur du poids dont l’erreur est la plus basse. L’un des problèmes que nous pouvons rencontrer et si on se situe dans un minimum local. Le problème est que nous voulons le minimum global et non un minimum local.

Extrema_example_original.svg.png
Illustration du problème de recherche.

Nous avons aussi des problèmes liées à une valeur du taux d’apprentissage trop petit ou trop grand. En effet, si le taux d’apprentissage et trop grand, la valeur du poids, va toujours fluctuer. En revanche, si la valeur du taux d’apprentissage est trop petite, la valeur du poids va mettre énormément de temps afin de converger.

Screen-Shot-2018-02-24-at-11.47.09-AM.png
Illustration du problème du taux d’apprentissage.
Source de l’image : https://www.jeremyjordan.me/nn-learning-rate/

Résumé

Durant la phase d’entraînement, nous allons chercher à améliorer notre système. Pour ce faire, nous allons, dans un premier temps, propager notre donnée dans notre système. Par la suite, nous allons obtenir un score que nous allons comparer à ce que l’on aurait aimé obtenir. Une fois l’erreur obtenue, nous allons rétro-propager cette erreur sur l’ensemble de notre système. Enfin, nous allons modifier la valeur de notre poids afin de minimiser cette erreur.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *