Lors du précédent article, je vous avais présenté le Perceptron, un modèle de neurone artificiel très simple mais aussi très limité. Aujourd’hui, nous allons voir comment la mise en réseau de plusieurs neurones permet de représenter d’améliorer la précision du modèle, et faire nos premiers pas vers les réseaux neuronaux modernes et donc le deep learning.

Limites du perceptron unique

Nous avons vu que le perceptron ne peut tracer que des frontières rectilignes pour départager les classes que l’on cherche à prédire. Ainsi en deux dimensions1)Ici, ce que j’appelle le nombre de dimensions, c’est le nombre de variables en entrée du neurone. Dans l’exemple présenté précédemment, il y avait 3 variables — taille, poids, âge, donc trois dimensions, et ce pour chacun des individus observés., un perceptron unique définit deux demi-plans.

Ci-dessus deux exemples en 2 dimensions (par simplicité j’ai supposé $W_0=0$ donc les droites passent par l’origine du plan). A gauche  l’algorithme trouve une frontière qui sépare correctement les observations positives ($y_i=1$) des négatives ($y_i=0$). A droite en revanche, aucune droite ne peut parfaitement séparer les positifs des négatifs. Ce qu’il nous faudrait, c’est pouvoir tracer plusieurs frontières rectilignes comme ci-dessous par exemple :


Rien de plus simple : si un perceptron trace une droite, $n$ perceptrons traceront $n$ droites, à partir des mêmes variables. Nous pouvons donc les mettre en réseau, par exemple comme ci-dessous.

Perceptron multiple

Ici, nous avons deux couches (« layers »), dont la première est dite « cachée » (hidden layer) car ses résultats ne sont pas directement accessibles à l’opérateur mais servent de valeur d’entrée à la couche suivante. En sortie de la deuxième couche, on obtient notre prédiction $y$.

Note mathématique : Les notations utilisées pour décrire les réseaux neuronaux sont souvent source de confusion à cause de la multiplication des indices. Une nouvelle fois, si vous n’êtes pas à l’aise avec les mathématiques employées dans ces articles, vous pouvez les ignorer et vous contenter de lire les explications en Français. Je détaille les conventions de notation en fin d’article.

On voit ici que nous avons à présent deux neurones travaillant en parallèle dans la première couche. Chacun effectue le même travail que notre perceptron unique : il pondère puis additionne les valeurs des variables $X_1$ et $X_2$ puis renvoie 1 si le résultat est positif, 0 dans le cas contraire. Les pondérations utilisées sont spécifiques à chaque neurone d’où la notation un peu complexe utilisée sur le schéma.

Chaque neurone de la première couche produit ainsi une nouvelle variable intermédiaire $X^{(2)}_{j}$ (avec $j$ = 1 ou 2). Le neurone de la deuxième couche utilise ces deux variables exactement de la même façon que ceux de la première couche : pondération (avec ses poids propres), somme, comparaison. Il renvoie 1 ou 0 selon que le résultat de ces opérations est positif ou non, ceci pour chaque individu de l’échantillon. Cette réponse constitue la prédiction finale de notre réseau.

Nous obtenons ainsi un degré de finesse plus important (ci-contre une simulation effectuée avec 4 neurones, sans terme de biais).

Néanmoins nous nous heurtons encore à deux limitations importantes :

  1. Il devient impossible de déterminer analytiquement les valeurs de $W^{(l)}_{j,k}$ qui permettent d’obtenir une erreur globale minimale,
  2. Les frontières restent nécessairement rectilignes, ce qui nous empêche de construire des modèles très complexes.

Optimisation numérique

Le premier problème nous contraint à recourir à des méthodes d’optimisation numérique, c’est-à-dire changer un tout petit peu chaque paramètre dans la « bonne » direction, calculer le résultat et répéter l’opération jusqu’à ce que la précision ne s’améliore plus. Pour cela, on définit une « fonction de perte » (loss function) qui quantifie l’écart entre les prévisions du modèle et les observations réelles du jeu de donnée utilisé pendant l’apprentissage. Tout le jeu de la phase d’apprentissage consistera à ajuster les paramètres $W^{(l)}_{j,k}$ de façon à minimiser cette fonction de perte.

Un exemple d’une telle fonction est $L = \frac{1}{m} \sum \limits_{i=1}^m (y_i – \hat y_i)^2$, dite somme des moindres carrés et utilisée en régression linéaire. Pour un problème de classification comme dans notre exemple précédent (obésité oui / non), une des fonctions les plus utilisées actuellement est le « softmax loss », qui convertit le résultat de la dernière couche de neurones en probabilité pour chaque classe (dans notre exemple, il n’y a que deux classes : obésité ou pas d’obésité, mais on rencontre fréquemment des problèmes comportant de multiples classes). La prédiction du modèle correspond donc à la classe la plus probable.

Mais l’optimisation d’un tel modèle est impossible avec la fonction d’activation que nous utilisions jusque là, à savoir la fonction marche (step).

HardLimitFunction.pngFonction marche

En effet, avec cette fonction disséminée dans tout notre réseau, le signal en sortie de chaque neurone peut passer de 0 à 1 de façon brutale en fonction de son signal d’entrée. Avec de multiples neurones, cela signifie que la précision des prédictions peut varier de façon erratique pour de tous petits ajustements des paramètres $W^{(l)}_{j,k}$, ce qui rend toute tentative d’optimisation illusoire.

Il faut donc recourir à des fonctions continues comme les fonctions sigmoïde, tangente hyperbolique ou encore ReLU (Rectified Linear Unit). Cette dernière est actuellement la plus utilisée en deep learning.SigmoidFunction.png

Fonction sigmoïde

Image result for relu activation

Fonction ReLU

Non-linéarité

L’autre avantage de ces fonctions d’activation dans notre réseau, c’est qu’elles permettent d’introduire des non-linéarités. En effet, avec la fonction marche, nous avions pour chaque neurone une réponse 0 ou 1, qui se traduisait par des frontières rectilignes entre les classes. Avec ces nouvelles fonctions d’activation, la réponse de chaque neurone peut maintenant prendre une infinité de valeurs. La combinaison successive de ces valeurs attribue une probabilité d’appartenance à chaque classe en sortie. On peut ainsi modéliser des phénomènes beaucoup plus subtils, ce qui résout le deuxième problème évoqué plus haut.

Pour « intuiter » cela, imaginez un paysage. En vous déplaçant dans le sens des latitudes et des longitudes, vous parcourez différentes valeurs de deux paramètres $W_1$ et $W_2$ respectivement. L’altitude représente la réponse $y$ du réseau. Avec uniquement des fonctions marche, le paysage ressemblerait à une plaine avec des plateaux, tous de même altitude, aux frontières rectilignes et aux flancs totalement verticaux. Avec les fonctions Sigmoïde ou ReLU, le paysage ressemble soudain beaucoup plus à ce à quoi nous sommes habitués : des plaines, des vallées et des plateaux aux flancs progressifs et aux formes plus complexes car résultant de la combinaison de plusieurs de ces fonctions dans des amplitudes et des orientations différentes.

Vers l’apprentissage

Si vous avez fait attention en début d’article, vous avez remarqué qu’en parlant de la phase d’optimisation numérique, je parlais de modifier légèrement les paramètres dans la « bonne » direction, sans préciser comment la déterminer. A présent que notre réseau n’est constitué que de fonctions continues, nous allons être capables de calculer cette direction à chaque itération en utilisant un objet mathématique appelé le gradient, et un algorithme intitulé gradient descent (ou algorithme du gradient en Français). Cela fera l’objet de notre prochain article.


Conventions de notation :

  • $i = \{1, …, m\}$ pour les indices des observations de l’échantillon (les individus par exemple)
  • $(l) = \{(1), …, (L)\}$ pour les indices des couches du réseau (moyen mnémotechnique : $l$ pour « layer »)
  • $j = \{1, …, n^{(l)}\}$ pour les indices des variables en entrée de la couche $(l)$. Le nombre de variables peut être différent pour chaque couche, d’où la notation $n^{(l)}$
  • $k = \{1,…, p^{(l)}\}$ pour les indices des neurones au sein de la couche $(l)$. Toutes les couches n’ont pas le même nombre de neurones, d’où la notation $p^{(l)}$
  • $y_i$ pour désigner le résultat connu pour l’observation $i$, c’est-à-dire ce dont on va essayer de s’approcher avec notre prédiction pendant la phase d’apprentissage
  • $\hat y_i$ pour désigner la prédiction finale du modèle pour l’observation $i$, que l’on compare à $\hat y_i$ pour évaluer la performance de notre modèle
  • $X_{i, j}$ pour désigner les variables d’entrée du réseau où $i$ correspond à l’observation considérée (par exemple, l’individu dans l’échantillon) et $j$ l’indice de la variable. Ainsi, pour reprendre notre exemple précédent, la taille (indice $1$) de l’individu n°10 sera notée : $X_{10, 1}$. En parlant des variables d’entrée de l’ensemble de l’échantillon en général, je prendrai un raccourci en notant simplement $X_j$ plutôt que $\{X_{1,j}, X_{2,j}, \, … \, , X_{m, j}\}$ (en réalité, $X_j$ est un vecteur contenant les valeurs $X_{i,j}$ pour tous les individus $i$).
  • De même, $X^{(l)}_{i,j}$ désigne l’observation n°$i$ de la $j^{ème}$ variable en entrée de la couche $(l)$. Par conséquent $X^{(1)}_j = X_j$ puisque l’entrée de la première couche est aussi l’entrée du réseau.
  • Pour ce qui concerne les paramètres du réseau (ce qu’on cherche à optimiser), $W^{(l)}_{j,k}$ désigne le poids appliqué à la variable $X^{(l)}_{j}$ par le neurone $k$ de la couche $(l)$. Il faut se souvenir que les poids sont communs à tous les individus observés d’où l’absence d’indice $i$.
  • Ainsi, dans le réseau dessiné ci-dessus, le neurone $2$ de la couche $(1)$ réalise l’opération suivante :

$$
X^{(2)}_2 = \sum\limits_{j=1}^n W^{(1)}_{j, 2} \times X^{(1)}_j
$$

Liens vers les articles précédents :
Introduction
Le neurone artificiel

References   [ + ]

1. Ici, ce que j’appelle le nombre de dimensions, c’est le nombre de variables en entrée du neurone. Dans l’exemple présenté précédemment, il y avait 3 variables — taille, poids, âge, donc trois dimensions, et ce pour chacun des individus observés.