[사이킷런] 부스팅 알고리즘(AdaBoost & GBM)
23 Mar 2020
Reading time ~6 minutes
이번 포스팅에서는 부스팅 알고리즘
의 개념과 그 한 종류인 AdaBoost와 GBM을 간단히 정리해보겠습니다.
부스팅 알고리즘(Boosting Algorithm)
부스팅 알고리즘은 여러 개의 약한 학습기(weak learner)를 순차적으로 학습-예측하면서 잘못 예측한 데이터에 가중치를 부여해 오류를 개선해나가는 학습방식입니다.
부스팅 알고리즘은 대표적으로 아래와 같은 알고리즘들이 있습니다.
- AdaBoost
- GBM(Gradient Boosting Machine)
- XGBoost
- LightGBM
- CatBoost
AdaBoost
AdaBoost는 Adaptive Boost의 줄임말로서 약한 학습기(weak learner)의 오류 데이터에 가중치를 부여하면서 부스팅을 수행하는 대표적인 알고리즘입니다. 속도나 성능적인 측면에서 Decision Tree를 약한 학습기로 사용하고 있습니다.
AdaBoost의 학습
AdaBoost는 아래와 같은 방식으로 학습을 진행합니다.
(1) 첫 번째 약한 학습기가 첫번째 분류기준(D1)으로 + 와 - 를 분류
(2) 잘못 분류된 데이터에 대해 가중치를 부여(두번째 그림에서 커진 + 표시)
(3) 두번째 약한 학습기가 두번째 분류기준(D2)으로 +와 -를 다시 분류
(4) 잘못 분류된 데이터에 대해 가중치를 부여(세번째 그림에서 커진 - 표시)
(5) 세 번째 약한 학습기가 세번째 분류기준(D3)으로 +와 -를 다시 분류해서 오류 데이터를 찾음
(6) 마지막으로 분류기들을 결합하여 최종 예측 수행(네번째 그림)
이처럼 AdaBoost는 순차적으로 학습시켜 개별 학습기에 가중치를 부여하여 모두 결합합니다. 가령, 첫 번째 학습기에는 가중치 0.3, 두 번째 학습기에 가중치 0.5, 세 번째 학습기에 가중치 0.8을 부여하여 모두 결합하여 예측을 수행합니다. 이를 통해 개별의 약한 학습기보다 성능 향상시킵니다.
AdaBoost의 실행코드
AdaBoost 모델에 human activity 데이터셋을 학습시켜 분류모델을 만들겠습니다.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')
# human activity 데이터 세트에 중복된 Feature명으로 인해 판다스 0.25버전 이상에서
# Duplicate name 에러가 발생하여 feature 이름을 수정하는 함수 설정
def get_new_feature_name_df(old_feature_name_df):
feature_dup_df = pd.DataFrame(data=old_feature_name_df.groupby('column_name').cumcount(), columns=['dup_cnt'])
feature_dup_df = feature_dup_df.reset_index()
new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, how='outer')
new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1])
if x[1] >0 else x[0] , axis=1)
new_feature_name_df = new_feature_name_df.drop(['index'], axis=1)
return new_feature_name_df
# 데이터셋을 구하는 함수 설정
def get_human_dataset():
# 각 데이터 파일들은 공백으로 분리되어 있으므로 read_csv에서 공백문자를 sep으로 할당
feature_name_df = pd.read_csv('human_activity/features.txt', sep='\s+',
header=None, names=['column_index', 'column_name'])
# 중복된 피처명을 수정하는 get_new_feature_name_df()를 이용하여 새로운 feature명 데이터프레임 생성
new_feature_name_df = get_new_feature_name_df(feature_name_df)
# 데이터프레임에 피처명을 컬럼으로 뷰여하기 위해 리스트 객체로 다시 반환
feature_name = new_feature_name_df.iloc[:, 1].values.tolist()
# 학습 피처 데이터세트와 테스트 피처 데이터를 데이터프레임으로 로딩
# 컬럼명은 feature_name 적용
X_train = pd.read_csv('human_activity/train/X_train.txt', sep='\s+', names=feature_name)
X_test = pd.read_csv('human_activity/test/X_test.txt', sep='\s+', names=feature_name)
# 학습 레이블과 테스트 레이블 데이터를 데이터 프레임으로 로딩, 컬럼명은 action으로 부여
y_train = pd.read_csv('human_activity/train/y_train.txt', sep='\s+', names=['action'])
y_test = pd.read_csv('human_activity/test/y_test.txt', sep='\s+', names=['action'])
# 로드된 학습/테스트용 데이터프레임을 모두 반환
return X_train, X_test, y_train, y_test
# 학습/테스트용 데이터 프레임 반환
X_train, X_test, y_train, y_test = get_human_dataset()
# AdaBoostClassifier 객체 생성후 학습 및 예측
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
base_model = DecisionTreeClassifier(max_depth = 5)
clf = AdaBoostClassifier(base_estimator = base_model,
n_estimators = 100,
random_state = 10,
learning_rate = 0.01)
clf.fit(X_train, y_train)
pred = clf.predict(X_test)
print('AdaBoost 정확도 : {:.4f}'.format(accuracy_score(y_test, pred)))
# 출력:
AdaBoost 정확도 : 0.8697
AdaBoost로 간단하게 모델을 만들어 수행한 결과 86.97%의 정확도를 나타냅니다.
AdaBoost의 하이퍼파라미터
-
base_estimators
- 학습에 사용하는 알고리즘
- default : None → DecisionTreeClassifier(max_depth = 1) 이 적용
-
n_estimators
-
생성할 약한 학습기의 개수를 지정
-
default : 50
-
-
learning_rate
- 학습을 진행할 때마다 적용하는 학습률(0~1 사이의 값)
- 약한 학습기가 순차적으로 오류값을 보정해나갈 때 적용하는 계수
- default : 1
- 낮은만큼 최소 손실값을 찾아 예측 성능이 높아질 수 있음
- 하지만 그 만큼 많은 수의 트리가 필요하고 시간이 많이 소요
n_estimators 를 늘린다면 생성되는 약한 학습기의 수는 늘어납니다. 하지만 이 여러 학습기들의 decision boundary가 많아지면서 모델이 복잡해집니다.
learning_rate 을 줄인다면, 가중치의 갱신 변동폭이 감소해서, 여러 학습기들의 decision boundary의 차이가 줄어듭니다.
이 두 파라미터를 잘 조정하는 것이 AdaBoost 하이퍼 파라미터 튜닝의 핵심입니다.
Gradient Boosting Machine(GBM)
GBM는 AdaBoost와 유사하지만 가중치 업데이트를 경사하강법(Gradient Descent)를 이용하여 최적화된 결과를 얻는 알고리즘입니다. GBM은 예측 성능이 높지만 Greedy Algorithm으로 과적합이 빠르게 되고, 시간이 오래 걸린다는 단점이 있습니다.
※ 경사하강법
분류의 실제값을 y, 피처를 x1, x2, x3, …., 이 피처들에 기반한 예측함수를 F(x)라고 하면,
오류식은 h(x) = y - F(x) 입니다. 이 오류식을 최소화하는 방향을 가지고 가중치 값을 업데이트 하는 방법입니다.
※ Greedy Algorithm(탐욕 알고리즘)
미래를 생각하지 않고 각 단계에서 가장 최선의 선택을 하는 기법으로 각 단계에서 최선의 선택을 한 것이 전체적으로도 최선이길 바라는 알고리즘입니다.하지만 모든 경우에서 그리디 알고리즘이 통하지는 않습니다. 가령 지금 선택하면 1개의 마시멜로를 받고, 1분 기다렸다 선택하면 2개의 마시멜로를 받는 문제에서는, 그리디 알고리즘을 사용하면 항상 마시멜로를 1개밖에 받지 못합니다. 지금 당장 최선의 선택은 마시멜로 1개를 받는 거지만, 결과적으로는 1분 기다렸다가 2개 받는 게 최선이기 때문입니다.
GBM의 실행코드
동일한 데이터셋으로 GBM을 이용한 예측모델을 만들어보겠습니다.
# Gradient Boosting Classifier 불러오기
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
import time
# GBM 수행시간 측정을 위함. 시작시간 설정
start_time = time.time()
# GBM 객체 생성 후 학습, 예측, 성능평가
gbm_clf = GradientBoostingClassifier(random_state = 0)
gbm_clf.fit(X_train, y_train)
gbm_pred = gbm_clf.predict(X_test)
gbm_accuracy = accuracy_score(y_test, gbm_pred)
print('GBM 정확도: {:.4f}'.format(gbm_accuracy))
print('GBM 수행시간: {:.1f}초'.format(time.time() - start_time))
# 출력:
GBM 정확도: 0.9386
GBM 수행시간: 138.6초
특별한 튜닝없이 GBM 모델로 예측을 수행했을 때 93.86%의 정확도를 기록했습니다.
GBM의 하이퍼파라미터
(1) Tree에 관한 하이퍼파라미터
-
max_depth
- 트리의 최대 깊이
- default : 3
- 깊이가 깊어지면 과적합될 수 있으므로 적절히 제어 필요
-
min_samples_split
-
노드를 분할하기 위한 최소한의 샘플 데이터수
→ 과적합을 제어하는데 사용합니다. 값이 작을수록 분할노드가 많아져 과적합 가능성 증가
-
default : 2
-
-
min_samples_leaf
-
리프노드가 되기 위한 최소한의 샘플 데이터수
→ 과적합을 제어하는데 사용합니다. 값이 작을수록 과적합 가능성 증가
-
default : 1
-
불균형 데이터의 경우 특정 클래스 데이터가 극도로 적을 수 있으므로 작은 값으로 설정 필요
-
-
max_features
- 최적의 분할을 위해 고려할 피처의 최대 갯수
- default : None
- int형으로 지정 → 피처 갯수
- float형으로 지정 → 전체 갯수의 일정 비율만큼 사용
sqrt
또는auto
→ 전체 피처 중 √(피처 개수) 만큼 선정log2
: 전체 피처 중 log2(전체 피처 개수) 만큼 선정
-
max_leaf_nodes
-
리프노드의 최대 갯수
-
default : None → 제한없음
-
(2) Boosting에 관한 하이퍼파라미터
- loss
- 경사하강법에서 사용할 loss function 지정
- default : deviance
- n_estimators
- 생성할 트리의 갯수를 지정
- default : 100
- 많을수록 성능을 좋아질 수 있지만 시간이 오래 걸림
- learning_rate
- 학습을 진행할 때마다 적용하는 학습률(0~1 사이의 값)
- 약한 학습기가 순차적으로 오류값을 보정해나갈 때 적용하는 계수
- default : 0.1
- 낮은만큼 최소 손실값을 찾아 예측 성능이 높아질 수 있음
- 하지만 그 만큼 많은 수의 트리가 필요하고 시간이 많이 소요
- subsample
- 개별트리가 학습에 사용하는 데이터 샘플링 비율(0~1 사이의 값)
- default : 1 (전체 데이터 학습)
- 이 값을 조절하여 트리 간의 상관도를 줄일 수 있음
GridSearchCV를 통한 GBM의 하이퍼파라미터 튜닝
from sklearn.model_selection import GridSearchCV
param = {
'n_estimators' : [100, 500], # 트리의 개수 : 100개 , 500개
'learning_rate' : [0.05, 0.1] # 학습률 : 0.05, 0.1
}
grid_cv = GridSearchCV(gb_clf, param_grid=param, cv=2, verbose=1, n_jobs=-1)
grid_cv.fit(X_train, y_train.values)
print('최적 하이퍼 파라미터: \n', grid_cv.best_params_)
print('최고 예측 정확도: {0:.4f}'.format(grid_cv.best_score_))
# 출력:
Fitting 2 folds for each of 4 candidates, totalling 8 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers.
[Parallel(n_jobs=-1)]: Done 2 out of 8 | elapsed: 2.2min remaining: 6.7min
[Parallel(n_jobs=-1)]: Done 8 out of 8 | elapsed: 4.8min finished
최적 하이퍼 파라미터:
{'learning_rate': 0.05, 'n_estimators': 500}
최고 예측 정확도: 0.9014
위의 간단한 GridSearch 에서는 learning_rate = 0.05 , n_estimators = 500 일 때, 90.14%의 정확도가 최고로 도출되었습니다.
# GridSearchCV를 이용해 최적으로 학습된 estimators로 예측 수행
gbm_pred = grid_cv.best_estimator_.predict(X_test)
gb_accuracy = accuracy_score(y_test, gbm_pred)
print('GBM 정확도: {0:.4f}'.format(gb_accuracy))
# 출력:
GBM 정확도: 0.9396
GridSearchCV를 통해 도출된 최적 하이퍼 파라미터로 최종 예측을 수행한 결과, 처음 default로 GBM을 수행했을 때보다 아주 소폭 향상되어 93.96%의 정확도를 나타냈습니다.
Reference
- 파이썬 머신러닝 완벽가이드
- https://www.zerocho.com/category/Algorithm/post/584ba5c9580277001862f188