본문 바로가기
ADP로ML정리

5. 의사결정나무 Decision Tree

by hits_gold 2024. 2. 20.
반응형

의사결정나무란

 결정 트리란 데이터에 있는 규칙을 학습을 통해 자동으로 찾아내 Tree기반의 "분류 규칙"을 만들고, 이 규칙을 통해 최대한 균일도가 높은(과적합 고려) 결정 클래스를 만드는 분류기법이다.

 하나의 규칙을 만들 때마다 특정 기준에 따라 "예/아니오"로 답할 수 있는 질문을 이어나가면서 학습한다. 이 때 규칙을 만들 떄마다 노드를 만들고, 가지를 치면서 내려간다. 이 결과는 규칙이 명확해 해석을 쉽게 할 수 있다.

 또한 선형성과 정규성 등의 가정이 필요하지 않아 전처리 과정에 모델의 성능이 큰 영향을 받지 않는다. 하지만 과적합의 가능성이 있어 일반화 성능이 우수하지 않다.

 

구조

  • 규칙 노드 (Decision Node) : 규칙 조건을 표시한 노드
  • 리프 노드 (Leaf Node) : 결정된 클래스 값을 표시한 노드

규칙

1. 분류분석 (종속변수 : 이산형)

  • 카이제곱 통계량 p값 : p값이 가장 작은 예측변수와 그 때의 최적분리에 의해서 자식마디를 형성
  • 지니지수 : 지니지수를 감소시키는 예측변수와 그 때의 최적분리에 의해 자식마디를 형성

위 예시에서 분기 후 지니지수의 감소량(정보 획득량)은 0.47 - 0.34 = 0.13

P는 k범주에 속하는 클래스의 비율, m는 범주의 개수

 

  • 엔트로피지수 : 엔트로피지수가 가장 작은 예측변수와 이 떄의 최적분리에 의해 자식마디를 형성

 

위 예시에서 분기 후 엔트로피지수의 감소량(정보 획득량)은 0.95 - 0.75 = 0.20

 

2. 회귀분석 (종속변수 : 연속형)

  • 분산분석에서 F통계량 : p값이 가장 작은 예측변수와 그 때의 최적분리에 의해서 자식마디를 형성
  • 분산의 감소량 : 분산의 감소량을 최대화하는 기준의 최적분리에 의해서 자식마디를 형성

 

복잡도 제어

 학습 시 트리의 크기를 의사결정나무 모델의 복잡도로 볼 수 있다. 모든 데이터가 각각 분할되도록 가지를 치도록 하는 모델이라면 학습 데이터에 대해 높은 예측력을 가지겠지만 과적합 문제가 발생할 수 있다. 이러한 문제를 방지하기 위해 가지치기를 이용해 트리의 성장을 제한하는데, sklearn에서 지원하는 방식은 사전가지치기(Pre-pruning)이다. 하이퍼파라미터를 조정해 트리의 최대 깊이나 리프의 최대 개수를 제한하거나, 노드가 분할하기 위한 포인트의 최소 개수를 정한다.

 

from sklearn.datasets import load_breast_cancer
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

cancer = load_breast_cancer()
x = pd.DataFrame(cancer.data, columns = cancer.feature_names)
y = cancer.target

X_train, X_test, y_train, y_test = train_test_split(x, y, stratify=y, train_size = 0.7, random_state = 42)
print(X_train.shape, X_test.shape)


clf = DecisionTreeClassifier(max_depth = 5)
clf.fit(X_train, y_train)

from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score
from sklearn.metrics import precision_score, recall_score, f1_score, plot_roc_curve

pred = clf.predict(X_test)

test_cm = confusion_matrix(y_test, pred)
test_acc = accuracy_score(y_test, pred)
test_pre = precision_score(y_test, pred)
test_re = recall_score(y_test, pred)
test_f1 = f1_score(y_test, pred)

print(test_cm)
print('\n')
print(f'정확도 : {round(test_acc*100, 2)}%')
print(f'정밀도 : {round(test_pre*100, 2)}%')
print(f'재현율 : {round(test_re*100, 2)}%')
print(f'F1 score : {round(test_f1*100, 2)}%')

from sklearn.metrics import classification_report
import matplotlib.pyplot as plt

report = classification_report(y_test, pred)
print(report)


print('ROC_AUC_SCORE : ', ra_score)
plot_roc_curve(clf, X_test, y_test)
plt.show()
ra_score = roc_auc_score(y_test, clf.predict_proba(X_test)[:, 1])

# 변수 중요도 확인

importances = clf.feature_importances_
cols = pd.DataFrame(X_train.columns)
feature_importances = pd.concat([cols, pd.DataFrame(importances)], axis = 1)
feature_importances.columns = ['feature_nm', 'importances']
print(feature_importances.sort_values('importances', ascending=False))

import numpy as np
feature_names = X_train.columns
target_names = np.array(['0', '1'])

import pydot
import pydotplus
import graphviz
from sklearn.tree import export_graphviz
dt_dot_data = export_graphviz(clf, feature_names = feature_names,
                             class_names = target_names,
                             filled=True, rounded=True,
                             special_characters = True)
dt_graph = pydotplus.graph_from_dot_data(dt_dot_data)

from IPython.display import Image
Image(dt_graph.create_png())

반응형