Le machine learning en partant de zéro · Leçon 3.4 · 17 sur 33 leçons prêtes · 12 min
Régularisation : ridge et lasso
En bref
Pénaliser les gros poids calme le modèle. Le lasso met les poids à zéro un par un : avec 3 variables sur 10, le R² de test passe de 0,470 à 0,386.
Échauffement Deux questions de leçons précédentes. Une minute, sans jamais bloquer la leçon.
Dans la leçon 3.1, un modèle à nombreux paramètres a appris le bruit : l’erreur d’entraînement baissait, l’erreur de test montait. Au lieu de retirer des variables au modèle, on peut les lui faire payer. La régularisation est une pénalité ajoutée à l’erreur d’entraînement : plus les poids sont grands, plus la pénalité l’est. Un nombre α fixe sa force. Aujourd’hui, vous essayez deux pénalités sur 10 variables de patients diabétiques et vous voyez que l’une d’elles choisit les variables toute seule.
Choisissez une réponse et votre degré de certitude.
Répondez pour débloquer le labo.
Labo interactif
Ce labo interactif fonctionne dans un navigateur avec JavaScript activé. Le texte qui l’entoure décrit ce qu’il montre.
Voir la réponse
Bonne réponse : L’IMC, l’indice de masse corporelle, parce qu’il porte seul le plus d’information
La dernière est l’IMC : pour α = 39,8, c’est la seule variable avec un poids. s2 disparaît la première pour de bon (dès α = 0,398). Avec 3 variables (IMC, pression artérielle et s5), le R² de test passe de 0,470 à 0,386.
À vous de jouer
- Faites monter α dans le lasso depuis la plus petite valeur. Dans quel ordre les lignes des poids atteignent-elles zéro ? Combien de variables restent pour α = 15,8, et combien cela coûte-t-il au test ?
- Observez s3. Quand touche-t-elle zéro pour la première fois, et que lui arrive-t-il ensuite ?
- Passez à ridge et poussez α jusqu’au bout. Un poids devient-il exactement nul ?
Une fois vérifié dans le lab, mettons un nom sur ce que vous voyez.
L’idée en quelques mots
La régression linéaire ordinaire choisit les poids qui rendent l’erreur sur les données d’entraînement la plus petite possible. La régularisation ajoute à cette erreur une pénalité pour la taille des poids. Le modèle doit maintenant arbitrer : mieux coller aux données ou garder des poids plus petits. Une variable ne garde un grand poids que si elle améliore vraiment les prédictions.
Ridge pénalise la somme des carrés des poids. Il tire tous les poids vers zéro, mais n’en annule aucun : dans le lab, pour α = 398, le R² de test tombe à 0,405 et les 10 variables ont encore un poids. Le lasso pénalise la somme des valeurs absolues des poids et les met à zéro un par un. Pour α = 15,8, il reste trois variables : IMC, pression artérielle et s5. Sans pénalité, le R² de test vaut 0,470 ; avec trois variables, 0,386. Le modèle est plus simple et moins coûteux à utiliser, mais il prédit moins bien.
Les variables sont exprimées en écarts-types. C’est important : la pénalité compte la taille des poids, donc une variable mesurée en grandes unités aurait un petit poids et échapperait à la pénalité.
Jak to policzono
- Données : Diabetes, Efron et al. (2004), Annals of Statistics. 442 patients, 10 variables mesurées au début de l’étude, cible : évolution de la maladie un an plus tard. Les auteurs n’ont pas indiqué de licence ; scikit-learn redistribue le jeu de données (licence BSD-3-Clause).
- Découpage aléatoire : 309 patients pour apprendre, 133 pour le test. Moyennes et écarts-types des variables calculés sur la partie d’entraînement seulement.
- Lasso : 11 valeurs de α de 0,01 à 100. Ridge : 11 valeurs de α de 0,1 à 100000 (dans scikit-learn, ridge compte la pénalité sur une autre échelle que le lasso, d’où les nombres différents).
- R² de test : la part de la variation du résultat des patients de test que le modèle explique (1 = tout, 0 = pas mieux que la moyenne).
Pourquoi ça marche
La pénalité du lasso croît linéairement : réduire un poids d’une unité économise toujours autant de pénalité, que le poids soit grand ou petit. Si une variable améliore l’ajustement de moins que ce que coûte la pénalité, il vaut mieux lui donner un poids exactement nul. La pénalité de ridge croît avec le carré du poids, donc les petits poids ne coûtent presque rien. Ridge réduit un poids de plus en plus, mais n’a jamais besoin de l’annuler.
La pénalité calme aussi les poids de variables qui disent presque la même chose. s1 et s2 sont deux résultats d’analyses très liés entre eux (dans le code plus bas, leur corrélation vaut 0,90). Avec la pénalité lasso la plus faible, s1 a un poids de −39,0 et s2 de +26,2 : deux grands nombres de signes opposés qui s’annulent presque. De tels poids changent beaucoup d’un échantillon à l’autre. La pénalité les ramène, puis le lasso retire s2 complètement, car s1 porte presque la même information.
Ce n’est pas un hasard d’un seul découpage. Dans le code plus bas, la même expérience tourne sur 50 découpages aléatoires : l’IMC reste le dernier dans 37 d’entre eux, s5 dans les 13 autres. La variable qui part la première change encore plus d’un découpage à l’autre : l’âge dans 21 découpages, s2 dans 16.
Erreur fréquente
Vrai ou faux : « le lasso a donné un poids nul à une variable, donc elle ne compte pas ». Faux. s2 part la première alors qu’elle est fortement liée à s1 : le lasso choisit une variable de la paire, et l’autre reçoit zéro parce qu’elle n’apporte rien de nouveau à côté de s1. Sans s1, s2 pourrait être importante. Le lab le montre aussi avec s3 : pour α = 0,158 son poids est nul, pour α = 0,398 il revient, parce qu’elle est liée à s4 (r = −0,73) et que le lasso fait passer le poids de l’une à l’autre.
Le choix entre variables corrélées peut tenir du hasard. Dans le code plus bas, la première à partir est tantôt s2, tantôt l’âge, et même la dernière à rester est parfois s5 au lieu de l’IMC, selon les patients qui tombent dans la partie d’entraînement. Un zéro du lasso veut dire : « ce modèle n’a pas eu besoin de cette variable, avec ces données et cet α ». Il ne veut pas dire : « cette variable n’a rien à voir avec la maladie ».
Quiz
Trois questions pour vérifier vos acquis. Choisissez une réponse ou dépliez la solution sous chaque question. Au clavier : les flèches choisissent, Entrée ou Espace valide.
À retenir
- La régularisation ajoute à l’erreur une pénalité pour les grands poids ; α fixe sa force.
- Ridge réduit tous les poids et n’en annule aucun ; le lasso les annule un par un et choisit des variables.
- Un modèle plus simple coûte en général un peu de précision : α se choisit en validation, pas sur le test.
- Un poids nul dans le lasso ne veut pas dire que la variable est sans importance : d’une paire de variables corrélées, le lasso en garde souvent une.
Pour aller plus loin : formule, code, pratique
Les maths
Ridge : min_w ‖y − Xw‖² + α · Σⱼ wⱼ². Lasso : min_w (1/(2n)) · ‖y − Xw‖² + α · Σⱼ |wⱼ|. C’est ainsi que scikit-learn les calcule, c’est pourquoi les mêmes nombres pour α n’ont pas le même sens dans les deux pénalités.
Pourquoi le lasso annule : avec des variables de moyenne 0 et de variance 1, un poids après une mise à jour vaut wⱼ = sign(ρⱼ) · max(|ρⱼ| − α, 0), où ρⱼ = (1/n) · xⱼᵀ rⱼ est le lien entre la variable et l’erreur restante sans elle. Quand |ρⱼ| ≤ α, le poids est exactement nul. Ridge donne wⱼ = ρⱼ / (1 + α/n) sous les mêmes hypothèses : le poids diminue, mais n’est nul que si ρⱼ = 0.
R² : R² = 1 − Σ(yᵢ − ŷᵢ)² / Σ(yᵢ − ȳ)², calculé sur les patients de test.
En code (Python)
La même expérience dans scikit-learn sur 50 découpages aléatoires (70 % pour apprendre, 30 % pour le test) : quelle variable le lasso garde en dernier, laquelle il retire en premier, et ce que coûte de garder au plus 3 variables.
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Lasso, lars_path
from sklearn.metrics import r2_score
X, y = load_diabetes(return_X_y=True)
names = np.array(["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"])
alphas = 10 ** np.linspace(-2, 2, 41) # la plage de pénalités du lab, plus fine
last, first, r2 = [], [], []
for s in range(50):
tr, te = train_test_split(np.arange(len(y)), test_size=0.3, random_state=s)
sc = StandardScaler().fit(X[tr])
Xtr, Xte = sc.transform(X[tr]), sc.transform(X[te])
path_alphas, _, coefs = lars_path(Xtr, y[tr] - y[tr].mean(), method="lasso") # le chemin exact du lasso, depuis la pénalité la plus forte
enters = [np.flatnonzero(coefs[j])[0] if coefs[j].any() else len(path_alphas) for j in range(10)] # où chaque variable reçoit un poids pour la première fois
last.append(names[np.argmin(enters)]); first.append(names[np.argmax(enters)]) # la première à recevoir un poids = la dernière à le perdre quand la pénalité grandit
W = np.array([Lasso(alpha=a, max_iter=100000).fit(Xtr, y[tr]).coef_ for a in alphas])
k3 = min(k for k in range(len(alphas)) if (W[k] != 0).sum() <= 3) # la pénalité la plus faible qui laisse au plus 3 variables
m3 = Lasso(alpha=alphas[k3], max_iter=100000).fit(Xtr, y[tr])
r2.append((r2_score(y[te], LinearRegression().fit(Xtr, y[tr]).predict(Xte)), r2_score(y[te], m3.predict(Xte))))
r2 = np.array(r2)
print(f"last feature lasso keeps, 50 splits: {', '.join(f'{n} {last.count(n)}' for n in sorted(set(last), key=lambda n: (-last.count(n), n)))}")
print(f"first feature it drops for good: {', '.join(f'{n} {first.count(n)}' for n in sorted(set(first), key=lambda n: (-first.count(n), n)))}")
print(f"test R2: no penalty {r2[:, 0].mean():.3f}, at most 3 features {r2[:, 1].mean():.3f}; lower with 3 in {(r2[:, 1] < r2[:, 0]).sum()} of 50")
print(f"correlation of s1 and s2: {np.corrcoef(X[:, 4], X[:, 5])[0, 1]:.2f}")
Sortie
last feature lasso keeps, 50 splits: bmi 37, s5 13
first feature it drops for good: age 21, s2 16, s4 9, s1 2, s6 2
test R2: no penalty 0.471, at most 3 features 0.379; lower with 3 in 50 of 50
correlation of s1 and s2: 0.90
Les découpages diffèrent de ceux du lab, donc les nombres aussi. L’image est la même : l’IMC reste le dernier dans 37 découpages sur 50 (s5 dans les 13 autres), et garder au plus 3 variables fait baisser le R² de test dans chaque découpage, en moyenne de 0,471 à 0,379. La première à partir est tantôt s2, tantôt l’âge : le lasso choisit entre variables liées, et le choix dépend des patients tombés dans la partie d’entraînement.
Télécharger le notebook (.ipynb). Ouvrez-le dans Jupyter ou importez-le dans Google Colab ; il télécharge lui-même les données manquantes.
Pour les praticiens
- Dans scikit-learn :
Ridge,LassoetElasticNet(un mélange des deux pénalités, utile avec des groupes de variables corrélées). Mettez la standardisation dans un pipeline (make_pipeline(StandardScaler(), Lasso())), pour que moyennes et écarts-types soient calculés sur l’entraînement seulement. - Choisissez la force de la pénalité par validation croisée :
RidgeCVetLassoCVle font d’un coup. Ne choisissez pas α d’après le score de test : ce serait régler sur le jeu de test (leçon 3.5). - La régression logistique de scikit-learn a une pénalité ridge par défaut (
penalty="l2") ; sa force est fixée parC, l’inverse de α : unCplus petit est une pénalité plus forte. - Si vous voulez parler de l’importance des variables, ne lisez pas seulement les zéros du lasso. Vérifiez la stabilité du choix sur de nombreux découpages, ou utilisez les méthodes du module 6.
Cela vous a-t-il aidé ?
Merci ! Cela nous aide à nous améliorer.
Aller plus loin dans l’Atlas
Articles de l’encyclopédie sur les notions de cette leçon :