Почему иногда мало просто усреднить несколько моделей
Bagging и averaging полезны, когда хотим сгладить ошибки и сделать прогноз устойчивее. Но иногда разные модели сильны на разных типах объектов. Линейная модель может хорошо ловить общий тренд, дерево — локальные нелинейности, бустинг — сложные взаимодействия признаков. Если просто усреднить их ответы, часть этой структуры потеряется. Stacking делает следующий шаг: он учит отдельную мета-модель понимать, когда и чьему прогнозу больше доверять.
Именно поэтому stacking часто воспринимается как «ансамбль ансамблей»: он не только собирает ответы, но и пытается научиться комбинировать их осмысленно.
Как выглядит идея двух уровней
$$ \hat y = g(\hat y^{(1)}, \hat y^{(2)}, \ldots, \hat y^{(m)}) $$В stacking итоговое предсказание строится не напрямую из исходных признаков, а из прогнозов нескольких базовых моделей. Мета-модель g учится превращать эти прогнозы в более сильный общий ответ.
\hat y— финальный ансамблевый прогнозg— модель второго уровня, которая комбинирует предсказания\hat y^{(1)} \ldots \hat y^{(m)}— прогнозы базовых моделей первого уровня
Главная идея здесь — разнообразие ошибок. Если модели первого уровня ошибаются по-разному, у мета-модели появляется шанс собрать более сильный итоговый прогноз, чем у каждой отдельной модели или у простого среднего.
Почему stacking легко сделать неправильно
Потому что в нем очень легко допустить утечку. Если мета-модель обучается на тех же предсказаниях, которые были получены на обучающих объектах без out-of-fold процедуры, она получает слишком оптимистичную картину. Поэтому правильный stacking почти всегда связан с кросс-валидационными предсказаниями первого уровня.
То есть stacking — это не просто «накидать моделей друг на друга», а аккуратная инженерная конструкция, где честность валидации критична.
Как это выглядит в Python
import pandas as pd # создаем небольшой классификационный пример
from sklearn.linear_model import LogisticRegression # берем линейную модель как один из базовых learners
from sklearn.tree import DecisionTreeClassifier # добавляем дерево как более нелинейный learner
from sklearn.ensemble import StackingClassifier # используем готовый stacking-ансамбль из sklearn
frame = pd.DataFrame({ # задаем признаки и target
'sessions': [1, 2, 3, 4, 5, 6, 7, 8], # активность пользователя
'avg_time': [2, 3, 3, 4, 5, 6, 8, 9], # средняя длительность
'tickets': [3, 2, 2, 1, 1, 1, 0, 0], # обращения в поддержку
'target': [0, 0, 0, 0, 1, 1, 1, 1], # бинарный target
})
X = frame[['sessions', 'avg_time', 'tickets']] # выделяем матрицу признаков
y = frame['target'] # сохраняем target отдельно
stack = StackingClassifier( # строим ансамбль из двух базовых моделей и одной мета-модели
estimators=[
('lr', LogisticRegression(max_iter=2000)), # первая модель хорошо ловит общий линейный сигнал
('tree', DecisionTreeClassifier(max_depth=3, random_state=42)), # вторая модель ловит пороги и нелинейности
],
final_estimator=LogisticRegression(max_iter=2000) # мета-модель учится комбинировать их прогнозы
)
stack.fit(X, y) # обучаем оба уровня ансамбля на данных
print(stack.predict(X).tolist()) # смотрим итоговые решения stacking-моделиStacking полезно понимать не как трюк для соревнований, а как способ формализовать очень человеческую идею: если несколько экспертов сильны в разных ситуациях, можно научиться собирать их ответы умнее, чем простым средним.