본문 바로가기

AI/기계학습(Machine Learning)

[기계학습/ML]12. 앙상블 학습 - 랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅

728x90
반응형

박 팀장이 Pupbani를 불렀다. 

  • "이사님이 베스트 머신러닝 알고리즘을 보고 하라 하시네요.."
  • "랜덤 포레스트를 사용해봐!!"

Pupbani는 박 팀장이 말한 랜덤 포레스트를 한번 해보기로 했다.

 

정형 데이터(Structured data)

  • 쉽게 말해 어떤 구조로 되어 있다는 뜻
  • csv, 데이터베이스, 엑셀에 저장하기 좋다.
  • 앙상블 학습(Ensemble Learning)
    • 정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘이다.
    • 랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅 등

비정형 데이터(Unstructured data)

  • 정형 데이터와 반대되는 데이터
  • 책의 글, 텍스트 데이터, 디지털카메라로 찍은 사진, 핸드폰으로 듣는 디지털 음악 등
  • 신경망 알고리즘에 사용한다.

랜덤 포레스트(Random Forest)

  • 앙상블 학습의 대표 주자
  • 결정 트리를 랜덤 하게 만들어 결정 트리(나무)의 숲을 만드는 것이다.
    • 각 결정 트리의 예측을 사용해 최종 예측을 만든다.

데이터 준비

  • 각 트리를 훈련하기 위한 데이터를 랜덤하게 만든다.
    • 입력한 훈련 데이터에서 랜덤하게 샘플을 추출하여 훈련 데이터를 만든다.
    • 한 샘플이 중복되어 추출될 수도 있다.
    • 이렇게 만들어진 샘플을 부트스트랩 샘플(Bootstrap Sample)이라고 한다.
  • 부트스트랩 샘플은 훈련 세트의 크기와 같게 만든다.

  • 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 다음 이 중에서 최선의 분할을 찾는다.
  • 분류 모델인 RandomForestClassifier는 기본적으로 전체 특성 개수의 제곱근만큼 특성을 선택한다.
  • 회귀 모델인 RandomForestRegression은 전체 특성을 사용한다.
  • 사이킷런의 랜덤 포레스트는 기본적으로 100개의 결정 트리를 이런 방식으로 학습한다.
    • 분류 : 각 트리의 클래스별 확률을 평균하여 가장 높은 확률을 가진 클래스를 예측으로 삼는다.
    • 회귀 : 각 트리의 예측을 평균한다.

  • 이제 데이터를 준비해보자
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

wine = pd.read_csv('https://bit.ly/wine-date')

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)
  • 훈련 세트와 테스트 세트를 만든다.

학습 및 평가

  • RandomForestClassifier 클래스를 사용한다.
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1)

print(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

  • 훈련 세트에 대해 과대 적합되어 있다.
  • 랜덤 포레스트는 앙상블이기 때문에 DecisionTreeClassifier가 제공하는 중요한 매개변수를 전부 제공한다.
  • 랜덤 포레스트 모델의 특성 중요도를 출력해보겠다.
rf.fit(train_input, train_target)
fimpt = rf.feature_importances_.reshape(-1,3).round(4) * 100
feature = list(wine.columns)[0:3]
print(pd.DataFrame(fimpt,columns=feature))

  • 이전에 결정 트리에의 특성 중요도와 비교해보자

결정트리 특성 중요도

  • 두 번째 특성인 당도의 중요도가 감소하고 알코올 도수와 pH가 올라갔다.
  • 이렇게 되는 이유는 랜덤 포레스트가 특성의 일부를 랜덤 하게 선택하여 결정 트리를 훈련하기 때문이다.
  • 과대 적합을 줄이고 일반화 선능을 높이는 데 도움이 된다.
  • RandomForestClassifier에는 자체적으로 모델을 평가하는 점수를 얻을 수 있다.
    • 랜덤 포레스트를 훈련하는 데 사용되는 샘플을 부트스트랩 샘플이라고 한다.
      • 훈련 세트에서 중복을 허용하여 뽑아 샘플을 만드는 것
    • 이 부트스트랩 샘플에 포함되지 않고 남는 샘플을 OOB(Out Of Bag) 샘플이라고 하는데 이 샘플을 사용하여 부트스트랩으로 훈련한 결정 트리를 평가할 수 있다.
    • oob_score 매개변수를 True로 해야 한다.(기본값 False)
rf = RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)

rf.fit(train_input, train_target)
print(f"OOB 점수 : {rf.oob_score_ * 100:.2f}%")

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

 

엑스트라 트리(Extra Trees)

  • 랜덤 포레스트와 매우 비슷하게 동작한다.
  • 기본적으로 100개의 결정 트리를 학습한다.
  • 결정 트리가 제공하는 대부분의 매개변수를 지원한다.
  • 전체 특성 중에 일부 특성을 랜덤 하게 선택하여 노드를 분할하는 데 사용한다.
  • 부트스트랩을 사용하지 않는다.
    • 각 결정 트리를 만들 때 전체 훈련 세트를 사용한다.
  • 노드를 분할할 때 무작위 분할을 한다.
  • splitter = 'random'인 결정 트리가 엑스트라 트리가 사용하는 결정 트리이다.
  • 트리를 앙상블 하기 때문에 과대 적합을 막고 검증 세트의 점수를 높이는 효과가 있다.
  • ExtraTreesClassifier 클래스를 사용한다. (회귀는 ExtraTreesRegressor)
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(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

  • 랜덤 포레스트와 비슷한 결과를 얻었다.
  • 하지만 노드를 랜덤 하게 분할하기 때문에 빠른 계산 속도를 가진다.
  • 특성 중요도는 다음과 같다.
et.fit(train_input, train_target)
fimpt = et.feature_importances_.reshape(-1,3).round(4) * 100
feature = list(wine.columns)[0:3]
print(pd.DataFrame(fimpt,columns=feature))

  • 결정 트리와 비교했을 때 당도의 의존성이 작다.

그레디언트 부스팅(Gradient Boosting)

  • 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블을 하는 방법이다.
  • 과대 적합에 강하고 일반적으로 높은 일반화 성능을 기대할 수 있다.
  • 경사 하강법을 사용하여 트리를 앙상블에 추가한다.
    • 분류 : 로지스틱 손실 함수
    • 회귀 : 평균 제곱 오차 함수
  • GradientBoostingClassifier 클래스를 사용한다.
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(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

  • 거의 과대 적합되지 않았다.
  • 결정 트리의 개수를 늘려도 과대 적합에 매우 강하다.
  • 학습률을 증가시키고 트리의 개수를 늘리면 성능이 더 향상될 수 있다.
  • 특성 중요도를 확인해 보겠다.
gb.fit(train_input, train_target)
fimpt = gb.feature_importances_.reshape(-1,3).round(4) * 100
feature = list(wine.columns)[0:3]
print(pd.DataFrame(fimpt,columns=feature))

  • 랜덤 포레스트보다 일부 특성(당도)에 더 집중한다.
  • 트리 훈련에 사용할 훈련 세트의 비율을 정하는 매개변수인 subsample이 있다.
    • 기본값은 1.0으로 전체 세트를 사용하는 것을 의미한다.
    • subsample이 1보다 작으면 훈련 세트의 일부를 사용한다.
      • 마치 일부 샘플을 랜덤 하게 선택하여 진행하는 확률적 경사 하강법이나 미니 배치 경사 하강법과 비슷하다.

 

히스토그램 기반 그레디언트 부스팅(Histogram-based Gradient Boosting)

  • 먼저 입력 특성을 256개의 구간으로 나눈다.
    • 노드들 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다.
  • 256개의 구간 중 하나를 떼어 놓고 누락된 값을 위해서 사용한다.
    • 입력에 누락된 특성이 있더라도 이를 따로 전 처리할 필요가 없다.
  • HistGradientBoostingClassifier 클래스를 사용한다.
    • n_estimators : 트리의 개수 제한
    • max_iter : 부스팅 반복 횟수
  • 2022.10.24일 기준으로 HistGradientBoostingClassifier의 위치가 sklearn.experimental에서 sklearn.ensemble로 옮겨졌다.
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(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

  • 과대 적합을 잘 억제하면서 그레디언트 부스팅보다 조금 더 높은 성능을 제공한다.
  • 특성 중요도를 확인해보자
    • sklearn permutation_ importance() 함수를 제공한다.
      • 특성을 하나씩 랜덤 하게 섞어 모델의 성능이 변화하는지를 관찰하고 어떤 특성이 중요한지를 계산한다.
      • n_repeats 매개변수는 랜덤 하게 섞을 횟수를 지정한다.
      • 다음의 3개를 sklearn bunch(사전과 유사한 구조)로 반환한다
        • importances_mean : 특성 중요도 평균
        • importances_std : 특성 중요도 표준편차
        • importances : 특성 중요도
hgb.fit(train_input, train_target)
from sklearn.inspection import permutation_importance

result = permutation_importance(hgb, test_input, test_target,
                                n_repeats = 10, random_state=42, n_jobs=-1)
print(f"특성 중요도 평균 : {result.importances_mean}")
print(f"특성 중요도 표준편차 : {result.importances_std}")
print(f"특성 중요도 : {result.importances}")

  • 결과를 보면 그레디언트 부스팅과 비슷하게 조금 더 당도에 집중하고 있다는 것을 알 수 있다.
  • 이 모델의 최종 성능을 확인해보자
print(f"모델 최종 정확도: {hgb.score(test_input, test_target)*100:.2f}%")

  • 단일 결정 트리보다는 앙상블 모델이 더 좋은 결과를 얻을 수 있다.

사이킷런 말고도 그레디언트 부스팅 알고리즘을 구현한 라이브러리가 있다.

XGBoost

  • 다양한 부스팅 알고리즘을 지원한다.
  • 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, n_jobs=-1)

print(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

LightGBM

  • 마이크로소프트에 개발한 모델이다.
  • 빠르고 최신 기술을 많이 적용하고 있어 인기가 점점 높아지고 있다.
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(f"훈련 세트 정확도: {np.mean(scores['train_score'])*100:.2f}%")
print(f"테스트 세트 정확도: {np.mean(scores['test_score'])*100:.2f}%")

 

이제 이사님에게 보고 하면 되겠군요!!

728x90
반응형