본문 바로가기

문돌이 존버/데이터 분석

앙상블 학습 - 부스팅(boosting) 알고리즘 개념 잡기

반응형
해당 글은 핸즈온 머신러닝 2판을 기준으로 작성되었습니다.

앙상블 학습(ensemble learning)은 일련의 예측기(분류 or 회귀 모델)로부터 예측을 수집하여 하나의 모델을 사용했을 때보다 더 좋은 예측을 얻을 수 있습니다. 쉽게 말해 대중의 지혜라고 표현하는데, 속담으로는 "백지장도 맞들면 낫다"가 적당하려나요? (좀 아닌 것 같지만ㅎㅎ)

부스팅은 앙상블 학습의 한 방법으로 이외에 배깅, 스택킹 등의 방법이 있습니다. 여기서는 부스팅에 대한 이야기를 해보겠습니다.

부스팅은 약한 학습기를 여러 개 연결하여 강한 학습기를 만드는 앙상블 방법을 말합니다. 부스팅 방법의 아이디어는 앞의 모델을 보완해나가면서 일련의 예측기를 학습시키는 것입니다. 부스팅 방법에는 여러 가지가 있지만 가장 인기 있는 것은 그레이디언트 부스팅(gradient boosting)에이다부스트(AdaBoost)입니다. 참고로 에이다부스트는 adaptive boosting의 줄임말입니다.

먼저, 그레이디언트 부스팅에 대해 이야기해보겠습니다. 간단히 말하면, 이전 예측기가 만든 잔여 오차(residual error)에 새로운 예측기를 학습시키는 것입니다. 따라서 모델 학습에서 항상 문제가 되는 variance-bias tradeoff에서 bias를 증가시키지 않고, variance를 감소시킬 수 있는 좋은 방법이기도 합니다. 

의사결정나무를 기반 예측기로 사용하는 간단한 회귀 문제를 살펴보겠습니다. 이를 그레이디언트 부스티드 회귀 트리(gradient boosted regression tree, 이하 GBRT)라고도 부릅니다.

from sklearn.tree import DecisionTreeRegressor

tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg.fit(X, y)
y2 = y - tree_reg1.predict(X)
tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X, y2)
y3 = y2 - tree_reg2.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X, y3)

3개의 트리를 포함하는 앙상블 모델이 생겼고, 새로운 샘플에 대해 예측하려면 모든 트리의 예측을 더하면 됩니다.

y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))

위의 코드를 수식으로 표현하면 이와 같습니다. $h(x)$을 예측기의 함수 식이라고 생각합시다. 여기에 우리가 가진 데이터를 입력하면 예측값 y_val이 나오고 그 차이는 $e$라고 표현합니다.

$y = h_1(x_1) + e_1$
$e_1 = h_2(x_2) + e_2$
$e_2 = h_3(x_3) + e_3$
$y = h_1(x_1) + h_2(x_2) + h_3(x_3) + e_3$

핵심은 실제 값과 처음에 예측한 값의 차이인 $e_1$을 새로운 y로 보고 이를 예측한다는 것입니다. 즉 잔차에 대한 함수를 계속해서 생성하는 것이고, 이를 반복하다 보면 마지막 잔차 $e_n$는 0에 가까워질 것입니다.  

<출처: hands-on machine learning 2>

잔차에 대한 함수를 생성하고 실제값과 차이를 최소화하는 과정에서 사용되는 개념이 gradient 입니다. 먼저 아래 Loss function 수식은 익숙하실 것입니다. 실제값과 예측값의 차이를 최소화하는 것이죠.

$minL =$ $1\over2 $ $\sum_{i=1}^n (y_i - f(x_i))^2$

# 위 loss function을 미분한 값
$\delta L\over \delta f(x_i)$ $=f(x_i) - y_i$
$y-f(x_i) =$ $-$$\delta L \over \delta f(x_i)$

미분한 값을 음수로 취하면 실제값에서 예측한 값을 빼는 gradient인 것을 알 수 있습니다. 

사이킷런을 이용하면 GBRT 앙상블을 간단하게 훈련시킬 수 있습니다. 이때 learning_rate 매개변수는 각 트리의 기여 정도를 조절합니다. 즉 0.1처럼 낮게 설정하면 앙상블을 훈련 세트에 학습시키기 위해 많은 트리가 필요하지만, 일반적으로 예측의 성능은 좋아집니다(단, 오버피팅의 위험도 있습니다). 쉽게 말해 축소(shrinkage)라고 부르는 규제 방법입니다.

from sklearn.ensemble import GradientBoostingRegressor

gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X, y)

최적의 트리 수를 찾기 위해선 어떻게 해야 할까요? 일단 조기 종료 기법(early stopping method)을 사용할 수 있습니다. staged_predict() 메서드를 이용하여 각 단계(트리 1개, 2개 등)에서 앙상블에 의해 만들어진 예측기를 순회하는 반복자를 반환합니다.

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

X_train, X_val, y_train, y_val = train_test_split(X, y)

gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120) # 초기엔 120개 트리로 훈련
gbrt.fit(X_train, y_train)

errors = [mean_squared_error(y_val, y_pred)
          for y_pred in gbrt.staged_predict(X_val)]
bst_n_estimators = np.argmin(errors) + 1 # 에러가 최소일 때의 트리 수 저장

gbrt_best = GradientBoostingRegressor(max_depth=2, n_estimators=bst_n_estimators)
gbrt_best.fit(X_train, y_train)

사이킷런의 GradientBoostingRegressor는 각 트리가 훈련할 때 사용할 훈련 샘플의 비율을 지정할 수 있는 subsample 매개변수도 지원합니다. 예를 들어, subsample=0.25라고 하면 각 트리는 무작위로 선택된 25%의 훈련 샘플로 학습되는 것이죠. 이럴 경우, 편향이 높아지는 대신 분산이 낮아지고, 훈련 속도가 상당히 빨라집니다. 이런 기법을 확률적 그레이디언트 부스팅(stochastic gradient boosting)이라고 합니다.

최적화된 그레이디언트 부스팅 구현으로는 XGBoost 파이썬 라이브러리가 유명합니다. 분산환경에서도 실행할 수 있도록 하여 빠른 속도, 확장성, 이식성의 장점을 가지고 있습니다.

import xgboost

xgb_reg = xgboost.XGBRegressor()
xgb_reg.fit(X_train, y_train) # 자동 조기 종료 기능 존재
# ex) eval_set=[(X_val, y_val)], early_stopping_rounds=2 
y_pred = xgb_reg.predict(X_val)

그레이디언트 부스팅의 동작 과정을 차근차근 설명한 동영상이 있어 첨부하니 참고하시기 바랍니다.

 

728x90
반응형