결정트리(Decision Tree) 모델
결정트리(decision tree) 모델은 분류 및 회귀에 사용되는 비모수적 지도학습방법으로 데이터의 특징들에서 추론된 규칙들에 의해 분류하여 예측값을 반환합니다. klearn.tree.DecisionTreeClassifier()
클래스를 사용하여 모델을 생성합니다.
클래스이 인수는 다음과 같습니다.
- max_depth: 분류 단계를 지정, 기본값은 min_sample_split에 지정된 수 또는 분류된 객체의 entropy가 최소가 될때까지 분류가 진행
- min_sample_split: 분류된 그룹의 데이터수로 기본값은 2, 이에 적합된 노드에서 학습은 종료됩니다.
- max_feature: 최적의 분류를 찾을 때 고려되는 특징의 수, 기본값은 none
- random_state: random_seed의 역할
다음은 make_classification() 함수를 적용하여 생성한 가공 데이터 셋에 대해 생성한 결정트리모델입니다.
import numpy as np import pandas as pd from sklearn.datasets import make_classification from sklearn.preprocessing import StandardScaler from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn import metrics import matplotlib.pyplot as plt import seaborn as sns
X1, y =make_classification(n_samples=100, random_state=41) X=X1[:, :2] X.shape, y.shape
((100, 2), (100,))
clf=DecisionTreeClassifier().fit(X, y) pred=clf.predict(X) acc=metrics.accuracy_score(y, pred) acc
1.0
결정트리의 각 단계를 노드(node)라고 하며 최상위의 노드를 뿌리 노드(root node), 다음의 각 단계를 노드(node), 마지막의 최종 결과를 터미널 노드 또는 잎 노드(terminal or leaf node)라고 합니다.
다음은 이 모델에 의한 분류 과정을 tree.plot_tree()
함수를 사용하여 시각화 한 것입니다. 함수의 인수인 max_depth에 정수를 전달하여 분류 단계를 분리하여 나타낼 수 있습니다. 다음 그림은 분류 2단계까지 나타낸 것이로 분류 기준과 대응하는 결과를 나타내고 있습니다.
from sklearn import tree
fig=plt.figure(figsize=(10, 8)) _=tree.plot_tree(clf, class_names=["0","1"], max_depth=2, filled=True, fontsize=9) plt.show()
위 그림의 각 노드는 분류기준과 그에 따른 결과를 나타냅니다. 결정트리와 다중 클래스의 분류 모델인 RandomForest는 분류기준으로 데이터의 엔트로피(entropy or impurity)로 부터 계산되는 정보획득 또는 gini index를 사용합니다. 위 그림에서는 gini index를 사용하는 것으로 식 1과 같이 계산됩니다.
\begin{align} \text{gini index}&=1-\sum^n_{i=0}p(x=i)^2\\\tag{식 1}n:&\,\text{클래스의 수}\\p(x=i):&\,\text{class i의 확률 }\end{align}
다음은 1단계에서 True로 분류된 노드의 라벨의 내용입니다. 모두가 1이므로 p(x=1)=1, p(x=0)=0이므로 gini index는 0이 됩니다.
t1=np.where(X[:,0]<=-1.471)[0] y[t1]
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
gini_t1=1-1**2; gini_t1
0
다음은 1단계에서 False로 분류된 노드의 라벨 내용입니다.
f1=np.where(X[:,0]>-1.471)[0] y[f1]
array([1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0])
np.unique(y[f1], return_counts=True)
(array([0, 1]), array([50, 39], dtype=int64))
T=len(f1) gini=1-(50/T)**2-(39/T)**2 gini
0.4923620754955182
결정트리는 모델에 대한 이해가 용이하며 시각화 할 수 있습니다. 또한 정규화, 누락값 제거등의 데이터의 전처리가 필요하지 않으며 다중 출력을 위한 모델을 생성할 수 있습니다. 그러나 지나치게 복잡한 트리를 형성하면 과적합의 문제를 유발시킬 수 있으며 데이터의 작은변화로 다른 트리가 생성되는 불안정성을 내재하고 있습니다. 즉, 이 모델을 일반화하는 것은 쉽지 않습니다.
다음은 데이터 pima-indians-diabetes에 대해 결정트리 모델을 생성합니다. 다운받은 데이터는 .csv 형식으로 pd.read_csv(경로, …)
함수로 호출합니다.
data=pd.read_csv("pima-indians-diabetes.csv") data.head(3)
preg | plas | pres | skin | test | mass | pedi | age | class | |
---|---|---|---|---|---|---|---|---|---|
0 | 6 | 148 | 72 | 35 | 0 | 33.6 | 0.627 | 50 | 1 |
1 | 1 | 85 | 66 | 29 | 0 | 26.6 | 0.351 | 31 | 0 |
2 | 8 | 183 | 64 | 0 | 0 | 23.3 | 0.672 | 32 | 1 |
위 데이터의 변수 class를 라밸로 설정하며 훈련세트(train)와 검정세트(test)로 분리하여 훈련세트로 학습합니다.
ind=data.drop(columns="class") de=data.iloc[:,-1] de[:3]
0 1 1 0 2 1 Name: class, dtype: int64
Xtr, Xte, ytr, yte=train_test_split(ind, de, test_size=0.1, random_state=1) Xtr.shape, Xte.shape
((691, 8), (77, 8))
훈련세트에 대해 모델을 생성하고 훈련과 검증 세트에 대한 정확도를 측정합니다. 정확도는 model의 메소드.score(X, y)
또는 metrics.accuracy_score(y, y_pre)
함수로 즉정할 수 있습니다.
sklearn.tree.DecisionTreeClassifier()
클래스로 모델을 생성합니다. 이 클래스 인수 max_depth에 의해 분류의 단계를 설정할 수 있으며 기본값은 분류된 객체들의 엔트로피(불순도)가 순수해 질 때까지 또는 min_sample_split에 지정된 수에 도달 할 때까지 이루어 집니다. 다음 결과는 max_depth를 지정하지 않은 경우로 훈련세트의 정확도는 1이지만 검정 세트에 대해서 67% 정도로 큰 차이를 보입니다. 즉, 이 모델의 일반화는 어렵다고 할 수 있습니다.
clfS=DecisionTreeClassifier(random_state=1).fit(Xtr, ytr) clfS.score(Xtr, ytr)
1.0
clfS.score(Xte, yte)
0.6753246753246753
모델의 max_depth의 변화에 따른 훈련과 검정 세트에 대한 정확도의 변화를 시각해 봅니다.
rng=np.arange(4, 19, 2) trScore={} teScore={} for i in rng: m=DecisionTreeClassifier(max_depth=i, random_state=1).fit(Xtr, ytr) trScore[i]=m.score(Xtr, ytr) teScore[i]=m.score(Xte, yte)
plt.figure(figsize=(4,3)) plt.plot(trScore.keys(), trScore.values(), color="b", label="Train set") plt.plot(teScore.keys(), teScore.values(), color="r", label="Test set") plt.xlabel("max_depth") plt.ylabel("accuacy") plt.legend(loc="best") plt.show()
위 결과는 이 모델에 의한 일반화가 쉽지 않음을 나타냅니다. 다음은 max_depth=14에서 이진분류 모델의 평가에 적용되는 여러 결과를 나타내봅니다(이진분류 추정기의 평가 참조).
model=DecisionTreeClassifier(max_depth=14, random_state=1).fit(Xtr, ytr)
def clf_eval(y, y_pre): confMat=confusion_matrix(y, y_pre) acc=accuracy_score(y, y_pre) preci=precision_score(y, y_pre) recall=recall_score(y, y_pre) f1=f1_score(y, y_pre) re=pd.Series([acc, preci, recall, f1], index=["accuracy","precision","recall", "F1 score"]) return(confMat, re)
predTr=model.predict(Xtr) tr_eval=clf_eval(ytr, predTr) tr_eval
(array([[452, 0], [ 1, 238]], dtype=int64), accuracy 0.998553 precision 1.000000 recall 0.995816 F1 score 0.997904 dtype: float64)
댓글
댓글 쓰기