2.3 Regularizarea L1 și L2
Când modelul învață „pe de rost" datele de antrenare, regularizarea îl obligă să rămână simplu — penalizând coeficienții mari.
Teorie
Reducerea supraînvățării (overfitting)
Un model supraînvățat are scor excelent pe train și slab pe validare (detaliem fenomenul în lecția 4.2). La modelele liniare, simptomul tipic sunt coeficienți uriași care se compensează reciproc. Soluția: adăugăm la funcția de cost o penalizare pentru coeficienți mari:
L2 (Ridge): cost = MSE + α · Σ wᵢ²
L1 (Lasso): cost = MSE + α · Σ |wᵢ|
Parametrul α (alpha) controlează cât de dură e penalizarea: α = 0 → regresie
obișnuită; α foarte mare → toți coeficienții împinși spre 0 (underfitting).
Lasso vs. Ridge — comparație
| Ridge (L2) | Lasso (L1) | |
|---|---|---|
| Penalizare | pătratul coeficienților | modulul coeficienților |
| Efect asupra coeficienților | îi micșorează pe toți, dar rar ajung exact 0 | îi duce pe unii exact la 0 |
| Folos suplimentar | stabilitate când feature-urile sunt corelate | selecție automată de feature-uri |
| Când îl alegi | multe feature-uri toate cât de cât utile | bănuiești că doar câteva feature-uri contează |
ElasticNet, care combină L1 și L2. Iar la
LogisticRegression, parametrul C este inversul lui α:
C mic = regularizare puternică.Aplicare în scikit-learn
from sklearn.linear_model import Ridge, Lasso, LinearRegression
from sklearn.preprocessing import StandardScaler
# regularizarea cere date standardizate!
sc = StandardScaler()
X_tr_s = sc.fit_transform(X_train)
X_va_s = sc.transform(X_val)
for Model, nume in [(LinearRegression, "simplu"), (Ridge, "ridge"), (Lasso, "lasso")]:
model = Model() if nume == "simplu" else Model(alpha=1.0)
model.fit(X_tr_s, y_train)
print(nume, model.score(X_va_s, y_val))
lasso = Lasso(alpha=0.1).fit(X_tr_s, y_train)
pastrate = [f for f, w in zip(X_train.columns, lasso.coef_) if w != 0]
print("Feature-uri păstrate de Lasso:", pastrate)
Problemă rezolvată: overfitting provocat și vindecat
Construim intenționat un model care supraînvață (regresie polinomială de grad 15 pe 30 de puncte), apoi îl reparăm cu Ridge — cel mai clar mod de a vedea regularizarea.
Codul experimentului
import numpy as np, matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.pipeline import make_pipeline
rng = np.random.RandomState(42)
X = np.sort(rng.uniform(0, 1, 30)).reshape(-1, 1)
y = np.sin(2 * np.pi * X).ravel() + rng.normal(0, 0.2, 30)
X_plot = np.linspace(0, 1, 300).reshape(-1, 1)
for model, eticheta in [
(make_pipeline(PolynomialFeatures(15), StandardScaler(), LinearRegression()), "fără regularizare"),
(make_pipeline(PolynomialFeatures(15), StandardScaler(), Ridge(alpha=0.1)), "Ridge α=0.1"),
]:
model.fit(X, y)
plt.plot(X_plot, model.predict(X_plot), label=eticheta)
plt.scatter(X, y, c="k", s=15)
plt.ylim(-2, 2); plt.legend(); plt.show()
Ce observi: modelul fără regularizare oscilează sălbatic ca să treacă prin fiecare punct (memorare); Ridge dă o curbă netedă, apropiată de sinusoida reală (generalizare).
Probleme propuse
Pentru α ∈ {0.001, 0.01, 0.1, 1, 10, 100}, antrenează Lasso pe un dataset cu 10+ feature-uri și desenează cum evoluează fiecare coeficient în funcție de α (scară logaritmică). Câte feature-uri supraviețuiesc la α = 10?
Adaugă la un dataset real 5 coloane de zgomot pur aleator. Verifică dacă Lasso le elimină (coeficient 0) și dacă Ridge doar le micșorează.
Reia problema prețului caselor (lecția 2.1) și compară pe validare: LinearRegression, Ridge și Lasso cu mai multe valori α. Alegerea lui α se face corect cu cross-validation — vezi lecția 4.3.