Ответ
При работе с градиентным бустингом (XGBoost, LightGBM, CatBoost) я применяю несколько уровней регуляризации, чтобы управлять сложностью модели и предотвращать переобучение.
Стратегии регуляризации, которые я использую:
-
Регуляризация самих деревьев (базовых learners):
max_depth/num_leaves: Жёстко ограничиваю глубину или количество листьев.min_child_weight(XGB) /min_data_in_leaf(LGBM): Задаю минимальную сумму весов (или количество объектов) в листе, чтобы не создавать слишком специфичные разделения.gamma(XGB): Минимальное снижение потерь для выполнения split.
-
L1 и L2 регуляризация на веса листьев:
reg_alpha(L1) иreg_lambda(L2) в XGBoost.lambda_l1,lambda_l2в LightGBM.- L1 может занулять веса некоторых листьев, действуя как отбор признаков на уровне дерева.
-
Стохастические методы:
subsample: Обучаю каждое дерево на случайной подвыборке данных (например, 0.8).colsample_bytree,colsample_bylevel,colsample_bynode: Случайно выбираю подмножество признаков для всего дерева, уровня или каждого разделения.
-
Контроль скорости обучения:
- Небольшой
learning_rate(например, 0.01-0.1) в сочетании с большим числомn_estimators. Это требует больше итераций, но делает процесс обучения более плавным и устойчивым.
- Небольшой
-
Ранняя остановка (Early Stopping):
- Это мой основной инструмент. Я всегда выделяю валидационную выборку и останавливаю обучение, когда её метрика перестаёт улучшаться.
Пример настройки регуляризованной модели в XGBoost:
import xgboost as xgb
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
dtrain = xgb.DMatrix(X_train, label=y_train)
dval = xgb.DMatrix(X_val, label=y_val)
params = {
'objective': 'binary:logistic',
'max_depth': 4, # Неглубокие деревья
'min_child_weight': 5, # Минимум объектов в листе
'subsample': 0.8, # Стохастичность по объектам
'colsample_bytree': 0.8, # Стохастичность по признакам
'eta': 0.05, # Малая скорость обучения
'reg_alpha': 0.1, # L1 регуляризация
'reg_lambda': 1.5, # L2 регуляризация
'eval_metric': 'logloss'
}
evals = [(dtrain, 'train'), (dval, 'eval')]
model = xgb.train(params, dtrain, num_boost_round=1000,
evals=evals, early_stopping_rounds=50, verbose_eval=50) Ответ 18+ 🔞
А, регуляризация в бустингах! Ну это, блядь, святое дело, если не хочешь, чтобы модель на тебя смотрела как на идиота и переобучилась так, что даже твою бабушку по IP-адресу распознает.
Слушай, я тебе как на духу. Когда дело доходит до XGBoost, LightGBM или этого кота с бустом, я регуляризацию впихиваю везде, куда только можно, как будто от этого зависит моя зарплата. Потому что иначе получается пиздопроебибна ситуация: модель на трейне показывает точность 99.9%, а на новых данных — хуй с горы.
Вот моя философия, по пунктам, чтобы не охуеть от сложности:
-
Деревья сами по себе. Первое правило — не дай им вырасти до небес.
max_depthилиnum_leavesжму как последнюю копейку. Зачем дереву 15 уровней? Чтобы каждый лист соответствовал одному конкретному наблюдению? Да похуй. Лучше 4-5, и будет счастье. Ещёmin_child_weight— это чтобы лист не создавался ради двух-трёх ушлёпков с краю датасета. Без этого — доверия ебать ноль. -
L1 и L2 — мои лучшие друзья. Это
reg_alphaиreg_lambda. L1, тот вообще красавец, может вес листа в ноль загнать, по сути как отбор фич внутри дерева. L2 — просто штрафует за большие веса, чтобы дерево не думало, что оно самый умный и может одним листом весь таргет предсказать. Без них — хитрая жопа получится, а не модель. -
Стохастика — наше всё.
subsampleи всякиеcolsample_by*. Суть проста: каждое новое дерево учится не на всём датасете и не на всех фичах, а на случайной их подвыборке. Это как заставить модель смотреть на мир не одним глазом, а каждый раз немного по-новому. Итог — ансамбль получается устойчивее, терпения ноль ебать к шуму в данных. -
Скорость обучения (
learning_rateилиeta). Вот тут многие охуевают и ставят 0.3. А потом удивляются, почему модель скачет как коза и сходится в какую-то хуйню. Я жму на 0.01-0.05, но затоn_estimatorsставлю овердохуища. Медленно, плавно, зато точно в цель. Это как не гнать на спуске с горы, а аккуратно тормозить. -
Ранняя остановка (Early Stopping). Это вообще манда с ушами, если её не использовать. Я всегда, блядь, оставляю валидационную выборку и смотрю, когда метрика на ней перестаёт расти. Как только улучшений нет 20-50 раундов — всё, стоп машина, дальше учиться нечему, только переобучение качается.
Вот тебе пример, как это выглядит в коде, чтобы не быть голословным:
import xgboost as xgb
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
dtrain = xgb.DMatrix(X_train, label=y_train)
dval = xgb.DMatrix(X_val, label=y_val)
params = {
'objective': 'binary:logistic',
'max_depth': 4, # Глубоко не лезем, а то вылезем в переобучение
'min_child_weight': 5, # Чтобы лист не для пары чудаков создавался
'subsample': 0.8, # Берём 80% данных для каждого дерева
'colsample_bytree': 0.8, # И 80% фич
'eta': 0.05, # Ползи, улитка, ползи
'reg_alpha': 0.1, # L1, зануляй лишнее
'reg_lambda': 1.5, # L2, успокой горячие головы-листья
'eval_metric': 'logloss'
}
evals = [(dtrain, 'train'), (dval, 'eval')]
model = xgb.train(params, dtrain, num_boost_round=1000,
evals=evals, early_stopping_rounds=50, verbose_eval=50)
Смотри, что здесь происходит: мы даём модели возможность сделать аж 1000 деревьев, но благодаря ранней остановке она, скорее всего, упрётся в потолок валидационной метрики где-то на 300-400. А все эти регуляризаторы не дают каждому отдельному дереву взъебать данные и выучить тренировочный шум. В итоге получается сбалансированная, крепкая модель, которая не бздит на новых данных. Красота, ёпта!