Régression Ridge : Guide Complet — Principes, Exemples et Implémentation Python

Régression Ridge : Guide Complet — Principes, Exemples et Implémentation Python

Régression Ridge : Guide Complet — Principes, Exemples et Implémentation Python

La régression Ridge est la réponse la plus élégante au surapprentissage dans les modèles linéaires. En ajoutant une pénalité L2 sur les coefficients, elle stabilise les estimations, réduit la variance et produit des modèles qui généralisent bien mieux sur des données invisibles — en particulier lorsque les variables sont fortement corrélées.

La régression ridge est l’un des algorithmes fondamentaux du Machine Learning supervisé. Elle prolonge la régression linéaire ordinaire en introduisant une régularisation L2, c’est-à-dire une pénalité proportionnelle au carré de la norme des coefficients. Cette modification apparemment simple résout l’un des problèmes les plus fréquents en modélisation : le surapprentissage dû à la multicollinéarité ou à un nombre élevé de variables. Que vous soyez étudiant en science des données ou praticien confronté à des données réelles imparfaites, maîtriser la régression ridge est une compétence indispensable. Dans ce guide, nous explorerons en détail la théorie mathématique, l’intuition géométrique, et une implémentation complète en Python avec scikit-learn.


Principe mathématique

Le problème de la régression linéaire ordinaire (MCO)

La régression linéaire par la méthode des moindres carrés ordinaires (MCO) cherche à minimiser l’erreur quadratique entre les valeurs observées y et les valeurs prédites Xw :


min min ||y – Xw||²
w
`

où :
y ∈ ℝⁿ est le vecteur des valeurs cibles,
X ∈ ℝⁿˣᵖ est la matrice des variables explicatives,
w ∈ ℝᵖ est le vecteur des coefficients à estimer,
||·||² désigne la norme euclidienne au carré (somme des carrés des résidus).

La solution analytique (lorsque X’X est inversible) est :

ŵ = (X'X)⁻¹ X'y

Le problème : quand X’X n’est pas inversible ou est mal conditionné

Lorsque les colonnes de X sont fortement corrélées entre elles (multicollinéarité), la matrice X’X devient mal conditionnée — ses valeurs propres s’approchent de zéro — ou même singulière. En conséquence :

  • Les coefficients estimés deviennent extrêmement sensibles au moindre bruit dans les données,
  • Ils prennent des valeurs exorbitantes positives et négatives qui se compensent,
  • La variance des prédictions explose sur des données de test.

C’est précisément ici qu’intervient la régression Ridge.

La solution Ridge : ajout d’une pénalité L2

La régression ridge modifie la fonction de coût en ajoutant un terme de régularisation L2 — la somme des carrés des coefficients, pondérée par un hyperparamètre α :


min min ||y – Xw||² + α · ||w||²
w
`

Ce qui s’écrit de manière équivalente :


min min Σᵢ (yᵢ – Σⱼ Xᵢⱼ·wⱼ)² + α · Σⱼ wⱼ²
w
`

α ≥ 0 est le paramètre de régularisation qui contrôle la force de la pénalité.

Solution analytique fermée

En dérivant cette fonction objectif par rapport à w et en annulant le gradient, on obtient la solution fermée de la régression ridge :

ŵ_ridge = (X'X + αI)⁻¹ X'y

I est la matrice identité de taille p×p.

Point crucial : le terme αI ajoute α à chaque valeur propre de X’X. Même si X’X était singulière (valeur propre nulle), la matrice X’X + αI devient automatiquement inversible dès que α > 0. C’est cette propriété qui rend Ridge si robuste.

Interprétation bayésienne

Sous un angle bayésien, la régression ridge correspond à l’estimateur du maximum a posteriori (MAP) avec un a priori gaussien sur les coefficients :

w ~ N(0, σ²/α · I)

Autrement dit, nous supposons a priori que les coefficients sont centrés autour de zéro avec une variance contrôlée par α. Plus α est grand, plus l’a priori est serré autour de zéro.


Intuition — Comment le comprendre ?

L’analogie du ressort

Imaginez chaque coefficient wⱼ comme une masse accrochée à un ressort dont le point d’ancrage est zéro. Le terme α · ||w||² agit comme l’énergie élastique du ressort :

  • Quand α = 0 (aucune régularisation) : les ressorts sont absents. Les coefficients peuvent prendre n’importe quelle valeur pour ajuster parfaitement les données d’entraînement — y compris des valeurs aberrantes. C’est la régression linéaire classique.
  • Quand α est petit : les ressorts sont très souples. Les coefficients restent proches des valeurs MCO, mais sont légèrement « tirés » vers zéro.
  • Quand α est grand : les ressorts sont très raides. Les coefficients sont fortement contraints vers zéro, le modèle devient plus simple (voire constant), et le risque de surapprentissage diminue.

Le compromis biais-variance

La régression ridge illustre parfaitement le compromis biais-variance :

Valeur de α Biais Variance Risque de surapprentissage
α = 0 Faible Élevée Fort (régression MCO)
α petit Modéré Modérée Contrôlé
α optimal Léger Faible Minimal
α très grand Élevé Très faible Modèle sous-ajusté

Contrairement au Lasso (régression L1), Ridge ne met jamais un coefficient exactement à zéro. Il les réduit tous progressivement vers zéro, mais ne fait pas de sélection de variables. Toutes les variables restent dans le modèle, avec des poids atténués.

Géométrie de la contrainte L2

Dans l’espace des coefficients, la contrainte L2 définit une boule (un cercle en 2D, une sphère en 3D). La solution Ridge est le point où la courbe de niveau de l’erreur quadratique touche cette boule. Parce que la boule est « ronde » et n’a pas de coins anguleux (contrairement au diamant L1 du Lasso), la solution se trouve presque toujours loin des axes — ce qui explique pourquoi Ridge ne produit pas d’annulation exacte des coefficients.


Implémentation Python — Exemple complet

Prérequis

Commencez par installer les bibliothèques nécessaires :

pip install scikit-learn numpy matplotlib

Code complet

# ============================================================
# Régression Ridge — Implémentation complète
# Article #003 — Guide complet sur la régression ridge
# ============================================================

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge, RidgeCV
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_regression

# -----------------------------------------------------------
# 1. GÉNÉRATION DES DONNÉES AVEC MULTICOLLINÉARITÉ
# -----------------------------------------------------------
# On crée un jeu de données avec des variables fortement corrélées
# pour illustrer le problème que Ridge résout.
# 1000 échantillons, 10 variables, 5 informatives
np.random.seed(42)

# Création de données avec multicollinéarité
n_samples, n_features = 200, 10

# Matrice de base
X_base = np.random.randn(n_samples, 5)

# On crée des variables corrélées en linéarisant les originales
X = np.column_stack([
    X_base,                          # 5 variables indépendantes
    X_base[:, 0] + 0.1 * np.random.randn(n_samples),  # corrélée à X0
    X_base[:, 0] + 0.1 * np.random.randn(n_samples),  # corrélée à X0
    X_base[:, 1] + 0.1 * np.random.randn(n_samples),  # corrélée à X1
    X_base[:, 2] + 0.1 * np.random.randn(n_samples),  # corrélée à X2
    X_base[:, 3] + 0.1 * np.random.randn(n_samples),  # corrélée à X3
])

# Coefficients vrais
w_true = np.array([3.0, -2.0, 1.5, 0.8, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0])

# Variable cible avec bruit
y = X @ w_true + 0.5 * np.random.randn(n_samples)

# -----------------------------------------------------------
# 2. PRÉPARATION DES DONNÉES
# -----------------------------------------------------------
# Division en train/test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Standardisation : CRUCIAL pour la régularisation !
# Chaque variable doit avoir moyenne 0 et écart-type 1
# sinon la pénalité L2 ne s'applique pas équitablement.
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# -----------------------------------------------------------
# 3. COMPARAISON : MCO vs Ridge avec différents alpha
# -----------------------------------------------------------
print("=" * 60)
print("Comparaison : Régression linéaire vs Ridge")
print("=" * 60)

# Régression linéaire ordinaire (alpha = 0)
from sklearn.linear_model import LinearRegression
mco = LinearRegression()
mco.fit(X_train_scaled, y_train)

# Différents niveaux de régularisation
alphas = [0.01, 0.1, 1.0, 10.0, 100.0]

for alpha in alphas:
    ridge = Ridge(alpha=alpha)
    ridge.fit(X_train_scaled, y_train)

    y_pred_train = ridge.predict(X_train_scaled)
    y_pred_test = ridge.predict(X_test_scaled)

    mse_train = mean_squared_error(y_train, y_pred_train)
    mse_test = mean_squared_error(y_test, y_pred_test)
    r2_test = r2_score(y_test, y_pred_test)

    print(f"\nAlpha = {alpha}")
    print(f"  MSE train : {mse_train:.4f} | MSE test : {mse_test:.4f}")
    print(f"  R² test   : {r2_test:.4f}")

# Performance MCO
y_pred_mco_train = mco.predict(X_train_scaled)
y_pred_mco_test = mco.predict(X_test_scaled)
print(f"\nMCO (pas de régularisation) :")
print(f"  MSE train : {mean_squared_error(y_train, y_pred_mco_train):.4f}")
print(f"  MSE test  : {mean_squared_error(y_test, y_pred_mco_test):.4f}")
print(f"  R² test   : {r2_score(y_test, y_pred_mco_test):.4f}")

# -----------------------------------------------------------
# 4. GRAPHIQUE 1 — Parcours des coefficients en fonction de alpha
# -----------------------------------------------------------
# On montre comment chaque coefficient se contracte quand alpha augmente
alpha_range = np.logspace(-3, 5, 200)

ridge_path = Ridge(copy_X=True)
ridge_path.fit(X_train_scaled, y_train)

# Calcul des coefficients pour chaque alpha
coefs_path = []
for a in alpha_range:
    model = Ridge(alpha=a)
    model.fit(X_train_scaled, y_train)
    coefs_path.append(model.coef_)

coefs_path = np.array(coefs_path)

plt.figure(figsize=(10, 6))
for i in range(n_features):
    plt.semilogx(alpha_range, coefs_path[:, i],
                 label=f'w{i}', linewidth=2)

plt.axvline(x=1.0, color='red', linestyle='--',
            label='alpha = 1 (référence)', alpha=0.7)
plt.xlabel('Alpha (échelle logarithmique)')
plt.ylabel('Valeur des coefficients')
plt.title('Parcours des coefficients — Régression Ridge\n'
          'Comment la pénalité L2 contracte chaque poids vers zéro')
plt.legend(bbox_to_anchor=(1.02, 1), loc='upper left')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('ridge_coefficient_paths.png', dpi=150, bbox_inches='tight')
plt.show()

# -----------------------------------------------------------
# 5. GRAPHIQUE 2 — Courbe MSE vs Alpha
# -----------------------------------------------------------
mse_train_list, mse_test_list = [], []

for a in alpha_range:
    model = Ridge(alpha=a)
    model.fit(X_train_scaled, y_train)

    y_pred_tr = model.predict(X_train_scaled)
    y_pred_te = model.predict(X_test_scaled)

    mse_train_list.append(mean_squared_error(y_train, y_pred_tr))
    mse_test_list.append(mean_squared_error(y_test, y_pred_te))

plt.figure(figsize=(10, 6))
plt.semilogx(alpha_range, mse_train_list, 'b-',
             label='Erreur entraînement', linewidth=2)
plt.semilogx(alpha_range, mse_test_list, 'r-',
             label='Erreur test', linewidth=2)

# Marquer le meilleur alpha
best_idx = np.argmin(mse_test_list)
best_alpha = alpha_range[best_idx]
plt.axvline(x=best_alpha, color='green', linestyle='--',
            label=f'Alpha optimal = {best_alpha:.2f}', alpha=0.7)

plt.xlabel('Alpha (échelle logarithmique)')
plt.ylabel('Erreur quadratique moyenne (MSE)')
plt.title('Compromis biais-variance — MSE en fonction de α\n'
          'Le minimum de la courbe test définit le meilleur alpha')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('ridge_mse_vs_alpha.png', dpi=150, bbox_inches='tight')
plt.show()

# -----------------------------------------------------------
# 6. UTILISATION DE RidgeCV — Sélection automatique de alpha
# -----------------------------------------------------------
print("\n" + "=" * 60)
print("RidgeCV — Sélection automatique de l'hyperparamètre")
print("=" * 60)

# RidgeCV teste plusieurs alpha avec validation croisée intégrée
alphas_cv = np.logspace(-3, 5, 100)
ridge_cv = RidgeCV(alphas=alphas_cv, cv=5)
ridge_cv.fit(X_train_scaled, y_train)

print(f"Alpha optimal sélectionné : {ridge_cv.alpha_:.4f}")

# Évaluation du modèle optimisé
y_pred_cv = ridge_cv.predict(X_test_scaled)
mse_cv = mean_squared_error(y_test, y_pred_cv)
r2_cv = r2_score(y_test, y_pred_cv)
print(f"MSE test (RidgeCV) : {mse_cv:.4f}")
print(f"R² test  (RidgeCV) : {r2_cv:.4f}")

# Comparaison des coefficients
print("\nComparaison des coefficients (valeurs vraies vs RidgeCV) :")
print(f"{'Variable':<12} {'Vrai':>8} {'RidgeCV':>10} {'MCO':>10}")
print("-" * 45)
for i in range(n_features):
    true_val = w_true[i] if i < len(w_true) else 0.0
    print(f"{f'w{i}':<12} {true_val:>8.3f} {ridge_cv.coef_[i]:>10.3f} {mco.coef_[i]:>10.3f}")

# -----------------------------------------------------------
# 7. GRAPHIQUE 3 — Comparaison visuelle Prédictions vs Réalités
# -----------------------------------------------------------
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# MCO (surapprentissage potentiel)
axes[0].scatter(y_test, y_pred_mco_test, alpha=0.6, edgecolors='w', s=50)
axes[0].plot([y.min(), y.max()], [y.min(), y.max()], 'r--', lw=2)
axes[0].set_xlabel('Valeurs réelles')
axes[0].set_ylabel('Valeurs prédites')
axes[0].set_title(f'MCO\nR² = {r2_score(y_test, y_pred_mco_test):.3f}')
axes[0].grid(True, alpha=0.3)

# Ridge optimal
axes[1].scatter(y_test, y_pred_cv, alpha=0.6, edgecolors='w', s=50)
axes[1].plot([y.min(), y.max()], [y.min(), y.max()], 'r--', lw=2)
axes[1].set_xlabel('Valeurs réelles')
axes[1].set_ylabel('Valeurs prédites')
axes[1].set_title(f'Ridge (CV)\nR² = {r2_cv:.3f}')
axes[1].grid(True, alpha=0.3)

# Graphique des coefficients
coef_labels = [f'w{i}' for i in range(n_features)]
x_pos = np.arange(n_features)
width = 0.3
axes[2].bar(x_pos - width/2, w_true[:n_features], width,
            label='Coefficients vrais', alpha=0.7)
axes[2].bar(x_pos + width/2, ridge_cv.coef_, width,
            label='RidgeCV', alpha=0.7)
axes[2].set_xlabel('Variables')
axes[2].set_ylabel('Valeur du coefficient')
axes[2].set_title('Coefficients vrais vs estimés (RidgeCV)')
axes[2].set_xticks(x_pos)
axes[2].set_xticklabels(coef_labels)
axes[2].legend()
axes[2].grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.savefig('ridge_comparaison.png', dpi=150, bbox_inches='tight')
plt.show()

print("\n✅ Analyse terminée. Consultez les graphiques générés :")
print("   - ridge_coefficient_paths.png")
print("   - ridge_mse_vs_alpha.png")
print("   - ridge_comparaison.png")

Explication étape par étape

Étape 1 — Génération des données : Nous créons artificiellement un problème de multicollinéarité en dupliquant des colonnes avec un faible bruit. Cela simule un scénario réel où plusieurs variables mesurent essentiellement la même chose.

Étape 2 — Standardisation : La régularisation L2 pénalise les coefficients en fonction de leur amplitude absolue. Si une variable est mesurée en milliers et une autre en centièmes, sans normalisation, la pénalité affectera disproportionnellement la première. StandardScaler résout ce problème en ramenant toutes les variables à moyenne nulle et variance unitaire.

Étape 3 — Comparaison systématique : Nous testons plusieurs valeurs de α pour observer le comportement du modèle. Notez comment le MSE d’entraînement augmente (biais croissant) tandis que le MSE de test diminue d’abord, puis remonte (variance insuffisante).

Étape 4 — Parcours des coefficients : Le graphique semilogarithmique montre la trajectoire de chaque coefficient. Avec Ridge, tous convergent progressivement vers zéro sans jamais l’atteindre exactement — c’est la signature de la régularisation L2.

Étape 5 — Courbe MSE : Le point d’intersection optimal entre les courbes train et test identifie le meilleur α. C’est la matérialisation visuelle du compromis biais-variance.

Étape 6 — RidgeCV : Au lieu de chercher manuellement, RidgeCV automatise la recherche par validation croisée k-fold (par défaut leave-one-out ou sur grille).

Étape 7 — Visualisation : Les trois graphiques offrent une vue complète : qualité des prédictions (scatter plots) et fidélité de l’estimation des coefficients.


Hyperparamètres

Voici les principaux hyperparamètres de la classe Ridge dans scikit-learn :

Hyperparamètre Rôle Valeurs typiques Impact
alpha Force de la régularisation L2 0.01, 0.1, 1.0, 10.0, 100.0 (test sur grille log) Paramètre clé : petit → proche de MCO, grand → coefficients fortement contractés vers 0
solver Algorithme d’optimisation « auto”\, « svd”\, « cholesky”\, « sparse_cg”\, « lsqr”\, « saga”\ « auto”\ choisit automatiquement. « cholesky”\ rapide pour petits jeux, « saga”\ pour grands jeux de données.
fit_intercept Estimer ou non le terme constant (bias) \True\ (par défaut), \False\ Si \True\, l’intercept est estimé séparément sans régularisation. Si les données sont déjà centrées, \False\ est acceptable.
max_iter Nombre maximum d’itérations Par défaut \None\ (dépend du solver) Pertinent pour les solveurs itératifs (« sparse_cg”\, « saga”). Augmenter si la convergence n’est pas atteinte.
tol Tolérance pour le critère d’arrêt \1e-3\, \1e-4\ (par défaut) Plus petit = convergence plus stricte mais plus longue.
copy_X Copier ou modifier X en place \True\ (par défaut), \False\ \True\ est plus sûr mais consomme plus de mémoire. Pour les très grands jeux, \False\ économise la RAM.
alpha* est de loin le plus critique. La pratique recommandée est d’utiliser RidgeCV ou GridSearchCV pour explorer une grille logarithmique (par ex.
p.logspace(-3, 5, 100)), plutôt que de fixer alpha manuellement.

Avantages et Limites

Avantages

  • Résout la multicollinéarité : Rend X’X inversible même quand les variables sont fortement corrélées, garantissant une solution stable.
  • Réduction du surapprentissage : La pénalité L2 limite la complexité du modèle en contrôlant l’amplitude des coefficients.
  • Solution analytique fermée : Pas besoin d’optimisation itérative coûteuse — le calcul est direct via (X’X + αI)⁻¹ X’y.
  • Convexité garantie : La fonction objectif est strictement convexe, assurant un unique minimum global. Pas de risque de rester coincé dans un minimum local.
  • Interprétabilité conservée : Le modèle reste linéaire et les coefficients restent interprétables, bien que shrinkés.
  • Efficace sur grands jeux : Avec les bons solveurs (ex. “saga”), Ridge scale bien avec le nombre d’échantillons.
  • Implémentation robuste : Scikit-learn fournit Ridge, RidgeCV, et RidgeClassifier couvrant régression et classification.

Limites

  • Pas de sélection de variables : Contrairement au Lasso, Ridge ne met jamais un coefficient exactement à zéro. Toutes les variables restent dans le modèle, ce qui peut nuire à l’interprétabilité quand p est très grand.
  • Sensible au scaling : La standardisation des variables est obligatoire pour un résultat cohérent. Sans cela, la pénalité n’est pas équitable entre variables.
  • Choix de alpha critique : Un mauvais α peut soit sous-régulariser (surapprentissage) soit sur-régulariser (sous-ajustement). La validation croisée est nécessaire.
  • Moins performant pour la parcimonie : Quand seulement quelques variables sont vraiment informatives (signal sparse), le Lasso est souvent préférable.
  • Linéarité : Comme toute régression linéaire pénalisée, Ridge ne capture pas les interactions non linéaires entre variables sans ingénierie de features explicite.

Cas d’usage

1. Génétique et bio-informatique — Données à haute dimension

Dans les études d’association pangénomique (GWAS), le nombre de gènes (variables) dépasse souvent largement le nombre d’individus (observations) — typiquement p > 10 000 pour n < 1 000. De nombreux gènes sont corrélés en raison du déséquilibre de liaison. La régression ridge est largement utilisée dans ce contexte car elle gère naturellement le cas p ≫ n grâce au terme αI qui régularise l’estimation. Des travaux pionniers comme ridge regression BLUP (RR-BLUP) l’appliquent à la prédiction de valeurs génétiques en sélection végétale et animale.

2. Finance — Modélisation de portfolios avec variables corrélées

En finance quantitative, les rendements de nombreux actifs ou facteurs de risque (taux d’intérêt, inflation, indices sectoriels) sont fortement corrélés entre eux. Une régression linéaire ordinaire produirait des coefficients instables et contre-intuitifs. Ridge stabilise les poids attribués à chaque facteur de risque, produisant des modèles de pricing ou de risk allocation plus robustes et généralisables.

3. Traitement du signal — Reconstruction de signaux bruités

En imagerie médicale (IRM) ou en traitement du signal, on cherche souvent à reconstruire un signal à partir de mesures incomplètes et bruitées. La régression ridge apparaît naturellement comme la régularisation Tikhonov, où le terme α||w||² pénalise les solutions trop complexes. C’est la méthode standard dans les problèmes inverses mal posés — ceux où la solution unique n’existe pas sans contrainte additionnelle.

4. NLP et recommandation — Régression sur features textuelles

Lorsqu’on encode des textes via TF-IDF ou des embeddings, on obtient des vecteurs à très haute dimension (des milliers de features). Beaucoup de ces features sont redondantes (synonymes, variations morphologiques). La régression ridge est utilisée comme baseline solide pour la régression sur texte (par ex. prédire un score de sentiment continu), car elle gère efficacement la redondance des features sans nécessiter de sélection manuelle.


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.