Wiki IA
Deep Learning

Réseaux de neurones

Du perceptron aux réseaux profonds

Les réseaux de neurones artificiels s'inspirent du fonctionnement du cerveau pour apprendre à partir de données. Ils sont la brique fondamentale du Deep Learning.

Le neurone biologique

Le cerveau contient ~86 milliards de neurones interconnectés.

     Dendrites                    Axone
    (entrées)                    (sortie)
        \  |  /                     |
         \ | /                      |
          \|/                       ↓
    ┌──────────────┐          ┌──────────┐
    │    Corps     │──────────│ Synapses │──→ Autres neurones
    │  cellulaire  │          └──────────┘
    └──────────────┘

    Si signal > seuil
    → neurone "fire"

Le perceptron (1957)

Premier modèle de neurone artificiel, inventé par Frank Rosenblatt.

Structure

        x₁ ──w₁──┐

        x₂ ──w₂──┼──→ Σ ──→ Seuil ──→ Sortie (0 ou 1)
                  │            │
        x₃ ──w₃──┘            │
                  │            │
           biais ─┘      y = 1 si Σ > 0
                         y = 0 sinon

Mathématiquement

z = Σ(wᵢxᵢ) + b = w₁x₁ + w₂x₂ + ... + wₙxₙ + b

y = step(z) = { 1 si z > 0
              { 0 sinon

Limites du perceptron simple

Le perceptron ne peut résoudre que des problèmes linéairement séparables.

✓ Peut apprendre          ✗ Ne peut PAS apprendre

    AND                        XOR
    ○ ● (1,1)=1               ● ○ (0,1)=1
    ○ ○ (0,0)=0               ○ ● (1,0)=1
                              ● ○ (0,0)=0
    Séparable par             ○ ● (1,1)=0
    une ligne
                              Non séparable !

En 1969, Minsky et Papert ont prouvé cette limitation, causant le premier "hiver de l'IA".

Perceptron multicouche (MLP)

La solution : empiler plusieurs couches de neurones.

Architecture

     Entrée        Couche cachée 1    Couche cachée 2      Sortie

       ○─────────────○─────────────────○───────────────────○
       │             │                 │
       ○─────────────○─────────────────○───────────────────○
       │             │                 │
       ○─────────────○─────────────────○
       │             │                 │
       ○─────────────○─────────────────○

   4 neurones      5 neurones        4 neurones         2 neurones

Pourquoi ça marche ?

Chaque couche apprend des représentations de plus en plus abstraites :

Image → Couche 1: bords, textures
      → Couche 2: formes simples
      → Couche 3: parties d'objets
      → Couche 4: objets entiers
      → Sortie: chat/chien

Fonctions d'activation

Sans fonction d'activation non-linéaire, un réseau profond équivaut à une seule couche linéaire.

ReLU (Rectified Linear Unit)

La plus utilisée aujourd'hui.

f(x) = max(0, x)

      │    /
      │   /
      │  /
──────┼───────
nn.ReLU()

Avantages :

  • Simple et rapide
  • Pas de vanishing gradient (pour x > 0)
  • Convergence rapide

Variantes :

  • Leaky ReLU : f(x) = max(0.01x, x)
  • PReLU : paramètre appris
  • ELU : exponentielle pour x < 0

Sigmoid

f(x) = 1 / (1 + e⁻ˣ)

Sortie entre 0 et 1

    1 ──────────────────
      │           ●●●●●
      │       ●●●
      │    ●●
    0 ●●──────────────────
nn.Sigmoid()

Usage : Dernière couche pour classification binaire (probabilité).

Problème : Vanishing gradient pour |x| grand.

Tanh

f(x) = (eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)

Sortie entre -1 et 1
nn.Tanh()

Softmax

Convertit un vecteur en distribution de probabilités.

softmax(xᵢ) = eˣⁱ / Σⱼ eˣʲ

Exemple :
[2.0, 1.0, 0.1] → [0.659, 0.242, 0.099]
                   Somme = 1.0
nn.Softmax(dim=1)

Usage : Dernière couche pour classification multiclasse.

GELU

Utilisée dans les Transformers (BERT, GPT).

f(x) = x · Φ(x)  où Φ = CDF gaussienne
nn.GELU()

Backpropagation

L'algorithme qui permet d'entraîner les réseaux profonds.

Principe

  1. Forward pass : calculer la sortie
  2. Calculer la loss : mesurer l'erreur
  3. Backward pass : calculer les gradients (règle de la chaîne)
  4. Mise à jour : ajuster les poids

Règle de la chaîne

         x → f → g → h → Loss

∂Loss/∂x = ∂Loss/∂h × ∂h/∂g × ∂g/∂f × ∂f/∂x

Vanishing Gradient

Problème historique : les gradients deviennent très petits dans les premières couches.

Gradient × 0.1 × 0.1 × 0.1 × 0.1 = très petit !

Les premières couches n'apprennent pas

Solutions :

  • ReLU au lieu de sigmoid
  • Initialisation appropriée (He, Xavier)
  • Batch Normalization
  • Connexions résiduelles (ResNet)

Optimiseurs

SGD (Stochastic Gradient Descent)

w = w - lr × gradient
optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

Adam

Combine momentum et adaptation du learning rate.

optim.Adam(model.parameters(), lr=0.001)

Le plus utilisé pour sa simplicité et ses bonnes performances.

Learning Rate Schedulers

Réduire le learning rate au cours de l'entraînement.

scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

Initialisation des poids

Une bonne initialisation est cruciale.

Xavier/Glorot

Pour sigmoid/tanh.

nn.init.xavier_uniform_(layer.weight)

He/Kaiming

Pour ReLU.

nn.init.kaiming_uniform_(layer.weight, nonlinearity='relu')

Implémentation complète

import torch
import torch.nn as nn
import torch.optim as optim

# 1. Définir le modèle
class MLP(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.BatchNorm1d(hidden_dim),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.BatchNorm1d(hidden_dim),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, output_dim)
        )

    def forward(self, x):
        return self.layers(x)

# 2. Créer le modèle
model = MLP(input_dim=784, hidden_dim=256, output_dim=10)

# 3. Définir loss et optimiseur
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 4. Boucle d'entraînement
for epoch in range(num_epochs):
    model.train()  # Mode entraînement
    for batch_x, batch_y in train_loader:
        # Forward
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)

        # Backward
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # Validation
    with torch.no_grad():
        correct = 0
        for batch_x, batch_y in val_loader:
            outputs = model(batch_x)
            correct += (outputs.argmax(1) == batch_y).sum().item()
        accuracy = correct / len(val_dataset)
        print(f"Epoch {epoch}: Acc={accuracy:.4f}")

Debugging et bonnes pratiques

Checklist

  1. Vérifier les shapes : print(x.shape) à chaque étape
  2. Commencer petit : peu de données, petit modèle
  3. Overfitter d'abord : si ça n'overfit pas, problème !
  4. Visualiser : loss, gradients, activations

Problèmes courants

SymptômeCause probableSolution
Loss = NaNLR trop grandRéduire LR, gradient clipping
Loss ne descend pasLR trop petit, bugAugmenter LR, vérifier code
Val loss augmenteOverfittingRégularisation, plus de données
Train loss stagneModèle trop petitPlus de couches/neurones

On this page