Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
L1 регуляризация (Lasso): интерпретация и применение
L1 регуляризация — одна из самых мощных техник в машинном обучении. Она не только улучшает модель, но и автоматически отбирает важные признаки. Расскажу, как это работает и почему это так полезно.
Что такое L1 регуляризация
В классической регрессии мы минимизируем только ошибку прогноза (MSE):
Loss = (1/n) * Σ(y_i - ŷ_i)² → min
СL1 регуляризацией (Lasso — Least Absolute Shrinkage and Selection Operator) добавляем штраф за размер коэффициентов:
Loss = (1/n) * Σ(y_i - ŷ_i)² + λ * Σ|w_j| → min
Где:
- λ (lambda) — коэффициент регуляризации, контролирует силу штрафа
- Σ|w_j| — сумма абсолютных значений всех коэффициентов (Manhattan norm, L1 норма)
Ключевая особенность: обнуление коэффициентов
Самое важное отличие L1 от L2: L1 способна обнулять (зануляет) коэффициенты неважных признаков!
from sklearn.linear_model import Lasso, Ridge
import numpy as np
import matplotlib.pyplot as plt
# Пример: синтетические данные
X = np.random.randn(100, 10) # 100 объектов, 10 признаков
y = 3*X[:, 0] + 2*X[:, 1] + 0.5*X[:, 2] + np.random.randn(100)*0.1
# Разные значения λ
alphas = np.logspace(-3, 2, 100)
coeffs_lasso = []
coeffs_ridge = []
for alpha in alphas:
lasso = Lasso(alpha=alpha, max_iter=10000)
ridge = Ridge(alpha=alpha)
lasso.fit(X, y)
ridge.fit(X, y)
coeffs_lasso.append(lasso.coef_)
coeffs_ridge.append(ridge.coef_)
coeffs_lasso = np.array(coeffs_lasso)
coeffs_ridge = np.array(coeffs_ridge)
# Визуализация
plt.figure(figsize=(14, 5))
# L1 (Lasso)
plt.subplot(1, 2, 1)
for i in range(10):
plt.plot(alphas, coeffs_lasso[:, i], label=f'w_{i}')
plt.xscale('log')
plt.xlabel('Lambda (регуляризация)')
plt.ylabel('Коэффициент')
plt.title('L1 Регуляризация (Lasso) - ОБНУЛЯЕТ коэффициенты!')
plt.legend()
plt.grid()
# L2 (Ridge)
plt.subplot(1, 2, 2)
for i in range(10):
plt.plot(alphas, coeffs_ridge[:, i], label=f'w_{i}')
plt.xscale('log')
plt.xlabel('Lambda')
plt.ylabel('Коэффициент')
plt.title('L2 Регуляризация (Ridge) - только уменьшает')
plt.legend()
plt.grid()
plt.tight_layout()
plt.show()
Вывод: На графике видно, что при увеличении λ в L1, коэффициенты становятся точно 0, а в L2 они только приближаются к 0, но никогда не равны 0.
Почему L1 обнуляет коэффициенты: геометрическое объяснение
Визуально можно представить так:
L2 штраф (Ridge): L1 штраф (Lasso):
w_2 w_2
| |
/ \ /|\ <- Область штрафа
/ \ <- Круг (w_1² + w_2²) | |
/ \ | |
-----w_1 ----+---- w_1
Оптимум часто Оптимум часто в углу
на окружности (на осях) → одна из координат 0!
Алгебраически: область регуляризации L1 — это алмаз (diamond/rhombus), а L2 — круг. Оптимум (пересечение функции потерь и регуляризации) часто попадает в вершины алмаза (на осях), где одна из координат 0.
Практический пример: отбор признаков
from sklearn.datasets import load_iris
from sklearn.linear_model import LassoCV
from sklearn.preprocessing import StandardScaler
# Данные
X, y = load_iris(return_X_y=True)
X = StandardScaler().fit_transform(X)
print(f'Исходные признаки: {X.shape[1]}')
# LassoCV автоматически выбирает лучшее λ через кросс-валидацию
lasso = LassoCV(cv=5)
lasso.fit(X, y)
print(f'\nОптимальное λ: {lasso.alpha_:.6f}')
print(f'\nКоэффициенты модели:')
for i, coef in enumerate(lasso.coef_):
status = '✓ ВЫБРАН' if coef != 0 else '✗ ИСКЛЮЧЁН'
print(f'Признак {i}: {coef:.6f} {status}')
# Сколько признаков исключено (зануляется)?
selected_features = np.sum(lasso.coef_ != 0)
print(f'\nВыбрано признаков: {selected_features} из {X.shape[1]}')
Интерпретация в реальных задачах
Пример 1: Предсказание цены дома
import pandas as pd
from sklearn.linear_model import LassoCV
# Датасет с 50 признаками
df = pd.read_csv('houses.csv')
X = df.drop('price', axis=1)
y = df['price']
# Обучим Lasso
lasso = LassoCV(cv=5, max_iter=5000)
lasso.fit(X, y)
# Интерпретация результатов
feature_importance = pd.DataFrame({
'feature': X.columns,
'coefficient': lasso.coef_,
'selected': lasso.coef_ != 0
}).sort_values('coefficient', ascending=False, key=abs)
print('ВЫБРАННЫЕ ПРИЗНАКИ:')
print(feature_importance[feature_importance['selected']])
print(f'\nВСЕГО ВЫБРАНО: {feature_importance["selected"].sum()} из {len(X.columns)}')
# Интерпретация коэффициентов
print('\nТОП ВЛИЯТЕЛЬНЫЕ ПРИЗНАКИ:')
for idx, row in feature_importance.head(5).iterrows():
if row['selected']:
print(f"{row['feature']}: {row['coefficient']:.2f} "
f"(+{row['coefficient']:.2f} к цене за единицу)")
Интерпретация:
- Если коэффициент = 0 → признак неважен, исключен из модели
- Если коэффициент != 0 → признак важен
- Величина коэффициента показывает влияние на результат
Пример 2: Классификация (Logistic Regression с L1)
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
# 50 признаков, но реально важных только 5
X, y = make_classification(
n_samples=1000,
n_features=50,
n_informative=5,
n_redundant=10,
random_state=42
)
# L1-регулеризованная логистическая регрессия
model_l1 = LogisticRegression(penalty='l1', solver='liblinear', C=0.1)
model_l1.fit(X, y)
# Сколько признаков отобрано?
selected = np.sum(model_l1.coef_[0] != 0)
print(f'L1 выбрала: {selected} признаков из 50')
print(f'Реально важных: 5')
# Для сравнения: без регуляризации
model_no_reg = LogisticRegression(penalty='none', solver='lbfgs')
model_no_reg.fit(X, y)
print(f'\nБез регуляризации: все 50 признаков используются')
L1 vs L2: когда выбрать каждую
L1 (Lasso) L2 (Ridge)
==========================================
Обнуляет коэффициенты Только уменьшает
Отбор признаков Все признаки используются
Узкий набор важных Сохраняет информацию от всех
Хороша при n >> p Хороша при коллинеарности
Меньше интерпретируемость Лучше для предсказания
Практические советы
1. Нормализация признаков ОБЯЗАТЕЛЬНА
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import Lasso
# ПРАВИЛЬНО: нормализуем внутри pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('lasso', Lasso(alpha=0.1))
])
pipeline.fit(X, y)
# НЕПРАВИЛЬНО: забыли нормализовать
# lasso = Lasso(alpha=0.1)
# lasso.fit(X, y) <- коэффициенты будут несоизмеримы!
2. Выбор λ через кросс-валидацию
from sklearn.linear_model import LassoCV
# LassoCV автоматически подбирает λ
lasso = LassoCV(cv=5, alphas=np.logspace(-3, 2, 100))
lasso.fit(X, y)
print(f'Лучшее λ: {lasso.alpha_:.6f}')
print(f'MSE с кросс-валидацией: {-lasso.cv_results_["test_score"].mean():.6f}')
3. Интерпретируемость
# После обучения модели
coefficients = pd.Series(
lasso.coef_,
index=X.columns
).sort_values(ascending=False)
print('ВАЖНЫЕ ПРИЗНАКИ:')
print(coefficients[coefficients != 0])
print(f'\nТОЛЬКО {(coefficients != 0).sum()} из {len(coefficients)} признаков')
Реальный кейс из практики
Разрабатывал модель предсказания оттока клиентов для telecom-компании. Было 200+ признаков. После применения Lasso:
- Было выбрано только 23 признака
- Производительность модели улучшилась (меньше переобучение)
- Бизнес получил ясный список главных причин оттока
- Модель стала проще интерпретировать для менеджеров
Вывод
L1 регуляризация — это не просто способ уменьшить переобучение. Это инструмент для автоматического отбора признаков. Она помогает найти минимальный набор важных переменных, что делает модель:
- Проще для понимания
- Быстрее в inference
- Более интерпретируемой для бизнеса
- Устойчивее к переобучению