[혼공머신] 앙상블 학습 : 랜덤포레스트, 엑스트라트리, 그레이디언트 부스팅

강민우·2022년 2월 10일
1
post-thumbnail

[혼자 공부하는 머신러닝+딥러닝] 책에 기반한 정리글입니다.
전체 소스코드는 아래 Github 링크에서 확인할 수 있습니다.

Github 링크

0. 개요

앙상블 학습은 더 좋은 예측 결과를 만들기 위해 여러 개의 모델을 훈련하는 머신러닝 알고리즘이다.

정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘이 바로 앙상블 학습이다. 이는 대부분 결정트리를 기반으로 만들어져 있다.

이번 편에서는 앙상블 학습 알고리즘의 여러 모델을 알아본다.

1. 랜덤 포레스트

1-1. 랜덤 포레스트란?

랜덤 포레스트는 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만든다. 이후 각 결정 트리의 예측을 사용해 최종 예측을 만든다.
랜덤 포레스트는 각 트리를 훈련하기 위한 데이터를 랜덤하게 만드는데, 우리가 입력한 훈련 데이터에서 랜덤하게 샘플을 추출하여 훈련 데이터를 만든다.

중복된 샘플을 뽑을 수도 있고, 이렇게 만들어진 샘플을 부트스트랩 샘플 이라고 부른다. 이 때, 부트스트랩 샘플은 훈련세트의 크기와 같게 만든다.

또한 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고르고, 이 중 최선의 분할을 찾는다.

분류 모델인 RandomForestClassifier은 전체 특성 개수의 제곱근만큼의 특성을 선택하고, 회귀모델인 RandomForestRegressor은 전체 특성을 사용한다.

사이킷런의 랜덤 포레스트는 기본적으로 100개의 결정트리를 이렇게 훈련한다. 분류일 때는 가장 높은 확률을 가진 클래스를 예측으로 삼고, 회귀라면 각 트리의 예측을 평균화한다.

1-2. 랜덤포레스트를 분류모델에 적용하기

csv파일을 numpy 배열로 변환 후 훈련세트와 테스트세트로 나눈다.

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, random_state=42, test_size=0.2)

RandomForestClassifier 클래스로 모델을 만든 후,
cross_validate()로 교차검증을 수행한다.

from sklearn.model_selection import cross_validate #교차 검증
from sklearn.ensemble import RandomForestClassifier #랜덤포레스트 분류모델
# 랜덤포레스트 객체 생성
rf = RandomForestClassifier(n_jobs=-1, random_state=42)

# return_train_score로 훈련세트 점수도 같이 반환, 기본값 false, 5폴드 교차검증
scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))
출력 0.9973541965122431 0.8905151032797809

훈련 세트 점수가 검증세트 점수보다 크게 높아, 과대적합됨을 알 수 있다.

랜덤 포레스트는 결정 트리의 앙상블이기 때문에 DecisionTreeClassifier가 제공하는 중요 매개변수들을 모두 사용 가능하다. 그 중, 특성 중요도도 출력 가능하다.

rf.fit(train_input, train_target)
print(rf.feature_importances_) # 도수, 당도, pH

# 결정 트리에서보다 당도의 중요도가 떨어지고, 다른 특성의 중요도 상승
# 특성의 일부를 랜덤하게 선택하여 훈련하기 때문
# 과대 적합을 줄이고 일반화 성능을 높임
출력 [0.23167441 0.50039841 0.26792718]

이전 시리즈의 결정트리에서보다, 당도의 중요도가 감소하고 도수와 pH특성의 중요도가 상승했다.

랜덤 포레스트가 특성의 일부를 랜덤하게 선택하여 결정 트리를 훈련하기 때문에 많은 특성이 훈련에 참여할 기회를 얻고, 이는 과대적합을 줄이고 일반화 성능을 높이는 데 도움이 된다.

한편, 부트스트랩 샘플에 포함되지 않고 남는 샘플이 있다. 이런 샘플을 OOB(Out Of Bag)라고 하며 이것으로 검증 세트의 역할을 할 수 있다.

oob_score 매개변수를 True로 지정하여 사용한다.

rf = RandomForestClassifier(n_jobs=-1, oob_score=True, random_state=42)
rf.fit(train_input, train_target)
rf.oob_score_ # 교차 검증으로 얻은 점수와 비슷함
출력 0.8934000384837406

OOB 점수를 사용하면 교차 검증을 대신할 수 있어, 훈련세트에 더 많은 샘플을 사용할 수 있다.

2. 엑스트라 트리

2-1. 엑스트라 트리란?

엑스트라 트리는 마찬가지로 100개의 결정 트리를 훈련하지만, 랜덤 포레스트와 다르게 부트스트랩 샘플을 사용하지 않는다.
결정 트리를 만들 때 전체 훈련 세트를 사용하지만, 노드를 분할할 때 가장 좋은 부분을 찾는 것이 아닌 무작위로 분할한다.

DecisionTreeClassifiersplitter 매개변수를 random으로 지정하여도 쓸 수 있다.

2-2. 엑스트라 트리 적용하기

사이킷런의 엑스트라 트리는 ExtraTreesClassifier 이다.

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']))
출력 0.9974503966084433 0.8887848893166506

랜덤 포레스트와 비슷한 결과를 얻었다.

엑스트라 트리가 무작위성이 좀 더 크기 때문에 더 많은 결정트리를 훈련해야 하지만, 랜덤하게 노드를 분할하기 때문에 빠른 계산속도를 가질 수 있다.

3. 그레이디언트 부스팅

3-1. 그레이디언트 부스팅이란?

깊이가 얖은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블하는 방법이다.

사이킷런의 GradientBoostingClassifier은 기본적으로 깊이가 3인 결정트리를 100개를 사용한다. 깊이가 얕은 결정 트리를 사용하기 때문에 과대적합에 강하고 높은 일반화 성능을 기대할 수 있다.

경사 하강법을 사용하여 트리를 앙상블에 추가하며, 분류에서는 로지스틱 손실 함수를, 회귀에서는 평균 제곱 오차 함수를 사용한다.

3-2. 그레이디언트 부스팅 적용하기

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']))
출력 0.8881086892152563 0.8720430147331015

교차 검증 결과, 과대적합에 매우 강함을 알 수 있다.

결정트리 개수인 n_estimators와 학습률인 learning_rate(기본값0.1)를 증가시키면 성능이 향상될 수 있다.

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']))
출력 0.9464595437171814 0.8780082549788999

4. 히스토그램 기반 그레이디언트 부스팅

4-1. 히스토그램 기반 그레이디언트 부스팅이란?

정형 데이터를 다루는 머신러닝 알고리즘 중 가장 인기가 높은 알고리즘이다.

이 알고리즘은 입력 특성을 256개의 구간으로 나눈다. 따라서 노드를 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다.

256개의 구간 중 하나를 떼어 두고 누락된 값을 위해 사용하기 때문에, 누락된 특성이 있더라도 따로 전처리할 필요가 없다.

4-2 히스토그램 기반 그레이디언트 부스팅 적용하기

from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier

# 성능을 높이려면 max_iter 매개변수 이용
hgb = HistGradientBoostingClassifier(random_state=42)
scores = cross_validate(hgb, train_input, train_target, return_train_score=True)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
출력 0.9321723946453317 0.8801241948619236

과대적합을 잘 억제하면서 그레이디언트 부스팅보다 조금 더 높은 성능을 제공한다.

permutation_importance() 함수로 특성 중요도를 계산할 수 있고, n_repeats 매개변수(기본값 5)는 랜덤하게 섞을 횟수를 지정할 수 있다.

from sklearn.inspection import permutation_importance

hgb.fit(train_input, train_target)
#특성 중요도, 평균, 표준편차 반환

result1= permutation_importance(hgb, train_input, train_target, n_repeats=10, random_state=42, n_jobs=-1)
print(result1.importances_mean) #훈련세트 특성 중요도

result2= permutation_importance(hgb, test_input, test_target, n_repeats=10, random_state=42, n_jobs=-1)
print(result2.importances_mean) #테스트세트 특성 중요도
print(hgb.score(test_input, test_target)) #성능
# 도수, 당도, pH
출력
[0.08876275 0.23438522 0.08027708]
[0.05969231 0.20238462 0.049     ]
0.8723076923076923  # 약 87%의 정확도

그레이디언트 부스팅과 비슷하게, 조금 더 당도에 집중하고 있는 것을 알 수 있다.

4-3. 이를 지원하는 다른 라이브러리

4-3-1. XGBoost

사이킷런의 cross_validate()와 같이 사용이 가능하고,
tree_method 매개변수를 hist로 지정하면 히스토그램 기반 그레이디언트 부스팅을 사용할 수 있다.

from xgboost import XGBClassifier

xgb = XGBClassifier(tree_method='hist', random_state=42)
scores = cross_validate(xgb, train_input, train_target, return_train_score=True)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
출력 0.8824322471423747 0.8726214185237284

4-3-2. LightGBM

cross_validate()와 같이 사용이 가능하다.

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']))
출력 0.9338079582727165 0.8789710890649293

5. 정리

랜덤 포레스트
결정 트리를 훈련하기 위해 부트스트랩 샘플을 만들고, 전체 특성 중 일부를 랜덤하게 선택하여 결정 트리를 만든다.

엑스트라 트리
랜덤 포레스트와 비슷하지만 부트스트랩 샘플을 사용하지 않고, 노드를 분할할 때 랜덤하게 분할한다. 때문에 랜덤포레스트보다 훈련 속도가 빠르지만 더 많은 트리가 필요하다.

그레이디언트 부스팅
깊이가 얕은 트리를 연속적으로 추가하여 손실 함수를 최소화한다. 성능이 뛰어나지만 랜덤포레스트나 엑스트라 트리보다 훈련 속도가 조금 느리다.

히스토그램 기반 그레이디언트 부스팅
가장 뛰어난 앙상블 학습이며, 훈련 데이터를 256개의 구간으로 변환하여 사용하기 때문에 노드 분할 속도가 매우 빠르다.

profile
어제보다 성장한 오늘

0개의 댓글