소소하지만 소소하지 않은 개발 공부/머신 러닝 교과서

Chapter 3.6 결정 트리 학습, 머신러닝교과서, pyhon

still..epochs 2022. 11. 29. 16:30

* 본 포스팅은 머신러닝교과서를 참조하여 작성되었습니다

3.6 결정 트리 학습

결정 트리(decision tree) 분류기는 설명이 중요할 때 아주 유용한 모델이다. 결정 알고리즘을 사용하면 트리의 루트(root)에서 시작해서 정보 이득(Information Gain)이 최대가 되는 특성으로 데이터를 나눈다. 반복 과정을 통해 리프 노드(leaf node)가 순수해질 때까지 모든 자식 노드에서 이 분할 작업을 반복한다. 트리의 최대 깊이를 제한하여 가지치기(pruning)를 한다.

 

3.6.1 정보 이득 최대화: 자원을 최대로 활용

import matplotlib.pyplot as plt
import numpy as np


def gini(p):
    return p * (1 - p) + (1 - p) * (1 - (1 - p))


def entropy(p):
    return - p * np.log2(p) - (1 - p) * np.log2((1 - p))


def error(p):
    return 1 - np.max([p, 1 - p])

x = np.arange(0.0, 1.0, 0.01)

ent = [entropy(p) if p != 0 else None for p in x]
sc_ent = [e * 0.5 if e else None for e in ent]
err = [error(i) for i in x]

fig = plt.figure()
ax = plt.subplot(111)
for i, lab, ls, c, in zip([ent, sc_ent, gini(x), err], 
                          ['Entropy', 'Entropy (scaled)', 
                           'Gini impurity', 'Misclassification error'],
                          ['-', '-', '--', '-.'],
                          ['black', 'lightgray', 'red', 'green', 'cyan']):
    line = ax.plot(x, i, label=lab, linestyle=ls, lw=2, color=c)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15),
          ncol=5, fancybox=True, shadow=False)

ax.axhline(y=0.5, linewidth=1, color='k', linestyle='--')
ax.axhline(y=1.0, linewidth=1, color='k', linestyle='--')
plt.ylim([0, 1.1])
plt.xlabel('p(i=1)')
plt.ylabel('impurity index')
# plt.savefig('images/03_19.png', dpi=300, bbox_inches='tight')
plt.show()

 

불순도 지표 비교

 

 

3.6.2 결정 트리 만들기

결정 트리는 특성 공간을 사각 격자로 나누기 때문에 복잡한 결정 경계를 만들 수 있다. 결정 트리가 깊어질수록 결정 경계가 복잡해지고 과대적합되기 쉽기 때문에 주의해야 한다.

 

사이킷런을 사용하여 지니 불순도 조건으로 최대 깊이가 4인 결정 트리를 학습해 보자.

from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='gini', max_depth=4, random_state=1)

tree.fit(X_train, y_train)
X_combined = np.vstack((X_train, X_test))
y_combined = np.hstack((y_train, y_test))
plot_decision_regions(X_combined, y_combined, classifier=tree, test_idx=range(105, 150))
plt.xlabel('petal length [cm]')
plt.ylabel('petal width [cm]')
plt.legend(loc = 'upper left')
plt.tight_layout()
plt.show()

결정 트리가 학습한 붓꽃 데이터 셋의 결정 경계
plot_tree() 함수로 그린 결정 트리

 

GraphViz 프로그램을 사용하여 더 나은 그림을 얻어보자!

plt.figure(figsize=(10,10))
tree.plot_tree(tree_model,
               impurity=True,
               filled=True, 
               rounded=True,
               class_names=['Setosa', 
                            'Versicolor',
                            'Virginica'],
               feature_names=['petal length', 
                              'petal width'])
plt.show()

- out_file = None 으로 설정하면 tree.dot 중간 파일을 디스크에 만들지 않고 dot 데이터를 바로 dot_data 변수에 할당한다. filled, rounded, class_names, feature_names 매개변수는 각각 색 추가, 상자 모서리의 라운드 처리, 각 노드에 다수 클래스 레이블 이름 표시, 분할 기준에 특성 이름 표시에 관한 옵션이다.

 

 

3.6.3 랜덤 포레스트로 여러 개의 결정 트리 연결

앙상블 발법은 뛰어난 분류 성능과 과대적합에 안정적이기 때문에 지난 10년간 머신 러닝 애플리케이션에서 큰 인기를 누렸다. 랜덤 포레스트는 결정 트리의 앙상블(ensemble)로 생각할 수 있다. 

 

랜덤 포레스트 이면의 아이디어는 여러 개의 (깊은) 결정 트리를 평균 내는 것이다. 개개의 트리는 분산이 높은 문제가 있지만 앙상블은 견고한 모델을 만들어 일반화 성능을 높이고 과대적합의 위험을 줄인다. 랜덤 포레스트 알고리즘은 다음 네 단계로 요약할 수 있다.

  1. n개의 랜덤한 부트스트랩(bootstrap) 샘플을 뽑는다.(훈련 데이터셋에서 중복을 허용하면서 랜덤하게 n개의 샘플을 선택한다.
  2. 부트스트랩 샘플에서 결정 트리를 학습한다. 각 노드에서 다음과 같이 수행한다.
    1. 중복을 허용하지 않고 랜덤하게 d개의 특성을 선택한다.
    2. 정보 이득과 같은 목적 함수를 기준으로 최선의 분할을 만드는 특성을 사용하여 노드를 분할한다.
  3. 단계 1~2를 k번 반복한다.
  4. 각 트리의 예측을 모아 다수결 투표(majority voting)로 클래스 레이블을 할당한다.

 

랜덤 포레스트는 결정 트리만큼 해석이 쉽지는 않지만 하이퍼파라미터 튜닝에 많은 노력을 기울이지 않아도 되는 것이 큰 장점이다. 일반적으로 랜덤 포레스트는 가지치기할 필요가 없다. 앙상블 모델이 개별 결정 트리가 만드는 잡음으로부터 매우 안정되어 있기 때문이다.

 

실전에서 자주 사용되지는 않지만 랜덤 포레스트 분류기에서 최적화할 만한 다른 하이퍼파라미터는 부트스트랩 샘플의 크기 n(단계 1)과 각 분할에서 무작위로 선택할 특성 개수 d(단계 2-1)이다. 부트스트랩 샘플의 크기 n을 사용하면 랜덤 포레스트의 편향-분산 트레이드오프를 조절할 수 있다.

 

사이킷런의 RandomForestClassifier를 포함하여 대부분의 라이브러리에서는 부트스트랩 샘플 크기를 원본 훈련 데이터셋의 샘플 개수와 동일하게 한다. 보통 이렇게 하면 균형 잡힌 편향-분산 트레이드오프를 얻는다.

from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(criterion='gini',
                               n_estimators=25,
                               random_state = 1,
                               n_jobs = 2)
forest.fit(X_train, y_train)
plot_decision_regions(X_combined, y_combined,
                     classifier=forest, test_idx=range(105,150))
plt.xlabel('petal length [cm]')
plt.ylabel('petal width [cm]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

랜덤 포레스트가 학습한 붓꽃 데이터셋의 결정 경계