Programming

Stacking Ensemble 실습 : Basic Stacking과 교차검증 기반 Stacking 이해하기

Lucas.Kim 2025. 12. 31. 00:55
반응형

1. Stacking 앙상블 개요

Stacking(스태킹) 은 여러 개의 서로 다른 모델이 만든 예측 결과 자체를 새로운 피처로 사용하여
또 다른 모델(메타 모델, Meta Model)이 최종 예측을 수행하는 앙상블 기법입니다.

즉,

  • 1단계(Base Model) : 여러 개의 서로 다른 모델이 예측 수행
  • 2단계(Meta Model) : 각 모델의 예측 결과를 입력값으로 다시 학습

하는 구조를 가집니다.

“모델들의 판단 결과를 다시 하나의 모델에게 맡기는 방식”이라고 이해하면 됩니다.

2. Basic Stacking Model 구조

⚠️ 주의점

Basic Stacking은 테스트 데이터를 메타 모델 학습에 재사용하기 때문에
데이터 누수(Data Leakage)오버피팅 위험이 있습니다.
→ 개념 이해용으로는 적절하지만, 실무에서는 개선된 방식이 필요합니다.

3. 데이터 및 라이브러리 로드

import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

4. 데이터 로딩 및 분리

cancer_data = load_breast_cancer()

X_data = cancer_data.data
y_label = cancer_data.target

X_train, X_test, y_train, y_test = train_test_split(
    X_data,
    y_label,
    test_size=0.2,
    random_state=0
)

 

  • 유방암 데이터셋
  • 이진 분류 문제
  • 학습 80%, 테스트 20%

5. 개별 기반 모델(Base Model) 및 메타 모델 생성

# 개별 기반 모델
knn_clf = KNeighborsClassifier(n_neighbors=4)
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0)
dt_clf = DecisionTreeClassifier()
ada_clf = AdaBoostClassifier(n_estimators=100)

# 메타 모델
lr_final = LogisticRegression(C=10)

 

구성 의도

  • KNN : 거리 기반
  • Decision Tree : 규칙 기반
  • RandomForest : 배깅 기반 앙상블
  • AdaBoost : 부스팅 기반 앙상블

→ 서로 다른 학습 방식의 모델을 섞어 다양성(Diversity) 확보

 

6. 개별 모델 학습

knn_clf.fit(X_train, y_train)
rf_clf.fit(X_train, y_train)
dt_clf.fit(X_train, y_train)
ada_clf.fit(X_train, y_train)

7. 개별 모델 예측 및 성능 확인

knn_pred = knn_clf.predict(X_test)
rf_pred = rf_clf.predict(X_test)
dt_pred = dt_clf.predict(X_test)
ada_pred = ada_clf.predict(X_test)

print(f'스태킹전 knn 정확도 : {round(accuracy_score(y_test, knn_pred), 2)}')
print(f'스태킹전 rf 정확도 : {round(accuracy_score(y_test, rf_pred), 2)}')
print(f'스태킹전 dt 정확도 : {round(accuracy_score(y_test, dt_pred), 2)}')
print(f'스태킹전 ada 정확도 : {round(accuracy_score(y_test, ada_pred), 2)}')



knn : 0.92
rf  : 0.96
dt  : 0.91
ada : 0.97

 

  • AdaBoost, RandomForest가 상대적으로 우수
  • 하지만 각 모델의 오답 패턴은 서로 다름

8. 예측 결과를 Stacking 형태로 변환

pred = np.array([knn_pred, rf_pred, dt_pred, ada_pred])
print(pred.shape)

(4, 114)


pred = np.transpose(pred)
print(pred.shape)

(114, 4)

 

👉 이제 각 행은
[KNN 예측, RF 예측, DT 예측, Ada 예측] 이라는 새로운 피처 벡터가 됩니다.

9. 메타 모델 학습 및 예측 (⚠️ 오버피팅 구조)

lr_final.fit(pred, y_test)
final = lr_final.predict(pred)

print(f'스태킹 후 meta 정확도 : {round(accuracy_score(y_test, final), 2)}')

스태킹 후 meta 정확도 : 0.97

문제점 설명

  • 테스트 데이터를 다시 학습에 사용
  • 실제 일반화 성능을 보장하지 못함
  • 실무에서는 사용하면 안 되는 방식

👉 이를 해결하기 위해 교차검증 기반 Stacking이 필요합니다.

 

10. CV(교차검증) 기반 Stacking

핵심 아이디어

  • 훈련 데이터 내부에서 교차검증을 통해 메타 모델용 학습 데이터 생성
  • 테스트 데이터는 한 번도 학습에 사용되지 않음

11. Stacking 데이터 생성 함수 정의

from sklearn.model_selection import KFold

def get_stacking_base_datasets(model, X_train_n, y_train_n, X_test_n, n_folds):
    kf = KFold(
        n_splits=n_folds,
        shuffle=True,
        random_state=0
    )

    # 메타 모델 학습용 데이터
    train_fold_pred = np.zeros((X_train_n.shape[0], 1))
    # 테스트 데이터 예측값 저장
    test_pred = np.zeros((X_test_n.shape[0], n_folds))

    print(model.__class__.__name__, "model 시작")

    for folder_counter, (train_index, valid_index) in enumerate(kf.split(X_train_n)):
        print('폴드세트 시작')

        X_tr = X_train_n[train_index]
        y_tr = y_train_n[train_index]
        X_te = X_train_n[valid_index]

        model.fit(X_tr, y_tr)

        # 검증 데이터 예측 → 메타 학습 데이터
        train_fold_pred[valid_index, :] = model.predict(X_te).reshape(-1, 1)

        # 테스트 데이터 예측 → 폴드별 저장
        test_pred[:, folder_counter] = model.predict(X_test_n)

    # 테스트 예측값 평균
    test_pred_mean = np.mean(test_pred, axis=1).reshape(-1, 1)

    return train_fold_pred, test_pred_mean

12. 각 모델별 Stacking 데이터 생성

knn_train, knn_test = get_stacking_base_datasets(knn_clf, X_train, y_train, X_test, 7)
rf_train, rf_test = get_stacking_base_datasets(rf_clf, X_train, y_train, X_test, 7)
dt_train, dt_test = get_stacking_base_datasets(dt_clf, X_train, y_train, X_test, 7)
ada_train, ada_test = get_stacking_base_datasets(ada_clf, X_train, y_train, X_test, 7)

13. 최종 Stacking 학습/테스트 데이터 생성

Stack_final_X_train = np.concatenate(
    (knn_train, rf_train, dt_train, ada_train),
    axis=1
)

Stack_final_X_test = np.concatenate(
    (knn_test, rf_test, dt_test, ada_test),
    axis=1
)

print(f'원본 학습 피쳐데이터셋 {X_train.shape}')
print(f'원본 테스트 피쳐셋 {X_test.shape}')
print(f'스태킹 학습 피쳐데이터셋 : {Stack_final_X_train.shape}')
print(f'스태킹 테스트 피쳐 데이터셋 {Stack_final_X_test.shape}')

원본 학습 : (455, 30)
원본 테스트 : (114, 30)
스태킹 학습 : (455, 4)
스태킹 테스트 : (114, 4)

14. 메타 모델 최종 학습 및 평가

lr_final.fit(Stack_final_X_train, y_train)
stack_final = lr_final.predict(Stack_final_X_test)

print(f'최종 모델 예측 정확도 : {accuracy_score(y_test, stack_final)}')

15. 결론 정리

방식특징

Basic Stacking 개념 이해용, 오버피팅 위험
CV 기반 Stacking 실무 사용 가능
핵심 예측값을 다시 학습 데이터로 사용
중요 포인트 반드시 교차검증 사용

Stacking은 모델 성능을 끌어올리는 강력한 방법이지만,
데이터 누수를 방지하는 구조 설계가 필수
입니다.

반응형