Как правильно настроить валидацию при использовании стекинга и блендинга?

«Как правильно настроить валидацию при использовании стекинга и блендинга?» — вопрос из категории Деревья и ансамбли, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Главная задача — избежать утечки данных из целевой переменной (y) в мета-признаки. Для этого используется двухуровневая процедура, часто реализуемая через k-фолдовую кросс-валидацию.

Схема корректной валидации для стекинга:

  1. Исходный набор данных делится на обучающую (Train) и финальную тестовую (Hold-out) выборки. Hold-out откладывается до самого конца и не участвует в создании мета-признаков.
  2. На обучающей выборке (Train) выполняется k-фолдовая кросс-валидация для каждой базовой модели (например, RandomForest, XGBoost, SVM).
  3. На каждом шаге CV модель обучается на k-1 фолдах, а ее предсказания для оставшегося фолда (out-of-fold predictions, OOF) сохраняются. В итоге получается полный вектор OOF-предсказаний по всем объектам Train.
  4. Эти OOF-предсказания от всех базовых моделей становятся мета-признаками для обучения мета-модели (например, линейной регрессии или логистической регрессии).
  5. Мета-модель обучается на этих мета-признаках и истинных значениях y_train.
  6. Для получения финальных предсказаний на 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), а не на всех фолдах. Метод проще, но менее устойчив, чем стекинг с кросс-валидацией.