← Назад к вопросам

Можешь ли проинтерпретировать L1

1.6 Junior🔥 191 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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
  • Более интерпретируемой для бизнеса
  • Устойчивее к переобучению
Можешь ли проинтерпретировать L1 | PrepBro