Sélectionner une page

Attention : les analogies en rapports avec la neurologie sont exagérées/simplifiées pour mieux comprendre le fonctionnement d’un réseau de neurones informatique. La réalité est plus complexe, et à ce jour, soulève encore certaines interrogations.

Théorie

Avant d’entrer dans le feu de l’action, commençons par un peu de théorie afin de mieux comprendre les enjeux.

Vulgarisation

Un réseau de neurones artificiel est une structure apprenante. Son but, contrairement aux intelligences artificielles classiques, est d’apprendre par l’expérience, et tend à reproduire le fonctionnement du cerveau.

En effet, le cerveau humain par exemple, est constitué de neurones, lesquels sont connectés les uns aux autres. Lorsqu’un “capteur” est excité (ex: les yeux reçoivent une image), l’information va parcourir le réseau de neurones, sous forme d’impulsion électrique, jusqu’à un/plusieurs neurones correspondant à des actions (ex : neurone contrôlant le biceps du bras droit).

En parcourant le réseau, le signal électrique va être modifié, et fragmenté. En effet, un neurone peut être connecté à plusieurs autres neurones. Suivant la “taille” de la connexion entre deux neurones, le signal va être “amplifié”/”atténué”. Le signal va donc se propager à travers le réseau.

Schématisation

Le cerveau est bien plus complexe que la simple vulgarisation faite précédemment. Même si nous ne maîtrisons pas toutes les subtilités d’un tel mécanisme, nous sommes malgré tout capables de réaliser une version simplifiée grâce aux outils mathématiques et informatiques. Il existe une multitude de “modèles” de réseaux de neurones. Ici, nous détaillerons un modèle basique, composé de couches, elles-mêmes composées de neurones. Dans ce modèle, chaque neurone d’une couche N est connecté à tous les neurones de la couche N+1. On parle alors de “Fully Connected Layers”.

Exemple de réseau de neurones

Il peut y avoir une infinité de couches. Le nombre de couches, et le nombre de neurones constituant chaque couche, sont une science à part entière, très complexe. Il est d’ailleurs impossible de définir avec exactitude le nombre “parfait” de couches et de neurones. Cela est en tout cas le cas pour les couches du milieu, appelées “couches cachées”. Pour la première et dernière couche, appelées “couches visibles”, le nombre de neurones est simple à définir :

  • il doit y avoir autant de neurones dans la première couche qu’il n’y a d’informations en entrée (ex: nombre de pixels de l’image à analyser)
  • il doit y avoir autant de neurones dans la dernière couche qu’il n’y a d’informations en sortie (ex: nombre de choix possibles).

Exemple 1

Si on crée un réseau de neurones, capable de nous dire si une image est celle d’un chien, ou d’un chat. Le nombre de réponses possibles est 2. Nous aurons donc deux neurones dans la dernière couche (un neurone pour le chat, et un neurone pour le chien).

On devra ainsi fournir une image au réseau. Supposons que l’image soit constituée de 784 pixels, chacun étant représenté par un nombre entier compris entre 0 et 255 (taux de gris; 0 = blanc; 255 = noir). Dans ce cas, nous aurons 784 neurones dans la première couche.

Lorsque nous demanderons au réseau d’analyser une image, nous “injecterons” dans chaque neurone de la première couche, la valeur du pixel associé. Une fois l’algorithme terminé, les deux neurones de la dernière couche auront chacun une valeur (ex: 0.12 et 0.95). Si le neurone n°1 correspond au chat, et que le neurone n°2 correspond au chien (définit lors de l’entraînement), alors cela signifie que le réseau pense qu’il y a 12% de chance qu’il s’agisse d’un chat, et 95% de chance qu’il s’agisse d’un chien. Le neurone le plus actif est donc le neurone n°2 (alias “chien”). Le réseau “pense” donc qu’il s’agit d’un chien.

On pourrait également utiliser des valeurs comprises entre -1 et 1, combinées à une fonction d’activation (voir plus bas).

Exemple 2

Si on crée un réseau de neurones, capable de prédire la température qu’il fera dans 3h, en fonction des paramètres météo actuels. Le nombre de neurones de la première couche correspondra au nombre de paramètres météo (ex: température actuelle, pression atmosphérique, etc…). Le résultat attendu est simple : il s’agit de la température. Nous aurons donc 1 seul neurone dans la dernière couche. La valeur de ce neurone correspondra ainsi à la température estimée par le réseau.

Une structure dynamique

Le but d’un réseau de neurones est d’évoluer grâce à l’expérimentation. Le mécanisme est similaire au principe de renforcement (ou punition) positif. C’est-à-dire qu’on va renforcer les comportements désirables via un système de récompense, et réprimer les comportements indésirables via un système de punition.

Pour réaliser ce mécanisme, nous associons à chaque connexion entre deux neurones, un poids. Ce poids est un coefficient multiplicateur, ayant une valeur aléatoire lors de la création du réseau. Grâce aux récompenses/punitions, tous ces poids vont être amplifiés ou atténués.

Durant tout le cycle de vie d’un réseau de neurones, le nombre de neurones et le nombre de couches ne changent pas (dans les modèles les plus “classiques”). Seul le poids des connexions évolue afin de s’affiner (et éventuellement quelques paramètres).

De plus, un neurone ne conserve pas d’information. Il contient une information lorsqu’il y a un traitement à effectuer, et transmet cette information, mais celle-ci est ensuite “détruite”. Les poids, quant à eux, arrêtent généralement d’évoluer une fois le réseau entraîné.

Entraînement

Lors de la création d’un réseau de neurones, celui-ci est constitué de poids aléatoires. Comme un enfant venant de naître, son comportement est aléatoire. Ainsi, au départ, si on demande à notre réseau d’analyser une information, celui-ci nous donnera un résultat qui ne sera pas pertinent (il ne “sait pas ce qu’il dit” car il n’est pas encore entraîné). Dans tous les cas, il apportera une réponse à notre question. Le but sera ensuite de lui donner une récompense, ou une punition, suivant la qualité de sa réponse.

En programmation, on peut développer un tel réseau, pour lequel on va automatiser l’apprentissage. Si l’on possède un jeu de questions, et les réponses associées, dans ce cas, on peut entraîner le réseau avec les “questions” (données en entrée), et comparer la réponse du réseau avec la vraie réponse, et ainsi lui donner une récompense, ou lui infliger une punition.

Ce système de récompense/punition est un algorithme, qui consiste à évaluer l’erreur du réseau (l’écart existant entre la réponse apportée, et la réponse réelle), et ainsi augmenter/diminuer le poids des connexions en fonction. La récompense/punition se fait sur chaque connexion entre les neurones, proportionnellement à l’écart avec la bonne réponse.

Un des algorithmes les plus connus ayant pour but de réaliser cette tâche s’appelle “back propagation”. C’est-à-dire que l’on va parcourir le réseau de neurones dans le sens inverse. Cette “back propagation” est en quelque sorte une rétrospective ayant lieu sur le parcours que l’information vient d’effectuer dans le réseau de neurones. Une fois que nous possédons le résultat attendu, nous pouvons prendre du recul, et modifier les poids.

Au final, un réseau de neurones consiste à créer des liens entre des données en entrées, et des données en sortie. Un être humain chercherait à déterminer l’impact qu’une données en entrée peut avoir sur une données en sortie.

Si le but de l’expérience était de calculer le risque de verglas, un être humain se dirait « la température est importante, mais pas l’organisation de la fête du village ».

Un réseau de neurones informatique, lui, pourrait établir une corrélation entre l’information « fête du village » et « nombre d’invités ». Ces deux données prises à part ne serviraient à rien dans le calcul du risque de verglas, mais ensemble, elles pourraient impacter le résultat (du fait de l’augmentation de la température du à la présence d’un nombre élevé de personnes au même endroit).

C’est en cela qu’un réseau de neurones informatiques peut se montrer plus performant qu’un être humain lors de la réalisation de tâches précises. Le réseau pourra établir des corrélations entre des données qui semblent n’avoir aucun rapport à première vue.

Exemple

Prenons l’exemple d’un réseau, analysant une image, afin de reconnaître le chiffre représenté. Imaginons que le pixel n°544 possède presque toujours une valeur élevée (proche de 255, donc pixel noir) lorsqu’il s’agit du chiffre “2”. Dans ce cas, les connexions qui découlent de ce pixel (et donc du neurone associé) et qui ont un impact sur la valeur du neurone correspondant au chiffre “2”, devront être importantes. Par contre, le pixel n°1 (pixel en haut à droite) est presque toujours blanc (valeur proche de 0) pour tous les chiffres, il n’a donc que peu d’intérêt. Les poids des connexions qui découlent de ce pixel doivent donc être diminués, car on considère qu’elles n’ont que peu d’intérêt dans l’analyse du chiffre.
Il serait tout à fait possible de faire ces ajustements manuellement (bien que extrêmement long), mais le but d’un réseau de neurones est d’automatiser ce fonctionnement afin qu’il tendent lui-même à atteindre les valeurs optimales pour chaque poids.

Exemple d’un réseau visant à analyser une image

Ici, la présence d’un pixel noir en entrée 544 aura un impact important sur la possibilité que le chiffre soit un 2. Néanmoins, ce même pixel pourrait être sans intérêt dans le cas où le chiffre serait un 5.

Exemple concret

Dans cet exemple, nous allons créer un réseau de neurones, qui devra jouer à un jeu que nous allons inventer. Nous détaillerons ainsi les calculs mathématiques qui entrent en jeu, et démontrerons les limitations d’une réseau constitué de 2 couches uniquement.

Règles du jeu

  • Nous avons deux paramètres, A et B.
  • A et B peuvent avoir comme valeur 0 ou 1
  • Suivant la valeur de A et de B, la réponse sera X, Y, ou Z
  • Si A = 1, alors X doit être égal à 1, 0 sinon
  • Si B = 1, alors Y doit être égal à 1, 0 sinon
  • Si A = 1 et B = 0, ou que A = 0 et B = 1, alors Z doit être égal à 1 (“OU exclusif” = “XOR”)

Structure du réseau

Il existe plein de structures possibles, mais nous allons en définir une très simple. Celle-ci n’aura que 2 couches :

  • La première, sera constituée de 2 neurones (le neurone A, et le neurone B)
  • La deuxième, sera constituée de 3 neurones (le neurone X, le neurone Y, et le neurone Z)
Réseau de neurones constitué de 2 couches
  • Nous avons au total 6 connexions, chacune associée à un poids nommé WN1 N2 (ici, N1 est le nom/numéro du neurone à gauche, et N2 est le nom/numéro du neurone à droite)

Lors de la création de ce réseau, nous allons attribuer une valeur aléatoire comprise entre -1 et 1 pour chaque poids.

Les nombres en verts correspondent aux valeurs des poids

Entraînement

La structure de notre réseau est en place, reste à définir l’algorithme qui permettra de faire transiter les informations au travers du réseau, et qui modifiera la structure en fonction du résultat.

Nous allons lancer un premier test avec la configuration du schéma ci-dessus, en donnant comme valeurs :

  • A = 1
  • B = 0

Nous avons ainsi le schéma suivant :

Entraînement du réseau de neurones

Ces deux informations (1 et 0), vont donc parcourir le réseau. Ainsi, nous allons devoir calculer la valeur de X, Y, et Z, en fonction de A, B, et du poids des connexions. Nous avons donc les formules suivantes :

  • X = (A x WAX) + (B x WBX) = (1 x -0.5) + (0 x 0.9) = -0.5
  • Y = (A x WAY) + (B x WBY) = (1 x 0.2) + (0 x -0.1) = 0.2
  • Z = (A x WAZ) + (B x WBZ) = (1 x -0.6) + (0 x 1) = -0.6

Le but ici est de multiplier la valeur de chaque neurone connecté, par le poids de la connexion, et de faire la somme.

Ensuite, nous devons appliquer ce qu’on appelle une fonction d’activation. Cette fonction va permettre de “normaliser” le signal. Il existe une multitude de fonctions d’activation. Dans cet exemple, nous utiliserons la fonction de Heaviside qui consiste à retourner 1 si le nombre est positif, et de retourner 0 si le nombre est négatif. A noter que la dérivée de la fonction de Heaviside est une fonction qui retourne 1 si le nombre est 0, et 1 sinon (à savoir pour la suite).

Nous obtenons donc les valeurs suivantes :

  • X = 0
  • Y = 1
  • Z = 0

D’après les règles du jeu que nous avons établies, le résultat devrait être :

  • X = 1
  • Y = 0
  • Z = 1

Notre réseau de neurones a donc faux pour chaque neurones (normal, il n’a jamais été entraîné, et ne connaît pas les règles…).

Back Propagation

Nous allons maintenant modifier la structure de notre réseau. Il s’agit là d’adapter la valeur des poids en fonction de la réponse. La backpropagation (ou Rétropropagation du gradient) est un algorithme permettant de corriger les poids en fonction de l’erreur commise (mais il existe d’autres algorithmes).

Pour pouvoir appliquer cet algorithme, il faut connaître la dérivée de la fonction d’activation. Ayant utilisé la fonction de Heaviside, la formule à appliquer pour sa dérivée est la suivante : si le nombre est égale à 0, le résultat est égal à 1, et 0 sinon.

Une fois de plus, il existe plusieurs méthodes permettant de calculer l’erreur. L’une d’elles correspond à la formule suivante :

    \[erreur = (resultatSouhaite - resultatObtenu)^2 \over 2\]

Le but de l’entraînement sera donc de réduire l’erreur.

1 – Calcul du Delta

Le delta (Δ) correspond à la différence entre le résultat obtenu, et le résultat souhaité. Ainsi, dans le cas présent, nous avons :

  • ΔX = 0 – 1 = -1
  • ΔY = 1 – 0 = 1
  • ΔZ = 0 – 1 = -1
2 – Taux d’apprentissage

Le taux d’apprentissage (α) est une valeur définie manuellement, qui diminue généralement au fil du temps, afin de réduire l’impact des expériences au fur et à mesure (étant donné qu’à sa « naissance », un réseau de neurones aura tout à apprendre, et qu’une fois « mature », les expériences ne doivent pas remettre en question tout ce qu’il a appris, mais seulement affiner).

3 – Calcul du nouveau poids

Avec le delta, et le taux d’apprentissage, nous pouvons calculer les nouveaux poids avec la formule suivante :

Wij = Wji – α x Δ x i 

Soit

nouveauPoids = ancienPoids – tauxApprentissage x difference x valeurDuNeuroneGauche

Nous avions les poids suivants :

  • WAX = -0.5
  • WBX = 0.9
  • WAY = 0.2
  • WBY = -0.1
  • WAZ = -0.6
  • WBZ = 1

Supposons que notre taux d’apprentissage soit à 0.05. Nous appliquons donc la formule ci-dessus, soit :

  • WAX = WAX – α x ΔX x A= (-0.5) – (0.05) x (-1) x 1 = -0.45
  • WBX = WBX – α x ΔX x B= (0.9) – (0.05) x (-1) x 0 = 0.9
  • WAY = WAY – α x ΔY x A= (0.2) – (0.05) x (1) x 1 = 0.15
  • WBY = WBY – α x ΔY) x B= (-0.1) – (0.05) x (1) x 0 = -0.1
  • WAZ = WAZ – α x ΔZ x A= (-0.6) – (0.05) x (-1) x 1 = -0.55
  • WBZ = WBZ – α x ΔZ x B= (1) – (0.05) x (-1) x 0 = 1.05

Si nous avions d’autres couches, le traitement serait similaire pour les poids des connexions inférieures, mais avec une formule mathématique permettant de calculer l’erreur des neurones proportionnellement à l’impact qu’ils ont eu sur l’erreur finale.

Fin de l’entraînement

Si notre réseau est correcte, et après un certain nombre d’entraînements, nous devrions aboutir à des poids proches de ceux-ci :

  • WAX = 1
  • WBX = 0
  • WAY = 0
  • WBY = 1
  • WAZ = ???
  • WBZ = ???

Dans cet exemple, le réseau ne convergera jamais entièrement. Les poids permettant de calculer X et Y vont se stabiliser, mais il est impossible pour ce réseau de trouver une valeur stable pour les poids des connexions de Z. Cela démontre un problème : le réseau n’est pas adapté pour ce type d’exercice (après tout, il ne possède que 5 neurones…). Dans ce cas, nous avons besoin d’une couche supplémentaire (ou plus).

C’est essentiellement en expérimentant que nous pouvons savoir si la structure du réseau est adaptée ou non.

Utilisation du réseau

A force d’effectuer des entraînements, le réseau devrait se stabiliser, c’est-à-dire que les poids devraient de moins en moins évoluer. Une fois que le réseau converge (les résultats sont fiables, et donc les taux d’erreurs sont faibles), le réseau peut-être utilisé. Pour cela, il suffit d’appliquer le même traitement que lors de l’entraînement, à l’exception qu’on n’effectuera plus la backpropagation.

En effet, à ce niveau, la structure du réseau ne doit plus évoluer. On peut désactiver l’apprentissage, ce qui permet notamment d’améliorer la rapidité d’exécution de l’algorithme.

Conclusion

Il existe une multitude de types de réseaux de neurones, que ce soit en terme de structure, ou bien de mécanismes qui régissent leur fonctionnement. D’autres paramètres tels que le « biais » sont parfois ajoutés afin de répondre à des problèmes précis. Néanmoins, tous tendent au même but : calculer les informations, à partir des données traversant un réseau pré-entraîné.

D’un point de vu informatique, il n’est pas difficile de reproduire l’expérience ci-dessus. En programmation, les neurones et les connexion peuvent être représentées dans n’importe quel langage. Il suffit alors d’implémenter les différentes fonctions (fonction d’activation, back propagation, etc…), et d’automatiser l’apprentissage (à partir d’un jeu de données en entrée, et des réponses). Plus le nombre d’entraînement sera élevé, plus le réseau sera performant (excepté dans certains cas, tels que le surapprentissage).

Il existe également des bibliothèques implémentant déjà des bases solides permettant d’implémenter des réseaux de neurones simples ou complexes, telles que le très connu TensorFlow. Bien sûr, de tels outils sont chaudement recommandés pour des cas d’utilisation réels. Mais si le but que vous recherchez est simplement de comprendre les mécanismes mathématiques qui se cachent derrière, une approche « vanilla » peut être très enrichissante.

Résumé

Neurone

On parle de neurones, ou de nœuds, pour désigner les objets à travers lesquels transitent les informations. Ces neurones sont regroupés en couches.

Couche

Un réseau de neurones est constitué de couches. Chaque couche est elle-même constituée de neurones. La première et la dernière couche sont appelées “couches visibles”. Les couches intermédiaires sont appelées “couches cachées”.

Inputs

Les inputs (données), désignent un ensemble de données fournies en entrée à un réseau de neurones.
Un réseau de neurones doit posséder dans sa première couche autant de neurones qu’il n’y a d’inputs.
Par exemple, si le but d’un réseau est d’analyser une image, ces données peuvent correspondre aux pixels qui composent l’image (ex: nombres entre 0 et 255 représentant le taux de “gris”).

Outputs

Les outputs désignent le résultat généré par un réseau de neurones à partir des inputs.
Un réseau de neurones doit posséder dans sa dernière couche autant de neurones qu’il n’y a d’outputs.
Par exemple, si le but d’un réseau de neurones est d’analyser une image, et de deviner s’il s’agit d’un chat ou d’un chien, il y aura alors 2 output (un pour le chat, l’autre pour le chien). Si le but d’un réseau de neurones est d’analyser une image, et de deviner de quel chiffre il s’agit (entre 0 et 9), il y aura alors 10 outputs (un pour chaque chiffre). Ainsi, en fournissant au réseau une image, on analysera les outputs afin de voir celui qui est le plus actif (résultat le plus proche de 1). Si le neurone du chiffre 4 est plus actif que les autres, cela signifie que le réseau “pense” que l’image correspond au chiffre 4.

Poids

On parle de poids pour définir la valeur d’une connexion entre deux neurones. Ce poids est un coefficient multiplicateur. Il évolue au cours de l’entraînement. Lors de la création du réseau, chaque connexion se voit attribuer un poids aléatoire compris entre -1 et 1. Lors de l’apprentissage, cette valeur va augmenter, ou diminuer, jusqu’à ce qu’elle se stabilise.

Biais

Le biais (ou “bias” en anglais) est une valeur aléatoire, associée à chaque neurone. Il sert au calcul de la fonction d’activation, afin d’éviter le sur-apprentissage, ou le sous-apprentissage.

Fonction d’activation

Il s’agit de la fonction permettant de convertir le signal d’un neurone (correspondant à la somme des neurones précédents multipliés par le poids des connexions).
La fonction d’activation la plus simple est la fonction de Heaviside, qui retourne 1 si la valeur est positive, et 0 si la valeur est négative.