본문 바로가기

프로그래밍/AI

[혼공머신 11기] 4주차 스터디 및 과제

반응형

4주차 후기

이전까지 훈련 세트와 테스트 세트로 데이터를 나누고, 테스트 세트의 결정 계수로 적합도를 판단하는 것이 무언가 찝찝했는데 이번 장에서 교차 검증을 배워서 그 찝찝함이 조금은 해소되었다.
인터넷에 교차 검증을 검색해보니 사이킷런 문서에 교차 검증에 대하여 영어로 상세히 설명되어 있는 것을 찾을 수 있었다. 그래서 사이킷런 User Guide를 살펴보았는데, 그동안 책에서 배웠던 내용들이 많이 설명되어 있었다. 여태 API 문서만 봤었는데 앞으로 공부하면서 이해가 잘 안되면 여기서도 내용을 더 읽어봐야 겠다.

앞선 장에서 하이퍼파라미터라는 개념을 공부하면서 우리의 예제에서는 어떤 값이 최적의 하이퍼파라미터인지 여러 값을 넣어 for문을 돌리고, 내 눈으로 직접 최적의 값을 찾았었다. 그때 들었던 생각이 "현업에서도 이렇게 찾는건가...??"라는 생각을 했었다. 그런데 그리드 서치를 보면서 "역시 뭔가 다른 방법이 있었군"이라는 생각이 들었다. 그리고 이거보다 더 나은 방법도 있지 않을까 생각했다.

마지막으로 앙상블은 지난주에 공부했던 경사 하강법처럼 이해하기 좀 어려웠다.
그래서 인터넷을 검색해보니 앙상블 기법이 voting, bagging, boostring으로 나눠지며 우리 책에서 공부한 것들이 bagging 방식 알고리즘이란 것을 알게 되었다.

한 챕터씩 넘어가며 어려운 개념이 계속해서 나와 인터넷을 찾아보고 있는데, 좀 더 정리된 방식으로 보고 싶어서 핸즈온 머신러닝 3판을 구매했다. 하지만 내가 기대한 느낌의 책은 아니였다. 그래도 올해는 머신러닝 관련 공부를 해보기로 마음 먹었으니, 혼공머신 스터디가 끝나면 이 책으로 좀 더 깊게 공부해봐야 겠다.


4주차 과제

기본 미션

교차 검증을 그림으로 설명하기

sklearn docs - 3.1. Cross-validation

먼저 전체 데이터에서 20~30% 정도를 테스트 세트로 떼어놓는다.
테스트 세트는 맨 마지막에 딱 한 번 최종 점수를 평가하는데 사용한다.

전체 데이터에서 테스트 세트를 뺀 나머지 훈련 세트를 K개로 나눈다. (그림에서 Fold 1 ~ 5)

위 그림은 5-fold-cross-validation 예제이다.
처음에는 Fold 2 ~ 5를 훈련 세트로 사용하고, Fold 1을 검증 세트로 사용하여 모델을 평가한다.
그 다음 Fold 1과 Fold 3 ~ 5를 훈련 세트로 사용하고, Fold 2를 검증 세트로 사용하여 모델을 평가한다.
그 다음 Fold 1 ~ 2와 Fold 4 ~5를 훈련 세트로 사용하고, Fold 3을 검증 세트로 사용하여 모델을 평가한다.
이런 방식으로 Fold 4, Fold 5도 검증 세트로 사용하여 모델 평가를 반복한다.

각 Split에서 나온 검증 점수의 평균으로 _최종 검증 점수_를 얻는다.

선택 미션

Ch.05(05-3) 앙상블 모델 손코딩 코랩 화면 인증하기

본 문서 하단에 포함한 코드와 코랩 실행 결과 스크린샷으로 대체합니다.


결정 트리

예제 문제

  • 알코올 도수, 당도, pH 값으로 레드 와인인지 화이트 와인인지 분류하기
###
# 알코올 도수, 당도, pH값으로 레드와인과 화이트와인 분류하기
###

# 와인 데이터 불러오기
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')

# 불러온 데이터 확인하기
print(f"""
# 와인 데이터 첫 5개 (class 0: 레드 / 1: 화이트)
{wine.head()}
""")

print("# info() 메서드로 데이터프레임 각 열의 타입과 누락된 데이터 존재여부 확인")
print(f"{wine.info()}\n")

print("# describe() 메서드로 각 열에 대한 간략한 통계 출력")
print(wine.describe())
print("=> 알코올 도수, 당도, pH값의 스케일이 다르기 때문에 표준화가 필요하다.\n")

# numpy 배열로 변환
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['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)

print(f"""# 훈련 세트와 테스트 세트의 크기 확인
훈련 세트 크기 : {train_input.shape}
테스트 세트 크기 : {test_input.shape}
""")

# 표준화 전처리
from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

# 분류를 위한 로지스틱 회귀 모델 훈련
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_scaled, train_target)

print(f"""# 훈련 세트와 테스트 세트의 결정계수
훈련 세트 : {lr.score(train_scaled, train_target)}
테스트 세트 : {lr.score(test_scaled, test_target)}
=> 훈련세트와 테스트세트 점수가 모두 낮아 '과소적합' 된 것 같다.
""")

print(f"""# 로지스틱 회귀가 학습한 계수와 절편
계수 : {lr.coef_}
절편 : {lr.intercept_}
""")

결정 트리 (Decision Tree)

  • 결정 트리 모델은 "스무고개"처럼 데이터를 구분할 수 있는 질문을 계속해서 추가하여 분류 정확도를 높일 수 있다.
  • 다른 머신러닝 모델에 비해 비전문가에세 설명하기 쉬운 모델을 만든다.
  • 결정 트리는 많은 앙상블 학습 알고리즘의 기반이 된다.
  • 사이킷런의 DecisionTreeClassifier 클래스를 사용할 수 있다.
  • 장점
    • 결정 트리 알고리즘은 특성값의 스케일에 영향을 받지 않기 때문에 표준화 전처리를 할 필요가 없다.
    • 특성 중요도를 활용하면 결정 트리 모델을 특성 선택에 활용할 수 있다.
###
# 결정 트리
###
# 결정 트리 모델 훈련
from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)

print(f"""# 결정 트리 모델로 훈련한 훈련 세트와 테스트 세트 결정계수
훈련 세트 : {dt.score(train_scaled, train_target)}
테스트 세트 : {dt.score(test_scaled, test_target)}
=> 훈련 세트에 비해 테스트 세트가 조금 낮은 "과대적합"된 모델이다.
""")

# plot_tree()로 결정 트리를 그림으로 그리기
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree

plt.figure(figsize=(10,7))
plot_tree(dt)

print("# 결정 트리 출력")
plt.show()
print()

print("# 결정 트리의 깊이를 제한하여 일부만 확대 출력")
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
print()

확대 출력된 결정 트리 동작 방식

  • Root 노드 : 당도(sugar)가 -0.239 이하인가?
    • -0.239 이하(YES) : 왼쪽 Leaf 노드로 이동
    • -0.239 초과(NO) : 오른쪽 Leaf 노드로 이동
  • samples=5197 : 루트 노드의 총 샘플 수는 5197개
  • [1258, 3939]
    • 음성 클래스(레드 와인) 1258개, 양성 클래스(화이트 와인) 3939개
    • 만약 마지막 Leaf 노드라면 두 값 중에서 개수가 더 많은 클래스로 예측한다.
  • gini : 불순도

불순도 (Gini Impurity)

  • 결정 트리가 최적의 질문을 찾기 위한 기준
  • DecisionTreeClassifier 클래스는 노드에서 데이터를 분할할 기준을 정하는 criterion 매개변수의 기본값은 gini이다.
  • 지니 불순도 계산법
    $$
    지니,불순도 = 1;-;(음성,클래스,비율^2;+;양성,클래스,비율^2)
    $$
  • criterion='entropy'를 지정하여 엔트로피 불순도를 사용할 수 있다.
  • 엔트로피 불순도 계산법
    $$
    엔트로피,불순도;=;-음성,클래스,비율;\times;\log_2(음성,클래스,비율);-;양성,클래스,비율);\times;\log_2(양성,클래스,비율)
    $$

정보 이득 (Information Gain)

  • 부모 노드(Parent Node)와 자식 노드(Child Node)의 불순도 차이를 정보 이득이라고 부른다.
  • 결정 트리 모델은 정보 이득이 최대가 되도록 데이터를 나눈다.
  • 정보 이득 계산법
    $$
    정보,이득;=;부모의,불순도;-;(왼쪽,노드,샘플,수,/,부모의,샘플,수);\times;왼쪽,노드,불순도;-;(오른쪽,노드,샘플,수,/,부모의,샘플,수);\times;오른쪽,노드,불순도
    $$

가지치기

  • 결정 트리의 가지치기를 해야 무한정 자라나는 트리가 만들어지지 않는다.
  • 가지치기를 통한 일반화를 하여 훈련 세트와 테스트 세트에 모두 맞는 모델을 만들수 있다.
  • 결정 트리를 제한 없이 성장하면 훈련 세트에 과대적합되기 쉽다.

특성 중요도

  • 결정 트리에 사용된 특성이 불순도를 감소하는데 기여한 정도
# max_depth=3 으로 훈련
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)
print(f"""# max_depth=3 으로 훈련한 훈련 세트와 테스트 세트 결정계수
표준화 데이터 훈련 세트 : {dt.score(train_scaled, train_target)}
표준화 데이터 테스트 세트 : {dt.score(test_scaled, test_target)}
=> 훈련 세트 성능은 낮아지고, 테스트 세트의 성능은 그대로이다.
""")

plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

# 표준화 전 데이터로 출력
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target)
print(f"""# 표준화 전 데이터를 사용한 훈련 세트와 테스트 세트 결정계수
표준화 전 데이터 훈련 세트 : {dt.score(train_input, train_target)}
표준화 전 데이터 테스트 세트 : {dt.score(test_input, test_target)}
=> 표준화 전/후의 결정계수가 동일하다.
""")

plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
print("=> 특성값을 표준점수로 바꾸지 않았기 때문에 오히려 이해하기가 더 쉽다.")

# 가장 유용한 특성을 나타내는 특성 중요도 계산
for index in range(0,3):
  print(f"특성 {index + 1} : {wine.columns[index]},     \t중요도 : {dt.feature_importances_[index]}")

# https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier.feature_importances_
dt = DecisionTreeClassifier(min_impurity_decrease=0.0005, random_state=42)
dt.fit(train_input, train_target)

print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))

plt.figure(figsize=(20,15), dpi=300)
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()


교차 검증과 그리드 서치

그동안 수행한 모델 평가의 문제점

  • 지금까지 "훈련 세트"에서 모델을 훈련하고, "테스트 세트"에서 모델을 평가했다.
  • 계속 "테스트 세트"로 성능을 확인하면, 점점 테스트 세트에 맞추게 된다.
  • "테스트 세트"는 가능한 모델을 만들고 나서 마지막에 딱 한 번만 사용하는 것이 좋다.

검증 세트 (Validation Set)

  • 훈련 세트에서 일부 데이터를 떼어 검증 세트로 사용한다.
    • AS-IS : 훈련 세트 80%, 테스트 세트 20%
    • TO-BE : 훈련 세트 60%, 검증 세트 20%, 테스트 세트 20%
  • 예를 들면 다음과 같이 사용할 수 있다.
  • 모델 훈련 및 검증 방법
    1. 훈련 세트로 모델을 훈련
    2. 검증 세트로 모델을 평가
    3. 마지막에 테스트 세트로 최종 점수를 평가
# 데이터 불러오기
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')

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

# 훈련 세트, 테스트 세트 만들기
#   - test_size 파라미터로 0.2를 전달 -> 테스트 세트로 20% 사용
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)

# 훈련 세트(sub)에서 검증 세트(val) 분리하기
sub_input, val_input, sub_target, val_target = train_test_split(train_input, train_target, test_size=0.2, random_state=42)

# 훈련 세트와 검증 세트의 크기 확인
print(f"""# 훈련 세트와 검증 세트 크기 확인
전체 데이터 크기 : {data.shape}
검증 세트 분리 전 훈련 세트 크기 : {train_input.shape}
테스트 세트 크기 : {test_input.shape}
훈련 세트 크기 : {sub_input.shape}
검증 세트 크기 : {val_input.shape}
""")

# 결정 트리 모델 훈련
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)

print(f"""# 훈련 세트와 검증 세트의 결정 계수
훈련 세트 : {dt.score(sub_input, sub_target)}
검증 세트 : {dt.score(val_input, val_target)}
=> 훈련 세트에 과대적합 되어 있다.
""")

교차 검증 (Cross Validation)

sklearn docs - 3.1. Cross-validation

  • 교차 검증은 검증 세트를 떼어 내어 평가하는 과정을 여러 번 반복한다. 그리고 나서 이 점수의 평균을 최종 검증 점수로 얻는다.
  • k-폴드 교차 검증 (k-fold cross validation)
    • "k-겹 교차 검증"이라고도 부른다.
    • 훈련 세트를 k개로 나눠서 교차 검증을 수행한다.
    • 예를 들어 훈련 세트를 세 부분으로 나눠서 교차 검증을 수행하면 "3-폴드 교차 검증"이라고 한다.
    • 보통 5-폴드 교차 검증이나 10-폴드 교차 검증을 많이 사용한다.
  • 사이킷런에 cross_validate()라는 교차 검증 함수가 있다.
# 교차 검증
from sklearn.model_selection import cross_validate
from sklearn.model_selection import StratifiedKFold
import numpy as np

splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv=splitter)

print(f"""
* cross_validate() 함수는 기본적으로 5-폴드 교차 검증을 수행한다.
  따라서 각 키마다 5개의 숫자가 담겨 있다.

# 모델을 훈련하는 시간
fit_time : {scores['fit_time']}

# 모델을 검증하는 시간
score_time : {scores['score_time']}

# 교차 검증의 최종 점수
test_score : {scores['test_score']}
test_score 평균값 : {np.mean(scores['test_score'])}
""")

하이퍼파라미터 튜닝

  • 모델 파라미터 : 머신러닝 모델이 학습하는 파라미터
  • 하이퍼파라미터 : 머신러닝 모델이 학습할 수 없어서 사용자가 지정해야 하는 파라미터 (사용자 지정 파라미터)
  • 튜닝 방법
    1. 먼저 라이브러리 기본값을 사용해 모델을 훈련한다.
    2. 검증 세트 점수 or 교차 검증을 통해 매개변수를 바꿔가며 훈련하여 최적값을 찾는다.
  • 주의할 점
    • 튜닝 할 매개변수를 동시에 바꿔가며 최적의 값을 찾아야 한다.
    • 한 매개변수의 최적값을 찾았더라도 다음 매개변수의 값이 바뀌면 최적값이 달라지기 때문이다.

그리드 서치(Grid Search)

  • 사이킷런에서는 최적의 하이퍼파라미터를 찾기 위해 그리드 서치를 제공한다.
  • GridSearchCV 클래스는 하이퍼파라미터 탐색과 교차 검증을 한 번에 수행한다.
    • 기본적으로 교차 검증을 수행하기 때문에 cross_validate() 함수를 호출할 필요가 없다.
    • cv 매개변수 기본값은 5이다. 따라서 기본적으로 5-폴드 교차 검증을 수행한다.
    • best_estimator_ 속성을 통해 최적의 하이퍼파라미터를 사용하여 자동으로 모델을 다시 훈련한다.
from sklearn.model_selection import GridSearchCV

###
# 간단한 그리드 서치 실습
###

# 탐색할 매개변수와 값 목록
params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}

# 그리드 서치 객체 생성
#  - n_jobs : 사용할 CPU 코어 개수. -1은 모든 코어 사용
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)

# 결정 트리 모델의 min_impurity_descrease 파라미터 값을 바꿔가며 총 5번 실행
gs.fit(train_input, train_target)

# 최적의 하이퍼파라미터로 모델 훈련
dt = gs.best_estimator_
print(f"""# 그리드 서치를 활용한 최적의 하이퍼파라미터 탐색
최적의 하이퍼파라미터 사용 시 결정계수 : {dt.score(train_input, train_target)}
최적의 매개변수 : {gs.best_params_}
각 매개변수 별 교차 검증 평균 점수 : {gs.cv_results_['mean_test_score']}
""")

###
# 복잡한 그리드 서치 실습
###

# 교차 검증 횟수 : 1350개(9 * 15 * 10) * 5(5-폴드 교차 검증) = 모델 수 총 6750개
params = {
    'min_impurity_decrease': np.arange(0.0001, 0.001, 0.0001),
    'max_depth': range(5, 20, 1),
    'min_samples_split': range(2, 100, 10)
}

# 그리드 서치
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

# 결과
print(f"""
최상의 매개변수 조합 : {gs.best_params_}
최상의 교차 검증 점수 : {np.max(gs.cv_results_['mean_test_score'])}
""")

랜덤 서치(Random Search)

  • 매개변수 값의 목록을 전달하지 않고, 매개변수를 샘플링 할 수 있는 확률 분포 객체를 전달한다.
  • RandomizedSearchCV 클래스를 사용한다.
  • 언제 사용할까?
    • 매개변수의 값이 수치여서 값의 범위, 간격을 정하기 어려울 때
    • 매개변수 조건이 너무 많아서 그리드 서치 수행시간이 오래 걸릴 때

파이썬의 scipy 라이브러리

  • 파이썬의 핵심 과학 라이브러리 중 하나
  • 적분, 보간, 선형 대수, 확률 등을 포함한 수치 계산 전용 라이브러리
from scipy.stats import uniform, randint

###
# 난수 생성 테스트
###
# randint로 0~10 사이의 정수값 샘플링
rgen = randint(0, 10)
np.unique(rgen.rvs(10000), return_counts=True)

# uniform으로 0~1 사이의 실수값 샘플링
ugen = uniform(0, 1)
ugen.rvs(10)


###
# 랜덤 서치
###
from sklearn.model_selection import RandomizedSearchCV

params = {
    'min_impurity_decrease': uniform(0.0001, 0.001),
    'max_depth': randint(20, 50),
    'min_samples_split': randint(2, 25),
    'min_samples_leaf': randint(1, 25)
}

# params에 정의된 매개변수 범위에서 총 100번(n_iter) 샘플링하여 교차 검증을 수행하여 최적의 매개변수 조합 찾기
rs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42, splitter='best'), params, n_iter=100, n_jobs=-1, random_state=42)
rs.fit(train_input, train_target)

print(f"""
최상의 매개변수 조합 : {rs.best_params_}
최상의 교차 검증 점수 : {np.max(rs.cv_results_['mean_test_score'])}
""")

# 최적 모델로 훈련하여 테스트 세트 성능 확인
dt = gs.best_estimator_
print(f"테스트 세트 성능 : {dt.score(test_input, test_target)}")


트리의 앙상블

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

  • 정형 데이터(structured data)
    • 어떤 구조로 되어 있는 데이터
    • CSV, Excel, Database 등
    • 앙상블 학습(ensemble learning) 알고리즘을 사용할 수 있다.
  • 비정형 데이터(unstructured data)
    • Database나 Excel로 표현하기 어려운 데이터
    • 책의 글과 같은 텍스트 데이터, 디지털카메라로 찍은 사진, 핸드폰으로 듣는 음악 등
    • 신경망 알고리즘을 사용할 수 있다.

앙상블(Ensemble)

  • 여러가지 알고리즘들을 종합해서 결과를 내는 모델링 방식
  • 대표적인 앙상블 기법
    • Voting : 서로 다른 알고리즘을 사용해 예측한 결과를 다수결이나 평균으로 결정
    • Bagging : 같은 알고리즘을 사용하되, 데이터를 무작위로 추출해 다양한 서브 데이터셋을 생성하고, 이를 병렬적으로 학습한 결과를 다슈결이나 평균으로 결정
    • Boostring : 같은 알고리즘을 사용하면서 앞서 학습한 결과를 가지고 다음 학습기가 순차적으로 학습하면서 오차를 줄여나가는 방식
  • 참고자료 : SUPERB AI - [3분 알고리즘] 랜덤 포레스트

랜덤 포레스트(Random Forest)

  • 대표적인 앙상블 학습 방법
  • Bagging 방식 알고리즘
  • 동작 방식
    1. 부트스트랩(Bootstrap)을 통해 다양한 서브 데이터셋을 생성
    2. 여러 개의 의사결정나무가 각각의 데이터셋을 학습하고 결과를 취합
  • 부트스트랩(Bootstrap) 방식
    • 데이터 세트에서 중복을 허용하여 데이터를 샘플링하는 방식
    • N개의 샘플 중에서 1개를 뽑은 후 다시 넣는다. 따라서 중복된 샘플을 뽑을 수도 있다.
    • 부트스트랩 방식으로 만들어진 샘플을 부트스트랩 샘플(bootstrap sample) 이라고 부른다.
  • 노드 분할 및 훈련
    • 전체 특성 중에서 일부 특성을 무작위로 고른 다음, 이 중에서 최선의 분할을 찾고, 사이킷런의 랜덤 포레스트는 기본적으로 100개의 결정 트리를 훈련한다.
    • 분류 모델(RandomForestClassifier)
      • 전체 특성의 제곱근 만큼 특성 선택
      • 각 트리의 클래스별 확률을 평균하여 가장 높은 확률을 가진 클래스를 예측으로 삼는다.
    • 회귀 모델(RandomForestRegressor)
      • 전체 특성 선택
      • 각 트리의 예측을 평균한다.
  • 장점
    • 랜덤하게 선택한 샘플과 특성을 사용하기 때문에 훈련 세트에 과대적합 되는 것을 막을 수 있다.
    • 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있다.
# 데이터를 불러와 훈련 세트와 테스트 세트로 나누기
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)

# 랜덤 포레스트 분류 - 교차 검증
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(oob_score=True, 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'])}
테스트 세트 점수 : {np.mean(scores['test_score'])}
""")

rf.fit(train_input, train_target)
print(f"""# 랜덤 포레스트 분류 - 특성 중요도
{rf.feature_importances_}
=> <결정 트리> 예제에서 생성된 특성 중요도랑 비교해보면
   하나의 특성에 과도하게 집중하지 않고 고르게 특성들이 훈련에 기여한다.
""")

# 부트스트램 샘플에 포함되지 않은 OOB(Out Of Bag) 샘플로 결정 트리 평가
print(f"""# OOB 샘플로 교차 검증을 대신하여 결정 트리 평가
OOB 점수 : {rf.oob_score_}
=> 교차 검증에서 나온 점수와 비슷하다.
""")

엑스트라 트리(Extra Tree)

  • 랜덤 포레스트와는 달리 부트스트램 샘플을 사용하지 않는다.
  • 즉, 결정 트리를 만들 때 전체 훈련 세트를 사용한다.
  • 무작위로 노드 분할을 한다.
  • 앞선 예제에서 splitter='random' 지정해 사용했는데, 엑스트라 트리가 사용하는 결정 트리가 splitter='random' 인 결정 트리다.
  • 성능은 낮은 반면 많은 트리를 앙상블하기 때문에 과대적합을 막고, 검증 세트의 점수를 높이는 효과가 있다.
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'])}
테스트 세트 점수 : {np.mean(scores['test_score'])}
""")

et.fit(train_input, train_target)
print(f"""# 엑스트라 트리 분류 - 특성 중요도
{et.feature_importances_}
""")

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

  • 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블 하는 방법
    • 사이킷런의 GradientBoostingClassifier은 기본적으로 깊이가 3인 결정 트리 100개를 사용한다.
  • 깊이가 옅은 결정 트리를 사용하기 때문에 과대적합에 강하고, 높은 일반화 성능을 가진다.
  • 경사 하강법을 사용하여 트리를 앙상블에 추가한다.학습률 매개변수로 속도를 조절한다.
  • 결정 트리를 계속해서 추가하면서 가장 낮은 곳으로 이동한다.
  • 분류 : 로지스틱 손실 함수 사용 / 회귀 : 평균 제곱 오차 함수 사용
  • 일반적으로 그레이디언트 부스팅이 랜덤 포레스트보다 더 높은 성능을 가지지만, 순서대로 트리를 추가하기 때문에 훈련 속도가 느리다.
# 그레이디언트 부스팅 - 교차 검증
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"""# 그레이디언트 부스팅 분류 - 교차 검증 결과 (결정 트리 개수 : 100 / 학습률 : 0.1)
훈련 세트 점수 : {np.mean(scores['train_score'])}
테스트 세트 점수 : {np.mean(scores['test_score'])}
""")

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(f"""# 그레이디언트 부스팅 분류 - 교차 검증 결과 (결정 트리 개수 : 500 / 학습률 : 0.2)
훈련 세트 점수 : {np.mean(scores['train_score'])}
테스트 세트 점수 : {np.mean(scores['test_score'])}
""")

gb.fit(train_input, train_target)
print(f"""# 그레이디언트 부스팅 분류 - 특성 중요도
{gb.feature_importances_}
""")

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

  • 정형 데이터를 다루는 머신러닝 알고리즘 중에 가장 인기가 높은 알고리즘
  • 동작 방식
    1. 입력 특성을 256개 구간으로 나눈다.
      • 노드를 분할할 때, 최적의 분할을 매우 빠르게 찾을 수 있다.
    2. 256개 구간 중에서 하나를 떼어 놓고 누락된 값을 위해서 사용한다.
    3. 따라서 입력에 누락된 특성이 있더라도 따로 전처리 할 필요가 없다.
  • 히스토그램 기반 그레이디언트 부스팅 알고리즘을 구현한 라이브러리
    • 사이킷런
    • XGBoost
    • LightGBM
from sklearn.ensemble import HistGradientBoostingClassifier

hgb = HistGradientBoostingClassifier(random_state=42, max_iter=100, learning_rate=0.1)
scores = cross_validate(hgb, train_input, train_target, return_train_score=True)
print(f"""# 히스토그램 기반 그레이디언트 부스팅 분류 - 교차 검증 결과 (반복 횟수 : 100 / 학습률 : 0.1)
훈련 세트 점수 : {np.mean(scores['train_score'])}
테스트 세트 점수 : {np.mean(scores['test_score'])}
""")

from sklearn.inspection import permutation_importance

# 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(f"""# 히스토그램 기반 그레이디언트 부스팅 분류 - 훈련 세트 특성 중요도
특성 중요도 : {result.importances}
평균 : {result.importances_mean}
표준 편차 : {result.importances_std}
""")

# permutation_importance() - 테스트 세트
result = permutation_importance(hgb, test_input, test_target, n_repeats=10, random_state=42, n_jobs=-1)
print(f"""# 히스토그램 기반 그레이디언트 부스팅 분류 - 테스트 세트 특성 중요도
특성 중요도 : {result.importances}
평균 : {result.importances_mean}
표준 편차 : {result.importances_std}
""")

# 테스트 세트 성능
print(f"테스트 세트 성능 : {hgb.score(test_input, test_target)}")

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

# 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(np.mean(scores['train_score']), np.mean(scores['test_score']))

반응형