본문 바로가기

파이썬/머신러닝

결정트리(Decision Tree) , 엔트로피 개념과 시각화까지

 

Decision Tree 모델은 가장 많이 쓰이는 지도학습 ML 모델 중 하나이다. 추후 학습할 앙상블 학습의 배깅 방법인 Random Forest는 Decision Tree 여러개를 모아 모델을 학습시키는 방식이다.

 

 

그렇기에 그 기초가 되는 결정트리 모델은 꼭꼭 잘 알고 있어야 한다는거! 결정트리는 분류와 회귀 모두 가능한 지도 학습 모델이다.

 

 

Decision Tree는 마치 SQL의 Case when문과 비슷하다.

다만, case when문은 사람이 직접 조건과 결과값을 정해주는거라면, decision tree는 X 변수를 넣어주면 모델이 알아서 조건과 결과값을 학습한다는 점에서 차이가 있다.

 

 

 

decision tree 모델은 마치 스무고개를 하듯 질문에 대한 Y/N에 따라 가지를 뻗쳐나간다.

한번 분기(가지가 뻗어나감)할 때마다 변수가 2개로 나뉜다.

 

출처: laptrinhx.com

각각을 Node(노드)라고 하는데 가장 첫번째 분류 기준을 Root Node라고 하고 그 다음부터 나오는 분류 기준은 Decision Node, 그에 따른 최종 결과값은 Leaf Node라고 한다.

 

 

출처: scikit-learn

 

만약 분기가 여러번 되면 어떨까? 아마 위와 같이 아주아주 복잡한 트리가 생성될 것이다.

이렇게 max_depth가 최대로 깊어진 모델은 train data에는 아주 잘 맞지만 test data에는 잘 맞지 않는 오버피팅 현상이 발생할 확률이 높다. 그렇기 때문에 decision tree 모델을 학습시킬 때는 적절한 max_depth를 설정해주는 것이 중요하다.

 

 

max_depth가 너무 작으면 과소적합 문제가, 너무 크면 과대적합 문제가 발생한다.

 

 

장점

 

1. 전처리 과정이 복잡하지 않음
(다른 모델은 정규화, null값 처리, dummy variables 생성 등 전처리 과정 필요)
2. multi-output을 갖는 데이터를 핸들링하기에 용이
3. 결과 해석이 용이함

 


단점

 

1. 과적합(오버피팅) 발생하기 쉬움
2. 아주 작은 variation으로도 불안정해질 수 있음
3. 특정 class가 dominant하다면 편향 문제 발생

4. 변수들이 independent하지 않은 경우 multi-output 문제 발생

 

보통 오버피팅과 편향 문제 때문에 Decision Tree를 단독으로 사용하기 보다는 앙상블 러닝의 Random Forest를 더 자주 사용한다.

 

 

 

엔트로피와 불순도

 

불순도(Impurity)는 해당 범주 안에 서로 다른 데이터가 얼마나 섞여 있는지를 나타내는 지표이다.

당연히 모델은 불순도를 최소화하는 방법으로 학습되어야 한다.

 

 

그리고 엔트로피(Entropy)는 불순도를 수치로 나타낸 것으로 엔트로피가 높으면 불순도가 높은 것이다.

 

 

위와 같은 Decision Tree에서 첫번째, 네번째 Leaf Node는 불순도가 0이고 두번째, 세번째 Leaf Node는 불순도가 높다고 할 수 있다.

 

 

사이킷런의 load_breast_cancer 데이터로 간단한 실습 코드를 짜보자.

 

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()
cancer.keys()
>>>dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size = 0.2, random_state=42)

#criterion 미지정시 기본값은 gini
tree = DecisionTreeClassifier(criterion='entropy',  max_depth=4, random_state=0)

tree.fit(X_train, y_train)
y_pred = tree.predict(X_test)

print('훈련세트 정확도: {:.3f}' .format(tree.score(X_train, y_train)))
print('테스트세트 정확도: {:.3f}' .format(tree.score(X_test, y_test)))

>>>훈련세트 정확도: 0.991
>>>테스트세트 정확도: 0.956
#confusion_matrix, classification_report로 성능 확인

from sklearn.metrics import confusion_matrix, classification_report

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

>>>[[39  4]
   [ 1 70]]
>>>  precision    recall  f1-score   support

           0       0.97      0.91      0.94        43
           1       0.95      0.99      0.97        71

    accuracy                           0.96       114
   macro avg       0.96      0.95      0.95       114
weighted avg       0.96      0.96      0.96       114

 

 

tree를 시각화하려면 아래와 같이 코드를 짜면 된다.

from sklearn.tree import export_graphviz

# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성
export_graphviz(tree, out_file="tree.dot", class_names=cancer.target_names , \
feature_names = cancer.feature_names, impurity=True, filled=True)

import graphviz

# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화 
with open("tree.dot") as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)

 

 

결과값을 보면 몇몇 leaf node들의 entropy값이 높은 것을 확인할 수 있다. 엔트로피는 낮을수록 좋으니 이 모델의 성능이 좋다고 하기는 어렵다.

 

 

다음번 포스팅에서는 타이타닉 데이터와 heart attack 데이터를 갖고 보다 더 정교한 tree 모델을 만들어보도록 하겠다.