Ответ
Главная задача — избежать утечки данных из целевой переменной (y) в мета-признаки. Для этого используется двухуровневая процедура, часто реализуемая через k-фолдовую кросс-валидацию.
Схема корректной валидации для стекинга:
- Исходный набор данных делится на обучающую (Train) и финальную тестовую (Hold-out) выборки. Hold-out откладывается до самого конца и не участвует в создании мета-признаков.
- На обучающей выборке (Train) выполняется
k-фолдовая кросс-валидация для каждой базовой модели (например, RandomForest, XGBoost, SVM). - На каждом шаге CV модель обучается на
k-1фолдах, а ее предсказания для оставшегося фолда (out-of-fold predictions, OOF) сохраняются. В итоге получается полный вектор OOF-предсказаний по всем объектам Train. - Эти OOF-предсказания от всех базовых моделей становятся мета-признаками для обучения мета-модели (например, линейной регрессии или логистической регрессии).
- Мета-модель обучается на этих мета-признаках и истинных значениях
y_train. - Для получения финальных предсказаний на Hold-out или новых данных:
- Каждая базовая модель, обученная на всем Train, делает предсказание.
- Эти предсказания подаются как входные признаки в обученную мета-модель.
Пример кода для создания OOF-признаков:
import numpy as np
from sklearn.model_selection import KFold
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
# Исходные данные (без Hold-out)
X_train, y_train = ...
kf = KFold(n_splits=5, shuffle=True, random_state=42)
oof_predictions = np.zeros((X_train.shape[0], len(base_models)))
# Для каждой базовой модели
for model_idx, base_model in enumerate(base_models):
col_oof = np.zeros(X_train.shape[0])
for fold, (train_idx, val_idx) in enumerate(kf.split(X_train, y_train)):
X_tr, X_val = X_train[train_idx], X_train[val_idx]
y_tr = y_train[train_idx]
# Обучаем базовую модель на фолде
model = clone(base_model)
model.fit(X_tr, y_tr)
# Предсказываем на валидационном фолде
col_oof[val_idx] = model.predict(X_val)
oof_predictions[:, model_idx] = col_oof
# OOF-предсказания — это мета-признаки для Train
meta_features_train = oof_predictions
# Обучаем мета-модель на этих признаках
meta_model = LinearRegression()
meta_model.fit(meta_features_train, y_train)
Блендинг — это упрощенная версия стекинга, где мета-признаки создаются путем предсказания базовых моделей на одной отдельной валидационной выборке (обычно 10-20% от Train), а не на всех фолдах. Метод проще, но менее устойчив, чем стекинг с кросс-валидацией.
Ответ 18+ 🔞
Давай разберём эту всю хрень со стекингом и блендингом, а то народ постоянно наступает на одни и те же грабли, а потом удивляется, почему модель нихуя не работает, а на тесте всё накрывается медным тазом.
Слушай, главная проблема, из-за которой всё летит в пизду — это утечка данных из целевой переменной y в мета-признаки. Представь, ты учишь модель на данных, где она уже знает ответы. Это как списать у самого себя на экзамене — результат будет овердохуища, но на реальной задаче ты окажешься полным лузером. Чтобы этого не было, нужна двухуровневая процедура, обычно через k-фолдовую кросс-валидацию. Запомни это, как «Отче наш».
Как правильно делать стекинг, чтобы не облажаться:
- Разделяй и властвуй. Берёшь все данные и сразу откладываешь финальную тестовую выборку (Hold-out) в самый тёмный угол. К ней не прикасаешься, пока не закончишь всю основную работу. Это святое.
- Работаем с обучающей выборкой (Train). На ней делаешь
k-фолдовую кросс-валидацию для каждой своей базовой модели — будь то RandomForest, XGBoost или какая-нибудь мартышлюшка в виде SVM. - Вот тут магия. На каждом шаге CV ты учишь модель на
k-1фолдах, а потом заставляешь её предсказывать на том единственном фолде, который она не видела (out-of-fold predictions, OOF). Эти предсказания ты аккуратно собираешь. В итоге у тебя получается полный вектор OOF-предсказаний по всем объектам Train. Это и есть твои честные мета-признаки, безо всякой утечки, ёпта! - Собираем пазл. Эти OOF-предсказания от всех твоих базовых моделей становятся входом для мета-модели (ну, там линейная регрессия или что-то подобное). Её ты и учишь на этих признаках и на реальных значениях
y_train. - Финальный аккорд. Когда надо предсказать что-то на отложенном Hold-out или на новых данных, ты делаешь так:
- Каждая базовая модель, которую ты заново обучил на всём Train, делает своё предсказание.
- Эти предсказания ты скармливаешь уже обученной мета-модели, и она выдаёт итоговый вердикт.
Смотри, как это выглядит в коде, чтобы не было подозрения, что я тебя развожу:
import numpy as np
from sklearn.model_selection import KFold
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
# Допустим, у тебя уже есть данные (Hold-out мы уже откусили и спрятали)
X_train, y_train = ...
kf = KFold(n_splits=5, shuffle=True, random_state=42)
# Массив, куда будем складывать OOF-предсказания от каждой модели
oof_predictions = np.zeros((X_train.shape[0], len(base_models)))
# Гоняем каждую базовую модель
for model_idx, base_model in enumerate(base_models):
col_oof = np.zeros(X_train.shape[0])
# А вот и наша k-fold CV
for fold, (train_idx, val_idx) in enumerate(kf.split(X_train, y_train)):
X_tr, X_val = X_train[train_idx], X_train[val_idx]
y_tr = y_train[train_idx]
# Клонируем модель, учим на фолде
model = clone(base_model)
model.fit(X_tr, y_tr)
# Предсказываем на валидационном фолде — то, что модель не видела
col_oof[val_idx] = model.predict(X_val)
# Записываем столбец OOF-предсказаний для этой модели
oof_predictions[:, model_idx] = col_oof
# Вуаля! oof_predictions — это и есть наши честные мета-признаки для Train
meta_features_train = oof_predictions
# Теперь учим мета-модель на этой красоте
meta_model = LinearRegression()
meta_model.fit(meta_features_train, y_train)
А теперь про блендинг. Это, по сути, ленивая упрощённая версия стекинга для тех, у кого терпения ноль ебать. Вместо того чтобы городить всю эту ёперный театр с фолдами, ты просто откусываешь от Train одну валидационную выборку (скажем, 10-20%), учишь на остатке базовые модели, а предсказания на этой валидашке используешь как мета-признаки. Метод проще, быстрее, но, ясное дело, менее устойчивый. Если тебе не жалко данных и хочется побыстрее — можно и так, но знай, что идешь по охуенно тонкому льду.