파이썬 머신러닝 완벽 가이드 (권철민 저)을 요약정리했습니다.
스태킹 앙상블
스태킹은 개별적인 여러 알고리즘을 서로 결합해 예측 결과를 도출한다는 점에서 배깅 및 부스팅과 공통점을 가지고 있다.
가장 큰 차이점은 개별 알고리즘으로 예측한 데이터를 기반으로 다시 예측을 수행한다는 것이다.
즉, 개별 알고리즘의 예측 결과 데이터 셋을 최종적인 메타 데이터 셋으로 만들어 별도의 ML알고리즘으로 최종학습을 수행하고
테스트 데이터를 기반으로 다시 최종 예측을 수행하는 방식이다.
스태킹 모델은 두 종류의 모델이 필요한데, 첫 번째는 개별적인 기반 모델이고, 두 번째는 이 개별 기반 모델의 예측 데이터를 학습 데이터로 만들어서 학습하는 최종 메타 모델 이다.
스태킹을 적용할 때는 많은 개별 모델이 필요하다. 2~3개의 개별 모델만을 결합해서는 쉽게 예측 성능을 향상시킬 수 없으며, 스태킹을 적용한다고 해서 반드시 성능 향상으로 이어지지는 않는다.
일반적으로 스태킹은 성능이 비슷한 모델을 결합해 좀 더 나은 성능 향상을 도출 하기 위해 적용한다.
M개의 로우, N개의 피처릴 가진 데이터 셋에 스태킹 앙상블을 적용한다고 가정하자.
학습에 사용할 ML 알고리즘은 총 3개이고, 먼저 모델별로 도출된 예측 레이블 값을 다시 합해서(스태킹) 새로운 데이터 셋을 만들고
이렇게 스태킹 된 데이터 셋에 대해 최종 모델을 적용해 최종 예측을 하는 것이 스태킹 앙상블 모델이다.
기본적인 스태킹 모델 구현
스태킹에 사용될 알고리즘 클래스를 생성.
개별 모델은 KNN/ RandomForest/ Decision Tree/ Adaboost 이며, 이들 모델의
예측 결과를 합한 데이터 셋으로 학습/예측하는 최종 모델은 로지스틱 회귀 이다.
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)
# 개별 ML 모델을 위한 Classifier 생성.
knn_clf = KNeighborsClassifier(n_neighbors=4)
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0)
dt_clf = DecisionTreeClassifier()
ada_clf = AdaBoostClassifier(n_estimators=100)
# 최종 Stacking 모델을 위한 Classifier생성.
lr_final = LogisticRegression(C=10)
개별 모델을 학습시킨다.
# 개별 모델들을 학습.
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)
AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None,
learning_rate=1.0, n_estimators=100, random_state=None)
개별 모델의 예측 데이터 셋을 반환하고 각 모델의 예측 정확도를 살펴보자.
# 학습된 개별 모델들이 각자 반환하는 예측 데이터 셋을 생성하고 개별 모델의 정확도 측정.
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('KNN 정확도: {0:.4f}'.format(accuracy_score(y_test, knn_pred)))
print('랜덤 포레스트 정확도: {0:.4f}'.format(accuracy_score(y_test, rf_pred)))
print('결정 트리 정확도: {0:.4f}'.format(accuracy_score(y_test, dt_pred)))
print('에이다부스트 정확도: {0:.4f} :'.format(accuracy_score(y_test, ada_pred)))
KNN 정확도: 0.9211
랜덤 포레스트 정확도: 0.9649
결정 트리 정확도: 0.9035
에이다부스트 정확도: 0.9561 :
개별 알고리즘으로부터 예측된 예측값을 칼럼 레벨로 옆으로 붙여서 피처 값으로 만든다.
그다음 최종 메타 모델인 로지스틱 회구에서 학습데이터로 다시 사용한다.
반환된 예측 데이터 셋은 1차원 형태의 ndarray이므로 먼저 반환된 예측 결과를 행 형태로 붙인 뒤,
넘파이의 transpose()를 이용해 행과 열 위치를 바꾼 ndarray로 변환하면 된다.
pred = np.array([knn_pred, rf_pred, dt_pred, ada_pred])
print(pred.shape)
# transpose를 이용해 행과 열의 위치 교환. 컬럼 레벨로 각 알고리즘의 예측 결과를 피처로 만듦.
pred = np.transpose(pred)
print(pred.shape)
(4, 114)
(114, 4)
이렇게 예측 데이터로 생성된 데이터 셋을 기반으로 최종 메타 모델인 로지스틱 회귀를 학습하고 예측 정확도를 측정해 보자.
lr_final.fit(pred, y_test)
final = lr_final.predict(pred)
print('최종 메타 모델의 예측 정확도: {0:.4f}'.format(accuracy_score(y_test , final)))
최종 메타 모델의 예측 정확도: 0.9737
개별 모델의 예측 데이터를 스태킹으로 재구성해 최종 메타 모델에서 학습하고 예측한 결과 ,정확도가 97.37%로 개별 모델 정확도 보다 향상 되었음을 알 수 있다.
CV 셋 기반의 Stacking
cv셋 기반의 스태킹 모델은 과적합을 개선하기 위해 최종 메타 모델을 위한 데이터 셋을 만들 때 교차 검즘 기반으로 예측된 결과 데이터 셋을 이용한다.
CV셋 기반의 스태킹은 이에 대한 개선을 위해 개별 모델들이 각각 교차 검증으로
메타 모델을 위한 학습용 스태킹 데이터 생성과 예측을 위한 테스트용 스태킹 데이터를 생성한 뒤
이를 기반으로 메타 모델이 학습과 예측을 수행한다. 과정을 자세히 살펴보면 다음과 같다.
- 데이터를 Fold 로나눔
- 각 모델 별로 Fold로 나누어진 데이터를 기반으로 훈련을 진행 (X_train, y_train_ 사용
- 이때 각 Fold 마다 뽑아진 훈련 데이터로 모델을 훈련하고 검증 데이터를 활용해 예측후 값을 저장
- 마찬가지로 각 Fold 마다 나온 model을 기반으로 원본 X_test데이터를 훈련하여 저장
- 2까지 진행해서 나온 각 모델별 예측 데이터(2-1)를 모두 stacking 하여 최종 모델의 훈련 데이터로 사용
- label은 원본 y_train 값으로 진행
- 2-2에서 나온 데이터로 예측을 수행하며 pred값을 뽑아냄
- 4에서 나온 pred와 y_test 값을 비교해서 최종 모델 평가
CV셋 기반의 스태킹에대한 자세한 설명은 아래 링크를 통해 확이할 수있다.
https://lsjsj92.tistory.com/559
cv 셋 기반의 스태킹 구현
from sklearn.model_selection import KFold
from sklearn.metrics import mean_absolute_error
# 개별 기반 모델에서 최종 메타 모델이 사용할 학습 및 테스트용 데이터를 생성하기 위한 함수.
def get_stacking_base_datasets(model, X_train_n, y_train_n, X_test_n, n_folds ):
# 지정된 n_folds값으로 KFold 생성.
kf = KFold(n_splits=n_folds, shuffle=False, 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('\t 폴드 세트: ',folder_counter,' 시작 ')
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)
#train_fold_pred는 최종 메타 모델이 사용하는 학습 데이터, test_pred_mean은 테스트 데이터
return train_fold_pred , test_pred_mean
이제 여러 개의 분류 모델별로 get_stacking_base_datasets 함수를 수행한다.
get_stacking_base_datasets() 함수를 호출해
각각 메타 모델이 추후에 사용할 학습용, 테스트용 데이터 셋을 반환한다.
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)
KNeighborsClassifier model 시작
폴드 세트: 0 시작
폴드 세트: 1 시작
폴드 세트: 2 시작
폴드 세트: 3 시작
폴드 세트: 4 시작
폴드 세트: 5 시작
폴드 세트: 6 시작
RandomForestClassifier model 시작
폴드 세트: 0 시작
폴드 세트: 1 시작
폴드 세트: 2 시작
폴드 세트: 3 시작
폴드 세트: 4 시작
폴드 세트: 5 시작
폴드 세트: 6 시작
DecisionTreeClassifier model 시작
폴드 세트: 0 시작
폴드 세트: 1 시작
폴드 세트: 2 시작
폴드 세트: 3 시작
폴드 세트: 4 시작
폴드 세트: 5 시작
폴드 세트: 6 시작
AdaBoostClassifier model 시작
폴드 세트: 0 시작
폴드 세트: 1 시작
폴드 세트: 2 시작
폴드 세트: 3 시작
폴드 세트: 4 시작
폴드 세트: 5 시작
폴드 세트: 6 시작
반환된 각 모델 별 학습 데이터와 테스트 데이터를 넘파이의 concatenate()를 이용해 합쳐준다.
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('원본 학습 피처 데이터 Shape:',X_train.shape, '원본 테스트 피처 Shape:',X_test.shape)
print('스태킹 학습 피처 데이터 Shape:', Stack_final_X_train.shape,
'스태킹 테스트 피처 데이터 Shape:',Stack_final_X_test.shape)
원본 학습 피처 데이터 Shape: (455, 30) 원본 테스트 피처 Shape: (114, 30)
스태킹 학습 피처 데이터 Shape: (455, 4) 스태킹 테스트 피처 데이터 Shape: (114, 4)
이렇게 만들어지는 Stack_final_X_train은 메타 모델이 학습할 학습용 피처 데이터 셋이다.
최종 메타 모델인 로지스틱 회귀를 스태킹된 학습용 피처 데이터 셋과 원본 학습 레이블 데이터로 학습한 후에
스태킹된 테스트 데이터 셋을 예측하고 정확도를 측정해 보자.
lr_final.fit(Stack_final_X_train, y_train)
stack_final = lr_final.predict(Stack_final_X_test)
print('최종 메타 모델의 예측 정확도: {0:.4f}'.format(accuracy_score(y_test, stack_final)))
최종 메타 모델의 예측 정확도: 0.9737
최종 메타 모델의 예측 정확도는 97.37%로 측정 되었다.
스태킹을 이루는 모델은 최적으로 파라미터를 튜닝한 상태에서 스태킹 모델을 만드는 것이 일반적이다.
여러 명으로 이뤄진 분석 팀에서 개별적으로 각각 모델을 최적으로 학습시켜서 스태킹 모델을 더 빠르게 최적화 시킬 수 있을 것이다.
스태킹은 분류 뿐만 아니라 회귀에도 적용가능하다.