Neurone biologique, neurone mathématique Grand Oral · mathématiques, SVT, NSI Un support de connaissances pour construire sa propre question

← Tous les textes du Grand Oral

MathsSVTNSI

Les logiciels qui reconnaissent un visage, traduisent un texte ou répondent à une question s'appuient sur des « réseaux de neurones artificiels ». Le mot vient de la biologie : en 1943, deux chercheurs ont voulu décrire par des mathématiques ce que fait une cellule nerveuse. Mais que fait vraiment un neurone ? Qu'est-ce qu'un neurone « mathématique » ? Et que veut dire, pour une machine, « apprendre » ?

Ce texte part de ce que l'on étudie en SVT sur le message nerveux, construit pas à pas le neurone mathématique, et montre comment il apprend. Toutes les mathématiques utilisées restent dans le programme du lycée : équations de droites, fonction exponentielle, dérivation, suites géométriques. En NSI, on rencontre en première une autre méthode d'apprentissage automatique, l'algorithme des $k$ plus proches voisins ; les neurones artificiels ne sont pas au programme, mais les programmes Python de ce texte n'utilisent que des boucles et des tests. Il rassemble des connaissances ; à chacun d'y choisir une direction et d'y construire sa question.

Sommaire
  1. Le neurone biologique
  2. Le neurone de McCulloch et Pitts
  3. Une frontière qui est une droite
  4. Le perceptron apprend
  5. Ce qu'un neurone ne sait pas faire
  6. Une fonction plus douce : la sigmoïde
  7. Apprendre en descendant la pente
  8. Aujourd'hui, et les limites de la comparaison
  9. Des directions pour construire sa question
Le neurone biologique Une cellule faite pour communiquer

Le cerveau humain contient environ $86$ milliards de neurones, et chacun est relié à plusieurs milliers d'autres. Un neurone est une cellule, avec un noyau, mais de forme très particulière :

  • les dendrites, de nombreux prolongements ramifiés, reçoivent les messages venus d'autres neurones ;
  • le corps cellulaire, qui contient le noyau, rassemble ces messages ;
  • l'axone, un prolongement unique qui peut mesurer de quelques millimètres à plus d'un mètre, transmet le message du neurone vers d'autres cellules ;
  • les synapses sont les zones de contact entre l'extrémité de l'axone d'un neurone et un autre neurone.
Dessin à l'encre d'un neurone : un corps cellulaire noir d'où partent vers le haut des dendrites très ramifiées et vers le bas un axone fin. Un neurone du cervelet (cellule de Purkinje) dessiné par Santiago Ramón y Cajal vers 1899 : les dendrites très ramifiées en haut, le corps cellulaire au centre, l'axone (a) en bas. Dessin : S. Ramón y Cajal, Wikimedia Commons, domaine public.

C'est l'Espagnol Santiago Ramón y Cajal (1852-1934) qui a montré, à la fin du XIXe siècle, que le système nerveux est fait de cellules séparées, les neurones, et non d'un réseau continu. Il a partagé le prix Nobel de médecine de 1906 avec l'Italien Camillo Golgi, dont la méthode de coloration lui avait permis de dessiner les neurones un par un.

Le message nerveux : tout ou rien

Au repos, l'intérieur d'un neurone est chargé négativement par rapport à l'extérieur : la différence de potentiel électrique entre les deux côtés de la membrane, appelée potentiel de repos, vaut environ $-70$ millivolts (mV).

Quand le neurone est stimulé, ce potentiel peut augmenter. S'il dépasse un seuil, d'environ $-50$ mV, il se produit un événement brutal et bref : le potentiel monte jusqu'à environ $+30$ mV puis revient à sa valeur de repos, le tout en une à deux millisecondes. C'est le potentiel d'action. Il se propage ensuite le long de l'axone, à une vitesse de $1$ à $100$ mètres par seconde selon les fibres.

  • Si le seuil n'est pas atteint, il ne se passe rien ; s'il est atteint, le potentiel d'action a toujours la même forme et la même hauteur. On dit qu'il obéit à la loi du tout ou rien.
  • Comment le neurone transmet-il alors l'intensité d'une stimulation ? Par le nombre de potentiels d'action par seconde : une stimulation forte produit des potentiels d'action plus rapprochés. On parle de codage en fréquence.

Les mécanismes du potentiel d'action ont été décrits en 1952 par les Britanniques Alan Hodgkin et Andrew Huxley, à partir de mesures sur l'axone géant du calmar (épais d'environ un demi-millimètre, assez pour y glisser une électrode). Ils l'ont mis en équations, avec des équations différentielles, et ont reçu le prix Nobel en 1963.

Les synapses et l'intégration

À une synapse, le potentiel d'action qui arrive au bout de l'axone provoque la libération d'une molécule, le neurotransmetteur, qui se fixe sur le neurone suivant et modifie son potentiel. Il y a deux sortes de synapses :

  • les synapses excitatrices rapprochent le potentiel du neurone suivant de son seuil (elles le font monter) ;
  • les synapses inhibitrices l'en éloignent (elles le font descendre).

Un neurone reçoit en même temps des milliers de messages, excitateurs et inhibiteurs. Il en fait en quelque sorte la somme, au niveau du début de son axone : c'est l'intégration. Si le total dépasse le seuil, il émet à son tour des potentiels d'action ; sinon, il reste silencieux. Cette somme se fait de deux façons :

  • sommation spatiale : des messages arrivés au même moment par des synapses différentes s'ajoutent ;
  • sommation temporelle : des messages arrivés très rapprochés par la même synapse s'ajoutent.

Toutes les synapses n'ont pas le même effet : certaines sont plus « efficaces » que d'autres. Retenons ces trois idées, qui vont servir de modèle : chaque entrée a un poids, positif ou négatif ; le neurone fait la somme ; il compare cette somme à un seuil.

La plasticité : les connexions changent

En 1949, le psychologue canadien Donald Hebb propose une idée qui va beaucoup compter : quand un neurone participe souvent à l'activation d'un autre, la synapse qui les relie se renforce. On la résume souvent par une formule anglaise : « neurons that fire together wire together » (les neurones qui s'activent ensemble se connectent ensemble). En 1973, Timothy Bliss et Terje Lømo observent effectivement, dans l'hippocampe du lapin, des synapses dont l'efficacité augmente durablement après avoir été très sollicitées. C'est l'une des bases de la plasticité cérébrale : le cerveau apprend en modifiant l'efficacité de ses connexions. C'est exactement l'idée que reprendront les neurones artificiels : apprendre, c'est modifier les poids.

Image de microscopie sur fond noir : de nombreux corps cellulaires lumineux verts reliés par un réseau de fins filaments. Des neurones en culture, rendus fluorescents, qui ont formé un réseau de connexions. Photo : ManuelSchottdorf, Wikimedia Commons, licence CC BY-SA 4.0.
Le neurone de McCulloch et Pitts La définition

En 1943, le neurologue américain Warren McCulloch et le jeune logicien Walter Pitts, âgé de vingt ans, publient un article qui propose un neurone « formel », c'est-à-dire réduit à une règle de calcul. Ils gardent du neurone biologique le tout ou rien, la somme et le seuil. Dans sa forme la plus simple, avec deux entrées :

  • les entrées $x_1$ et $x_2$ valent $0$ ou $1$ (pas de message ou un message) ;
  • chaque entrée a un poids, $w_1$ et $w_2$, un nombre réel : positif pour une synapse excitatrice, négatif pour une synapse inhibitrice ;
  • le neurone calcule la somme pondérée à laquelle il ajoute un nombre $b$, appelé biais :
$$z = w_1x_1 + w_2x_2 + b\,;$$
  • la sortie $s$ vaut $1$ si $z > 0$, et $0$ sinon.

Le biais joue le rôle du seuil : dire que $z > 0$, c'est dire que $w_1x_1 + w_2x_2 > -b$. Le seuil est donc $-b$ : plus $b$ est négatif, plus il est difficile de déclencher le neurone.

var opt = {fontSize: 13, useKatex: false, useMathJax: false, parse: false, fixed: true, highlight: false, anchorX: 'middle', anchorY: 'middle'}; var lo = {strokeColor: '#455a64', strokeWidth: 1.5, fixed: true, highlight: false, lastArrow: {type: 2, size: 6}}; board.create('circle', [[0.6, 3.3], 0.45], {strokeColor: '#1565c0', fillColor: '#e3f2fd', fixed: true, highlight: false}); board.create('circle', [[0.6, 0.7], 0.45], {strokeColor: '#1565c0', fillColor: '#e3f2fd', fixed: true, highlight: false}); board.create('text', [0.6, 3.3, 'x1'], opt); board.create('text', [0.6, 0.7, 'x2'], opt); board.create('circle', [[4.3, 2], 0.7], {strokeColor: '#c62828', fillColor: '#ffebee', fixed: true, highlight: false}); board.create('text', [4.3, 2, 'z'], Object.assign({}, opt, {fontSize: 16})); board.create('text', [4.3, 0.85, 'somme'], Object.assign({}, opt, {fontSize: 11})); board.create('segment', [[1.05, 3.15], [3.65, 2.25]], lo); board.create('segment', [[1.05, 0.85], [3.65, 1.75]], lo); board.create('text', [2.2, 3.45, 'poids w1'], Object.assign({}, opt, {fontSize: 11})); board.create('text', [2.2, 0.55, 'poids w2'], Object.assign({}, opt, {fontSize: 11})); board.create('segment', [[4.3, 4.3], [4.3, 2.75]], lo); board.create('text', [5.3, 4.1, 'biais b'], Object.assign({}, opt, {fontSize: 11})); board.create('polygon', [[5.75, 1.25], [8.95, 1.25], [8.95, 2.75], [5.75, 2.75]], {fillColor: '#fff8e1', borders: {strokeColor: '#ef6c00'}, vertices: {visible: false}, fixed: true, highlight: false}); board.create('text', [7.35, 2.3, 'z > 0 ?'], Object.assign({}, opt, {fontSize: 12})); board.create('text', [7.35, 1.7, 'oui : 1, non : 0'], Object.assign({}, opt, {fontSize: 10})); board.create('segment', [[5.0, 2], [5.75, 2]], lo); board.create('segment', [[8.95, 2], [9.75, 2]], lo); board.create('text', [10.05, 2, 's'], opt); Le neurone de McCulloch et Pitts : les entrées sont multipliées par leurs poids, additionnées avec le biais, et la sortie vaut $1$ si cette somme est strictement positive.
neurone biologiqueneurone mathématique
messages reçus (ou non)entrées $x_1$, $x_2$ valant $1$ ou $0$
efficacité d'une synapsepoids $w_1$, $w_2$
synapse excitatrice ou inhibitricepoids positif ou négatif
sommation spatialesomme $w_1x_1 + w_2x_2$
seuil du potentiel d'actionseuil $-b$
tout ou riensortie $1$ ou $0$
Un neurone qui calcule « ET »

McCulloch et Pitts montrent que leurs neurones savent faire des opérations de logique. Prenons $w_1 = 1$, $w_2 = 1$ et $b = -1{,}5$. Calculons la sortie pour les quatre cas possibles :

$x_1$$x_2$$z = x_1 + x_2 - 1{,}5$sortie $s$
$0$$0$$-1{,}5$$0$
$0$$1$$-0{,}5$$0$
$1$$0$$-0{,}5$$0$
$1$$1$$0{,}5$$1$

La sortie vaut $1$ seulement quand les deux entrées valent $1$ : ce neurone calcule « $x_1$ ET $x_2$ ». Le seuil $1{,}5$ est trop haut pour une seule entrée, mais il est dépassé par deux entrées ensemble.

« OU » et « NON »

Le OU. Gardons les poids $1$ et $1$ mais baissons le seuil : $b = -0{,}5$. Alors $z = x_1 + x_2 - 0{,}5$ : il vaut $-0{,}5$ quand les deux entrées sont nulles, et au moins $0{,}5$ dès qu'une entrée vaut $1$. La sortie vaut $1$ dès qu'au moins une entrée vaut $1$ : c'est « $x_1$ OU $x_2$ ».

Le NON. Avec une seule entrée, un poids négatif (une synapse inhibitrice) et un biais positif : $w_1 = -1$ et $b = 0{,}5$. Si $x_1 = 0$, $z = 0{,}5 > 0$ et la sortie vaut $1$ ; si $x_1 = 1$, $z = -0{,}5$ et la sortie vaut $0$. Le neurone renvoie le contraire de son entrée.

Avec ET, OU et NON, on peut construire n'importe quel calcul logique, donc, en principe, tout ce que fait un ordinateur. McCulloch et Pitts en concluaient qu'un réseau de neurones assez grand pouvait tout calculer. Mais il restait une question : dans leur modèle, c'est l'humain qui choisit les poids. Comment un réseau pourrait-il les trouver tout seul ?

Une frontière qui est une droite

Regardons le neurone autrement. Les deux entrées forment un point $M(x_1\,;x_2)$ du plan, et l'on peut d'ailleurs autoriser des entrées qui ne valent pas seulement $0$ ou $1$ : par exemple la taille et le poids d'un animal, ou deux mesures faites sur une image. Le neurone répond $1$ ou $0$ selon le point. Quels points donnent $1$ ?

Ce sont les points tels que $w_1x_1 + w_2x_2 + b > 0$. Or, en classe de première, on a vu que l'ensemble des points $M(x\,;y)$ tels que $ax + by + c = 0$, avec $a$ et $b$ non tous deux nuls, est une droite, dont le vecteur $\vec{n}(a\,;b)$ est un vecteur normal (orthogonal à la droite). Ici, l'équation $w_1x_1 + w_2x_2 + b = 0$ est donc celle d'une droite $\mathcal{D}$, de vecteur normal $\vec{n}(w_1\,;w_2)$.

Exemple : le neurone ET. La frontière a pour équation $x_1 + x_2 - 1{,}5 = 0$, c'est-à-dire $x_2 = -x_1 + 1{,}5$ : une droite de pente $-1$ qui coupe l'axe des ordonnées en $1{,}5$. Le point $(1\,;1)$ est au-dessus, les trois autres en dessous. Pour le OU, la droite est $x_2 = -x_1 + 0{,}5$ : seul le point $(0\,;0)$ est en dessous.

Un neurone découpe donc le plan en deux par une droite. On dit qu'il réalise une classification linéaire : il range chaque point dans l'une ou l'autre de deux catégories, selon le côté de la droite où il se trouve. « Apprendre » va consister à trouver une bonne droite, c'est-à-dire de bons nombres $w_1$, $w_2$ et $b$.

Le perceptron apprend Rosenblatt et la machine Mark I

En 1958, le psychologue américain Frank Rosenblatt présente le perceptron : un neurone de McCulloch et Pitts qui trouve lui-même ses poids, à partir d'exemples. Il en construit une version électrique, le « Mark I Perceptron », reliée à une caméra de $20 \times 20 = 400$ cellules photoélectriques, capable d'apprendre à reconnaître des formes simples. Les poids y sont réglés par des potentiomètres que des moteurs électriques tournent pendant l'apprentissage. La presse de l'époque s'enthousiasme, parfois bien au-delà de ce que la machine savait faire.

Photographie en noir et blanc d'une grande armoire électronique avec des panneaux de câblage, et un appareil photo sur trépied devant. Le Mark I Perceptron, d'après le manuel d'utilisation de 1960. À gauche, la caméra ; au centre, les panneaux de connexions. Image : J. C. Hay, A. E. Murray, Wikimedia Commons, domaine public.
La règle d'apprentissage

On dispose d'exemples dont on connaît la bonne réponse $y$ ($0$ ou $1$). On part de poids quelconques, par exemple tous nuls, et l'on présente les exemples un par un. Pour chaque exemple $(x_1\,;x_2)$ :

  1. on calcule la sortie $s$ du neurone ;
  2. on calcule l'erreur $e = y - s$. Elle vaut $0$ si le neurone a bien répondu, $1$ s'il a répondu $0$ au lieu de $1$, et $-1$ s'il a répondu $1$ au lieu de $0$ ;
  3. on corrige les poids et le biais :
  • $w_1 \leftarrow w_1 + e\,x_1$ ;
  • $w_2 \leftarrow w_2 + e\,x_2$ ;
  • $b \leftarrow b + e$.

(La flèche signifie « prend la nouvelle valeur ».) Si le neurone a bien répondu, $e = 0$ et rien ne change. On recommence avec tous les exemples, plusieurs fois, jusqu'à ce qu'il n'y ait plus d'erreur.

Pourquoi la correction va dans le bon sens

Supposons que le neurone ait répondu $0$ alors qu'il fallait $1$ : $z \leqslant 0$ et $e = 1$. Les nouveaux poids sont $w_1 + x_1$, $w_2 + x_2$ et $b + 1$. Recalculons la somme $z'$ pour le même exemple, terme par terme :

  • $(w_1 + x_1)\,x_1 = w_1x_1 + x_1^2$ ;
  • $(w_2 + x_2)\,x_2 = w_2x_2 + x_2^2$ ;
  • le biais devient $b + 1$.

En additionnant, on retrouve $z = w_1x_1 + w_2x_2 + b$ et les termes en plus :

$$z' = z + x_1^2 + x_2^2 + 1.$$

Comme $x_1^2 + x_2^2 + 1 \geqslant 1$, on a $z' \geqslant z + 1$ : la somme a augmenté d'au moins $1$, elle se rapproche de la zone où le neurone répond $1$. Dans le cas inverse ($e = -1$), le même calcul donne $z' = z - (x_1^2 + x_2^2 + 1)$ : la somme diminue. C'est l'idée de Hebb, transformée en calcul : on renforce ou on affaiblit les connexions selon le résultat.

Un apprentissage à la main : le OU

Apprenons le OU au perceptron, à partir de $w_1 = w_2 = b = 0$. On présente les quatre exemples dans l'ordre, et l'on recommence. Chaque ligne donne les poids avant l'exemple, le calcul, et la correction. Les lignes où le neurone se trompe sont en rose.

tour$(x_1\,;x_2)$$y$$(w_1\,;w_2\,;b)$$z$$s$$e$
1$(0\,;0)$$0$$(0\,;0\,;0)$$0$$0$$0$
1$(0\,;1)$$1$$(0\,;0\,;0)$$0$$0$$1$
1$(1\,;0)$$1$$(0\,;1\,;1)$$1$$1$$0$
1$(1\,;1)$$1$$(0\,;1\,;1)$$2$$1$$0$
2$(0\,;0)$$0$$(0\,;1\,;1)$$1$$1$$-1$
2$(0\,;1)$$1$$(0\,;1\,;0)$$1$$1$$0$
2$(1\,;0)$$1$$(0\,;1\,;0)$$0$$0$$1$
2$(1\,;1)$$1$$(1\,;1\,;1)$$3$$1$$0$
3$(0\,;0)$$0$$(1\,;1\,;1)$$1$$1$$-1$
3$(0\,;1)$$1$$(1\,;1\,;0)$$1$$1$$0$
3$(1\,;0)$$1$$(1\,;1\,;0)$$1$$1$$0$
3$(1\,;1)$$1$$(1\,;1\,;0)$$2$$1$$0$

Détaillons la deuxième ligne : avec des poids nuls, $z = 0 \times 0 + 0 \times 1 + 0 = 0$, qui n'est pas strictement positif, donc $s = 0$, alors qu'il fallait $y = 1$ : $e = 1 - 0 = 1$. On corrige : $w_1 \leftarrow 0 + 1 \times 0 = 0$, $w_2 \leftarrow 0 + 1 \times 1 = 1$, $b \leftarrow 0 + 1 = 1$.

Au troisième tour, après la première ligne, plus aucune erreur : le perceptron a trouvé $w_1 = 1$, $w_2 = 1$ et $b = 0$, c'est-à-dire la sortie $1$ si $x_1 + x_2 > 0$. C'est bien le OU, avec d'autres nombres que ceux choisis plus haut : il y a beaucoup de droites qui conviennent, et l'apprentissage en a trouvé une.

Ça marche toujours ? Un théorème démontré au début des années 1960 (on l'admet ici) affirme que si une droite sépare parfaitement les exemples de réponse $1$ de ceux de réponse $0$, alors l'apprentissage s'arrête au bout d'un nombre fini de corrections, sur une droite qui les sépare. La figure suivante le montre sur des points placés au hasard.

var opt = {fontSize: 12, useKatex: false, useMathJax: false, parse: false, fixed: true, highlight: false}; var bopt = {fontSize: 11, useKatex: false, useMathJax: false, parse: false}; board.create('polygon', [[0, 0], [1, 0], [1, 1], [0, 1]], {fillColor: '#fafafa', fillOpacity: 1, borders: {strokeColor: '#b0bec5', strokeWidth: 1}, vertices: {visible: false}, fixed: true, highlight: false}); var P = [], pts = [], w1 = 0, w2 = 0, b = 0, k = 0, corr = 0, actif = false, cur = -1, fini = false; function nouveaux() { for (var i = 0; i < pts.length; i++) { board.removeObject(pts[i]); } P = []; pts = []; var a = 0.5 + Math.random(), c = -0.5 - Math.random(), d = -(a * 0.5 + c * 0.5) + (Math.random() - 0.5) * 0.3; while (P.length < 30) { var x = 0.05 + 0.9 * Math.random(), y = 0.05 + 0.9 * Math.random(), v = a * x + c * y + d; if (Math.abs(v) < 0.1) { continue; } P.push([x, y, v > 0 ? 1 : 0]); } for (var i = 0; i < P.length; i++) { (function (i) { pts.push(board.create('point', [P[i][0], P[i][1]], {name: '', size: 4, fixed: true, highlight: false, showInfobox: false, fillColor: P[i][2] ? '#1565c0' : '#c62828', strokeColor: function () { return i === cur ? '#000' : (sortie(i) === P[i][2] ? (P[i][2] ? '#1565c0' : '#c62828') : '#ff9800'); }, strokeWidth: function () { return i === cur ? 3 : (sortie(i) === P[i][2] ? 1 : 3); }})); })(i); } w1 = -a; w2 = -c; b = 0; k = 0; corr = 0; cur = -1; fini = false; } function sortie(i) { return (w1 * P[i][0] + w2 * P[i][1] + b > 0) ? 1 : 0; } function erreurs() { var n = 0; for (var i = 0; i < P.length; i++) { if (sortie(i) !== P[i][2]) { n++; } } return n; } function etape() { if (fini) { return; } cur = k % P.length; var e = P[cur][2] - sortie(cur); if (e !== 0) { w1 += e * P[cur][0]; w2 += e * P[cur][1]; b += e; corr++; } k++; if (erreurs() === 0) { fini = true; actif = false; } } var nul = function () { return w1 === 0 && w2 === 0; }; var dr = board.create('line', [function () { return nul() ? -100 : b; }, function () { return nul() ? 0 : w1; }, function () { return nul() ? 1 : w2; }], {strokeColor: '#2e7d32', strokeWidth: 2, fixed: true, highlight: false}); board.create('text', [0, 1.25, function () { return 'w1 = ' + w1.toFixed(2).replace('.', ',') + ' w2 = ' + w2.toFixed(2).replace('.', ',') + ' b = ' + b; }], opt); board.create('text', [0, 1.13, function () { return 'exemples vus : ' + k + ' corrections : ' + corr + ' erreurs : ' + erreurs(); }], opt); board.create('text', [0, 1.04, function () { return fini ? 'plus aucune erreur : la droite sépare les deux couleurs' : ''; }], Object.assign({}, opt, {strokeColor: '#2e7d32'})); board.create('button', [0, -0.1, function () { return actif ? 'Pause' : 'Lancer'; }, function () { if (!fini) { actif = !actif; } board.update(); }], bopt); board.create('button', [0.27, -0.1, 'Une étape', function () { actif = false; etape(); board.update(); }], bopt); board.create('button', [0.62, -0.1, 'Nouveaux points', function () { actif = false; nouveaux(); board.update(); }], bopt); board.create('text', [0, -0.25, 'bleu : réponse 1, rouge : réponse 0 ; cerclé orange : mal classé'], Object.assign({}, opt, {fontSize: 10})); nouveaux(); board.update(); setInterval(function () { if (actif) { etape(); board.update(); } }, 150); Le perceptron apprend à séparer les points bleus (réponse $1$) des points rouges (réponse $0$). Il part d'une droite placée au hasard. À chaque étape, il examine un point (cerclé de noir) ; s'il le classe mal, il corrige ses poids, et la droite verte bouge.
Ce qu'un neurone ne sait pas faire Le OU exclusif est impossible

Le OU exclusif (noté XOR en informatique) vaut $1$ quand une seule des deux entrées vaut $1$ : « fromage ou dessert », mais pas les deux.

$x_1$$x_2$$x_1$ XOR $x_2$
$0$$0$$0$
$0$$1$$1$
$1$$0$$1$
$1$$1$$0$
Aucun neurone seul ne calcule le OU exclusif

Raisonnons par l'absurde : supposons qu'il existe des nombres $w_1$, $w_2$ et $b$ tels que le neurone calcule le OU exclusif. Traduisons chaque ligne du tableau par une inégalité.

  • $(0\,;0)$ doit donner $0$ : $b \leqslant 0$.
  • $(1\,;0)$ doit donner $1$ : $w_1 + b > 0$.
  • $(0\,;1)$ doit donner $1$ : $w_2 + b > 0$.
  • $(1\,;1)$ doit donner $0$ : $w_1 + w_2 + b \leqslant 0$.

Additionnons les deuxième et troisième inégalités : $w_1 + w_2 + 2b > 0$, c'est-à-dire $w_1 + w_2 + b > -b$. Or, d'après la première, $-b \geqslant 0$. Donc $w_1 + w_2 + b > 0$, ce qui contredit la quatrième inégalité.

La supposition de départ est donc fausse : aucun choix de $w_1$, $w_2$ et $b$ ne convient.

Géométriquement, c'est évident sur la figure ci-dessous : les points de réponse $1$, $(1\,;0)$ et $(0\,;1)$, sont sur une diagonale du carré, ceux de réponse $0$ sur l'autre ; aucune droite ne peut mettre les deux premiers d'un côté et les deux autres de l'autre.

En 1969, les Américains Marvin Minsky et Seymour Papert publient un livre, Perceptrons, qui démontre rigoureusement ce genre de limites. L'enthousiasme retombe, les financements aussi : c'est le début de ce qu'on a appelé le premier « hiver de l'intelligence artificielle ».

La solution : plusieurs couches

Ce qu'un neurone ne sait pas faire, trois neurones le peuvent, si on les branche les uns sur les autres. On remarque que « une seule entrée vaut $1$ » revient à « au moins une vaut $1$, mais pas les deux », c'est-à-dire « OU, et NON ET ».

  • Un premier neurone calcule $h_1 = x_1$ OU $x_2$ (poids $1$ et $1$, biais $-0{,}5$).
  • Un deuxième calcule $h_2 = x_1$ ET $x_2$ (poids $1$ et $1$, biais $-1{,}5$).
  • Un troisième reçoit $h_1$ et $h_2$ et calcule $s$ : sa somme est $z = h_1 - h_2 - 0{,}5$ (poids $1$ et $-1$, biais $-0{,}5$).
$x_1$$x_2$$h_1$ (OU)$h_2$ (ET)$z = h_1 - h_2 - 0{,}5$$s$
$0$$0$$0$$0$$-0{,}5$$0$
$0$$1$$1$$0$$0{,}5$$1$
$1$$0$$1$$0$$0{,}5$$1$
$1$$1$$1$$1$$-0{,}5$$0$

La sortie est bien le OU exclusif. Les neurones $h_1$ et $h_2$ forment une couche cachée, entre les entrées et la sortie. Géométriquement, chacun trace une droite, et le dernier neurone combine les deux : la zone de réponse $1$ est la bande comprise entre les deux droites.

var opt = {fontSize: 12, useKatex: false, useMathJax: false, parse: false, fixed: true, highlight: false}; board.create('polygon', [[-0.45, 0.95], [0.95, -0.45], [1.55, -0.45], [1.55, -0.05], [-0.05, 1.55], [-0.45, 1.55]], {fillColor: '#bbdefb', fillOpacity: 0.6, borders: {visible: false}, vertices: {visible: false}, fixed: true, highlight: false}); board.create('line', [[0, 0.5], [0.5, 0]], {strokeColor: '#ef6c00', strokeWidth: 2, fixed: true, highlight: false}); board.create('line', [[0, 1.5], [1.5, 0]], {strokeColor: '#6a1b9a', strokeWidth: 2, fixed: true, highlight: false}); board.create('point', [0, 0], {name: '', size: 5, color: '#c62828', fixed: true}); board.create('point', [1, 1], {name: '', size: 5, color: '#c62828', fixed: true}); board.create('point', [1, 0], {name: '', size: 5, color: '#1565c0', fixed: true}); board.create('point', [0, 1], {name: '', size: 5, color: '#1565c0', fixed: true}); board.create('text', [0.6, 1.42, 'droite de h2 (ET)'], Object.assign({}, opt, {strokeColor: '#6a1b9a'})); board.create('polygon', [[0, 0], [1, 0], [1, 1], [0, 1]], {fillOpacity: 0, borders: {strokeColor: '#90a4ae', dash: 2, strokeWidth: 1}, vertices: {visible: false}, fixed: true, highlight: false}); board.create('text', [-0.32, -0.1, '(0 ; 0)'], opt); board.create('text', [1.06, -0.1, '(1 ; 0)'], opt); board.create('text', [-0.32, 1.12, '(0 ; 1)'], opt); board.create('text', [1.06, 1.12, '(1 ; 1)'], opt); board.create('text', [0.05, -0.36, 'droite de h1 (OU)'], Object.assign({}, opt, {strokeColor: '#ef6c00'})); Le OU exclusif : les points bleus doivent donner $1$, les rouges $0$. Aucune droite seule ne les sépare. Avec deux neurones cachés, la zone de réponse $1$ est la bande bleue entre les deux droites.

Il restait un problème, que le perceptron ne résout pas : dans un réseau à plusieurs couches, comment savoir quels poids corriger quand la sortie est fausse ? La réponse passe par la dérivation.

Une fonction plus douce : la sigmoïde Le défaut du tout ou rien

La sortie du perceptron est donnée par une fonction en escalier : $0$ si $z \leqslant 0$, $1$ si $z > 0$. Cette fonction est constante par morceaux : sa dérivée est nulle partout (sauf en $0$, où elle n'est pas dérivable). Une petite modification d'un poids ne change donc presque jamais la sortie ; et quand elle la change, c'est d'un coup. Impossible de savoir dans quel sens modifier un poids pour améliorer « un peu » le résultat.

On remplace donc l'escalier par une fonction qui passe de $0$ à $1$ de façon continue et dérivable : la fonction sigmoïde, définie sur $\mathbb{R}$ par

$$\sigma(x) = \frac{1}{1 + \mathrm{e}^{-x}}.$$

Elle a aussi un sens biologique : la fréquence des potentiels d'action d'un neurone augmente avec la stimulation, d'abord lentement, puis vite, puis elle plafonne, car un neurone ne peut pas émettre plus de quelques centaines de potentiels d'action par seconde. La sigmoïde a exactement cette allure, si l'on interprète $\sigma(x)$ comme une fréquence ramenée entre $0$ et $1$.

Étude de la fonction sigmoïde Limites, dérivée, variations

Limites. Quand $x$ tend vers $+\infty$, $-x$ tend vers $-\infty$ donc $\mathrm{e}^{-x}$ tend vers $0$ : par quotient, $\sigma(x)$ tend vers $\dfrac{1}{1+0} = 1$. Quand $x$ tend vers $-\infty$, $\mathrm{e}^{-x}$ tend vers $+\infty$, donc $1 + \mathrm{e}^{-x}$ aussi, et $\sigma(x)$ tend vers $0$. La courbe a deux asymptotes horizontales, d'équations $y = 0$ et $y = 1$.

Dérivée. Posons $u(x) = 1 + \mathrm{e}^{-x}$ : $u$ est dérivable, strictement positive, et $u'(x) = -\mathrm{e}^{-x}$. Comme $\sigma = \dfrac{1}{u}$, la formule $\left(\dfrac{1}{u}\right)' = -\dfrac{u'}{u^2}$ donne

$$\sigma'(x) = \frac{\mathrm{e}^{-x}}{\left(1 + \mathrm{e}^{-x}\right)^2}.$$

Variations. Le numérateur et le dénominateur sont strictement positifs, donc $\sigma'(x) > 0$ : $\sigma$ est strictement croissante sur $\mathbb{R}$. Avec les limites, on en déduit que $0 < \sigma(x) < 1$ pour tout réel $x$.

Une formule remarquable. Calculons $1 - \sigma(x)$ :

$$1 - \sigma(x) = \frac{1 + \mathrm{e}^{-x} - 1}{1 + \mathrm{e}^{-x}} = \frac{\mathrm{e}^{-x}}{1 + \mathrm{e}^{-x}}.$$

En multipliant par $\sigma(x) = \dfrac{1}{1 + \mathrm{e}^{-x}}$, on retrouve exactement $\sigma'(x)$ :

$$\sigma'(x) = \sigma(x)\big(1 - \sigma(x)\big).$$

Cette formule permet de calculer la dérivée directement à partir de la valeur de $\sigma$, ce qui économise beaucoup de calculs dans un réseau. Par exemple, $\sigma(0) = \dfrac{1}{1 + 1} = \dfrac{1}{2}$ et $\sigma'(0) = \dfrac{1}{2} \times \dfrac{1}{2} = \dfrac{1}{4}$.

On peut aussi montrer que $\sigma(-x) = 1 - \sigma(x)$ (multiplier le numérateur et le dénominateur de $\sigma(-x)$ par $\mathrm{e}^{-x}$) : la courbe est symétrique par rapport au point $\left(0\,;\tfrac{1}{2}\right)$. Cette fonction n'est pas nouvelle : c'est la courbe de croissance d'une population limitée, étudiée par Verhulst en 1838 (voir le texte Les systèmes proies-prédateurs).

var opt = {fontSize: 12, useKatex: false, useMathJax: false, parse: false, fixed: true, highlight: false}; var k = board.create('slider', [[-6, 0.8], [-2.2, 0.8], [0.5, 1, 10]], {withLabel: false, snapWidth: 0.5}); board.create('text', [-6, 0.6, function () { return 'k = ' + k.Value().toFixed(1).replace('.', ','); }], opt); board.create('functiongraph', [function (x) { return x > 0 ? 1 : 0; }, -6.5, 6.5], {strokeColor: '#ef6c00', strokeWidth: 2, dash: 2, highlight: false}); board.create('functiongraph', [function (x) { return 1 / (1 + Math.exp(-k.Value() * x)); }, -6.5, 6.5], {strokeColor: '#c62828', strokeWidth: 2.5, highlight: false}); En rouge, la courbe de $x \mapsto \sigma(kx)$ ; en pointillés orange, la fonction en escalier du perceptron. Quand $k$ augmente avec le curseur, la sigmoïde se rapproche de l'escalier, tout en restant dérivable.
Apprendre en descendant la pente L'idée : suivre le signe de la dérivée

Apprendre, c'est rendre l'erreur la plus petite possible. Imaginons que l'erreur dépende d'un seul poids $w$ : c'est une fonction $E(w)$, et l'on cherche son minimum. Le cours de terminale dit comment savoir dans quel sens aller :

  • si $E'(w) > 0$, la fonction est croissante autour de $w$ : pour diminuer l'erreur, il faut diminuer $w$ ;
  • si $E'(w) < 0$, elle est décroissante : il faut augmenter $w$.

Dans les deux cas, on va dans le sens de $-E'(w)$. D'où la méthode de descente de gradient : on choisit un nombre $\eta > 0$ (la lettre grecque « êta »), appelé pas d'apprentissage, et l'on construit la suite

$$w_{n+1} = w_n - \eta\,E'(w_n).$$

C'est l'image d'un randonneur dans le brouillard qui veut descendre au fond d'une vallée : il sent la pente sous ses pieds et fait un pas vers le bas, d'autant plus grand que la pente est forte.

Un exemple que l'on sait étudier entièrement

Prenons $E(w) = (w - 3)^2 + 1$, dont le minimum est évidemment atteint en $w = 3$. On a $E'(w) = 2(w - 3)$, donc

$$w_{n+1} = w_n - 2\eta\,(w_n - 3).$$ Quand la méthode converge-t-elle ?

Posons $v_n = w_n - 3$ (l'écart au minimum). Alors

$v_{n+1}$ & $=$ & $w_{n+1} - 3$ & & $=$ & $w_n - 3 - 2\eta\,(w_n - 3)$ & & $=$ & $(1 - 2\eta)\,v_n$ &

La suite $(v_n)$ est géométrique de raison $q = 1 - 2\eta$, donc $v_n = q^n v_0$. D'après le cours sur les suites géométriques, si $v_0 \neq 0$, $(v_n)$ tend vers $0$ si et seulement si $-1 < q < 1$, c'est-à-dire

$$-1 < 1 - 2\eta < 1 \iff 0 < \eta < 1.$$

La méthode converge vers le minimum si et seulement si $0 < \eta < 1$.

pas $\eta$raison $q = 1 - 2\eta$comportement de $w_n$
$0{,}1$$0{,}8$s'approche de $3$, lentement
$0{,}25$$0{,}5$l'écart est divisé par $2$ à chaque pas
$0{,}5$$0$atteint $3$ en un seul pas
$0{,}75$$-0{,}5$s'approche de $3$ en sautant d'un côté à l'autre
$1{,}5$$-2$s'éloigne de plus en plus : la méthode diverge

C'est une leçon générale : un pas trop petit rend l'apprentissage lent, un pas trop grand le rend instable. Le choix de $\eta$ est l'un des réglages délicats des réseaux de neurones.

Un neurone sigmoïde qui apprend

Prenons maintenant un neurone avec une seule entrée, qui vaut toujours $1$, un poids $w$ et la sigmoïde en sortie : sa sortie est $\sigma(w)$. On veut qu'il réponde $0{,}9$. On mesure l'erreur par le carré de l'écart :

$$E(w) = \big(\sigma(w) - 0{,}9\big)^2.$$

La dérivée. La fonction $E$ est de la forme $u^2$ avec $u(w) = \sigma(w) - 0{,}9$, et $(u^2)' = 2u\,u'$. Avec la formule $\sigma' = \sigma(1 - \sigma)$ :

$$E'(w) = 2\big(\sigma(w) - 0{,}9\big)\,\sigma(w)\big(1 - \sigma(w)\big).$$

Le minimum exact. Comme $\sigma(w)\big(1 - \sigma(w)\big) > 0$, $E'(w)$ a le signe de $\sigma(w) - 0{,}9$. Il s'annule quand $\sigma(w) = 0{,}9$. Résolvons cette équation, qui s'écrit $\dfrac{1}{1 + \mathrm{e}^{-w}} = 0{,}9$ :

  • en prenant l'inverse des deux membres : $1 + \mathrm{e}^{-w} = \dfrac{10}{9}$ ;
  • en retranchant $1$ : $\mathrm{e}^{-w} = \dfrac{1}{9}$ ;
  • en appliquant le logarithme : $-w = \ln\dfrac{1}{9} = -\ln 9$, donc $w = \ln 9 \approx 2{,}197$.

$E$ est décroissante puis croissante, et son minimum, $0$, est atteint en $w = \ln 9$. Ici, on sait trouver la réponse par le calcul ; dans un vrai réseau, avec des millions de poids, on ne le sait pas, d'où la descente de gradient.

La descente, à la main. Partons de $w_0 = 0$ avec un pas $\eta = 5$.

  • $\sigma(0) = 0{,}5$, donc $\sigma(0)\big(1 - \sigma(0)\big) = 0{,}25$ et $E'(0) = 2 \times (-0{,}4) \times 0{,}25 = -0{,}2$.
  • $w_1 = 0 - 5 \times (-0{,}2) = 1$. Puis $\sigma(1) \approx 0{,}731$ : la sortie s'est rapprochée de $0{,}9$.
  • $\sigma(1)\big(1 - \sigma(1)\big) \approx 0{,}197$, donc $E'(1) \approx 2 \times (-0{,}169) \times 0{,}197$, soit $E'(1) \approx -0{,}066$.
  • $w_2 \approx 1 + 5 \times 0{,}066 \approx 1{,}332$, et $\sigma(w_2) \approx 0{,}791$.
$n$$0$$1$$2$$3$$5$$10$
$w_n$$0$$1$$1{,}332$$1{,}512$$1{,}718$$1{,}951$
$\sigma(w_n)$$0{,}5$$0{,}731$$0{,}791$$0{,}819$$0{,}848$$0{,}876$

La sortie se rapproche de $0{,}9$ à chaque pas, de plus en plus lentement, car la pente diminue près du minimum.

var opt = {fontSize: 12, useKatex: false, useMathJax: false, parse: false, fixed: true, highlight: false}; var bopt = {fontSize: 11, useKatex: false, useMathJax: false, parse: false}; function s(w) { return 1 / (1 + Math.exp(-w)); } function E(w) { return Math.pow(s(w) - 0.9, 2); } function dE(w) { return 2 * (s(w) - 0.9) * s(w) * (1 - s(w)); } board.create('functiongraph', [E, -0.1, 6.3], {strokeColor: '#2e7d32', strokeWidth: 2, highlight: false}); var eta = 5, W = [0]; var ch = board.create('curve', [[0], [0]], {strokeColor: '#c62828', strokeWidth: 1.5, highlight: false}); ch.updateDataArray = function () { this.dataX = []; this.dataY = []; for (var i = 0; i < W.length; i++) { this.dataX.push(W[i]); this.dataY.push(E(W[i])); } }; var pt = board.create('point', [function () { return W[W.length - 1]; }, function () { return E(W[W.length - 1]); }], {name: '', size: 4, color: '#c62828', fixed: true}); board.create('text', [0.2, 0.285, function () { var w = W[W.length - 1]; return 'n = ' + (W.length - 1) + ' w = ' + w.toFixed(3).replace('.', ',') + ' σ(w) = ' + s(w).toFixed(3).replace('.', ','); }], opt); board.create('text', [0.2, 0.262, function () { return 'pas η = ' + eta + ' ; minimum en ln 9 ≈ 2,197'; }], opt); board.create('button', [0.2, 0.232, 'Un pas', function () { var w = W[W.length - 1]; W.push(w - eta * dE(w)); board.update(); }], bopt); board.create('button', [1.6, 0.232, 'η = 5', function () { eta = 5; W = [0]; board.update(); }], bopt); board.create('button', [2.75, 0.232, 'η = 10', function () { eta = 10; W = [0]; board.update(); }], bopt); board.create('button', [4.05, 0.232, 'η = 30', function () { eta = 30; W = [0]; board.update(); }], bopt); board.create('text', [5.2, 0.015, 'w'], opt); La courbe de l'erreur $E$ et les pas successifs de la descente de gradient, à partir de $w_0 = 0$. Avec $\eta = 30$, le premier pas va beaucoup trop loin, au-delà du minimum, et il faut ensuite revenir.
Avec des millions de poids

Dans un vrai réseau, l'erreur dépend de tous les poids à la fois. On applique la même idée à chaque poids séparément : on regarde comment l'erreur varie quand on ne modifie que ce poids-là, en gardant les autres fixes, et l'on fait un petit pas dans le sens qui la fait diminuer. Le calcul de toutes ces dérivées utilise sans cesse la dérivée d'une fonction composée, comme pour $E'(w)$ : on part de l'erreur en sortie et l'on remonte couche par couche vers les entrées. Cette méthode, la rétropropagation du gradient, a été popularisée en 1986 par David Rumelhart, Geoffrey Hinton et Ronald Williams. Elle a relancé les réseaux de neurones : elle permet d'entraîner des réseaux à plusieurs couches, et donc de dépasser les limites du perceptron.

Aujourd'hui, et les limites de la comparaison Des réseaux partout

Dans les années 1990, le Français Yann LeCun met au point des réseaux capables de lire les chiffres écrits à la main : ils ont été utilisés pour lire automatiquement les montants des chèques. À partir des années 2010, grâce à la puissance des processeurs graphiques et à d'immenses quantités de données, les réseaux « profonds », à très nombreuses couches, s'imposent en reconnaissance d'images, en traduction, en reconnaissance de la parole. En 2024, le prix Nobel de physique est attribué à John Hopfield et Geoffrey Hinton pour leurs travaux fondateurs sur l'apprentissage par réseaux de neurones artificiels.

Le principe de base n'a pas changé depuis le perceptron : des sommes pondérées, une fonction qui décide de la sortie, et des poids corrigés pour diminuer l'erreur. Ce qui a changé, c'est le nombre de neurones et de couches, et la quantité d'exemples.

Un neurone artificiel n'est pas un neurone

Le neurone mathématique est une modélisation : il garde quelques traits du neurone biologique et laisse tout le reste de côté. Il ne faut pas confondre le modèle et la réalité.

  • Le temps. Un vrai neurone émet des potentiels d'action à des instants précis, et le moment où ils arrivent compte (sommation temporelle). Le neurone formel calcule d'un coup, sans notion de temps.
  • La complexité de la cellule. Les dendrites ne se contentent pas d'additionner : elles effectuent elles-mêmes des calculs. Les neurotransmetteurs sont nombreux et d'effets variés, et d'autres cellules du cerveau, les cellules gliales, participent à son fonctionnement.
  • L'apprentissage. On ne sait pas si le cerveau fait quoi que ce soit qui ressemble à la rétropropagation. Un enfant reconnaît un chat après en avoir vu quelques-uns ; un réseau artificiel a besoin de milliers d'exemples.
  • L'énergie. Le cerveau humain consomme environ $20$ watts, à peu près autant qu'une petite ampoule ; l'entraînement des grands réseaux artificiels consomme énormément plus d'électricité.

Les neurones artificiels se sont inspirés du cerveau, mais ils ont surtout évolué comme des outils mathématiques et informatiques. Ils servent aussi en retour aux neurosciences, comme modèles simplifiés à comparer aux mesures.

Le perceptron en Python

Ce programme applique la règle du perceptron aux tableaux du OU, du ET et du OU exclusif, et affiche, à chaque tour, le nombre d'erreurs et les poids. Pour le OU et le ET, les erreurs tombent à $0$ ; pour le OU exclusif, elles ne disparaissent jamais, comme le prévoit la démonstration.

def apprendre(exemples, nb_tours): w1, w2, b = 0, 0, 0 for tour in range(nb_tours): erreurs = 0 for (x1, x2), y in exemples: z = w1 * x1 + w2 * x2 + b if z > 0: s = 1 else: s = 0 e = y - s # erreur : 0, 1 ou -1 if e != 0: erreurs = erreurs + 1 w1, w2, b = w1 + e * x1, w2 + e * x2, b + e print("tour", tour + 1, ": erreurs =", erreurs, " w1 =", w1, " w2 =", w2, " b =", b) entrees = [(0, 0), (0, 1), (1, 0), (1, 1)] OU = list(zip(entrees, [0, 1, 1, 1])) ET = list(zip(entrees, [0, 0, 0, 1])) XOR = list(zip(entrees, [0, 1, 1, 0])) print("Le OU :"); apprendre(OU, 4) print("Le ET :"); apprendre(ET, 6) print("Le OU exclusif :"); apprendre(XOR, 6)
Des directions pour construire sa question

Ce texte n'est pas un Grand Oral. Il rassemble des connaissances dans lesquelles chacun peut choisir une direction, la creuser et formuler sa propre question. Quelques directions possibles :

Pour l'échange avec le jury : les notions du cours à maîtriser

Le jury peut interroger sur les chapitres de spécialité mobilisés. Avec ce thème, il est utile de connaître parfaitement : les équations cartésiennes de droites et le vecteur normal (première) ; la fonction exponentielle, ses limites et sa dérivée ; la dérivation (quotient, $\dfrac{1}{u}$, $u^2$, fonction composée) et le lien entre signe de la dérivée et variations ; la fonction logarithme ; les suites géométriques et leur limite ; le raisonnement par l'absurde.

Pour réviser ces chapitres, l'application Maths FlashBac reprend tout le cours de spécialité en cartes.

Pour aller plus loin