트리의 앙상블¶
정형 데이터와 비정형 데이터¶
정형 데이터: CSV, 데이터베이스, 엑셀에 저장하기 쉬운 데이터
- 정형 데이터에 대해 가장 뛰어난 성과를 내는 알고리즘이 바로 앙상블 학습이다.
비정형 데이터: 텍스트, 사진, 음악 등 데이터베이스나 엑셀로 표현하기 어려운 데이터
- 비정형 데이터에 대해서는 신경망 알고리즘이 가장 뛰어난 성과를 낸다.
랜덤 포레스트¶
랜덤 포레스트는 앙상블 학습의 대표 주자 중 하나로 안정적인 성능 덕에 널리 사용된다.
- 랜덤 포레스트의 학습 방법은 아래와 같다.
- 입력한 훈련 데이터에서 랜덤하게 추출하여 트리 훈련을 위한 데이터를 준비한다.
- 이 때 한 샘플이 중복되어 추출될 수 있으며, 이렇게 만들어진 샘플을 부트스트랩 샘플(Bootstrap sample)이라고 한다.
- 기본적으로 부트스트랩 샘플은 훈련 세트의 크기와 같게 만든다.
- 또한 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 다음 이 중에서 최선의 분할을 찾는다.
- 분류 모델인 RandomForestClassifer는 기본적으로 전체 특성 개수의 제곱근만큼의 특성을 선택한다.
- 회귀 모델인 RandomForestRegressor는 전체 특성을 이용한다.
- 사이킷런의 랜덤 포레스트는 기본적으로 100개의 결정 트리를 이런 방식으로 훈련한다.
- 그 다음 분류일 때는 각 트리의 클래스별 확률을 평균하여 가장 높은 확률을 가진 클래스를 예측으로 삼는다.
- 회귀일 때는 단순히 각 트리의 예측을 평균한다.
- 랜덤포레스트는 랜덤하게 선택한 샘플과 특성을 사용하기 대문에 훈련 세트에 과대적합하는 것을 막아주고 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있다. -종종 기본 매개변수 설정만으로도 아주 좋은 결과를 낸다.
In [1]:
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
wine = pd.read_csv('https://bit.ly/wine_csv_data')
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()
train_input, test_input, train_target, test_target = train_test_split(data, target, test_size=0.2, random_state=42)
In [2]:
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs=-1, random_state=42) #100개의 트리를 이용하므로 모든 CPU 이용
scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1) #그러한 특성에 대한 5-fold CV를 진행해야 하므로 모든 CPU 이용
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
- 다소 과대적합된 결과이다.
- 그러나 여기에서는 알고리즘을 조사하는 것이 목적이므로 매개변수는 더 이상 조정하지 않겠다.
- 사실 이 예제는 매우 간단하고 특성이 많지 않아 그리드 서치를 사용하더라도 하이퍼파라미터 튜닝 결과가 크게 나아지지 않는다.
- 랜덤 포레스트는 결정 트리의 앙상블이므로 DecisionTreeClassifier가 제공하는 중요한 매개변수를 모두 제공한다.
- criterion, max_depth, max_features, min_samples,split, min_impurity_decrease, min_samples_leaf 등이다.
- 또한 각 결정 트리의 큰 장점 중 하나인 특성 중요도를 계산한다.
- 랜덤 포레스트의 특성 중요도는 각 결정 트리의 특성 중요도를 취합한 것이다.
In [3]:
rf.fit(train_input, train_target)
print(rf.feature_importances_)
- 일반 결정 트리에서의 중요도가 [0.12 0.87 0.01] 였던 것과 비교하면 두 번째 특성의 중요도가 감소하고 나머지 특성의 중요도가 높아진 것을 확인할 수 있다.
- 이런 이유는 랜덤 포레스트가 특성의 일부를 랜덤하게 선택하여 결정 트리를 훈련하기 때문이다.
- 그 결과 하나의 특성에 과도하게 집중하지 않고 좀 더 많은 특성이 훈련에 기여할 기회를 가는다.
- 이는 과대적합을 줄이고 일반화 성능을 높이는 데 도움이 된다.
- RandomForestClassifier에 재밌는 기능이 하나 있는데, 자체적으로 모델을 평가하는 점수를 얻을 수 있다.
- 부트스트랩 샘플에 포함되지 않고 남는 샘플을 OOB(Out of bag) 샘플이라고 하는데, 이 샘플을 이용하여 부트스트랩 샘플로 훈련한 결정트리를 평가할 수 있다.
- 마치 검증 세트의 역할을 하는 것이다.
In [4]:
rf = RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)
rf.fit(train_input, train_target)
print(rf.oob_score_)
교차 검증에서 얻은 점수와 매우 비슷하다.
OOB 점수를 사용하면 교차 검증을 대신할 수 있어서 결과적으로 훈련 세트에 더 많은 샘플을 사용할 수 있다.
엑스트라 트리¶
- 엑스트라 트리는 랜덤 포레스트와 매우 비슷하게 동작한다.
- 차이점은 엑스트라 트리는 부트스트랩 샘플을 사용하지 않는다는 것이다.
- 즉 각 결정 트리를 만들 때 전체 훈련 세트를 사용한다.
- 대신 노드를 분할할 때 가장 좋은 분할을 찾는 것이 아니라 무작위로 분할한다.
- 사실 앞 절의 예제가 엑스트라 트리로 훈련한 결과였다.
In [5]:
from sklearn.ensemble import ExtraTreesClassifier
et = ExtraTreesClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(et, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
- 랜덤 포레스트와 비슷한 결과를 얻었다.
- 이 예제는 특성이 많지 않아 두 모델의 차이가 크지 않다.
- 보통 엑스트라 트리가 무작위성이 좀 더 크기 때문에 랜덤 포레스트보다 더 많은 결정트리를 훈련해야 한다.
- 하지만 랜덤하게 노드를 분할하기 때문에 빠른 계산 속도가 엑스트라 트리의 강점이다.
In [6]:
et.fit(train_input, train_target)
print(et.feature_importances_)
Gradient boosting¶
- 그레이디언트 부스팅은 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완해주는 방식으로 앙상블 하는 방법이다.
- 사이킷런의 GradientBoostingClassifer는 기본적으로 깊이가 3인 결정 트리를 100개 사용한다.
- 깊이가 얕은 결정 트리를 사용하기 때문에 과대적합에 강하고 일반적으로 높은 일반화 성능을 기대할 수 있다.
- gradient라는 이름에서 알 수 있듯 경사 하강법를 사용하여 트리를 앙상블에 추가한다. -분류에서는 로지스틱 손실 함수를 사용하고 회귀에서는 평균 제곱 오차 함수를 사용한다.
- 경사하강법에서 정확한 최적값을 찾기 위해 손실 함수가 천천히 조금씩 이동해야 하므로, 깊이가 얕은 트리를 사용한다고 보면 된다.
- 또한 학습률 매개변수로 속도를 조절한다.
In [7]:
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
- 거의 과대적합이 되지 않는다.
- 그레이디언트 부스팅은 결정 트리의 개수를 늘려도 과대적합에 매우 강하다.
- 학습률을 증가시키고 트리의 개수를 늘리면 조금 더 성능이 향상될 수 있다.
In [8]:
gb = GradientBoostingClassifier(n_estimators=500, learning_rate=0.2, random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
- 결정 트리의 개수를 500개로 5배나 늘렸지만 과대적합을 잘 억제하고 있다.
- 학습률 learning_rate의 기본값은 0.1이다.
- 그레이디언트 부스팅도 특성 중요도를 제공한다.
- 결과에서 볼 수 있듯, 그레이디언트 부스팅이 랜덤 포레스트보다 일부 특성(당도)에 더 집중한다.
In [9]:
gb.fit(train_input, train_target)
print(gb.feature_importances_)
재미있는 매개변수가 하나 있다. 트리 훈련에 사용할 훈련 세트의 비율을 정하는 subsample이다.
- 이 매개변수의 기본값은 1.0으로 전체 훈련 세트를 사용한다.
- 하지만 subsample의 값이 1보다 작으면 훈련 세트의 일부를 사용한다.
- 이는 마치 SGD나 mini-batch GD와 유사하다.
일반적으로 그레이디언트 부스팅이 랜덤 포레스트보다 조금 더 높은 성능을 얻을 수 있다. 하지만 순서대로 추가하기 때문에 훈련 속도가 느리다.
- 이를 개선한 것이 다음에 소개할 히스토그램 기반 그레이디언트 부스팅이다.
히스토그램 기반 그레이디언트 부스팅¶
- 히스토그램 기반 그레이디언트 부스팅은 정형 데이터를 다루는 머신러닝 알고리즘 중에 가장 인기가 높은 알고리즘이다.
- 히스토그램 기반 그레이디언트 부스팅은 먼저 입력 특성을 256개의 구간으로 나눈다.
- 따라서 노드를 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다.
- 256개의 구간 중에서 하나를 떼어놓고 누락된(비어있는) 값을 위해서 사용한다.
- 따라서 입력에 누락된 특성이 있더라도 이를 따로 전처리할 필요가 없다.
- 기본 매개변수에서 안정적인 성능을 얻을 수 있음
- 트리의 개수를 지정할 때 n_estimators가 아닌 max_iter를 사용함
In [10]:
from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier
hgb = HistGradientBoostingClassifier(random_state=42)
scores = cross_validate(hgb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
과대적합을 잘 억제하면서 그레이디언트 부스팅보다 조금 더 높은 성능을 보여준다.
특성 중요도를 계산하기 위해 permutation_importance() 함수를 사용한다.
- 이 함수는 특성을 하나씩 랜덤하게 섞어서 모델의 성능이 변화하는지를 관찰하여 어떤 특성이 중요한지를 계산한다.
- n_repeats 매개변수는 랜덤하게 섞을 횟수를 지정한다. 기본값은 5이다.
In [11]:
from sklearn.inspection import permutation_importance
hgb.fit(train_input, train_target)
result = permutation_importance(hgb, train_input, train_target, n_repeats=10,
random_state=42, n_jobs=-1)
print(result.importances_mean)
테스트 세트의 결과를 보면 그레이디언트 부스팅과 비슷하게 조금 더 당도에 집중하고 있다는 것을 알 수 있다.
In [12]:
hgb.score(test_input, test_target)
Out[12]:
- 랜덤 서치에서 테스트 정확도가 86%였던 것과 비교해 성능이 조금 향상됬다.
XGBoost¶
- 사이킷런 말고도 히스토그램 기반 그레이디언트 부스팅 알고리즘을 구현한 라이브러리가 여럿 있다.
- 그 중에서 대표적인 라이브러리가 XGBoost이다.
- 이 라이브러리 역시 코랩에서 사용할 수 있고 사이킷런의 cross_validate() 함수와 함께 사용할 수도 있다.
- XGBoost는 다양한 부스팅 알고리즘을 지원한다. tree_method 매개변수를 'hist'로 지정하면 히스토그램 기반 그레이디언트 부스팅을 사용할 수 있다.
In [13]:
from xgboost import XGBClassifier
xgb = XGBClassifier(tree_method='hist', random_state=42)
scores = cross_validate(xgb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
LightGBM¶
- 또 다른 히스토그램 기반 그레이디언트 부스팅 라이브러리는 마이크로소프트에서 만든 LightGBM이다.
- LightGBM은 빠르고 최신 기술을 많이 적용하고 있어 인기가 점점 높아지고 있다.
In [14]:
from lightgbm import LGBMClassifier
lgb = LGBMClassifier(random_state=42)
scores = cross_validate(lgb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
사실 사이킷런의 히스토그램 기반 그레이디언트 부스팅이 LightGBM에서 영향을 많이 받았다.
In [15]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:90% !important; }</style>"))
'혼자 공부하는 머신러닝 + 딥러닝' 카테고리의 다른 글
[혼공머신] 13. K-Means Clustering (0) | 2021.12.18 |
---|---|
[혼공머신] 12. Clustering Algorithm (0) | 2021.12.18 |
[혼공머신] 10. Cross Validation & Grid Search (0) | 2021.12.18 |
[혼공머신] 9. Decision Tree (0) | 2021.12.18 |
[혼공머신] 8. Stochastic Gradient Descent (0) | 2021.12.18 |