Aller au contenu principal

Algorithmes avancés · L3 · Section 9/11

Réseaux de neurones

Progression

Points d’expérience : XPSérie de jours consécutifs : · —Progression du module : — / —compris

#Réseaux de neurones artificiels

Prérequis : dérivées et règle de la chaîne (parcours d'analyse) ; produit matrice-vecteur ; notion de minimum d'une fonction.

Objectifs d'apprentissage : dérouler le passage avant d'un réseau ; expliquer le rôle de l'activation non linéaire ; comprendre le cycle d'entraînement et la rétropropagation comme application de la règle de la chaîne ; discuter surapprentissage et régularisation.

#1. Principe

Un réseau de neurones organise des unités de calcul en couches :

  • Couche d'entrée : reçoit les données (une dimension par caractéristique).
  • Couches cachées : transforment les données par des combinaisons non linéaires.
  • Couche de sortie : produit le résultat (une valeur de régression, ou une probabilité par classe).

Chaque connexion porte un poids, chaque neurone ajoute un biais. Le nombre de couches et de neurones (l'architecture) fixe la famille de fonctions explorée ; l'entraînement choisit une fonction dans cette famille.

#Animation interactive

Chargement...

Entrées : un réseau déjà entraîné et des entrées à faire varier. Sorties : les activations de chaque neurone, couche par couche. Lecture conseillée : faites glisser une seule entrée et observez comment la modification se propage à travers les couches, amplifiée ou atténuée selon les poids.

#2. Le neurone artificiel

Un neurone reçoit des entrées x1,,xnx_1, \dots, x_n, les combine linéairement avec ses poids w1,,wnw_1, \dots, w_n et son biais bb, puis applique une fonction d'activation ff :

z=iwixi+b,a=f(z)z = \sum_i w_i x_i + b, \qquad a = f(z)

x1x2σw1=0.80w2=-0.40a=0.5940.500.30
z = w1·x1 + w2·x2 + b = 0.80×0.50 + -0.40×0.30 +0.10 = 0.380
a = σ(z) = 0.594
x1
x2
w1
w2
b (biais)
Astuce: le code couleur des connexions indique le signe du poids (bleu = positif, rouge = négatif) et l’épaisseur reflète sa magnitude.

Entrées de la démonstration : les deux poids, le biais et le point d'entrée. Sorties : la droite de décision obtenue si f est le pas unitaire, ou la transition douce avec une sigmoïde. Faites varier les poids et observez que le neurone seul ne sait découper que par une droite : c'est la limite à percer avec des couches.

#Fonctions d'activation courantes

NomFormulePlageRôle, compromis
Sigmoïde\sigma(x) = 1/(1+e^{-x})[0, 1]Interprétable comme probabilité ; gradient qui s'évanouit aux extrêmes
Tangente hyperboliquetanh(x)\tanh(x)[-1, 1]Zéro-centrée, mêmes extrêmes écrasés
ReLUmax(0,x)\max(0, x)[0, +∞[Simple, gradient constant sur les positifs ; standard des couches cachées

Pourquoi l'activation non linéaire est indispensable : composées d'étapes purement linéaires, les couches se réduisent à une seule transformation linéaire (W2(W1x)=(W2W1)xW_2(W_1 x) = (W_2 W_1) x). Sans non-linéarité, un réseau de cent couches ne distingue pas mieux qu'un neurone unique : c'est l'exemple du XOR ci-dessous.

#3. Implémentation : passage avant

pythonpython

1import numpy as np2 3class Couche:4    """Couche dense : a = f(W @ x + b)."""5    def __init__(self, n_entrees, n_neurones, activation):6        limite = 1.0 / np.sqrt(n_entrees)     # initialisation à variance contrôlée7        self.W = np.random.uniform(-limite, limite, (n_neurones, n_entrees))8        self.b = np.zeros(n_neurones)9        self.activation = activation10 11    def forward(self, x):12        z = self.W @ x + self.b13        return self.activation(z)14 

L'initialisation borne les poids par 1 / sqrt(n_entrees) pour garder la variance des activations stable d'une couche à l'autre : tout initialiser à zéro rendrait les neurones d'une couche indiscernables, tout mettre trop grand sature les activations.

#4. Entraînement : perte, gradient, rétropropagation

L'entraînement ajuste les poids pour minimiser une fonction de perte qui mesure l'écart entre sortie produite et sortie attendue (erreur quadratique ici, entropie croisée pour la classification). Le cycle :

  1. Passage avant : calculer la sortie pour un exemple.
  2. Perte : mesurer l'écart à la cible.
  3. Rétropropagation : calculer le gradient de la perte par rapport à chaque poids, en appliquant la règle de la chaîne de la sortie vers l'entrée.
  4. Mise à jour : wwηL/ww \leftarrow w - \eta \, \partial L / \partial w, où η\eta est le taux d'apprentissage.
pythonpython

1class CoucheEntrainable:2    def __init__(self, n_entrees, n_neurones, activation, derivee_activation):3        limite = 1.0 / np.sqrt(n_entrees)4        self.W = np.random.uniform(-limite, limite, (n_neurones, n_entrees))5        self.b = np.zeros(n_neurones)6        self.f = activation7        self.df = derivee_activation8 9    def forward(self, x):10        self.x = x                             # mémoriser pour le retour11        self.z = self.W @ x + self.b12        return self.f(self.z)13 14    def backward(self, dL_da):

Le cœur est dans trois lignes : backward applique la règle de la chaîne locale (L/z=L/af(z)\partial L/\partial z = \partial L/\partial a \cdot f'(z)), en déduit les gradients des poids par produit externe, et rend L/x\partial L/\partial x à la couche précédente. L'erreur « remonte » ainsi de la sortie vers l'entrée, d'où le nom.

#5. Exercice : apprendre le XOR

Un seul neurone ne peut pas représenter le XOR (les quatre points ne sont pas séparables par une droite) ; un réseau avec une couche cachée le peut.

pythonpython

1import numpy as np2 3X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)4y = np.array([0, 1, 1, 0], dtype=float)          # table XOR5 6cachee = CoucheEntrainable(2, 4, relu, d_relu)7sortie = CoucheEntrainable(4, 1, sigmoid, d_sigmoid)8 9eta = 0.510for epoque in range(20000):11    perte_totale = 0.012    for x, cible in zip(X, y):13        a1 = cachee.forward(x)14        a2 = sortie.forward(a1)

Vérification observable : la perte moyenne chute de l'ordre de 10110^{-1} vers 10410^{-4} ou moins, et les prédictions approchent 0, 1, 1, 0 à ±0.05 près. Si l'entraînement bloque sur une perte d'environ 0.25, supprimez ReLU au profit d'une activation non morcelée (tanh sur les deux couches) ou relancez avec une autre initialisation : le réseau s'est bloqué dans un plateau de gradients nuls, un accident classique avec ReLU et si peu de données.

#6. Surapprentissage et régularisation

Un réseau assez large peut mémoriser son jeu d'entraînement, y compris le bruit, et généraliser mal. Les signaux et remèdes standards :

  • Découper les données : entraînement, validation, test ; évaluer sur des données jamais vues pendant l'ajustement.
  • Régularisation L2 : ajouter à la perte un terme λw2\lambda \sum w^2 qui pénalise les poids extrêmes, décision plus lisse.
  • Dropout : désactiver aléatoirement des neurones à l'entraînement, forcer une redondance robuste.
  • Arrêt précoce : arrêter quand la perte de validation remonte, même si celle d'entraînement continue de baisser.

Le cours Apprentissage supervisé du module Data Science approfondit ces méthodes ; ici, retenez le diagnostic : l'écart entre perte d'entraînement et perte de validation est le symptôme à surveiller.

#7. Applications

  1. Reconnaissance d'images : classification, détection d'objets (réseaux convolutifs pour la structure spatiale).
  2. Traitement du langage : traduction, génération (réseaux récurrents, puis transformeurs).
  3. Reconnaissance vocale : conversion parole vers texte.
  4. Jeux et décision : valeurs d'états et politiques (AlphaGo et ses successeurs).
  5. Détection d'anomalies : fraudes, défauts industriels.
Que se passerait-il si l'on retirait toutes les fonctions d'activation ?
Que se passerait-il si l'on retirait toutes les fonctions d'activation ?