기본 콘텐츠로 건너뛰기

[ML] 결정트리(Decision Tree) 모델

[ML] 결정트리(Decision Tree) 모델

결정트리(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)

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b