족장님, 한빛미디어 사랑합니다..♥


05. 트리 알고리즘

 

05-1 결정 트리

  • 시작하기 전에
    와인을 알코올, 당도, pH 값을 활용하여 분류해보려고 한다.

 

<로지스틱 회귀로 와인 분류하기>

import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
wine.info()

데이터를 불러온 후 info() 메서드를 사용하면 데이터프레임의 각 열의 데이터 타입과 누락된 데이터 여부를 알 수 있다. 6497개의 샘플 중 non-null count가 6497 이므로 누락된 값은 없는 것을 알 수 있다.

만약 누락된 값이 있다면 그 데이터를 버리거나 평균값으로 채운 후 사용할 수 있는데, 그렇게 하는 과정에서도 훈련 세트의 통계 값으로 테스트세트를 변환해야 한다. 즉, 훈련 세트의 평균 값으로 테스트 세트의 누락값을 채워야 한다.

wine.head()

.

head() 메서드는 데이터프레임의 처음 5개 샘플을 보여준다. 0이면 레드와인, 1이면 화이트와인이라고 한다.

 

wine.describe()

describe() 메서드는 열에 대한 간략한 통계를 출력해준다. 최솟값, 최댓값, 평균값 등을 볼 수 있다. 이를 통해 각 항목들의 스케일이 다른 것을 알 수 있고, 해당 데이터를 사용하기 전에 특성을 표준화해줘야 한다.

 

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(train_input.shape, test_input.shape)

train_test_split() 메서드는 기본적으로 25%의 설정값으로 테스트 세트를 지정한다. 하지만 샘플 개수가 충분히 많다면 사이즈를 조절할 수 있다. test_size 매개변수를 활용하여 값을 지정할 수 있다. 5197개의 훈련, 1300개의 테스트 세트로 나뉘었다.

 

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(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target)) 

print(lr.coef_, lr.intercept_)

표준정규화를 사용한 후 로지스틱 회귀모델을 훈련해 본 결과 77~78의 훈련 점수가 나왔다. 둘의 점수가 모두 낮으니 과소적합이 된 것으로 추정된다. 회귀가 학습한 계수와 절편값을 출력한 결과를 들여다보려고 한다.

 

 

<설명하기 쉬운 모델과 어려운 모델>

 

앞서 확인한 계수와 절편값을 설명하기에는 너무 어렵다. 이 모델이 왜 저런 계수 값을 학습했는지 '이해'한다기보다는 '추측'에 가까울 것 같다. 숫자로는 이해할 수 없기 때문에 순서도를 그리며 설명한다면 조금 더 이해도가 높아지지 않을까?

 

<결정 트리>

결정트리(Decision Tree) 모델이란 마치 스무고개처럼 하나씩 질문을 던져 정답을 맞춰가는 모델이다. 즉, 진실(True)과 거짓(False)을 통해 예/아니오로 질문을 이어가며 정답을 찾아 학습한다. 데이터를 잘 나눌 수 있는 질문을 찾는다면 질문을 추가해서 분류 정확도를 높일 수 있다. 사이킷런이 결정트리 알고리즘을 제공한다.

 

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)

print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

결정트리 알고리즘을 활용했더니 훨씬 점수가 높아졌다. 하지만 훈련세트에만 높은 점수로 미루어 보아 과대적합이 발생한 것으로 추측된다. 사이킷런의 메서드를 통해 이 모델을 그림으로 표현할 수 있다.

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()

plot_tree() 메서드를 통해 트리를 그려볼 수 있다. 일반적으로 나무는 밑에서 위로 자라지만, 결정 트리는 위에서부터 아래로 탑다운(Top-down) 형태로 자란다. 맨 위의 파란색으로 동그라미한 노드루트 노드(root node)라 하고 화살표 모양으로 표시한 아래쪽에 주렁주렁 달린 노드리프 노드(leaf node)라고 한다.

회귀 모델의 경우 최종 도달한 리프 노드의 값의 평균이 새로운 샘플 x에 대한 예측값이라고 할 수 있다. 만약 분류 모델의 경우 k-최근접 이웃 알고리즘과 마찬가지로, 최종 조달한 라프 노드의 값 중 다수의 샘플들을 통해 예측할 수 있다. 

노드란 결정 트리를 구성하는 핵심 요소로, 훈련 데이터의 특성에 대한 테스트를 표현한다. 하지만 이렇게 보기엔 복잡하니 트리의 깊이를 제한해서 출력할 수 있다.

max_features 매개변수를 통해 사용할 특성의 갯수를 지정할 수 있다. 기본값은 None이라 모든 값을 사용하게 되는 것이고, 만약 특성의 갯수보다 작다고 하면 random하게 특성을 선택한다.

plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

plot_tree() 메서드를 사용하면 지정한만큼만 트리를 출력할 수 있다. max_depth 매개변수를 1로 준다면 루트 노트를 제외하고 추가적으로 하나의 노드를 더 확장하여 그린다. filled 매개변수는 클래스에 맞게 노드의 색을 칠할 수 있다. feature_names 매개변수에는 특성의 이름을 전달할 수 있다.

여기서 그림이 담고 있는 정보를 하나씩 살펴보면 루트노드는 당도가 -0.239 이하인지에 대해 묻는 것이고, 작거나 같으면 좌측, 크면 우측으로 이동하는 것이다. 그에 따라 이동한 sample은 루트노드의 5197개 샘플 중 좌측이 2922개, 우측이 2275개다. 또한 plot_tree() 함수에서 filled=True로 지정하면 클래스마다 색상을 부여하고, 어떤 클래스의 비율이 높아지면 점점 진한 색으로 표현한다고 한다.

결정트리에서는 리프 노트에서 가장 많은 클래스가 예측 클래스가 되고 만약 여기서 트리의 성장을 멈춘다면 좌측 샘플과 우측 샘플 모두 양성 클래스의 갯수가 많기 때문에 모두 양성 클래스로 예측이 될 수 있다.

노드 상자안에 gini라는 것이 있는데 이것에 대해 좀 더 자세히 알아보려 한다.

 

<불순도>

gini는 지니 불순도를 의미한다. 불순도는 결정 트리가 최적의 질문을 찾기 위한 기준이다. DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값은 'gini'이다.  criterion 매개변수는 노드에서 데이터를 분할할 기준을 정하는 것인데 앞서 당도 -0.239를 기준으로 나눈 것 또한 criterion 매개변수에 지정한 지니 불순도(Gini impurity)를 사용한 것이다.

 

지니 불순도클래스의 비율을 제곱해서 더한 다음 1에서 빼는 것이 계산 방법이다. 즉, 음성 클래스의 비율을 제곱한 것과 양성 클래스의 비율을 제곱한 것을 더해서 1에서 빼면 된다.

앞서 나뉜 샘플의 불순도를 계산하면 0.367이 나오고, 만약 정확히 절반씩 나뉜다면 0.5의 지니 불순도가 나타나는 것을 알 수 있다. 노드에 하나의 클래스만 있다면 지니 불순도는 0이 되고 이러한 노드를 순수노드라고 부른다.

부모의 불순도와 자식의 불순도를 뺐을 때 그 차이가 가장 크게 나오는 방향으로 노드를 분할하는 것이 결정트리의 방향이다. 이 불순도의 차이를 정보 이득(information gain)이라고 부른다. 하지만 사이킷런에서 제공하는 또 다른 불순도 기준이 있는데, criterion='entropy'를 지정하여 엔트로피 불순도를 사용할 수 있다. 이 불순도도 클래스 비율을 사용하지만 제곱이 아닌 밑이 2인 로그를 사용한다.

노드를 순수하게 나룰수록 정보 이득이 커지는데, 앞선 트리에서는 무한하게 자람에 따라 과대적합이 발생한 것을 알 수 있다.

 

<가지치기>

결정 트리도 무한하게 자라지 않도록 가지치기가 필요하다. 왜냐하면 과대적합이 발생할 수 있기 때문이다. 보통은 트리의 최대 깊이를 지정함으로써 설정이 가능하다. 

dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

max_depth 매개변수를 3으로 지정함으로써 루트 노드 아래로 최대 3개의 노드까지만 성장하도록 설정하였다. 이를 트리 그래프로 그려볼 수 있다.

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

맨 위의 루트노드가 깊이 0, 가장 밑의 리프노드가 깊이 3이다. 깊이 1의 노드는 모두 당도를 기준으로 훈련 세트를 나눈다. 하지만 깊이 2의 노드는 맨 왼쪽만 당도(sugar)로 나누고, 왼쪽에서 두 번째 노드는 도수(alcohol)로 나누는 것을 알 수 있다. 오른쪽 두 노드는 pH를 기준으로 나눈다.

깊이 3의 노드 중 유일하게 붉은 빛을 내는 세 번째 노드만 음성 클래스가 더 많다. 이 노드에 도달해야만 레드 와인으로 인식을 하고, 조건은 알코올 도수가 0.454보다 작으며 당도는 -0.802와 -0.239 사이어야 한다.

하지만 당도가 음수로 된 것이 좀 이상하다... 결정 트리 알고리즘의 장점은 특성값의 스케일이 알고리즘에 영향을 미치지 않는다는 점이다. 따라서 표준화 전처리를 할 필요가 없다.

dt = DecisionTreeClassifier(max_depth=3, 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))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

트리를 그려보면, 특성값을 표준점수로 바꾸지 않아 훨씬 이해하기 쉽다. 즉, 당도가 1.625 와 4.325 사이에 있으면서 알코올 도수가 11.025 와 같거나 작은 것이 레드 와인이고 그 외에는 전부 화이트 와인임을 알 수 있다.

결정 트리는 어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해준다. 

 

결정 트리 모델의 feature_importances_ 속성을 통해, 두번째 속성인 당도가 0.87 정도로 특성 중요도가 높은 것을 알 수 있다. 이들을 모두 더하면 1이 된다. 특성 중요도는 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산한다. 이를 활용하면 결정 트리 모델을 특성 선택에 활용할 수 있다.

# 기본미션

결정트리 예시


05-2 교차 검증과 그리드 서치

  • 시작하기 전에

max_depth를 바꾸면 성능의 변화가 있지 않을까? 또 일반화 성능을 올바르게 예측하려면 테스트 세트로 만들어서 테스트 세트로 평가하면 안 되지 않을까?

 

<검증 세트>

테스트 세트를 사용하지 않으면 모델의 과대/과소 적합 여부를 판단할 수 없다. 테스트 세트를 사용하지 않고 측정하는 법은 훈련 세트를 또 나누는 것이다. 이 데이터를 검증 세트(validation set)라고 부른다.

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()

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_input, val_input, sub_target, val_target = train_test_split(train_input, train_target, test_size=0.2, random_state=42)

print(sub_input.shape, val_input.shape)

데이터를 읽어와서 테스트와 훈련 세트로 나누고 다시 훈련세트와 검증세트로 나눈다. test_size 매개변수를 0.2로 지정하여 train_input의 약 20%를 val_input으로 만들고 크기를 확인해보면 훈련 세트가 4,157개, 검증 세트가 1,040개로 나눠진 것을 확인할 수 있다.

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)
print(dt.score(sub_input, sub_target))
print(dt.score(val_input, val_target))

모델을 평가해보니 훈련 세트에 과적합이 된 것을 확인할 수 있다.

 

<교차 검증>

검증 세트를 너무 조금 떼어 놓으면 점수가 들쭉날쭉하고 불안정하다. 이럴 때 교차 검증(cross validation)을 이용하면 더욱 안정적인 점수를 얻고 더 많은 데이터를 사용할 수 있다. 교차 검증은 검증 세트를 떼어 평가하는 과정을 여러번 반복한다. 그 다음 이 점수를 평균하여 최종 검증 점수를 얻는다.

위의 그림은 3-폴드 교차 검증인데 훈련 세트를 세 부분으로 나눠서 교차 검증을 수행하는 것을 3-폴드 교차 검증이라고 한다. 통칭 k-fold 교차 검증(k-fold cross validation)이라고 하며, 훈련 세트를 몇 부분으로 나누냐에 따라 다르게 부른다.

from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)

cross_validate() 메서드를 통해 평가할 모델 객체를 매개변수로 전달한다. 이 함수는 딕셔너리를 반환하고, 처음 2개의 키는 각각 모델을 훈련하는 시간과 검증하는 시간을 의미한다. 각 키마다 5개의 숫자가 담겨 있고 기본적으로 5-폴드 교차 검증을 수행한다.

 

import numpy as np
print(np.mean(scores['test_score']))

교차 검증을 통해 입력한 모델에서 얻을 수 있는 최상의 검증 점수를 가늠해 볼 수 있다. 하지만 cross_validate() 함수는 훈련 세트를 섞어 폴드를 나누지 않기 때문에 만약 교차 검증을 할 때 훈련 세트를 섞으려면 분할기(splitter)를 지정해야 한다.

from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))

splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))

KFold 클래스도 동일하게 사용할 수 있다. n_splits 매개변수는 몇 폴드 교차 검증을 할지 정한다. 

<하이퍼파라미터 튜닝>

모델이 학습할 수 없어서 사용자가 지정해야만 하는 파라미터를 하이퍼파라미터라고 한다. 이것을 튜닝하는 작업은 먼저 라이브러리가 제공하는 기본값을 사용해 훈련을 한 다음 검증 세트의 점수나 교차 검증을 통해 조금씩 바꾸는 순서로 진행된다. 보통 모델은 최소 1-2개에서 5-6개까지의 매개변수를 제공한다.

from sklearn.model_selection import GridSearchCV
params = {'min_impurity_decrease' : [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

dt = gs.best_estimator_
print(dt.score(train_input, train_target))

결정 트리 모델에서 최적의 max_depth 값을 찾았을 때, 값을 고정하고 min_samples_split을 바꿔가며 최적의 값을 찾는 과정이 하이퍼파라미터 튜닝이 아니다. 왜냐하면 min_samples_split 매개변수의 값이 바뀌면 max_depth 의 값이 바뀌기 때문이다. 따라서 두 매개변수를 동시에 바꿔가며 최적의 값을 찾아야 한다.

매개변수가 많아질수록 문제는 더 복잡해지는데, 사이킷런에서 제공하는 그리드 서치(Grid Search)를 사용하면 편리하게 구현할 수 있다. 

fit() 메서드를 통해 결정트리 모델의 min_samples_split 값을 바꿔가며 실행했다. GridSearchCV의 cv 매개변수 기본값은 5이다. 값마다 5-폴드 교차 검증을 수행하기 때문에 결국 25개의 모델을 훈련하는 것이다. 이처럼 많은 모델을 훈련하기 때문에 n_jobs 매개변수로 CPU 코어수를 지정할 수 있다. 기본값은 1이고 -1로 지정하면 모든 코어를 사용한다. 그리드 서치로 찾은 최적의 매개변수는 best_params_ 속성에 저장되어 있다.

 

print(gs.best_params_)
print(gs.cv_results_['mean_test_score'])

best_index = np.argmax(gs.cv_results_['mean_test_score'])
print(gs.cv_results_['params'][best_index])

0.001이 가장 좋은 값으로 선택된 것을 알 수 있다. 또한 두번째 list에서는 첫번째 값이 가장 크다. argmax() 함수를 통해 가장 큰 값의 인덱스를 추출할 수 있고, 인덱스를 활용해 params 키에 저장된 매개변수를 출력할 수 있다. 이 값이 앞서 출력한 gs.best_params_ 와 동일한지 확인해보면 같은 것을 알 수 있다.

이 과정을 정리해보자면 다음과 같다.

1. 탐색할 매개변수를 지정한다.

2. 훈련 세트에서 그리드 서치를 수행하여 최상의 평균 검증 점수가 나오는 매개변수 조합을 찾는다. 이 조합은 그리드 서치 객체에 저장된다.

3. 그리드 서치는 최상의 매개변수에서 전체 훈련 세트를 사용하여 최종 모델을 훈련하다. 이 모델 또한 그리드 서치 객체에 저장된다.

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(gs.best_params_)

arange() 함수는 첫 매개변수 값에서 시작하여 두 번째 매개변수에 도달할 때까지 세 번째 매개변수를 계속 더한 배열을만든다. 0.0001에서 시작하여 0.001씩 더해서 0.001이 되도록 만드는 것이다. range() 함수는 정수만 사용할 수 있는데,  max_depth 를 5에서 20까지 1씩 증가시킨다. min_samples_split 은 2에서 100까지 10씩 증가시키는 것을 알 수 있다. 9개 * 15개 * 10개 이므로 총 1,350번의 교차 검증횟수가 진행되며 5-폴드가 기본이므로 6,750개의 모델이 수행된다.

최상의 교차검증 점수를 확인해보니 이러한 값이 나왔다.

 

<랜덤 서치>

매개변수의 범위나 간격을 미리 정하기 어렵거나 너무 많은 매개변수가 있을 때 그리드 서치 시간이 오래 걸린다. 그럴 때 랜덤 서치(Random Search)를 사용하면 좋다. 랜덤 서치에는 매개변수 값의 목록을 전달하는 것이 아니라 매개변수를 샘플링할 수 있는 확률 분포 객체를 전달한다.

이를 위해선 싸이파이(scipy) 라이브러리를 불러와야하는데 사이킷런에서는 넘파이와 사이파이를 많이 사용한다.

from scipy.stats import uniform, randint

rgen = randint(0, 10)
rgen.rvs(10)

uniform, randint 클래스는 주어진 범위에서 균등하게 값을 뽑는다(샘플링한다). randint() 는 정숫값을 뽑고, uniform() 은 실숫값을 뽑는다.

np.unique(rgen.rvs(1000), return_counts=True)

ugen = uniform(0, 1)
ugen.rvs(10)

난수 발생기와 유사한 원리로 이루어진다. 샘플링 횟수는 최대한 크게 하는 것이 좋다. 왜냐하면 샘플링 횟수가 많을수록 최적의 매개변수를 찾을 확률도 높아지기 때문이다.

 

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),}

from sklearn.model_selection import RandomizedSearchCV
gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params, n_iter=100, n_jobs=-1, random_state=42)
gs.fit(train_input, train_target)

print(gs.best_params_)

0.00001에서 0.001 사이의 실숫값을 샘플링하여 매개변수에 지정한다. 매개변수를 다시 100번 샘플링하여 교차 검증을 수행한 후 최적의 매개변수 조합을 찾아 출력하면 위와 같은 결과가 나온다.

테스트 세트의 성능을 확인하면 검증 세트에 대한 점수보다 조금 작은 것이 일반적이다. 앞으로 수동으로 매개변수를 바꾸는 대신에, 훨씬 쉽게 파라미터를 조절할 수 있게 되었다.


05-3 트리의 앙상블

<정형 데이터와 비정형 데이터>

데이터를 나눌 때 보통 정형 데이터와 비정형 데이터로 많이 나누는데, 정형 데이터(structured data)는 csv파일처럼 excel, db 등 정형화 플랫폼에 저장할 수 있는 데이터를 말한다. 행과 열이 잘 구분되는 나열된 데이터들이다. 보통 대부분의 데이터가 정형 데이터다. 하지만 정형화되지 않은 데이터도 존재하는데, 데이터베이스나 엑셀로 표현하기 어려운 텍스트 데이터, 이미지, 음악 등의 데이터들을 비정형 데이터(unstructured data)라고 부른다.

정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘이 앙상블 학습이다. 앙상블 알고리즘은 대부분 결정 트리를 기반으로 만들어졌다. 비정형 데이터를 다루기 위해서는 신경망 알고리즘을 많이 사용한다.

 

<랜덤 포레스트>

랜덤 포레스트(Random Forest)는 앙상블 학습의 대표 주자로 안정적인 성능 덕분에 널리 사용되고 있다. 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만들고 각 트리의 예측을 사용해 최종 예측을 만드는 것이다.

 

예를 들어 1,000개의 가방에서 100개씩 샘플을 뽑는다면 먼저 1개를 뽑고, 뽑았던 1개를 다시 가방에 넣는다. 이런 과정으로 샘플링을 진행한다면 중복된 샘플을 뽑을 수 있다. 이렇게 만들어진 샘플을 부트스트랩 샘플(bootstrap sample)이라고 부른다. 보통 중복을 허용하여 데이터를 샘플링하는 방식을 의미한다.

사이킷런의 랜덤포레스트는 기본적으로 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_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(n_jobs=-1, random_state=42)
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']))

cross_validate() 함수를 사용해 교차 검증을 하고 모든 CPU 코어를 사용한 다음 출력 결과를 보면 다소 훈련 세트에 과대적합된 것을 알 수 있다. 랜덤 포레스트는 결정트리의 앙상블이기 때문에 결정트리 클래스가 제공하는 모든 매개변수를 제공한다.

rf.fit(train_input, train_target)
print(rf.feature_importances_)

결정트리의 큰 장점 중 하나인 특성 중요도를 출력해보면, 앞서 출력한 특성 중요도와 다른 것을 알 수 있다. 그 이유는 램던 포레스트가 특성을 랜덤하게 선택하여 훈련하기 때문이다. 고로 과대적합을 줄이고 일반화 성능을 높이는 데 도움이 된다.

rf =  RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)
rf.fit(train_input, train_target)
print(rf.oob_score)

랜덤 포레스트에서 부트스트랩 샘플에 포함되지 않고 남았던 샘플을 OOB(out of bag) 샘플이라고 하는데, 이 샘플을 사용하여 결정트리를 평가할 수 있다. 마치 검증세트처럼 oob_score 매개변수를 True로 지정하여 점수를 출력해볼 수 있다.


● 마무리

트리 알고리즘의 대표인 결정 트리를 기반으로 한 랜덤 포레스트, 교차 검증과 그리드 설치, 앙상블까지 살펴볼 수 있었다. 사이킷런에서 제공하는 라이브러리를 통해 모델을 쉽게 사용하고 매개변수를 다루는 법부터 다양한 기본 모델을 배우는 시간이었다.

코랩에서 실행한 파일을 공유드리니, 필요하신 분은 다운받아 사용하시면 됩니다.

Chapter05_Tree_algorithm.ipynb
0.41MB

+ Recent posts