Как работают L1 и L2 регуляризация в градиентном бустинге (например, в XGBoost, LightGBM)?

Ответ

В градиентном бустинге L1 и L2 регуляризация применяются непосредственно к весам листьев деревьев, составляющих ансамбль, чтобы контролировать сложность модели и бороться с переобучением. Они добавляют штрафной член к оптимизируемой функции потерь.

Механизм в контексте бустинга:

Пусть Obj(θ) = Loss(θ) + Ω(θ), где Ω(θ) — член регуляризации.

  1. L2-регуляризация (Ridge, параметр lambda / reg_lambda):

    • Штраф: Ω(θ) = λ * Σ(w²), где w — веса листьев в дереве.
    • Эффект: "Сжимает" веса листьев равномерно, приближая их к нулю, но редко обнуляя полностью. Это делает модель более гладкой и устойчивой, снижая дисперсию предсказаний.
  2. L1-регуляризация (Lasso, параметр alpha / reg_alpha):

    • Штраф: Ω(θ) = α * Σ|w|.
    • Эффект: Может обнулять веса некоторых листьев, выполняя тем самым встроенный отбор признаков (feature selection) на уровне структуры дерева. Если разделение по какому-то признаку дает малый вклад в уменьшение функции потерь с учетом штрафа L1, вес соответствующего листа может стать нулевым, и это разделение по сути игнорируется.

Практическое использование в XGBoost:

import xgboost as xgb

params = {
    'objective': 'reg:squarederror',
    'max_depth': 6,
    'learning_rate': 0.1,
    'reg_lambda': 1.5,  # Сила L2-регуляризации (по умолчанию 1)
    'reg_alpha': 0.1,   # Сила L1-регуляризации (по умолчанию 0)
    'subsample': 0.8
}

model = xgb.XGBRegressor(**params)
model.fit(X_train, y_train)

Сравнение и выбор:

  • L2 (lambda) обычно используется чаще, так как она более стабильна и лучше справляется с мультиколлинеарностью.
  • L1 (alpha) полезен, когда есть подозрение, что многие признаки избыточны или слабо информативны, и нужна более простая, интерпретируемая модель.
  • Часто их используют вместе (Elastic Net подход в линейных моделях), что также возможно в градиентном бустинге, задав оба параметра ненулевыми.

Ответ 18+ 🔞

Слушай, а вот это реально интересный момент, который многие на разборе задач просто пролетают мимо, как мартышлюшка мимо банана. Речь про то, как в градиентном бустинге эти ваши регуляризации L1 и L2 на самом деле работают. Не как в линейных моделях, блядь, а именно внутри каждого дерева, в этих самых весах листьев.

Представь себе, что ты строишь ансамбль из деревьев. Каждое дерево — это не просто «да/нет», у каждого листа есть свой вес, на который умножается прогноз. Так вот, чтобы модель не возомнила себя богом статистики и не начала запоминать шум, мы её штрафуем. Прямо в целевую функцию, которую она оптимизирует, мы добавляем член Ω(θ). И вот тут начинается магия.

Как это на самом деле работает:

  1. L2-регуляризация (она же Ridge, параметр lambda или reg_lambda):

    • Штрафует модель за СУММУ КВАДРАТОВ весов листьев. Формула: Ω(θ) = λ * Σ(w²).
    • Что делает на практике? Она как строгий, но справедливый тренер: «Окей, чувак, ты можешь быть сильным, но не выёбывайся». Веса листьев она прижимает к нулю, делает модель более гладкой и устойчивой. Переобучение снижается, потому что модель перестаёт делать резкие, хуёвые скачки на выбросах. Дисперсия предсказаний падает. Но обнулить вес полностью она его редко заставит.
  2. L1-регуляризация (она же Lasso, параметр alpha или reg_alpha):

    • Штрафует за СУММУ МОДУЛЕЙ этих весов. Формула: Ω(θ) = α * Σ|w|.
    • А вот это уже поинтереснее. Это как тренер-садист: «Не нужен твой хилый сплит — на хуй его в утиль». Она может взять и обнулить вес листа полностью. По сути, это встроенный отбор признаков прямо внутри дерева. Если какое-то разбиение даёт мизерный выигрыш в качестве, а штраф за него большой — вес обнуляется, и это разделение игнорируется. Модель становится проще и интерпретируемее, иногда даже слишком.

Как это выглядит в коде, например, в XGBoost:

import xgboost as xgb

params = {
    'objective': 'reg:squarederror',
    'max_depth': 6,
    'learning_rate': 0.1,
    'reg_lambda': 1.5,  # Вот она, сила L2. По умолчанию 1, можно крутить.
    'reg_alpha': 0.1,   # А это — L1. По умолчанию 0 (выключена).
    'subsample': 0.8
}

model = xgb.XGBRegressor(**params)
model.fit(X_train, y_train)

Так что же в итоге выбрать, ёпта?

  • L2 (reg_lambda) — это классика, рабочий инструмент. Используют её чаще, потому что она стабильнее, особенно когда признаки между собой скоррелированы (эта самая мультиколлинеарность). Настроил — и в целом доволен, волнение ебать снижается.
  • L1 (reg_alpha) — это когда у тебя подозрение ебать чувствуется, что половина признаков — это шум или дубли. Хочешь получить более простую, «редкую» модель. Но будь осторожен, она может быть капризной.
  • А самое охуенное — их можно использовать вместе, как Elastic Net. Задал оба параметра больше нуля — и получил гибрид: модель и сжимает веса (L2), и может некоторые занулить (L1). Главное — не переборщить, а то модель вообще учиться перестанет, терпения ноль ебать будет.