콘텐츠로 이동

Stacking (Stacked Generalization)

논문 정보

항목 내용
제목 Stacked Generalization
저자 David H. Wolpert
학회/저널 Neural Networks
연도 1992
링크 https://www.sciencedirect.com/science/article/abs/pii/S0893608005800231

개요

핵심 아이디어

"여러 모델의 예측을 입력으로 받아, 메타 모델이 최종 예측을 학습한다"

Level 0 (Base Learners):          Level 1 (Meta Learner):

Input X ──┬──► Model 1 ──► pred_1 ──┐
          │                         │
          ├──► Model 2 ──► pred_2 ──┼──► Meta Model ──► Final Prediction
          │                         │
          └──► Model 3 ──► pred_3 ──┘

Voting과의 차이

특성 Voting Stacking
결합 방식 단순 평균/투표 학습된 가중치
모델 간 상호작용 없음 메타 모델이 학습
유연성 낮음 높음
과적합 위험 낮음 있음 (교차 검증 필요)

Stacking 알고리즘

기본 구조

Algorithm: Stacking

Input: 학습 데이터 (X, y), 기반 학습기 {L_1, ..., L_K}, 메타 학습기 L_meta
Output: 스택 앙상블 모델

# Phase 1: 기반 학습기 훈련 (with cross-validation)
for each base learner L_k:
    for each fold i in K-fold:
        # i번째 fold 제외하고 학습
        L_k.fit(X_train_i, y_train_i)

        # i번째 fold 예측 (out-of-fold)
        predictions_i = L_k.predict(X_val_i)

    # 전체 데이터에 대한 out-of-fold 예측
    meta_features_k = concatenate(predictions_i for all i)

# Phase 2: 메타 학습기 훈련
meta_X = stack(meta_features_1, ..., meta_features_K)  # (n_samples, K)
L_meta.fit(meta_X, y)

# Phase 3: 최종 학습기 재훈련 (전체 데이터로)
for each base learner L_k:
    L_k.fit(X, y)

return (L_1, ..., L_K, L_meta)

교차 검증의 중요성

왜 Out-of-Fold 예측이 필요한가?

잘못된 방법 (정보 누출):
X_train ──► Model ──► pred_train ──► Meta Model
    └─────────────────────────────────────┘
       동일 데이터로 학습 + 예측 = 과적합!

올바른 방법 (Out-of-Fold):
X_train ──┬── Fold 1,2,3 학습 ──► Fold 4 예측 ──┐
          ├── Fold 1,2,4 학습 ──► Fold 3 예측 ──┼──► Meta Model
          ├── Fold 1,3,4 학습 ──► Fold 2 예측 ──┤
          └── Fold 2,3,4 학습 ──► Fold 1 예측 ──┘

학습에 사용되지 않은 데이터로 예측 = 일반화 보장

scikit-learn 구현

StackingClassifier

from sklearn.ensemble import StackingClassifier, RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

# 데이터
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 기반 학습기 (다양한 유형)
base_learners = [
    ('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
    ('svc', make_pipeline(StandardScaler(), SVC(probability=True, random_state=42))),
    ('knn', make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))),
    ('dt', DecisionTreeClassifier(max_depth=5, random_state=42))
]

# 메타 학습기
meta_learner = LogisticRegression(max_iter=1000)

# Stacking
stacking = StackingClassifier(
    estimators=base_learners,
    final_estimator=meta_learner,
    cv=5,                      # 내부 교차 검증 폴드 수
    stack_method='auto',       # 'auto', 'predict_proba', 'decision_function', 'predict'
    passthrough=False,         # True면 원본 특성도 메타 학습기에 전달
    n_jobs=-1
)

stacking.fit(X_train, y_train)

# 평가
print(f"Stacking Test Accuracy: {stacking.score(X_test, y_test):.4f}")

# 개별 모델과 비교
for name, model in base_learners:
    model.fit(X_train, y_train)
    score = model.score(X_test, y_test)
    print(f"{name}: {score:.4f}")

StackingRegressor

```python from sklearn.ensemble import StackingRegressor from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.linear_model import Ridge, Lasso, ElasticNet from sklearn.svm import SVR from sklearn.datasets import fetch_california_housing from sklearn.metrics import mean_squared_error, r2_score import numpy as np