scikit-learnMiddleTechnical
В чём разница между L1 (Lasso) и L2 (Ridge) регуляризацией?
L2 (Ridge) штрафует сумму квадратов весов — уменьшает их, но не обнуляет. L1 (Lasso) штрафует сумму модулей — обнуляет неважные веса, делая feature selection. ElasticNet объединяет оба штрафа.
Математическая суть регуляризации
Регуляризация добавляет штраф к функции потерь, чтобы ограничить сложность модели и уменьшить переобучение. Целевая функция принимает вид: Loss(w) + λ * Penalty(w).
- L2 (Ridge): Penalty = Σ w_i² — сумма квадратов весов. Штраф дифференцируем везде.
- L1 (Lasso): Penalty = Σ |w_i| — сумма модулей весов. Штраф недифференцируем в нуле.
- ElasticNet: комбинация L1 + L2: α * L1 + (1-α) * L2.
Ключевое отличие: разреженность
L1 приводит точно к нулю некоторые веса (feature selection), L2 — только уменьшает их, но не обнуляет. Это объясняется геометрически: гиперромб L1 имеет угловые точки на осях, куда «притягивается» решение оптимизации.
Ridge Regression (L2)
import numpy as np
from sklearn.linear_model import Ridge, RidgeCV
from sklearn.datasets import make_regression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
X, y, true_coef = make_regression(
n_samples=200, n_features=20, n_informative=5,
noise=10, coef=True, random_state=42
)
# Pipeline с масштабированием (обязательно!)
ridge_pipe = Pipeline([
("scaler", StandardScaler()),
("ridge", Ridge(alpha=1.0)), # alpha = lambda (сила регуляризации)
])
ridge_pipe.fit(X, y)
coefs = ridge_pipe.named_steps["ridge"].coef_
print(f"Zero coefficients: {np.sum(np.abs(coefs) < 1e-6)}") # 0 — ни один не обнулился
print(f"Non-zero: {np.sum(np.abs(coefs) > 1e-6)}") # 20
# Автоматический подбор alpha
ridge_cv = RidgeCV(alphas=[0.1, 1.0, 10.0, 100.0], cv=5)
ridge_cv.fit(StandardScaler().fit_transform(X), y)
print(f"Best alpha: {ridge_cv.alpha_}")
Lasso Regression (L1)
from sklearn.linear_model import Lasso, LassoCV
lasso_pipe = Pipeline([
("scaler", StandardScaler()),
("lasso", Lasso(alpha=0.1, max_iter=5000)),
])
lasso_pipe.fit(X, y)
coefs = lasso_pipe.named_steps["lasso"].coef_
print(f"Zero coefficients: {np.sum(np.abs(coefs) < 1e-6)}") # ~15 обнулились
print(f"Selected features: {np.sum(np.abs(coefs) > 1e-6)}") # ~5
# Кросс-валидация для alpha
lasso_cv = LassoCV(alphas=np.logspace(-3, 1, 50), cv=5, max_iter=5000)
lasso_cv.fit(StandardScaler().fit_transform(X), y)
print(f"Best alpha: {lasso_cv.alpha_:.4f}")
print(f"Non-zero features: {np.sum(lasso_cv.coef_ != 0)}")
ElasticNet — лучшее из обоих
from sklearn.linear_model import ElasticNet, ElasticNetCV
# l1_ratio=1.0 -> чистый Lasso, 0.0 -> чистый Ridge
en = ElasticNet(alpha=0.1, l1_ratio=0.5, max_iter=5000)
en.fit(StandardScaler().fit_transform(X), y)
print(f"Non-zero: {np.sum(en.coef_ != 0)}")
Когда что использовать
- L2 (Ridge): все признаки потенциально полезны, коррелированные признаки (сохраняет оба), данных мало, нет необходимости в интерпретируемости.
- L1 (Lasso): нужна автоматическая отбор признаков, данных мало и признаков много, нужна интерпретируемость (знать, какие признаки важны).
- ElasticNet: много коррелированных признаков и нужен одновременно отбор и стабильность — Lasso нестабильно выбирает один из коллинеарных признаков, ElasticNet выбирает группу.
Подводные камни
- Масштабирование обязательно: L1 и L2 штрафуют абсолютные значения весов. Без StandardScaler признаки с большим диапазоном получат несправедливо большой штраф.
- alpha в sklearn != lambda в учебниках: в sklearn
alpha— это сила регуляризации. В LogisticRegression используется обратный параметрC = 1/alpha: меньше C = сильнее регуляризация. - Lasso нестабилен при мультиколлинеарности: из двух коррелированных признаков Lasso произвольно обнуляет один. ElasticNet распределяет веса.
- max_iter для Lasso: по умолчанию 1000 итераций может не хватить. При предупреждении ConvergenceWarning увеличьте до 5000-10000.
- Ridge не делает feature selection: все признаки остаются в модели, это нужно учитывать при интерпретации.
- Интерпретация коэффициентов: сравнивать можно только при одинаковом масштабировании признаков. Без StandardScaler коэффициенты несопоставимы.
Common mistakes
- Объяснять
l1 vs l2 regularizationтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
l1 vs l2 regularizationс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.