MLP (Multi-Layer Perceptron) : Guide Complet — Principes, Exemples et Implémentation Python

MLP (Multi-Layer Perceptron) : Guide Complet — Principes, Exemples et Implémentation Python

MLP (Multi-Layer Perceptron) : Guide complet — Principes, Exemples et Implémentation Python

Résumé

Le Multi-Layer Perceptron (MLP), ou perceptron multicouche, est l’architecture de réseau de neurones la plus fondamentale et la plus largement utilisée en apprentissage automatique. Il s’agit d’un modèle de type feed-forward composé d’une couche d’entrée, d’une ou plusieurs couches cachées et d’une couche de sortie, où chaque neurone d’une couche est entièrement connecté à tous les neurones de la couche suivante. Grâce à l’algorithme de rétropropagation du gradient (backpropagation), le MLP est capable d’apprendre des relations non linéaires complexes entre les entrées et les sorties, ce qui en fait un outil puissant pour la classification, la régression et bien d’autres tâches.

Ce guide explorera en détail les principes mathématiques, l’intuition profonde, l’implémentation pratique en Python et les cas d’usage concrets du MLP.

Principe mathématique du Multi-Layer Perceptron

Architecture générale

Un MLP se compose de trois types de couches :

  • Couche d’entrée : reçoit les données brutes. Chaque neurone correspond à une caractéristique (feature) du vecteur d’entrée.
  • Couches cachées : une ou plusieurs couches intermédiaires qui transforment progressivement les entrées en représentations de plus haut niveau. Chaque couche applique une transformation linéaire suivie d’une fonction d’activation non linéaire.
  • Couche de sortie : produit le vecteur de résultat final, dont la dimension dépend de la tâche (un neurone pour la régression, autant que de classes pour la classification multi-classes).

Forward Pass (Propagation avant)

Le cœur du MLP est le forward pass, c’est-à-dire le calcul séquentiel de la sortie à partir de l’entrée. Pour chaque couche l, la transformation s’écrit :

$$h_l = \sigma(W_l \cdot h_{l-1} + b_l)$$

où :

  • $h_{l-1}$ est le vecteur d’activations de la couche précédente ($h_0 = x$, le vecteur d’entrée)
  • $W_l$ est la matrice des poids de la couche l, de dimension (nombre_de_neurones_l, nombre_de_neurones_l-1)
  • $b_l$ est le vecteur des biais de la couche l
  • $\sigma$ est la fonction d’activation non linéaire
  • $h_l$ est le vecteur d’activations résultant de la couche l

Les fonctions d’activation les plus courantes sont :

  • Sigmoïde : $\sigma(z) = \frac{1}{1 + e^{-z}}$ — comprime la sortie entre 0 et 1, idéale pour la classification binaire
  • Tangente hyperbolique (tanh) : $\sigma(z) = \tanh(z)$ — comprime entre -1 et 1, centrée sur zéro, souvent préférée à la sigmoïde dans les couches cachées
  • ReLU (Rectified Linear Unit) : $\sigma(z) = \max(0, z)$ — la plus utilisée aujourd’hui, simple et efficace, évite le problème du gradient vanishing
  • Leaky ReLU : $\sigma(z) = \max(0.01z, z)$ — variante de ReLU qui autorise un petit gradient négatif

Fonction de coût (Loss function)

La fonction de coût mesure l’écart entre les prédictions du réseau et les valeurs cibles. Le choix dépend de la tâche :

  • Erreur quadratique moyenne (MSE) : $L = \frac{1}{n} \sum_{i=1}^{n} (y_i – \hat{y}_i)^2$ — utilisée pour la régression
  • Entropie croisée binaire (Binary Cross-Entropy) : $L = -\frac{1}{n} \sum_{i=1}^{n} [y_i \log(\hat{y}_i) + (1 – y_i) \log(1 – \hat{y}_i)]$ — pour la classification à deux classes
  • Entropie croisée catégorielle (Categorical Cross-Entropy) : $L = -\sum_{i=1}^{n} \sum_{c=1}^{C} y_{i,c} \log(\hat{y}_{i,c})$ — pour la classification multi-classes avec softmax en sortie

Backward Pass (Rétropropagation du gradient)

La rétropropagation est l’algorithme clé qui permet d’entraîner le MLP. Le principe consiste à calculer le gradient de la fonction de coût par rapport à chaque poids du réseau en utilisant la règle de chaîne du calcul différentiel, en remontant de la couche de sortie vers la couche d’entrée.

Pour la couche de sortie L, le gradient est :

$$\frac{\partial L}{\partial W_L} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_L} \cdot \frac{\partial z_L}{\partial W_L} = \delta_L \cdot h_{L-1}^T$$

où $\delta_L = \frac{\partial L}{\partial z_L}$ est le terme d’erreur de la couche de sortie.

Pour une couche cachée l, on propage l’erreur vers l’arrière :

$$\delta_l = (W_{l+1}^T \cdot \delta_{l+1}) \odot \sigma’(z_l)$$

où $\odot$ désigne le produit élément par élément (produit de Hadamard) et $\sigma’$ est la dérivée de la fonction d’activation.

Mise à jour des poids

Une fois les gradients calculés, les poids sont mis à jour par descente de gradient :

$$w \leftarrow w – \eta \cdot \frac{\partial L}{\partial w}$$

$\eta$ est le taux d’apprentissage (learning rate), un hyperparamètre critique qui contrôle la taille des pas d’optimisation. Un taux trop grand provoque l’instabilité, un taux trop petit ralentit la convergence.

Intuition : comment le MLP apprend des représentations

Le MLP ne se contente pas de mémoriser des associations entre entrées et sorties. Il construit progressivement des représentations de plus en plus abstraites à travers ses couches.

Imaginez un enfant qui apprend à reconnaître des animaux :

  1. Première couche (traits simples) : comme la rétine de l’enfant qui détecte des contours, des couleurs et des formes élémentaires, la première couche cachée du MLP extrait des caractéristiques de bas niveau — des combinaisons linéaires simples des entrées, filtrées par la fonction d’activation.
  2. Deuxième couche (motifs) : comme l’enfant qui combine les traits pour reconnaître des oreilles pointues, une queue longue ou des rayures, la deuxième couche combine les traits simples en motifs plus complexes. Elle est capable de détecter des interactions non linéaires entre les caractéristiques de la première couche.
  3. Troisième couche et au-delà (concepts) : comme l’enfant qui assemble les motifs pour former le concept de “chat” ou “chien”, les couches profondes du MLP combinent les motifs en concepts de haut niveau, directement utiles pour la tâche finale.

C’est cette hiérarchie de représentations — traits → motifs → idées — qui donne au MLP sa puissance expressive. Le théorème d’approximation universelle (Cybenko, 1989) démontre formellement qu’un MLP avec une seule couche cachée et un nombre suffisant de neurones peut approximer n’importe quelle fonction continue sur un compact, avec une précision arbitraire.

Implémentation Python du Multi-Layer Perceptron

1. Avec scikit-learn : MLPClassifier

Scikit-learn propose une implémentation robuste et simple d’utilisation :

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report

# Génération de données
X, y = make_classification(
    n_samples=5000, n_features=20, n_informative=15,
    n_redundant=5, n_classes=3, random_state=42
)

# Division entraînement / test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# Normalisation des données (essentielle pour les MLP)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Création et entraînement du MLP
mlp = MLPClassifier(
    hidden_layer_sizes=(128, 64, 32),  # trois couches cachées
    activation='relu',                  # fonction d'activation ReLU
    solver='adam',                      # optimiseur Adam
    alpha=0.001,                        # régularisation L2
    learning_rate_init=0.001,           # taux d'apprentissage
    max_iter=500,                       # nombre maximum d'itérations
    random_state=42,
    early_stopping=True,                # arrêt anticipé
    validation_fraction=0.15,           # fraction de validation
    verbose=True
)

mlp.fit(X_train_scaled, y_train)

# Évaluation
y_pred = mlp.predict(X_test_scaled)
print(classification_report(y_test, y_pred))
print(f"Précision entraînement : {mlp.score(X_train_scaled, y_train):.4f}")
print(f"Précision test : {mlp.score(X_test_scaled, y_test):.4f}")
print(f"Nombre d'itérations : {mlp.n_iter_}")
print(f"Perte finale : {mlp.loss_:.6f}")

2. Avec TensorFlow / Keras

TensorFlow offre un contrôle plus fin sur l’architecture et l’entraînement :

import tensorflow as tf
from tensorflow.keras import layers, models, callbacks
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# Préparation des données
X, y = make_classification(
    n_samples=5000, n_features=20, n_informative=15,
    n_redundant=5, n_classes=3, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Encodage one-hot pour la classification multi-classes
y_train_cat = tf.keras.utils.to_categorical(y_train, num_classes=3)
y_test_cat = tf.keras.utils.to_categorical(y_test, num_classes=3)

# Construction du modèle
model = models.Sequential([
    layers.Input(shape=(20,)),
    layers.Dense(128, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
    layers.Dropout(0.3),                          # régularisation par dropout
    layers.Dense(64, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
    layers.Dropout(0.2),
    layers.Dense(32, activation='relu'),
    layers.Dense(3, activation='softmax')         # sortie multi-classes
])

model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

model.summary()

# Callbacks : early stopping et réduction du learning rate
early_stop = callbacks.EarlyStopping(
    monitor='val_loss', patience=15, restore_best_weights=True
)
reduce_lr = callbacks.ReduceLROnPlateau(
    monitor='val_loss', factor=0.5, patience=5, min_lr=1e-6
)

# Entraînement
history = model.fit(
    X_train_scaled, y_train_cat,
    validation_split=0.15,
    epochs=200,
    batch_size=32,
    callbacks=[early_stop, reduce_lr],
    verbose=1
)

# Évaluation
loss, accuracy = model.evaluate(X_test_scaled, y_test_cat)
print(f"Perte test : {loss:.4f}")
print(f"Précision test : {accuracy:.4f}")

3. Visualisation des activations des couches

Il est instructif de visualiser ce que chaque couche a appris :

import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras.models import Model

# Modèle intermédiaire pour extraire les activations
layer_outputs = [layer.output for layer in model.layers if isinstance(layer, layers.Dense)]
activation_model = Model(inputs=model.input, outputs=layer_outputs)

# Extraction des activations sur un échantillon de test
activations = activation_model.predict(X_test_scaled[:10])

# Visualisation des distributions d'activations
fig, axes = plt.subplots(1, len(activations), figsize=(15, 4))
for i, act in enumerate(activations):
    axes[i].hist(act.flatten(), bins=50, alpha=0.7, edgecolor='black')
    axes[i].set_title(f"Couche {i+1} : dim={act.shape[1]}")
    axes[i].set_xlabel("Valeur d'activation")
    axes[i].set_ylabel("Fréquence")
plt.tight_layout()
plt.show()

# Courbe d'entraînement
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(history.history['loss'], label='Entraînement')
ax1.plot(history.history['val_loss'], label='Validation')
ax1.set_title('Évolution de la perte')
ax1.set_xlabel('Époque')
ax1.set_ylabel('Perte')
ax1.legend()

ax2.plot(history.history['accuracy'], label='Entraînement')
ax2.plot(history.history['val_accuracy'], label='Validation')
ax2.set_title('Évolution de la précision')
ax2.set_xlabel('Époque')
ax2.set_ylabel('Précision')
ax2.legend()
plt.tight_layout()
plt.show()

Guide des hyperparamètres

Le choix des hyperparamètres est déterminant pour la performance du Multi-Layer Perceptron. Voici un guide détaillé :

Hyperparamètre Description Valeurs typiques Conseils
hidden_layer_sizes Nombre de couches et de neurones (64, 32), (128, 64, 32) Commencez simple, augmentez progressivement
activation Fonction d’activation ‘relu’, ‘tanh’, ‘logistic’ ReLU est le choix par défaut recommandé
solver Algorithme d’optimisation ‘adam’, ‘sgd’, ‘lbfgs’ Adam pour grands jeux, LBFGS pour petits
alpha Paramètre de régularisation L2 0.0001 – 0.1 Augmentez si surapprentissage détecté
learning_rate_init Taux d’apprentissage initial 0.001 – 0.01 Réduisez si oscillation, augmentez si convergence lente
max_iter Nombre maximum d’itérations 200 – 1000 Utilisez early_stopping plutôt qu’une limite fixe
tol Tolérance de convergence 1e-4 (défaut) Réduisez pour plus de précision
batch_size Taille des lots (Keras) 32, 64, 128 32 est un bon point de départ
early_stopping Arrêt anticipé True/False Toujours recommandé pour éviter le surapprentissage

Stratégies de régularisation

  • Dropout : désactive aléatoirement un pourcentage de neurones à chaque itération, forçant le réseau à ne pas dépendre excessivement de neurones individuels.
  • Early Stopping : interrompt l’entraînement lorsque la performance sur l’ensemble de validation cesse de s’améliorer pendant patience époques.
  • Régularisation L2 : ajoute une pénalité proportionnelle au carré des poids à la fonction de coût, limitant leur magnitude.
  • Batch Normalization : normalise les activations de chaque couche, accélérant la convergence et réduisant la sensibilité à l’initialisation.

Avantages et limites du MLP

Avantages

  • Universalité : grâce au théorème d’approximation universelle, le MLP peut modéliser n’importe quelle relation fonctionnelle continue
  • Flexibilité : applicable à la classification, la régression, l’apprentissage non supervisé (autoencodeurs) et même l’apprentissage par renforcement
  • Simplicité relative : architecture facile à comprendre et à implémenter par rapport aux architectures spécialisées (CNN, RNN)
  • Rapidité d’inférence : une fois entraîné, le MLP effectue des prédictions extrêmement rapides
  • Pas d’hypothèses fortes : contrairement à la régression linéaire, le MLP ne suppose pas de relation linéaire entre les variables

Limites

  • Boîte noire : difficile à interpréter ; comprendre pourquoi le réseau prend une décision particulière reste un défi majeur
  • Données tabulaires : pour les données structurées, les arbres de décision et les forêts aléatoires surpassent souvent le MLP
  • Données séquentielles ou spatiales : les RNN/LSTM/Transformers et les CNN sont mieux adaptés respectivement aux séries temporelles et aux images
  • Sensibilité à l’échelle : les entrées doivent être normalisées ; sans prétraitement, la convergence est compromise
  • Risque de surapprentissage : avec trop de neurones ou trop d’époques, le MLP mémorise le bruit au lieu d’apprendre le signal
  • Initialisation sensible : une mauvaise initialisation des poids peut mener au problème du gradient vanishing ou exploding

4 cas d’usage concrets du Multi-Layer Perceptron

1. Détection de fraude bancaire

Un MLP entraîné sur des caractéristiques de transactions (montant, lieu, instant, historique du client) peut détecter des comportements anormaux en temps réel. Les couches cachées apprennent des combinaisons complexes de signaux faibles qui, pris individuellement, ne seraient pas suspects mais qui ensemble révèlent une fraude. Le problème de classes déséquilibrées est géré par des techniques de pondération des classes ou de sur-échantillonnage (SMOTE).

2. Diagnostic médical assisté

En combinant des résultats d’analyses sanguines, des données démographiques et des antécédents médicaux, un MLP peut aider à prédire le risque de maladie (diabète, maladies cardiovasculaires). Chaque couche du réseau construit progressivement une représentation de plus en plus riche de l’état de santé du patient. L’interprétabilité reste un défi, mais des techniques comme SHAP ou LIME permettent d’expliquer les prédictions.

3. Prévision de la demande en énergie

Un MLP peut prédire la consommation d’électricité d’une région en fonction de la température, du jour de la semaine, de la période de l’année et de l’activité économique. Les interactions non linéaires entre ces variables (ex. : l’impact de la température est différent en semaine et le week-end) sont naturellement capturées par les couches cachées non linéaires du réseau.

4. Système de recommandation basique

En encodant les préférences des utilisateurs et les caractéristiques des produits sous forme de vecteurs denses, un MLP peut apprendre à prédire la note qu’un utilisateur donnerait à un produit. Les premières couches captent les affinités simples, tandis que les couches profondes découvrent des préférences complexes et contextuelles. C’est le principe de base derrière les systèmes de recommandation modernes, bien que les architectures actuelles utilisent des embeddings et des couches d’attention.

Voir aussi

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.