머신러닝(AI학습 34)

이유진·2024년 7월 8일

--16.앙상블 학습(트리).ipynb--

앙상블 (ensemble) 학습

  • 트리 알고리즘 활용

정형 데이터 와 비정형 데이터

  1. 정형데이터(Structured Data)
    • 주로 행 과 열로 구성된 형태의 데이터
    • ex) CSV, 데이터베이스, 엑셀 파일 ..
  2. 비정형 데이터 (Unstructured Data)
    • 정형데이터 형태로 표현하기 어려운 데이터들
    • ex) 책, 글 텍스트, 사진, 음악

랜덤 포레스트

Random Forest

Decision Tree : 나무

Random Forest : decision tree 를 랜덤하게 만들어 '숲'을 만든다.

각 dicision tree 의 예측을 사용해 '최종예측'을 만든다.


부트스트랩 샘플 (bootstrap sample)

랜덤 포레스트는 각 트리를 훈련하기 위한 데이터를 아래와 같이 랜덤하게 만든다.

입력한 훈련 데이터에서 '랜덤하게 샘플을 추출'하여 훈련데이터를 만드는데.

이때 한 샘플이 '중복' 되어 추출될 수도 있습니다.

이런식으로 만들어진 샘플을 부트스트랩 샘플이라고 한다.

기본적으로 부트스트랩 샘플은 '훈련세트와 같은 크기'로 만든다.

ex) 1000개가 들어있는 가방에서 1000개의 샘플을 뽑는다. (중복허용)

RandomForestClassifier

각 노드를 분할할때 '전체 특성' 중에서 '일부 특성'을 '무작위' 로 고른다음

이 중에서 '최선의 분할'을 찾는다.

RandomForestClassifier 는 기본적으로 '전체 특성의 개수의 제곱근 만큼'의 특성을 선택한다.

ex) 4개의 특성이 있다 -> 노드마다 2개를 랜덤하게 선택하여 사용

회귀모델은 RandomForestRegressor : <- '전체특성을 사용' 함

사이킷런의 RandomForest 는 기본적으로 100개의 결정트리를 훈련.

'분류' 일때는 각 트리의 클래스별 확률을 평균하여 가장 높은확률을 가진 클래스를 예측으로 삼는다.

'회귀' 일때는 단순히 각 트리의 예측의 평균값.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os

base_path = r'/content/drive/MyDrive/dataset'

file_path = os.path.join(base_path, 'wine.csv')
wine_df = pd.read_csv(file_path)
wine_df

wine_df.columns

data = wine_df[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine_df['class'].to_numpy()

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = \
train_test_split(data, target, test_size = 0.2, random_state=42)

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_validate

rf = RandomForestClassifier(n_jobs=-1, random_state=42)

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

scores

print(np.mean(scores['train_score']), np.mean(scores['test_score']))

↑ overfit 되긴 했지만, 이번 예제에서는 조정하지 않겠습니다.

  • 특성 중요도 (feature importance)

"""
랜덤포레스트는 '결정트리의 앙상블'이기 때문에
DecisionTreeClassifier 가 제공하는 중요 매개변수를 모두 제공함.

=> criterion, max_depth, max_features, min_samples_split,
min_impurity_decrease, min_samples_leaf 등..

결정트리의 큰 장점중 하나 => '특성 중요도' 계산
랜덤포레스트의 특성중요도는 각각의 결정트리의 특성중요도를 취합한 결과.
"""
None

rf.fit(traininput, train_target)
rf.feature_importances

"""
알코올 , 당도 , ph
[0.12345626, 0.86862934, 0.0079144 ] <= 지난 단원의 특성 중요도

[0.23167441, 0.50039841, 0.26792718] <= 이번 랜덤포레스트의 특성 중요도

이전에 비해 당도의 중요도가 감소하고, 알코올과 pH의 중요도가 상승했다.

왜?
랜덤포레스트는 '특성의 일부'를 '랜덤하게 선택'하여 결정트리를 훈련하기 때문!
그 결과! 하나의 특성에 과도하게 집중되지 않고, 좀 더 많은 특성이 훈련에 기여할 확률이 높아진다.

=> 과대적합을 줄이고, '일반화' 성능을 높이는데 도움이 된다.

"""
None

  • OOB (Out Of Bag) 샘플

"""
RandomForestClassifier는 자체적으로 모델 평가 하는 기능이 있다.
부트스트랩샘플 만들 때 포함되지 않은 샘플들 (이를 OOB)
OOB를 활용하여 결정트리를 평가함.
"""
None

rf = RandomForestClassifier(
oob_score=True, # OOB 활용!
n_jobs=-1, random_state=42)

rf.fit(traininput, train_target)
print(rf.oob_score
)

엑스트라 트리

Extra Tree

ExtraTreeClassifier

"""
랜덤포레스트와 매우 비슷하게 동작!!

차이점! 부트스트랩 샘플을 사용하지 않는다!
=> 결정트리 만들때 '전체훈련세트' 를 사용함!
=> 대신! 노드를 분할할 때 가장 좋은 분할을 찾는 것이 아니라 무작위로 분할!

결정트리에서 이런식으로 특성을 무작위로 분할하면 성능이 낮아진다!!
그러나 많은 트리를 앙상블 하기 때문에 과대적합을 막고 검증세트 점수를 높이는 효과가 있다.
"""
None

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)

scores

print(np.mean(scores['train_score']), np.mean(scores['test_score']))

특성중요도

et.fit(traininput, train_target)
print(et.feature_importances
)

그레디언트 부스팅

Gradient Boosting

GradientBoostingClassifier

"""
그레디언트 부스팅은 깊이가 얕은 결정 트리를 사용하여 트리의 오차를 보완하는 방식으로 앙상블 하는 방법
GradientBoostingClassifier 는 기본적으로 깊이가 3인 결정트리를 100개 사용.

깊이가 얕은 트리를 사용하기 때문에 과대적합에는 강하고, 높은 일반화 성능 기대함.
"""
None

"""
'경사하강법'을 사용함.
'분류' 에서는 로지스틱손실 함수 사용.
'회귀' 에서는 평균제곱오차 함수 사용.

'그레디언트 부스팅'은 결정트리를 계속 추가하면서 가장 낮은곳을 찾아 이동합니다.
손실 함수의 낮은곳으로 '천천히 조금씩' 이동하기 위해 얕은 트리를 사용하는것.
"""
None

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.9974503966084433 0.8887848893166506 <- Extra Tree

0.8881086892152563 0.8720430147331015 <- Gradient Boosting
거의 과대적합이 억제되고 있다.
"""
None

gb = GradientBoostingClassifier(
n_estimators = 500, # 트리의 개수를 늘려보자
learning_rate = 0.2, # 학습률 증가 (디폴트 0.1)
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.9974503966084433 0.8887848893166506 <- Extra Tree

0.8881086892152563 0.8720430147331015 <- Gradient Boosting

0.9464595437171814 0.8780082549788999 <- Gradient Boosting(파라미터 조정)
"""
None

gb.fit(train_input, train_target)

gb.featureimportances # 특성 중요도

"""
알코올 당도 pH
0.23167441, 0.50039841, 0.26792718 <= Random Forest

0.20183568 0.52242907 0.27573525 <= Extra Tree

0.15872278, 0.68010884, 0.16116839 <= Gradient Boosting (파라미터)
"""
None

  • subsample 파라미터

subsample : 트리 훈련에 사용할 훈련세트의 비율을 정함.

기본값 1.0 => '전체훈련세트' 사용

1.0보다 작으면 => '훈련세트의 일부' 사용

=> 확률적 경사하강법, 미니배치 경사하강법과 비슷하게

경사 하강법 단계마다 일부 샘플을 랜덤하게 선택하여 진행.

일반적으로 그레디언트 부스팅이 랜덤포레스트보다 성능은 좀 더 높지만, 훈련속도가 느리다.

이유는! n_jobs 매개변수가 없다. (병렬 프로세싱 안된다...)

순서대로 트리를 추가해가면서 훈련하기 때문이다.

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

Histogram-based Gredient Boosting

"""
입력데이터의 특성을 '구간'으로 나눈다. (기본 256개의 구간)
=> 노드를 분할할때 최적의 분할을 매우 빠르게 찾을 수 있다.

256개의 구간중에서 하나는 떼어놓는다. => 누락된 값을 위해 사용하기 위함.
"""
None

HistGredientBoostingClassifier

기본 매개변수에서 안정적인 성능이 나온다.

부스팅 반복횟수를 지정하는 max_iter 사용 <- 성능을 높일 때 이 매개변수를 조정한다.

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']))

"""
0.8881086892152563 0.8720430147331015 <- Gradient Boosting
0.9321723946453317 0.8801241948619236 <- HistGradientBoosting

"""
None

특성중요도 : permutation_importance() 사용

특성을 하나씩 랜덤하게 섞어서 모델의 성능이 변화하는지 관찰한 후 어떤 특성이 중요한지 계산.

n_repeats : 랜덤하게 섞을 횟수 지정(디폴트 5)

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)

result.importances_mean

"""
알코올 당도 pH
0.23167441, 0.50039841, 0.26792718 <= Random Forest

0.20183568, 0.52242907, 0.27573525 <= Extra Tree

0.15872278, 0.68010884, 0.16116839 <= Gradient Boosting (파라미터)

0.08876275, 0.23438522, 0.08027708 <= HistGradientBoosting
"""
None

이번에는 테스트세트에서 특성중요도 계산

result = permutation_importance(hgb, test_input, test_target, n_repeats=10, random_state=42, n_jobs=-1)

result.importances_mean

테스트 세트에서의 성능

hgb.score(test_input, test_target)

XGBoost

사이킷런 말고도 히스토그램 기반 그레디언트 부스팅 알고리즘 구현한 라이브러리들중 대표적!!

다양한 부스팅 알고리즘 지원

tree_method='hist' => 히스토그램 기반 그레디언트 부스팅 알고리즘을 사용할 수 있다

colab 에서는 바로 사용 가능

Anaconda 환경 : conda install xgboost

http://xgboost.ai/

http://xgboost.readthedocs.io/en/latest

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

"""
마이크로소프트에서 만든 히스토그램 기반 그레디언트 부스팅 라이브러리
"""
None

colab 에는 바로 사용 가능!

Anaconda 에선 conda install lightgbm

https://github.com/microsoft/LightGBM

https://lightbgm.readthedocs.io/en/latest <= 온라인 문서

from lightgbm import LGBMClassifier

lgb = LGBMClassifier(tree_method='hist', 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']))

앙상블 학습

1. 랜덤포레스트 - 부스트 샘플 사용

2. 엑스트라트리 - 전체 데이터 셋 사용 노드 분할

3. 그레디언트 부스팅 - 점진적으로 증가시키며 하강법 사용

4. 히스토그램 기반 그레디언트 부스팅 - 256개

profile
독해지자

0개의 댓글