의사 결정 나무 예제 | [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기 12 개의 정답

당신은 주제를 찾고 있습니까 “의사 결정 나무 예제 – [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기“? 다음 카테고리의 웹사이트 https://you.charoenmotorcycles.com 에서 귀하의 모든 질문에 답변해 드립니다: https://you.charoenmotorcycles.com/blog. 바로 아래에서 답을 찾을 수 있습니다. 작성자 Minsuk Heo 허민석 이(가) 작성한 기사에는 조회수 36,835회 및 좋아요 396개 개의 좋아요가 있습니다.

Table of Contents

의사 결정 나무 예제 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기 – 의사 결정 나무 예제 주제에 대한 세부정보를 참조하세요

의사결정트리 (Decision Tree) 알고리즘을 중학생도 이해할 수 있는 쉬운 예제와 함께 설명해드립니다. 쉬운 예제 설명 후 주요 용어도 함께 설명해드립니다.
제가 만든 모든 머신러닝 관련 영상은 아래 재생목록에서 쉽게 찾으실 수 있습니다.
https://www.youtube.com/playlist?list=PLVNY1HnUlO241gILgQloWAs0xrrkqQfKe

의사 결정 나무 예제 주제에 대한 자세한 내용은 여기를 참조하세요.

8.1 의사결정나무 – 데이터 사이언스 스쿨

의사결정나무(decision tree)**는 여러 가지 규칙을 순차적으로 적용하면서 독립 … 이 예제에서는 독립변수 공간을 공간상에 표시하기 위해 꽃의 길이와 폭만을 독립 …

+ 자세한 내용은 여기를 클릭하십시오

Source: datascienceschool.net

Date Published: 6/9/2021

View: 4885

7.15.3 R에서 의사결정나무(Decision Tree) Example (2)

의사결정나무분석은 시장조사, 광고조사, 의학연구, 품질관리 등의 다양한 분야에서 활용되고, 구체적인 활용 예는 고갯 타켓팅, 고객들의 신용점수화, …

+ 여기를 클릭

Source: m.blog.naver.com

Date Published: 7/25/2021

View: 3391

ADP) 1-1-1. 의사결정나무 예제 (+파이썬으로 분류트리, 회귀 …

의사결정나무 예제 (+파이썬으로 분류트리, 회귀트리 구현하기). 성실한 나무 2021. 12. 1. 14:18. 의사결정나무(=결정트리, Decision Tree)란?

+ 여기에 더 보기

Source: lovelydiary.tistory.com

Date Published: 8/10/2022

View: 1457

4장 의사결정나무

다음의 [예제 1]은 R 패키지 {rpart}의 rpart() 함수를 이용하여 의사결정나무 분석을 수행한다. rpart는 recursive partitioning and regression tree의 약어이다.

+ 더 읽기

Source: contents2.kocw.or.kr

Date Published: 4/13/2022

View: 1898

9. 의사결정나무(Decision Tree) 에 대해서 알아보자 with Python

여기서 다루는 내용은 다음과 같다. 1. 의사결정나무란? 2. 의사결정나무 모형 만들기. 3. 의사결정나무 구현하기. 4. 예제 with Python …

+ 여기에 표시

Source: zephyrus1111.tistory.com

Date Published: 11/2/2021

View: 321

의사 결정 나무 예시 [feat.무료 템플릿 다운로드] – EdrawSoft

템플릿은 영어로 됐지만 EdrawMax 프로그램으로 열면, 텍스트를 한국어로 변경 가능합니다. 2. 의사결정 나무 템플릿 X 9개. Decision Tree Example, Decision Tree …

+ 여기에 보기

Source: www.edrawsoft.com

Date Published: 6/15/2022

View: 2947

머신러닝 – 4. 결정 트리(Decision Tree) – 귀퉁이 서재

결정 트리(Decision Tree, 의사결정트리, 의사결정나무라고도 함)는 … 공식을 활용하여 엔트로피를 구하는 예제를 살펴보겠습니다.

+ 더 읽기

Source: bkshin.tistory.com

Date Published: 4/18/2021

View: 5674

[Python] 의사결정나무(DecisionTree) 구현 – SH의 학습노트

iris 데이터는 이전 게시글의 예제에서 계속적으로 다루어왔다. 4개의 feature 변수가 있으며, 3개의 target 변수가 있다. 자세하게 알아보는 과정은 …

+ 여기에 표시

Source: todayisbetterthanyesterday.tistory.com

Date Published: 4/8/2021

View: 9992

주제와 관련된 이미지 의사 결정 나무 예제

주제와 관련된 더 많은 사진을 참조하십시오 [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

[머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기
[머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기

주제에 대한 기사 평가 의사 결정 나무 예제

  • Author: Minsuk Heo 허민석
  • Views: 조회수 36,835회
  • Likes: 좋아요 396개
  • Date Published: 2016. 11. 27.
  • Video Url link: https://www.youtube.com/watch?v=n0p0120Gxqk

8.1 의사결정나무 — 데이터 사이언스 스쿨

import io import pydot from IPython.core.display import Image from sklearn.tree import export_graphviz def draw_decision_tree ( model ): dot_buf = io . StringIO () export_graphviz ( model , out_file = dot_buf , feature_names = feature_names ) graph = pydot . graph_from_dot_data ( dot_buf . getvalue ())[ 0 ] image = graph . create_png () return Image ( image ) def plot_decision_regions ( X , y , model , title ): resolution = 0.01 markers = ( ‘s’ , ‘^’ , ‘o’ ) colors = ( ‘red’ , ‘blue’ , ‘lightgreen’ ) cmap = mpl . colors . ListedColormap ( colors ) x1_min , x1_max = X [:, 0 ] . min () – 1 , X [:, 0 ] . max () + 1 x2_min , x2_max = X [:, 1 ] . min () – 1 , X [:, 1 ] . max () + 1 xx1 , xx2 = np . meshgrid ( np . arange ( x1_min , x1_max , resolution ), np . arange ( x2_min , x2_max , resolution )) Z = model . predict ( np . array ([ xx1 . ravel (), xx2 . ravel ()]) . T ) . reshape ( xx1 . shape ) plt . contour ( xx1 , xx2 , Z , cmap = mpl . colors . ListedColormap ([ ‘k’ ])) plt . contourf ( xx1 , xx2 , Z , alpha = 0.4 , cmap = cmap ) plt . xlim ( xx1 . min (), xx1 . max ()) plt . ylim ( xx2 . min (), xx2 . max ()) for idx , cl in enumerate ( np . unique ( y )): plt . scatter ( x = X [ y == cl , 0 ], y = X [ y == cl , 1 ], alpha = 0.8 , c = [ cmap ( idx )], marker = markers [ idx ], s = 80 , label = cl ) plt . xlabel ( data . feature_names [ 2 ]) plt . ylabel ( data . feature_names [ 3 ]) plt . legend ( loc = ‘upper left’ ) plt . title ( title ) return Z

7.15.3 R에서 의사결정나무(Decision Tree) Example (2) : 은행 대출 데이터셋

1. 들어가기

의사결정나무(Decision Tree)는 데이터를 나무구조로 도표화하여 분석하는 방법으로 분류와 희귀분석에도 사용할 수 있습니다. 조금 더 구체적으로 말하면, 의사결정 트리는 각각의 내부 노드에 존재하는 개별 속성의 비동질성을 평가하는 이진 트리로서, 각각의 잎 노드(leaf-node)는 의사결정의 경로에 따라 나타나는 결과값 또는 클래스에 대응됩니다. 새로운 입력값을 넣으면, 의사결정 트리의 뿌리(Root)서부터 순회하며 결과값을 예측하게 됩니다.1)

의사결정나무분석은 시장조사, 광고조사, 의학연구, 품질관리 등의 다양한 분야에서 활용되고, 구체적인 활용 예는 고갯 타켓팅, 고객들의 신용점수화, 캠페인 반응분석, 고객행동예측, 고객 세분화 등을 들 수 있습니다.3) 이번 포스팅은 R에서 의사결정나무를 이용하여 은행 대출 데이터셋을 분류예측하는 예제를 살펴보도록 하겠습니다.

2-1. 의사결정나무의 구성요소

의사결정나무는 말그대로 나무모양으로 나타낼 수 있습니다. 자세히 말하면 아래 그림과 같이 나무를 거꾸로 해놓은 모양입니다.

ADP) 1-1-1. 의사결정나무 예제 (+파이썬으로 분류트리, 회귀트리 구현하기)

의사결정나무(=결정트리, Decision Tree)란?

분류와 회귀 문제에 널리 사용한다. 결정에 다다르기 위해 예/아니오 질문을 이어 나가면서 학습한다. 트리의 노드node는 질문이나 정답을 담은 네모 상자이다. 특히 마지막 노드는 리프leaf라고도 한다. if-then 규칙의 가장 마지막 부분, 혹은 트리의 마지막 가지 부분을 의미한다. 트리 모델에서 잎 노드는 어떤 레코드에 적용할 최종적인 분류 규칙을 의미한다. 엣지edge는 질문의 답과 다음 질문을 연결한다. 맨 위의 노드는 루트노드root node이다.

결정 트리를 학습한다는 것은 정답에 가장 빨리 도달하는 예/아니오 질문 목록을 학습한다는 뜻이다. 머신러닝에서는 이런 질문들을 테스트라고 한다. 트리를 만들 때 알고리즘은 가능한 모든 테스트(질문)에서 타깃값에 대해 가장 많은 정보를 가진 것을 고른다. 테스트를 통해 분기가 일어난 결과값을 볼 때 0과 1로 나는 데이터 개수를 확인할 수 있다. 각 테스트는 하나의 특성에 대해서만 이루어지므로 나누어진 영역은 항상 축에 평행한다. 데이터를 분할 하는 것은 각 분할된 영역(리프)이 한 개의 타깃값(하나의 클래스 혹은 하나의 회귀분석 결과)을 가질 때까지 반복된다. 타깃 하나로만 이뤄진 리프 노드를 순수 노드pure node라고 한다.

파이썬의 Scikit learn 패키지를 활용하면 분류트리와 회귀트리를 손쉽게 구현할 수 있다. 아래에서 분류트리와 회귀트리의 함수 사용방법을 정리해보았다.

Scikit Learn으로 분류트리 구현하기 – DecisionTreeClassifier()

타겟값이 클래스를 갖는 범주형 변수일 때 사용하는 결정트리이다. 분류 예제는 다음과 같다.

sklearn.tree로부터 DecisionTreeClassifier, plot_tree, export_text 함수들을 import한다. 그리고 X, y 별로 훈련용 데이터셋과 테스트용 데이터셋을 나눈다.

Scikit learn 결정트리를 위해 불러올 함수들 – DecisionTreeClassifier, plot_tree, export_text

DecisionTreeClassifier()함수로 결정트리를 학습할 방식들을 파라미터로 입력한다. 여기에 들어가는 파라미터들은 아래와 같다. 물론 다 넣을 필요는 없고 학습 결과에 따라서 사용할 파라미터를 선택하고 설정하면 된다.

criterion: 불순도 척도. ‘gini’, ‘entropy’, default=’gini’

splitter: 각 노드의 분할 전략. 최적분할과 최적 랜덤분할. ‘best’, ‘random’, default=’best’

max_depth: 트리 깊이의 최대값. 값을 입력하지 않을 경우, 모든 leaf가 pure해질때까지 혹은, 분기된 노드 속 샘플수가 설정한 최소 샘플수(min_samples_split)보다 적게 될때까지 분기한다. ‘int값 입력’

min_samples_split: 분기 할 node 내 샘플의 최소 개수. 최소 개수보다 node 내 샘플 수가 적으면 leaf가 pure하지 않더라도 분기를 멈춘다. float 입력 시, 전체 샘플 개수 대비 float 비율만큼의 개수로 최소 샘플수가 설정된다. ‘int 혹은 float값 입력’, default=2

min_samples_leaf: leaf 노드에 있어야 하는 최소 샘플 수. 왼쪽 혹은 오른쪽 branch에 각각 min_samples_leaf만큼 훈련 샘플이 있어야 분기가 된다. ‘int 혹은 float값 입력’, default=2

min_weight_fraction_leaf: 모든 샘플의 가중치의 합계의 최소 가중치의 비율, 입력되지 않으면 모든 샘플의 가중치는 동일하다. ‘float 입력’, default=0.0

max_features: 최적의 분할을 찾기 위해 고려하는 feature의 개수. ‘int or flaot값’, ‘auto’, ‘sqrt’, ‘log2’, default=None.

random_state: 추정기의 무작위성 제어. ‘int 입력’, default=None

max_leaf_nodes: 최대 노드 수. default=None

min_impurity_decrease: 최소 불순도 감소값 이하인 경우에는 분기가 일어나지 않는다. ‘float’, default=0.0

class_weight: class별로 가중치를 둔다. ‘dict’, ‘list of dict’, ‘balanced’, default=None, dict의 예시 – {class_label: weight} or [{0: 1, 1: 1}, {0: 1, 1: 5}, {0: 1, 1: 1}, {0: 1, 1: 1}] instead of [{1:1}, {2:5}, {3:1}, {4:1}].

ccp_alpha: 최소 비용-복잡성 가지치기에 사용되는 복잡도 매개변수. ccp_alpha보다 작은 비용-복잡도를 가진 하위 트리 중에 비용-복잡도가 가장 큰 하위 트리가 선택된다.

plot_tree에 결정 트리 객체를 입력하면 아래와 같이 plotting이 가능하다. 텍스트로 확인하고 싶으면 export_text를 하면 된다.

파이썬 결정 트리, 분류트리 그리기

Scikit Learn으로 회귀트리 구현하기 -DecisionTreeRegressor()

타겟값이 연속형 변수일 때 사용하는 결정트리이다. 회귀 예제는 다음과 같다. sklearn.tree로부터 DecisionTreeRegressor, plot_tree, export_text 함수들을 import한다. 그리고 X, y 별로 훈련용 데이터셋과 테스트용 데이터셋을 나눈다. 파이썬 결정트리, 회귀트리 그리기

DecisionTreeRegressor()함수로 결정트리를 학습할 방식들을 파라미터로 입력한다. 여기에 들어가는 파라미터들은 아래와 같다. 물론 다 넣을 필요는 없고 학습 결과에 따라서 사용할 파라미터를 선택하고 설정하면 된다.

criterion: 불순도 척도. ‘squared_error’, ‘friedman_mse’, ‘absolute_error’, ‘poisson’, default=’squared_error’ ‘squared_error’: 평균 제곱 오차(MSE), 변수선택 기준으로서의 분산 감소와 동일하다. 각 끝 노드의 평균을 사용한 L2 loss를 최소화한다. ‘friedman_mse’ : 잠재적 분할을 위한 Friedman의 improvement score를 포함하는 MSE. ‘absolute_error’: 오차의 절대값의 평균, 각 끝 노드의 중앙값을 사용한 L1 loss를 최소화 한다. ‘poisson’: 분기를 찾기 위해 포아송 편차 감소를 사용한다.

splitter: 각 노드의 분할 전략. 최적분할과 최적 랜덤분할. ‘best’, ‘random’, default=’best’

max_depth: 트리 깊이의 최대값. 값을 입력하지 않을 경우, 모든 leaf가 pure해질때까지 혹은, 분기된 노드 속 샘플수가 설정한 최소 샘플수(min_samples_split)보다 적게 될때까지 분기한다. ‘int값 입력’

min_samples_split: 분기 할 node 내 샘플의 최소 개수. 최소 개수보다 node 내 샘플 수가 적으면 leaf가 pure하지 않더라도 분기를 멈춘다. float 입력 시, 전체 샘플 개수 대비 float 비율만큼의 개수로 최소 샘플수가 설정된다. ‘int 혹은 float값 입력’, default=2

min_samples_leaf: leaf 노드에 있어야 하는 최소 샘플 수. 왼쪽 혹은 오른쪽 branch에 각각 min_samples_leaf만큼 훈련 샘플이 있어야 분기가 된다. ‘int 혹은 float값 입력’, default=2

min_weight_fraction_leaf: 모든 샘플의 가중치의 합계의 최소 가중치의 비율, 입력되지 않으면 모든 샘플의 가중치는 동일하다. ‘float 입력’, default=0.0

max_features: 최적의 분할을 찾기 위해 고려하는 feature의 개수. ‘int or flaot값’, ‘auto’, ‘sqrt’, ‘log2’, default=None.

random_state: 추정기의 무작위성 제어. ‘int 입력’, default=None

max_leaf_nodes: 최대 노드 수. default=None

min_impurity_decrease: 최소 불순도 감소값 이하인 경우에는 분기가 일어나지 않는다. ‘float’, default=0.0

ccp_alpha: 최소 비용-복잡성 가지치기에 사용되는 복잡도 매개변수. ccp_alpha보다 작은 비용-복잡도를 가진 하위 트리 중에 비용-복잡도가 가장 큰 하위 트리가 선택된다.

tree.score(X, y)로부터 얻는 값은 해당 모델의 결정계수이다. 모델의 설명력을 의미한다. 회귀트리의 경우, 훈련셋과 테스트셋의 설명력이 40~50%로 낮게 나타났다. 이럴 땐 파라미터를 조정하거나 아예 다른 학습 방식을 강구해야 한다.

안드레아스 뮐러, 세라 가이도, 파이썬 라이브러리를 활용한 머신러닝 번역개정판 (서울: 한빛미디어, 2019)

스티븐 마슬랜드, 알고리즘 중심의 머신러닝 가이드 제2판 Machine Learning: An Algorithmic Perspective, Second Edition (경기: 제이펍, 2017)

피터 브루스, 앤드루 브루스, 피터 게데크, 데이터 과학을 위한 통계 2판 (서울: 한빛미디어, 2021)

sklearn.tree.DecisionTreeClassifier, Scikit Learn,

https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier

9. 의사결정나무(Decision Tree) 에 대해서 알아보자 with Python

이번 포스팅에서는 모형의 해석이 쉽다는 장점을 가진 의사결정나무를 공부한 내용을 포스팅하려고 한다. 의사결정이 무엇인지 알아보고 의사결정나무 모형을 직접 구현하는 방법을 소개하고 마지막에 실제 데이터를 이용하여 앞서 만든 모형이 잘 동작하는지 확인해볼 것이다. 또한 sklearn을 이용하는 방법도 소개한다.

여기서 다루는 내용은 다음과 같다.

1. 의사결정나무란?

2. 의사결정나무 모형 만들기

3. 의사결정나무 구현하기

4. 예제 with Python

1. 의사결정나무란?

– 정의 –

의사결정나무(Decision Tree)는 입력값에 대한 예측값을 나무 형태로 나타내어주는 모형이다.

– 용어 정리 –

먼저 의사결정나무에서 사용되는 주요 용어를 살펴보자.

뿌리 마디(root node) : 시작되는 마디로 전체 자료를 포함한다.

자식 마디(childe node) : 하나의 마디로부터 분리된 2개 이상의 마디들

부모 마디(parent node) : 주어진 마디의 상위 마디

끝마디(terminal node) : 자식 마디가 없는 마디

중간 마디(internal node) : 부모 마디와 자식 마디가 모두 있는 마디

가지(branch) : 연결되어있는 2개 이상의 마디 집합

깊이(depth) : 뿌리 마디로부터 끝마디까지 연결된 마디의 개수

아래 그림을 보면 용어를 이해하는데 도움이 될 것이다(노드와 마디는 같은 뜻이다).

– 장단점 –

1) 장점

의사결정나무는 모형의 해석이 쉽다. 여기서 모형의 해석이 쉽다는 것은 입력값이 주어졌을 때 설명변수의 영역을 따라가며 출력값이 어떻게 나올 수 있는지 알기 쉽다는 것이다.

2) 단점

예측력이 다른 지도 학습모형에 비하여 떨어진다. 이것은 어찌 보면 당연하다. 왜냐하면 의사결정나무는 설명변수의 일부 영역에서 단순히 평균 또는 다수결 법칙에 의하여 예측을 수행하기 때문이다. 특히 회귀나무의 예측력은 더 좋지 않은 편인데 이는 해당 마디에 포함된 반응 변수의 평균을 예측값으로 추정하는데 아무래도 평균을 사용하다보니 이상치에 영향을 받을 수 있기 때문이다.

반응형

2. 의사결정나무 모형 만들기

의사결정나무 모형은 크게 성장(growing), 가지치기(Pruning) 단계를 통하여 만들어진다.

성장(growing) 단계에서는 먼저 최적화할 목적함수를 정한다. 각 마디에서 이 목적함수를 최적화하는 변수와 그 변수의 분리 기준을 찾아 의서결정나무를 성장시키며 사전에 정의된 정지 규칙(stopping rule)을 만족하면 성장을 중단한다. 가지치기(pruning) 단계에서는 과적합(Overfitting)을 방지, 해석이 안 되는 규칙 등 불필요한 가지를 제거한다.

출력 변수의 종류에 따라서 의사결정나무의 종류도 나누어지게 된다. 출력 변수가 연속형인 경우 회귀나무(regression tree), 범주형인 경우 분류나무(classification)라고 한다. 이제 회귀나무와 분류나무를 만들어내는 방법을 알아보자. 여기서는 마디를 2진 분리시키는 CART(Classification and Regression Tree) 알고리즘을 소개한다.

1. 회귀나무(Regression Tree)

먼저 데이터 $(x_i, y_i), i=1,2,\ldots, n$ 이 주어졌다고 하자. 여기서 $x_i = (x_{i1}, \ldots, x_{ip})^t$이다. 의사결정나무 모형은(회귀나무든 분류나무든 마찬가지) 다음과 같이 나타낼 수 있다.

$$f(x) = \sum_{m=1}^Mc_mI(x\in R_m)\tag{2.1}$$

여기서 $R_1, \ldots, R_M$은 $M$개로 쪼갠 설명변수들의 (겹치지 않는) 영역이다. 식 (2.1)이 의미하는 것은 설명변수 벡터 $x$가 $R_m$에 포함된다면 $c_m$으로 예측하겠다는 것이다. 또한 식 (2.1)로 부터 모형은 $c_m$과 $R_m$에 의해 결정된다는 것을 알 수 있다. 따라서 $c_m, R_m$을 어떻게 구할 것인지 알아야 한다.

1.1 불순도(Impurity)

$c_m, R_m$은 불순도라는 측도를 이용하여 정하게 된다. 회귀나무에서는 불순도의 측도로 오차 제곱합을 사용한다. CART 알고리즘은 부모마디로부터 2 마디로 분리한다. 따라서 두 개의 영역으로 계속해서 나눠나간다. 주어진 $j$번째 설명변수 $x_j$와 해당하는 값(또는 집합) $s$에 대하여 두 영역을 $A_1(j, s), A_2(j, s)$라 하자. 만약 $x_j$가 연속형이라면 $s$는 실수가 되며 두 영역은 다음과 같이 정의할 수 있다.

$$A_1(j, s) = \{x_j : x_j\leq s\}, A_2(j, s) = \{x_j : x_j > s\}$$

만약 $x_j$가 범주형이라면 $s$는 집합이 되며 두 영역은 다음과 같이 정의할 수 있다.

$$A_1(j, s) = \{x_j \in s \}, A_2(j, s) = \{x_j \in s^c \}$$

최적 분리 변수와 그 변수에 해당하는 기준은 다음의 최적화 문제를 풀어서 구하게 된다.

$$\min_{j, s}\left ( \min_{c_1^*}\sum_{x_i\in A_1(j,s)}(y_i-c_1^*)^2 + \min_{c_2^*}\sum_{x_i\in A_2(j,s)}(y_i-c_2^*)^2 \right)$$

주어진 $j, s$에 대하여 최적 $c_1^*, c_2^*$는 각각 $A_1(j, s), A_2(j, s)$에 속하는 $y_i$의 평균이 된다. 이렇게 최적 변수와 그에 대응하는 분리 기준을 찾았다면 분리된 두 영역에 대해서 동일한 과정을 반복한다. 이를 반복하면 회귀나무(의사결정나무)를 성장시킬 수 있다.

표기상 $R_m$과 $A_1, A_2$가 헷갈릴 수 있는데 각 마디에서 분리된 두 영역 $A_1, A_2$들을 이용하여 분리된 영역이 $R_m$인 것이다. 아래 그림에서 왼쪽은 $x_1$에서 3을 기준으로 분리했을 때 영역과 $f(x)$를 나타낸 것이고 오른쪽은 $x_2$에서 4를 기준으로 분리했을 때 영역과 $f(x)$를 나타낸 것이다.

1.2 분리 기준 후보 선정

한편 분리 기준을 정할 때 후보가 되는 $s$는 $x_j$가 연속형인 경우 가질 수 있는 유니크한 값을 후보로 정하며 범주형인 경우는 공집합과 전체집합을 제외하고 중복되지 않는 부분집합을 고려한다. 예를 들어

$x_j$가 연속형이고 가질 수 있는 값의 집합이 {1.2, 1.5, 1.7}라고 하면 분리 기준 후보는 1.2, 1.5을 분리 기준 후보로 지정한다(또는 3개의 값의 사이에 들어있는 중앙값 1.35, 1.6을 써도 된다). $x_j$가 범주형이고 전체 집합이 {1, 2, 3, 4}라면 {1}, {1, 2} 등을 고려한다. 이때 {1, 2}를 분리 기준 후보로 두었다면 {3, 4}는 할 필요가 없다. {3, 4}는 {1, 2}의 여집합이라서 {1, 2}를 분리 기준 후보로 사용했을 때 이미 고려가 된 것이기 때문이다.

1.3 가지치기(Pruning)

앞 절에서 의사결정나무를 성장시키는 방법을 알아보았다. 그렇다면 언제까지 성장시켜야 하는지에 대한 질문이 나올 수 있다. 왜냐하면 너무 깊이가 깊은 나무는 과적합의 문제가 있고 깊이가 얕은 나무는 과소 적합의 문제가 있을 수 있기 때문이다. 이러한 문제를 하기 위해 나무의 깊이가 너무 깊어지지 않도록 가지치기를 하게 된다. 가지치기에는 사전 가지치기(pre-pruning)와 사후 가지치기(post-pruning)가 있다.

사전 가지치기는 사전에 정지 규칙을 만들어 나무의 성장을 중지시키는 방법이다. 예를 들면 깊이를 4로 미리 지정한다거나 마디에 해당하는 샘플 수가 5개 이하이면 성장을 멈추는 방법이 모두 사전 가지치기에 해당한다. 이때 사전 가지치기에서 사용할 깊이와 샘플 수는 검증 데이터를 통하여 결정하는 것이 일반적이다.

사후 가지치기는 주어진 나무 $T_0$에 대하여 아래의 목적함수를 최소화하는 나무 모형 $T_{\alpha}\subset T_0$을 찾는다.

$$C_{\alpha}(T) = \sum_{m=1}^{|T|}N_mQ_m(T)+\alpha |T|$$

여기에서 $|T|$는 끝마디 개수, $N_m$은 영역 $R_m$에서의 데이터 개수, $\hat{c}_m$은 $R_m$에서의 $y_i$의 평균, 그리고

$$Q_m(T) = \frac{1}{N_m}\sum_{x_i\in R_m}(y_i-\hat{c}_m)^2 $$

이다.

이때 $\alpha \geq 0$은 나무 모형의 복잡도와 적합도를 조절하는 Tuning Parameter로 \alpha가 크면 (작으면) $T_{\alpha}$의 크기는 작아(커) 진다. $\alpha=0$이면 사후 가지치기는 하지 않고 $T_0$를 최종 의사결정나무로 선정한다. $\alpha$는 교차검증법을 이용하여 추정한다.

사후 가지치기(Post Pruning)를 하는 방법과 구현은 다음 포스팅해서 다루려고 한다.

2. 분류나무(Classification Tree)

분류 나무에서 분리 기준을 정하는 목적함수는 카이제곱 통계량, 지니 계수(Gini Index), 엔트로피 지수(Entropy)를 불순도의 측도로 사용하며 앞서 살펴본 회귀나무를 성장시키는 방법과 동일하다. 입력값 $x\in R_m$가 주어졌을 때 분류나무의 예측값은 $R_m$에 속하는 $y_i$의 범주가 가장 많은 범주를 예측값으로 한다. 즉, 분류나무는 예측값을 $R_m$에서 다수결의 원칙으로 정하는 것이다.

분류나무에서 사용하는 불순도의 측도를 알아보기 위해 데이터가 아래와 같이 분리되었다고 해보자.

2.1 카이제곱 통계량

먼저 2개로 분리되는 마디를 아래와 같이 관측 도수(기대 도수)를 포함한 2×2 테이블로 만들어준다.

Good Bad Total 왼쪽 자식 노드 32 (56) 48 (24) 80 오른쪽 자식 노드 178 (154) 42 (66) 220 Total 210 90 300

카이제곱 통계량 $\chi^2$은 아래와 같이 계산한다.

$$\chi^2 = \sum_{k=1}^4(E_k-O_k)^2/E_k$$

여기서 $E_k$와 $O_k$는 각각 기대 도수와 관측 도수이다. 분리 기준은 카이제곱 통계량이 최대가 되는 값으로 정한다.

이 표에서 카이제곱 통계량은

$$\frac{(56-32)^2}{56}+\frac{(24-48)^2}{24}+\frac{(154-178)}{154}+\frac{(66-42)}{66} = 46.75$$

가 된다.

2.2 지니 지수(Gini Index)

한 노드에서 지니 지수 $G$는 다음과 같이 정의된다.

$$G = G_LP(\text{Left}) +G_RP(\text{Right})\tag{2.2.1}$$

여기서

$$\begin{align}\small G_L &= 2\cdot[P(\text{Good in Left})P(\text{Bad in Left})] \\ &= P(\text{Good in Left})[1-P(\text{Good in Left})] \\ &+ P(\text{Bad in Left})[1-P(\text{Bad in Left})] \end{align}$$

이고 $G_R$도 비슷하게 정의할 수 있다. 분리 기준은 지니 지수가 최소가 되도록 하는 값으로 정한다. 앞의 나무에 대해서 지니 지수는 다음과 같다.

$$\begin{align}2\left (\frac{32}{80}\cdot\frac{48}{80}\cdot\frac{80}{300}+\frac{178}{220}\cdot\frac{42}{220}\cdot\frac{220}{300}\right ) = 0.355\end{align}$$

여기서는 $y_i$가 2개의 클래스인 것만 고려하였는데 만약 $K$개의 클래스를 갖고 있다고 하자. 이때 $p_k^L$을 왼쪽 마디에 있는 데이터 중에서 클래스가 $k$인 데이터의 비율이라고 하자. 그렇다면 식 (2.2.1)에서 $G_L = \sum_{k=1}^Kp_k^L(1-p_k^L)$이 된다. 유사하게 $G_R$도 정의할 수 있다.

2.3 엔트로피 지수(Entropy Index)

한 노드에서 엔트로피 지수 $E$는 다음과 같이 정의한다.

$$E = E_LP(\text{Left}) + E_RP(\text{Right}) \tag{2.3.1}$$

이고 $$E_L=-P(\text{Good in Left})\log_2 P(\text{Good in Left}) \\ -P(\text{Bad in Left})\log_2P(\text{Bad in Left})$$이고 $E_R$도 비슷하게 정의된다.

분리 기준은 엔트로피 지수를 최소화시키는 값으로 정한다.

$$-\left (\frac{32}{80}\log_2\frac{32}{80} + \frac{48}{80}\log_2\frac{48}{80}\right )\cdot\frac{80}{300} \\ -\left ( \frac{178}{220}\log_2\frac{178}{220} + \frac{42}{220}\log_2\frac{42}{220} \right )\cdot\frac{220}{300} = 0.7747$$

여기서는 $y_i$가 2개의 클래스인 것만 고려하였는데 만약 $K$개의 클래스를 갖고 있다고 하자. 이때 $p_k^L$을 왼쪽 마디에 있는 데이터 중에서 클래스가 $k$인 데이터의 비율이라고 하자. 그렇다면 식 (2.3.1)에서 $E_L = -\sum_{k=1}^Kp_k^L\log_2 p_k^L$이 된다. 유사하게 $E_R$도 정의할 수 있다.

2.4 불순도 측도의 고찰

여기서는 클래스가 2개인 경우를 가정하고 설명하려고 한다. 보통 불순도는 한 노드 안에서 두 클래스의 분포가 얼마나 섞여 있는지를 의미한다. 아래 그림은 부모 노드로부터 분리된 자식 노드들을 2가지 경우로 나타낸 것이다.

1) 불순도가 높은 경우

2) 불순도가 낮은 경우

불순도는 순한 정도의 반대이므로 1) 번의 불순도가 2) 번 보다 큰 것을 알 수 있다. 그리고 불순도가 낮으면 그만큼 예측되는 클래스가 더 분명해지기 때문에 분리 기준을 정할 때에는 불순도가 최소화되는 것을 골라야 한다.

앞에서 불순도를 측정하는 측도로써 카이제곱 통계량, 지니 지수, 엔트로피 지수를 소개하였다. 지니 지수와 엔트로피 지수 같은 경우는 하나의 클래스가 다른 클래스보다 많은 경우 그 값이 작아지며 이는 불순도가 최소화된다는 것을 의미한다. 그 이유를 살펴보자.

식 (2.2.1)에서 지니 지수 $G$를 수식으로 다시 쓰면 다음과 같다.

$$G = 2*[p_{GL}(1-p_{GL})p_L+p_{GR}(1-p_{GR})p_R]\tag{2.4.1}$$

위 식에서 왼쪽 부분인 $G_L = p_{GL}(1-p_{GL})$를 살펴보자. $G_L$의 성질을 살펴보기 위해 $f(x) = x(1-x), (0 \leq x \leq 1)$의 그래프를 그려보았다.

$f(x) = x(1-x)$ 그래프

위 그림에서 알 수 있듯이 $G_L$은 $p_{GL} = 0.5$인 경우 최대가 되며 $p_{GL}$이 0 또는 1인 경우 최소가 된다. 전자는 마디 안에 두 클래스(Good, Bad)가 반반씩 섞여있어서 불순도가 가장 높은 경우이고 후자는 하나의 클래스만 존재한다. 따라서 하나의 클래스가 다른 클래스보다 많은 경우 $p_{GL}$이 0 또는 1에 가까워져서 불순도 여기서는 지니 지수가 작아지게 된다. 식 (2.4.1)의 오른쪽 부분도 마찬가지이다. 요약하자면 지니 지수가 최소화되는 분리 기준을 정해야 한다는 것이다.

다음으로 식 (2.3.1)에서 엔트로피 지수 $E$를 수식으로 다시 쓰면 다음과 같다.

$$E = E_Lp_L+E_Rp_R\tag{2.4.2}$$

이때 $E_L = -p_{GL}\log_2p_{GL}-(1-p_{GL})\log_2(1-p_{GL})$이고 $E_R = -p_{GR}\log_2p_{GR}-(1-p_{GR})\log_2(1-p_{GR})$이다. $E_L$에 대한 성질을 알아보기 위해 $f(x) = -x\log_2 x – (1-x)\log_2 (1-x), (0 < x < 1)$의 그래프를 그려보자. $f(x) = $ 위 그림을 토대로 $E_L$을 보면 지니 지수와 마찬가지로 $p_{GL} = 0.5$인 경우 최대가 되며 $p_{GR}$ 0 또는 1에 가까워지는 경우 계속 감소하게 된다. 전자는 마디 안에 두 클래스(Good, Bad)가 반반씩 섞여있어서 불순도가 가장 높은 경우가 되고 후자는 하나의 클래스만 존재한다. 따라서 하나의 클래스가 다른 클래스보다 많은 경우 $p_{GL}$이 0 또는 1에 가까워져서 불순도 여기에서는 엔트로피 지수가 작아지게 될 것이다. 식 (2.4.2)의 $E_R$도 마찬가지이다. 즉, 엔트로피 지수를 최소화시키는 값을 분리 기준으로 정하는 것이 좋다는 것이다. 카이제곱 통계량은 지니 지수, 엔트로피 지수와는 불순도 관점이 다르다. 지니 지수와 엔트로피 지수는 순도가 높은 다시 말하면 한쪽 클래스가 다른 클래스보다 많아지는 분리 기준을 정한다. 하지만 카이제곱 통계량은 왼쪽 마디에서 두 클래스의 분포와 오른쪽 마디에서 두 클래스의 분포 차이를 크게 하는 분리 기준을 선택한다. 예를 들어 다음과 같이 2가지 경우로 분리가 되었다고 해보자. 지니 지수와 엔트로피 지수 관점에서 봤을 때에는 왼쪽 표의 분리 기준을 선택한다. 왜냐하면 왼쪽 마디와 자식 마디에서 Bad 클래스가 일방적으로 많으므로 불순도가 작기 때문이다. 하지만 카이제곱 통계량은 오른쪽 분리 기준을 선택한다. 왜냐하면 왼쪽 표의 경우 왼쪽 마디에서의 Good, Bad 분포(1대 14)와 오른쪽 마디에서의 Good, Bad 분포(1대 14)가 같기 때문에 카이제곱 통계량 값은 작기 때문이다(이 경우 정확히 0이 된다). 그렇다면 어떤 것을 써야 할까? 한 클래스를 잘 분리해내고 싶다면 지니 지수와 엔트로피 지수를 사용하고 설명 변수가 타겟 클래스 분포에 영향을 미치는지 확인하고 싶다면 카이제곱 통계량을 사용할 수 있다. 그리고 지니 지수와 엔트로피 지수 같은 경우 엔트로피 지수는 로그 계산을 포함하고 있기 때문에 계산적인 측면에서는 지니 지수가 유리하며 두 지수를 사용했을 때 성능 차이는 크지 않다고 한다. 2.5 다른 불순도의 측도들 이외에도 오분류율, 정보 획득(Infomation Gain), 분산 감소량(Variance Reduction)등 여러 가지 불순도 측도가 있다. 이에 대한 내용은 여기에 가면 자세히 나와있다. 반응형 3. 의사결정나무 구현하기 이제 의사결정나무를 파이썬으로 구현해보자. 전체 코드는 아래에 첨부해두었다. decision_tree_final.ipynb 0.22MB - 의사결정나무 생성 알고리즘 - 아래 그림은 의사결정나무 알고리즘을 그림으로 나타낸 것이다. 의사결정나무 알고리즘 먼저 주어진 데이터의 조건을 확인한다. 여기서 말하는 조건이란 한 마디의 최소 샘플 수, 최대 깊이이며 분류나무의 경우 주어진 데이터의 라벨이 하나인 조건이 추가된다. 이때 조건을 만족한 노드의 분리를 멈춘다. 만약 해당 노드가 왼쪽(오른쪽) 노드라면 오른쪽(왼쪽) 데이터에 대해서 동일한 과정을 반복한다. 만약 조건을 만족하지 않는 경우 모든 설명 변수에 대하여 분리기준 후보를 생성한다. 그런 다음 불순도 측도를 이용하여 최적 분리기준을 찾고 이를 통하여 데이터를 분리한다. 이 경우 데이터는 왼쪽 데이터와 오른쪽 데이터로 나누어지고 이 2개의 데이터에 대해서 동일한 과정을 반복한다. - 의사결정나무 알고리즘 구현하기 - 이제 의사결정나무를 파이썬을 이용하여 구현해보자~!! 내용이 길어서 다음과 같이 나누었다. 3.1 모듈 임포트, 부가적인 함수 및 클래스 정의 3.2 메인 클래스 정의 3.1 모듈 임포트, 부가적인 함수 및 클래스 정의 먼저 이번 포스팅에서 사용할 모듈을 임포트해준다. from graphviz import * from sklearn.datasets import load_iris from sklearn import tree from collections import Counter from itertools import chain, combinations import ast import matplotlib.pyplot as plt import pandas as pd import numpy as np import graphviz import seaborn as sns 먼저 의사결정나무의 기본이 되는 마디(Node) 클래스를 정의한다. 상황에 따라서 마디와 노드 두 가지를 혼용해서 쓰겠다. 해당 클래스는 노드 식별 아이디(nodeId), 노드에 표시할 텍스트(label) 등을 초기화해준다. 여기서 level 필드는 노드가 속한 층으로 뿌리 노드의 경우 level은 1이다. 이 필드를 추가한 이유는 나중에 각 층마다 노드 배경색을 다르게 해 주기 위함이다. class Node: def __init__(self,nodeId, label, isRoot=False,parentNode=None, leftNode=None,rightNode=None,isTerminal=False, attr={}): self.nodeId = nodeId ## 노드 식별 아이디 self.label = label ## 노드 텍스트 self.attr = attr ## 노드 스타일 속성 self.isRoot = isRoot ## 루트 노드 여부 self.parentNode = parentNode ## 부모 마디(노드) self.leftNode = leftNode ## 왼쪽 자식 노드(마디) self.rightNode = rightNode ## 오른쪽 자식 노드 self.isTerminal = isTerminal ## 터미널 노드 여부 self.level = 0 ## 노드가 속한 층 다음은 의사결정나무를 graphviz를 이용하여 시각화해주는 함수이다. 이 함수는 먼저 뿌리 마디를 그려주고 왼쪽 마디와 오른쪽 마디를 지나면서 마디의 스타일을 적용하고 화살표를 부모와 연결시켜준다. graphviz 사용법은 추후 포스팅하겠다. def visualize_tree(tree): def add_node_edge(tree, dot=None): if dot is None: dot = Digraph() # name = tree dot.node(name = str(tree.nodeId), label = str(tree.label), **tree.attr) ## left if tree.leftNode: dot.node(name=str(tree.leftNode.nodeId),label=str(tree.leftNode.label), **tree.leftNode.attr) dot.edge(str(tree.nodeId), str(tree.leftNode.nodeId), **{'taillabel':"yes",'labeldistance':'2.5'}) dot = add_node_edge(tree.leftNode, dot) if tree.rightNode: dot.node(name=str(tree.rightNode.nodeId),label=str(tree.rightNode.label), **tree.rightNode.attr) dot.edge(str(tree.nodeId), str(tree.rightNode.nodeId), **{'headlabel':" no",'labeldistance':'2'}) dot = add_node_edge(tree.rightNode, dot) return dot dot = add_node_edge(tree) return dot 다음은 rgb 색상을 16진수로 바꿔주는 함수이다. 여기 에서 고수님이 만든 코드가 있어서 가져왔다. def RGBtoHex(vals, rgbtype=1): """Converts RGB values in a variety of formats to Hex values. @param vals An RGB/RGBA tuple @param rgbtype Valid valus are: 1 - Inputs are in the range 0 to 1 256 - Inputs are in the range 0 to 255 @return A hex string in the form '#RRGGBB' or '#RRGGBBAA' """ if len(vals)!=3 and len(vals)!=4: raise Exception("RGB or RGBA inputs to RGBtoHex must have three or four elements!") if rgbtype!=1 and rgbtype!=256: raise Exception("rgbtype must be 1 or 256!") #Convert from 0-1 RGB/RGBA to 0-255 RGB/RGBA if rgbtype==1: vals = [255*x for x in vals] #Ensure values are rounded integers, convert to hex, and concatenate return '#' + ''.join(['{:02X}'.format(int(round(x))) for x in vals]) 다음은 입력값이 정수인지 아닌지 출력하는 함수이다. def is_integer_num(n): if isinstance(n, int): return True if isinstance(n, float): return n.is_integer() return False 3.2 메인 클래스 정의 이제 주인공이 등장할 차례다. 이 부분은 코드가 꽤 길어서 중복되는 부분은 생략하고 설명하겠다. 먼저 DecisionTree 클래스를 정의한다. 인스턴스 생성 시 분류나무인지 회귀나무인지 정해주는 tree_type인자를 입력하도록 했다. class DecisionTree: def __init__(self, tree_type='classification',): tree_types = ['classification','regression'] assert tree_type in tree_types, f'tree_type must be the one of the {tree_types}' self.tree_type = tree_type ## 트리 유형 self.impurity_measure = None ## 불순도 측도 self.root = None ## 트리 노드 self.node_id = 0 ## 노드 아이디 self.col_names = None ## 칼럼 이름 self.col_types = None ## 변수 타입 self.X = None ## train data X self.y = None ## train data y self.leaf_attr = None ## 끝마디 스타일 속성 다음으로 의사결정나무의 모든 마디를 돌 수 있는 traverseInOrder 함수를 정의했다. 이는 의사결정나무의 깊이를 계산할 때 필요하며 깊이는 getDepth를 이용하여 계산한다. 또한 각 노드가 몇 층에 있는지 확인해보기 위해 getLevel 함수도 정의하였다. class DecisionTree: ''' 중략 ''' def traverseInOrder(self, node): res = [] if node.leftNode != None: res = res + self.traverseInOrder(node.leftNode) res.append(node) if node.rightNode != None: res = res + self.traverseInOrder(node.rightNode) return res def getDepth(self, root): res = self.traverseInOrder(root) res = [abs(node.level) for node in res] return max(res) def getLevel(self, node, counter = 1): if node.parentNode is None: return counter else: counter += 1 counter = self.getLevel(node.parentNode,counter) return counter 다음은 변수의 타입을 자동으로 설정하는 함수이다. 변수 타입을 수동으로 하기 귀찮을 때 쓰면 좋다. 여기서는 디폴트로 해당 변수의 유니크한 원소 개수가 15개 이하라면 범주형으로 아닌 경우 연속형으로 지정했다. class DecisionTree: ''' 중략 ''' def determineTypeOfCol(self,X, num_unique_values_threshold=15): col_types = [] for col in X.columns: unique_values = X[col].unique() example_value = unique_values[0] if (isinstance(example_value, str)) or (len(unique_values) <= num_unique_values_threshold): col_types.append('categorical') else: col_types.append('continuous') self.col_types = col_types 다음으로 마디에 포함된 데이터 라벨이 하나인지 아닌지 알려주는 isPure, 자식 마디의 불순도를 계산하는 impurity, 개별 마디에 포함된 불순도를 계산하는 individualImpurity, 그리고 엔트로피와 지니 지수 그리고 제곱 평균오차를 계산하는데 필요한 _entropy, _gini, _mse를 정의하였다. 마지막으로 끝마디를 생성하는 createLeaf 도 정의하였다. class DecisionTree: ''' 중략 ''' def isPure(self,y): if len(np.unique(y)) > 1: return False return True def impurity(self, left_y, right_y): y = self.y n = len(left_y)+len(right_y) if self.impurity_measure == ‘chi2’: try: label = np.unique(y) contingency_table = dict() expected_table = dict() for l in label: temp1 = [] temp1.append(np.sum(left_y==l)) temp1.append(np.sum(right_y==l)) contingency_table[l] = temp1 temp2 = [] temp2.append((np.sum(left_y==l) + np.sum(right_y==l))*len(left_y)/n) temp2.append((np.sum(left_y==l) + np.sum(right_y==l))*len(right_y)/n) expected_table[l] = temp2 observed = np.array([v for k,v in contingency_table.items()]).flatten() expected = np.array([v for k,v in expected_table.items()]).flatten() impurity_val = np.nansum(np.square(observed-expected)/expected) except RuntimeWarning: raise else: pl, pr = len(left_y)/n, len(right_y)/n impurity_val = pl*self.individualImpurity(left_y)+\ pr*self.individualImpurity(right_y) return impurity_val def individualImpurity(self, y): if self.impurity_measure == ‘entropy’: return self._entropy(y) elif self.impurity_measure == ‘gini’: return self._gini(y) elif self.impurity_measure == ‘mse’: return self._mse(y) def _entropy(self, y): _, counts = np.unique(y, return_counts=True) ps = counts / len(y) return -np.sum([p * np.log2(p) for p in ps if p > 0]) def _gini(self, y): _, counts = np.unique(y, return_counts=True) ps = counts / len(y) return np.sum([p*(1-p) for p in ps if p > 0]) def _mse(self, y): if len(y) == 0: return 0 mse = np.mean(np.square(y-np.mean(y))) return mse def createLeaf(self, y, tree_type): if tree_type == ‘classification’: classes, counts = np.unique(y, return_counts=True) index = counts.argmax() return classes[index] else: return np.mean(y)

이제 분리 기준 후보를 만들고 최적 분리 기준을 선택하는 과정에서 필요한 함수들이다. powerset_generator 함수는 변수가 범주형일 때 공집합과 전체집합을 제외한 모든 부분집합을 구해준다. splitSet 함수는 변수가 범주형일 때 분리 기준 후보를 만들어준다. 여기서는 하나의 부분집합을 알면 나머지 여집합은 필요가 없으므로 실제 필요한 분리 기준은 powerset_generator에서 구해준 모든 부분집합 중 절반만 필요하다는 것을 주목하자. getPotentialSplits 함수는 모든 변수에 대한 분리 기준 후보를 생성한다. 변수 인덱스를 키, 변수의 분리 기준 후보 리스트를 값으로 하는 딕셔너리를 리턴한다. split은 해당 변수와 분리 기준을 이용하여 왼쪽 마디, 오른쪽 마디에 각각 들어갈 데이터 인덱스를 리턴한다. 마지막으로 determinBestSplit은 getPotentialSplits가 생성한 분리 기준을 통하여 최적 변수와 그에 대응하는 분리 기준, 그리고 불순도 측도 값을 리턴한다.

class DecisionTree: ”’ 중략 ”’ def powerset_generator(self, i): for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)): yield set(subset) def splitSet(self, x): ps = [i for i in self.powerset_generator(x) if i != set() and len(i) != len(x)] idx = int(len(ps)/2) split_set = [] for j in range(idx): split_set.append(tuple(ps[j])) return split_set def getPotentialSplits(self,X): potential_splits = {} col_types = self.col_types for col_idx in range(X.shape[1]): unique_value = np.unique(X[:,col_idx]) if col_types[col_idx] == ‘continuous’: potential_splits[col_idx] = unique_value else: potential_splits[col_idx] = self.splitSet(unique_value) return potential_splits def split(self,X, col_idx, threshold): X_col = X[:,col_idx] col_types = self.col_types if col_types[col_idx] == ‘continuous’: left_idx = np.argwhere(X_col<=threshold).flatten() right_idx = np.argwhere(X_col>threshold).flatten() else: left_idx = np.argwhere(np.isin(X_col,threshold)).flatten() right_idx = np.argwhere(~np.isin(X_col,threshold)).flatten() return left_idx, right_idx def determinBestSplit(self, X, y, potential_splits): best_split_column, best_split_value, opt_impurity = ”, ”, ” if self.impurity_measure in [‘entropy’,’gini’,’mse’]: opt_impurity = np.infty for col in potential_splits: for val in potential_splits[col]: left_idx, right_idx = self.split(X,col,val) cur_impurity = self.impurity(y[left_idx],y[right_idx]) if cur_impurity <= opt_impurity: opt_impurity = cur_impurity best_split_column = col best_split_value = val else: opt_impurity = -np.infty for col in potential_splits: for val in potential_splits[col]: left_idx, right_idx = self.split(X,col,val) cur_impurity = self.impurity(y[left_idx],y[right_idx]) if cur_impurity >= opt_impurity: opt_impurity = cur_impurity best_split_column = col best_split_value = val return best_split_column, best_split_value, opt_impurity

다음으로 fit 함수는 의사결정나무 모형을 적합시키는 함수이며 내부 함수 _growTree를 통하여 의사결정나무를 성장시키는 구조로 만들었다. 최종 의사결정나무는 DecisionTree 클래스에서 root 필드에 넣어주었다. 나무를 성장시킨 뒤에는(line 42~) 각 마디에 층을 계산하고 층별로 마디에 배경색을 지정한다. 그리고 분류나무인 경우 끝마디에 예측 라벨을 포함하는 데 이때 같은 라벨인 경우에는 같은 스타일을 갖도록 지정했다. 그리고 나서 나무의 각 층별로 마디의 스타일을 지정했다.

class DecisionTree: ”’ 중략 ”’ def fit(self,X,y,impurity_measure=’entropy’,min_sample=5, max_depth=5, type_of_col=None, auto_determine_type_of_col=True, num_unique_values_threshold = 15 ): ”’ impurity_measure : 불순도 측도 min_sample : 노드가 포함해야하는 최소 샘플 개수, max_depth : 나무 최대 깊이 설정 type_of_col : 변수 타입 리스트 auto_determine_type_of_col : 변수 타입 자동 생성 여부 num_unique_values_threshold : 범주형으로 지정할 최대 유니크 원소 개수 ”’ self.X = X self.y = y if auto_determine_type_of_col: self.determineTypeOfCol(X, num_unique_values_threshold) else: if type_of_col is None: raise ValueError(‘When auto_determine_type_of_col is False, then type_of_col must be specified’) assert X.shape[1] == len(type_of_col), ‘type_of_col has the same length of X columns’ give_type_of_col = list(set(type_of_col)) for toc in give_type_of_col: if toc != ‘categorical’ and toc != ‘continuous’: raise ValueError(‘type_of_col must contain categorical or continuous’) self.col_types = type_of_col tree_type = self.tree_type impurity_measures = [‘entropy’,’gini’,’chi2′] if tree_type == ‘classification’ else [‘mse’] assert impurity_measure in impurity_measures,\ f’impurity_measure must be the one of the {impurity_measures}’ self.impurity_measure = impurity_measure tree_type = self.tree_type self.root = self._growTree(X,y,tree_type,min_sample=min_sample, max_depth=max_depth) ### assign node a style iod = self.traverseInOrder(self.root) root_node = [node for node in iod if node.nodeId == 1][0] root_node.isRoot = True ## set node level for nd in iod: nd.level = self.getLevel(nd) colors = sns.color_palette(‘hls’, self.getDepth(self.root)) ## set node level if tree_type == ‘classification’: leaf_color = sns.color_palette(‘pastel’, len(np.unique(y))) leaf_attr = dict() for i, l in enumerate(np.unique(y)): leaf_attr[l] = {‘shape’:’box’, ‘color’:f'{RGBtoHex(leaf_color[i])}’, ‘fontcolor’:f'{RGBtoHex(leaf_color[i])}’,’peripheries’:’2′} self.leaf_attr = leaf_attr for l in range(1,self.getDepth(self.root)+1): color = RGBtoHex(colors[l-1]) for nd in iod: if nd.level == l: if nd.isTerminal: if tree_type == ‘classification’: nd.attr = leaf_attr[nd.label] else: nd.attr = {‘shape’:’box’,’peripheries’:’2′} else: nd.attr = {‘shape’:’box’,’style’:’filled’, ‘fillcolor’:f'{color}’}

_growTree 함수는 의사결정나무를 성장시킨다. 이 부분은 중요하므로 자세히 살펴보자.

class DecisionTree: ”’ 중략 ”’ def _growTree(self, X, y, tree_type, counter=0, min_sample=5, max_depth=5): self.node_id += 1 if counter == 0: global col_names col_names = X.columns self.col_names = X.columns if isinstance(X, pd.DataFrame): X = X.values if isinstance(y, pd.Series): y = y.values else: X = X if (self.isPure(y)) or (len(y) <= min_sample) or (counter == max_depth): leaf = self.createLeaf(y, tree_type) if isinstance(leaf, float): if not leaf.is_integer(): leaf = round(leaf,2) return Node(self.node_id, label=leaf, isTerminal=True) else: counter += 1 potential_splits = self.getPotentialSplits(X) best_split_column, best_split_value, opt_impurity =\ self.determinBestSplit(X, y, potential_splits) opt_impurity = round(opt_impurity,4) left_idx, right_idx = self.split(X,best_split_column,best_split_value) ## check for empty data if len(left_idx) == 0 or len(right_idx) == 0: leaf = self.createLeaf(y, tree_type) if isinstance(leaf, float): if not leaf.is_integer(): leaf = round(leaf,2) return Node(self.node_id, label=round(leaf,4), isTerminal=True) total_sample = len(y) col_name = col_names[best_split_column] if self.col_types[best_split_column] == 'continuous': question = f'{col_name} <= {best_split_value} '+\ f'{self.impurity_measure} : {opt_impurity} '+\ f'Samples : {total_sample}' else: question = f'{col_name} in {best_split_value} '+\ f'{self.impurity_measure} : {opt_impurity} '+\ f'Samples : {total_sample}' node = Node(self.node_id, label=question) left_child = self._growTree(X[left_idx,:],y[left_idx],tree_type,counter, min_sample, max_depth) right_child = self._growTree(X[right_idx,:],y[right_idx],tree_type,counter, min_sample, max_depth) if left_child.label == right_child.label: node = left_child else: node.leftNode = left_child node.rightNode = right_child left_child.parentNode = node right_child.parentNode = node return node line 10~18 먼저 첫 번째 호출에서는 칼럼명을 col_names 필드에 저장하고 X를 2d numpy array로 바꿔준다. 마찬가지로 y도 1d numpy array로 바꿔준다. line 21~26 그리고 해당 마디 안에 클래스가 하나밖에 없거나 샘플의 개수가 min_sample 이하일 때, 또는 나무의 깊이가 max_depth에 도달하면 성장을 멈추고 끝마디를 생성한다. line 29~34 counter는 하나씩 증가시켜준다. 이는 나무의 깊이를 확인하는데 필요하다. 그리고 모든 설명변수에 대해서 분리 기준 후보를 구하고 이를 이용하여 최적 변수와 분리 기준을 구한다. 해당 마디에서 불순도 측도값을 확인하기 위해 이를 opt_impurity에 저장한다. 그리고 최적 변수와 분리 기준을 이용하여 해당 마디에 속하는 y 데이터를 분리한다. line 37~42 y 데이터를 분리한 후 데이터 어느 한쪽에 데이터가 없을 경우 끝마디를 생성한다. 회귀나무의 경우 마디에 표시할 텍스트가 숫자이므로 너무 길어지지 않게 하기 위해 소수 둘째 자리만 표시하도록 했다. line 44~54 현재 마디에 속한 총 샘플 개수와 최적 변수의 칼럼명을 저장하고 노드에 표시할 텍스트를 해당 변수가 연속형인 경우와 범주형인 경우로 나누어서 설정한다. 연속형인 경우는 분리 기준보다 작거나 큰 것으로 나뉘므로 '<='을 포함시켰고 범주형은 분리 기준이 집합이 되므로 'in'을 포함시켰다. line 56~59 중간 마디를 생성한 후 왼쪽 마디와 오른쪽 마디를 생성한다. line 62~68 먼저 왼쪽 마디(left_child)와 오른쪽 마디(right_child)의 예측 결과가 같은 경우는 굳이 분리하지 않아도 되므로 이때에는 중간 마디를 왼쪽 끝마디로 대체한다. 그게 아니라면 왼쪽 마디와 오른쪽 마디의 부모를 중간 마디(node)로 설정한다. 이제 만들어진 의사결정나무를 가지고 새로운 데이터에 대하여 예측을 수행하는 함수를 정의했다. 예측은 predict를 통하여 할 수 있으며 predict 내부에 _traverse_tree 함수를 통하여 예측하는 구조로 되어있다. _traverse_tree는 끝마디에 도달하면 그 마디의 예측값을 리턴하고 끝마디가 아닌 경우는 노드에 포함된 질문에 따라서 _traverse_tree 함수를 재귀적으로 호출한다. class DecisionTree: ''' 중략 ''' def predict(self,X): return np.array([self._traverse_tree(x, self.root) for _, x in X.iterrows()]) def _traverse_tree(self, x, node): if node.isTerminal: if isinstance(node.label, str): node.label = node.label.replace(' ','') return node.label question = node.label.split(' ')[0] if ' <= ' in question: col_name, value = question.split(' <= ') if x[col_name] <= float(value): return self._traverse_tree(x, node.leftNode) return self._traverse_tree(x, node.rightNode) else: col_name, value = question.split(' in ') if x[col_name] in ast.literal_eval(value): return self._traverse_tree(x, node.leftNode) return self._traverse_tree(x, node.rightNode) 반응형 4. 예제 with Python 이제 앞에서 구현한 것과 실제 데이터를 이용하여 의사결정나무를 만들어보자. 여기에서는 sklearn 라이브러리에서 분류나무와 회귀나무를 만들어주는 DecisionTreeClassifier와 DecisionTreeRegressor 클래스의 사용법도 알아보고 결과를 비교해보고자 한다. 여기서 다룰 데이터는 그 유명한 붓꽃(Iris) 데이터, 타이타닉 데이터, 자전거 공유 데이터이다. 데이터는 아래에 첨부해 두었다(iris 데이터는 모듈에서 불러올 수 있다) - 타이타닉 데이터 - titanic.csv 0.06MB titanic_description.txt 0.00MB - 자전거 공유 데이터 - bike_share.csv 0.05MB bike_share_description.txt 0.00MB 1. 붓꽃 데이터 먼저 데이터를 불러오자. 이때 붓꽃 종류는 species 열에 넣어두었다. iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df['species'] = [iris.target_names[x] for x in iris.target] 여기서는 붓꽃 클래스를 분류하는 문제이므로 분류나무를 만들 것이다. 최대 깊이는 3으로 지정했고 4개 변수가 모두 연속형이므로 type_of_col 인자에 'continuous'가 4개 포함된 리스트를 넘겨주었다. 타입은 수동으로 지정했으므로 auto_determine_type_of_col은 False로 했다. X, y = df.iloc[:,:4], df['species'].values clf = DecisionTree() clf.fit(X,y, max_depth=3, type_of_col=['continuous']*4, auto_determine_type_of_col=False) 이제 만들어진 나무를 눈으로 확인해볼 시간이다. 혹시 오류가 나는 사람들은 graphviz를 설치하고 환경변수를 따로 설정해줘야 한다. 이에 대한 방법은 여기 를 참고하자. visualize_tree(clf.root) 나무가 잘 완성되었다. 참고로 위 시각화 결과를 그림파일로 저장하고 싶다면 다음과 같이 해주자. 아래 코드는 결과를 현재 위치에 'iris_tree.png'로 저장해주는 것이다. dot = visualize_tree(clf.root) dot.format = 'png' dot.render(filename='iris_tree', directory='./', cleanup=True) 이 나무의 훈련데이터 정확도를 알아보자(테스트 데이터가 아니다). np.sum(y == clf.predict(X))/len(y) 97.3%가 나왔다. 다시 한번 말하지만 이는 테스트 데이터를 이용한 것은 아니다. 따라서 실제 모형 성능 평가 시에는 훈련데이터와 테스트 데이터를 나눠야 할 것이다. 이번엔 sklearn을 이용하여 나무를 생성해보자. sklearn.tree.DecisionTreeClassifier 클래스를 이용하여 분류나무를 생성할 수 있다. 사용법은 인스턴스 생성시 불순도 측도 criterion, 최대 깊이 max_depth, 자식 마디의 최소 샘플수 min_samples_split을 지정한다. 그리고 fit을 통하여 분류나무를 생성한다. X, y = iris.data, iris.target clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_split=5) clf = clf.fit(X, y) 여기서 만들어진 나무가 앞서 구현한 나무와 성능이 같은지 확인해보았다. np.sum(y == clf.predict(X))/len(y) 정확하게 일치하였다. sklearn에서는 tree.export_graphviz를 통하여 시각화할 수 있다. dot_data = tree.export_graphviz(clf, out_file=None, feature_names=iris.feature_names, class_names=iris.target_names, filled=False, rounded=True, special_characters=False) graph = graphviz.Source(dot_data) graph 만약 결과를 그림파일로 저장하고 싶다면 아래 코드를 추가한다. graph.format = 'png' graph.render(filename='titanic_tree_sk', directory='./', cleanup=True) 엔트로피 계산이 조금 다르게 나왔다. 이는 나중에 확인해봐야겠다. 그리고 분리기준이 다른데 나는 실제 데이터 값을 사용했고 sklearn에서는 인접한 두 값의 중간값을 사용했기 때문이다. 여기서 주목해야 할 부분은 위 빨간 네모 안에 있는 3개의 마디가 모두 'virginica'으로 예측한다는 것 이다. 이 경우 사실 분리할 필요가 없고 이 3개 노드를 그냥 하나의 끝마디로 해도 무방하다. 앞에서 살펴본 나무가 바로 이러한 점을 반영한 것이다(앞의 그림에서 확인해보자). 붓꽃 데이터를 통해 확인해본 결과 앞서 구현한 의사결정나무가 잘 작동하는 것 같다. 반응형 2. 타이타닉 데이터 먼저 데이터를 불러온다. df = pd.read_csv('../dataset/titanic.csv') 이 데이터에는 결측치가 포함되어 있다. 제거하는 대신 Age은 중간값, Embark는 최빈값으로 채웠다. median_age = df['Age'].median() mode_embarked = df['Embarked'].mode()[0] df = df.fillna({'Age':median_age, 'Embarked':mode_embarked}) 이제 분류나무를 만들어보자. 최대 깊이는 4로 설정하였다. 여기서는 변수의 타입을 자동으로 설정(디폴트)했다. X = df[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']] y = df['Survived'].values clf = DecisionTree() clf.fit(X,y,max_depth=4) 시각화해보자. visualize_tree(clf.root) 이제 훈련데이터를 통하여 훈련 정확도를 체크해보자. np.sum(y == clf.predict(X))/len(y) 훈련 정확도는 83.8% 정도가 된다. 이번엔 sklearn을 이용해보자. sklearn 문서에 따르면 sklearn에서는 변수가 범주형인 경우를 구현하지 않았다고 한다. ㅠ.ㅠ 하루빨리 구현되었으면 좋겠다. (범주를 숫자로 바꿔서 해볼 수 있겠지만 완벽하지 않다) 따라서 연속형 변수만 따로 뽑아주었다. X = df[['Age', 'Fare']] y = df['Survived'] 나무를 만들어보자. tree.DecisionTreeClassifier를 이용하여 분류나무 만드는 법은 앞에서 설명했으므로 생략한다. clf = tree.DecisionTreeClassifier(max_depth=4, min_samples_split=5) clf = clf.fit(X, y) 정확도를 확인해보자. np.sum(y == clf.predict(X))/len(y) 70.9%가 나왔다. 모든 변수를 고려하지 않았기 때문에 앞에서 살펴본 83.8% 보다 낮은 것은 당연하다. 나무를 시각화해보자. dot_data = tree.export_graphviz(clf, out_file=None, feature_names=X.columns, class_names=[str(x) for x in np.unique(y)], filled=False, rounded=True, special_characters=False) graph = graphviz.Source(dot_data) graph 나는 이 경우에도 내가 구현한 모형이 정확하게 일치하는지 확인하고 싶었다. 앞에서 지니 지수를 사용했으므로 이번엔 불순도의 측도를 지니 지수로 했으며 최대 깊이도 동일하게 4로 설정하였다. myclf = DecisionTree() myclf.fit(X,y,impurity_measure='gini',type_of_col=['continuous']*2, max_depth=4) 정확도를 확인해보았다. 앞에 결과와 정확하게 소수점 한자리도 틀리지 않고 정확하다. 하는 김에 시각화도 해보았다. 3. 자전거 공유 데이터 이번엔 회귀나무를 만들 것이다. 데이터를 불러오자. df = pd.read_csv('../dataset/bike_share.csv') 날짜 정보를 좀 더 효율적으로 사용하기 위하여 아래와 같이 추가적인 변수를 만들어 준다. df = df.rename(columns = {"dteday": "date"}) date_column = pd.to_datetime(df.date) df["day_of_year"] = date_column.dt.dayofyear df["day_of_month"] = date_column.dt.day df["quarter"] = date_column.dt.quarter df["week"] = date_column.dt.isocalendar()['week'] df["is_month_end"] = date_column.dt.is_month_end df["is_month_start"] = date_column.dt.is_month_start df["is_quarter_end"] = date_column.dt.is_quarter_end df["is_quarter_start"] = date_column.dt.is_quarter_start df["is_year_end"] = date_column.dt.is_year_end df["is_year_start"] = date_column.dt.is_year_start df.set_index('date', inplace=True) df['response'] = df['cnt'] df.drop(['cnt','instant','registered'],axis=1,inplace=True) 이번엔 데이터 셋을 나누었다. ## split data train_df = df.iloc[:-61] test_df = df.iloc[-61:] # Nov and Dec of 2012 회귀나무를 만들어보자. X = train_df.drop('response',axis=1) y = train_df['response'] type_of_col = [] for col in X.columns: if col not in ['temp', 'atemp', 'hum', 'windspeed']: type_of_col.append('categorical') else: type_of_col.append('continuous') reg = DecisionTree(tree_type = 'regression') reg.fit(X,y,impurity_measure='mse',max_depth=3) 만들어진 회귀나무는 직접 봐야 속이 후련할 것이다. 테스트 데이터를 통해 모형의 성능을 알아보자. 성능의 측도는 오차제곱합으로 선택하였다. X_test = test_df.drop('response',axis=1) y_test = test_df['response'] np.mean(np.sum(np.square(y_test - reg.predict(X_test)))) 120,894,567 정도가 나왔다. 이번엔 sklearn으로 회귀나무를 만들어보자. sklearn.tree.DecisionTreeRegressor를 이용하여 만들 수 있다. 여기에서도 연속형 변수만 가져왔다. ## split data df = df[['temp', 'atemp', 'hum', 'windspeed', 'casual','response']] train_df = df.iloc[:-61] test_df = df.iloc[-61:] # Nov and Dec of 2012 X = train_df.drop('response',axis=1) y = train_df['response'] DecisionTreeRegressor 클래스의 인스턴스를 생성한다. 여기서는 최대 깊이 3, 최소 자식 마디에 포함된 샘플 수는 5로 지정했다. regressor = tree.DecisionTreeRegressor(max_depth=3, min_samples_split=5) regressor.fit(X, y) 오차제곱합을 계산해보면 다음과 같다. X_test = test_df.drop('response',axis=1) y_test = test_df['response'] np.mean(np.sum(np.square(y_test - regressor.predict(X_test)))) 오차 제곱합은 216,335,303로 계산되었다. 이는 앞에서 모든 변수를 다 고려한 경우의 오차 제곱합보다 두배 정도로 높은 것이다. 이제 시각화해보자. dot_data = tree.export_graphviz(regressor, out_file=None, feature_names=X.columns, filled=False, rounded=True, special_characters=False) graph = graphviz.Source(dot_data) graph 이번에도 내가 만든 모형의 결과가 sklearn을 사용한 결과와 일치하는지 확인해보았다. reg = DecisionTree(tree_type = 'regression') reg.fit(X,y,impurity_measure='mse',max_depth=3) 마찬가지로 오차 제곱합을 구해주었다. np.mean(np.sum(np.square(y_test - reg.predict(X_test)))) 값이 살짝 다르다. 내가 구현한 코드에서는 회귀 나무의 예측값을 소수점 2번째 자리까지 나타내도록 했는데 이러한 차이로 인하여 값이 다르게 나온 것이다. 시각화 결과를 비교해보면 나무는 동일한 것을 알 수 있다. 이번 포스팅에서는 의사결정나무의 정의, 불순도 측도, 나무 생성 알고리즘 그리고 파이썬을 이용하여 구현해보고 이를 실제 데이터에 적용해보았다. 또한 sklearn을 이용하여 나무를 만드는 방법도 알아보았다. 이번 포스팅은 특히 힘들었다. 의사결정나무를 만드는 방법은 유투브에 도움을 많이 받았지만 시각화에서 삽질을 많이했기 때문이다. 내가 직접 시각화를 해야겠다고 생각한 이유는 R이나 sklearn에서 제공하는 나무 그림이 맘에 들지 않아서다.. 그래도 이번 기회에 시각화도 해보고 의사결정나무를 이론적으로 공부한 것과 더불어 구현까지 해봐서 뜻깊은 시간이었다. 다음 포스팅에서는 가지치기를 소개하려고 한다. 물론 내 관심이 다른 곳에 생기면 다른 주제로 포스팅할 수 있다 ㅎㅎ;; 참고자료 Decision Tree : Gini Index vs Entropy - https://quantdare.com/decision-trees-gini-vs-entropy/ 김용대 외 4명 R을 이용한 데이터마이닝 Decision Tree in Python - https://www.youtube.com/channel/UCbXgNpp0jedKWcQiULLbDTA Coding a Decision Tree from Scratch in Python - https://www.youtube.com/channel/UCCtkE-r-0Mvp7PwAvxzSdvw Decision Tree - https://scikit-learn.org/stable/modules/tree.html

의사 결정 나무 예시 [feat.무료 템플릿 다운로드]

의사 결정 나무 예시 [feat.무료 템플릿 다운로드]

1. 의사 결정 나무란 무엇인가?

네이버 지식백과에 의하면 “의사 결정 나무”(decision tree)란 어느 방안이 선택될 것인가라는 것과 일어날 수 있는 불확실한 상황 중에서 어떤 것이 실현되는가라는 것에 의해 여러 결과가 생긴다는 상황을 나뭇가지 모양으로 도식화한 것입니다. 의사결정 나무를 구성하는 요소에는 결정나무의 골격이 되는 대안과 불확실한 상황, 결과로서의 이익 또는 손실, 불확실한 상황과 결과가 생기는 확률이 있는데요. 이 요소들이 결정점과 불확실점으로 결합되어 의사결정나무를 만들게 됩니다.

의사 결정 나무를 처음부터 그리기엔 시간이 많이 소모될 수 있는데요. 그래서 오늘은 무료 템플릿 9개를 가져왔습니다. 아래 템플릿을 이용하시면 프로젝트에 필요한 시간 및 리소스를 절약할 수 있습니다. 템플릿은 영어로 됐지만 EdrawMax 프로그램으로 열면, 텍스트를 한국어로 변경 가능합니다.

2. 의사결정 나무 템플릿 X 9개

예시 1: 의사결정 나무의 예

지정된 의사결정 나무의 예는 기대값 분석에 관한 것입니다. 이는 기업이 시간이 지남에 따라 다양한 생산성 앱을 개발하는 경우 발생할 수 있는 기업의 수익 가능성을 결정합니다. 이러한 예는 누군가 EdrawMax로 의사결정 나무를 만들려고 하는 경우 도움이 될 것입니다.

예시 2: 온라인 의사결정 나무

의사결정 나무의 예는 라이프 스타일과 유전학으로 신장결석 발병 위험을 예측하는 것 처럼 건강상의 위험을 나타냅니다. 온라인 의사결정 나무 메이커에서 이러한 예를 복사하고 플로우차트를 그릴 수 있습니다.

예시 3: 의사결정 나무 알고리즘의 예

세 번째 의사결정 나무의 예는 한 개인의 일상적 일과를 묘사합니다. 다이어그램은 정해진 날에 수행하기로 되어있는 다양한 활동을 보여줍니다. 이러한 기법은 시간 관리에 유용하고 계획을 단순하지만 효과적으로 만듭니다.

예시 4: 의사결정 나무 가지치기의 예

네 번째 의사결정 나무의 예는 모든 브랜치와 노드가 있습니다. 이러한 브랜치는 다양한 결과로 이어질 수 있는 대안적 기회 및 의사결정에 대해 알려줍니다.

예시 5: 파이썬(Python) 의사결정 나무

지정된 의사결정 나무의 예는 파이썬(Python) 의사결정 나무를 나타냅니다. 이는 복잡한 문제를 논리적으로 만듭니다. 또한 기계 학습에도 쓸모가 있습니다. 데이터 과학자들은 종종 이러한 기법을 사용합니다. 의사결정 나무는 사물을 이해하기 쉽게 해주고 시각적으로 끌리기도 합니다.

의사결정 나무는 운영 연구에서 전략적 파악에 이르기까지 다양한 문제를 해결하는 데 유용합니다.

예시 6: 의사 결정 나무 예시

지정된 의사결정 나무의 예는 사건의 기회와 가능한 결과를 추측하기 위한 의사결정 지원 툴입니다. 리소스 비용을 절감할 수 있고 이 나무는 쓸모있는 목적에 유용합니다.

많은 이산 속성 또는 부울(Boolean) 함수는 의사결정 절차를 쉽게 만들고, 더욱이 의사결정 나무 알고리즘은 문제를 해결하는 데 도움을 줍니다.

예시 7: 대화형 의사 결정 나무

지정된 의사 결정 나무의 예는 상장 기업 타임라인에 관한 것입니다. 이는 시간이 지남에 따라 기업의 발전 경로를 파악하는 데 유용합니다. 템플릿은 타임라인의 시각적 표시 역할을 합니다.

예시 8: 데이터 마이닝의 의사결정 나무

이러한 의사 결정 나무 템플릿은 로직으로 마무리하는 데 유용합니다. 이것으로 인해 평가 프로세스가 쉬워집니다. 또한 설명 가능성, 해석 가능성 및 의사결정 지원 시스템에도 도움이 됩니다.

예시 9: 통계적 검정 의사결정 나무

지정된 의사결정 나무의 예는 취업 면접에 대한 보기입니다. 이는 긍정적이거나 부정적인 반응에 도달하는 데 유용하고 일련의 결과를 낳을 수 있습니다

3. 의사 결정 나무를 쉽게 그릴 수 있는 소프트웨어-EdrawMax

의사 결정 나무 프로그램 EdrawMax를 사용하면 쉽고 빠르게 의사 결정 나무 다이어그램을 그릴 수 있습니다. 또한, 링크 생성을 클릭하면 팀원들과 공유도 가능합니다. 다이어그램 소프트웨어중 독보적으로 한국어를 지원하고, 무료 템플릿도 많습니다.

EdrawMax는 초보자도 어려움 없이 다이어그램을 그릴 수 있도록 직관적이고, 간편하게 디자인되었습니다. 프로그램에 내장된 의사 결정 나무 템플릿을 바로 편집하거나 또는 에서 다른 사용자들이 만든 수천개의 템플릿을 무료로 가져다 사용할 수도 있습니다.

EdrawMax를 선택하는 이유 280 가지 이상의 다이어그램 유형.

팀원과 공동 협업 가능, 클라우드 지원.

개인 클라우드 지원, 강력한 호환성(비지오 파일 호환 가능).

인쇄, 공유, 다운로드 가능.

26000개 이상의 기호 및 아이콘.

2000개 이상의 무료 템플릿.

관련 글

4. 결정 트리(Decision Tree)

결정 트리(Decision Tree, 의사결정트리, 의사결정나무라고도 함)는 분류(Classification)와 회귀(Regression) 모두 가능한 지도 학습 모델 중 하나입니다. 결정 트리는 스무고개 하듯이 예/아니오 질문을 이어가며 학습합니다. 매, 펭귄, 돌고래, 곰을 구분한다고 생각해봅시다. 매와 펭귄은 날개를 있고, 돌고래와 곰은 날개가 없습니다. ‘날개가 있나요?’라는 질문을 통해 매, 펭귄 / 돌고래, 곰을 나눌 수 있습니다. 매와 펭귄은 ‘날 수 있나요?’라는 질문으로 나눌 수 있고, 돌고래와 곰은 ‘지느러미가 있나요?’라는 질문으로 나눌 수 있습니다. 아래는 결정 트리를 도식화한 것입니다.

출처: 텐서 플로우 블로그

이렇게 특정 기준(질문)에 따라 데이터를 구분하는 모델을 결정 트리 모델이라고 합니다. 한번의 분기 때마다 변수 영역을 두 개로 구분합니다. 결정 트리에서 질문이나 정답을 담은 네모 상자를 노드(Node)라고 합니다. 맨 처음 분류 기준 (즉, 첫 질문)을 Root Node라고 하고, 맨 마지막 노드를 Terminal Node 혹은 Leaf Node라고 합니다.

출처: ratsgo’s blog

전체적인 모양이 나무를 뒤짚어 높은 것과 같아서 이름이 Decision Tree입니다.

프로세스

결정 트리 알고리즘의 프로세스를 간단히 알아보겠습니다.

출처: 텐서 플로우 블로그

먼저 위와 같이 데이터를 가장 잘 구분할 수 있는 질문을 기준으로 나눕니다.

출처: 텐서 플로우 블로그

나뉜 각 범주에서 또 다시 데이터를 가장 잘 구분할 수 있는 질문을 기준으로 나눕니다. 이를 지나치게 많이 하면 아래와 같이 오버피팅이 됩니다. 결정 트리에 아무 파라미터를 주지 않고 모델링하면 오버피팅이 됩니다.

출처: 텐서 플로우 블로그

가지치기(Pruning)

오버피팅을 막기 위한 전략으로 가지치기(Pruning)라는 기법이 있습니다. 트리에 가지가 너무 많다면 오버피팅이라 볼 수 있습니다. 가지치기란 나무의 가지를 치는 작업을 말합니다. 즉, 최대 깊이나 터미널 노드의 최대 개수, 혹은 한 노드가 분할하기 위한 최소 데이터 수를 제한하는 것입니다. min_sample_split 파라미터를 조정하여 한 노드에 들어있는 최소 데이터 수를 정해줄 수 있습니다. min_sample_split = 10이면 한 노드에 10개의 데이터가 있다면 그 노드는 더 이상 분기를 하지 않습니다. 또한, max_depth를 통해서 최대 깊이를 지정해줄 수도 있습니다. max_depth = 4이면, 깊이가 4보다 크게 가지를 치지 않습니다. 가지치기는 사전 가지치기와 사후 가지치기가 있지만 sklearn에서는 사전 가지치기만 지원합니다.

알고리즘: 엔트로피(Entropy), 불순도(Impurity)

불순도(Impurity)란 해당 범주 안에 서로 다른 데이터가 얼마나 섞여 있는지를 뜻합니다. 아래 그림에서 위쪽 범주는 불순도가 낮고, 아래쪽 범주는 불순도가 높습니다. 바꾸어 말하면 위쪽 범주는 순도(Purity)가 높고, 아래쪽 범주는 순도가 낮습니다. 위쪽 범주는 다 빨간점인데 하나만 파란점이므로 불순도가 낮다고 할 수 있습니다. 반면 아래쪽 범주는 5개는 파란점, 3개는 빨간점으로 서로 다른 데이터가 많이 섞여 있어 불순도가 높습니다.

출처: ratsgo’s blog

한 범주에 하나의 데이터만 있다면 불순도가 최소(혹은 순도가 최대)이고, 한 범주 안에 서로 다른 두 데이터가 정확히 반반 있다면 불순도가 최대(혹은 순도가 최소)입니다. 결정 트리는 불순도를 최소화(혹은 순도를 최대화)하는 방향으로 학습을 진행합니다.

엔트로피(Entropy)는 불순도(Impurity)를 수치적으로 나타낸 척도입니다. 엔트로피가 높다는 것은 불순도가 높다는 뜻이고, 엔트로피가 낮다는 것은 불순도가 낮다는 뜻입니다. 엔트로피가 1이면 불순도가 최대입니다. 즉, 한 범주 안에 서로 다른 데이터가 정확히 반반 있다는 뜻입니다. 엔트로피가 0이면 불순도는 최소입니다. 한 범주 안에 하나의 데이터만 있다는 뜻입니다. 엔트로피를 구하는 공식은 아래와 같습니다.

엔트로피 공식

(Pi = 한 영역 안에 존재하는 데이터 가운데 범주 i에 속하는 데이터의 비율)

엔트로피 예제

공식을 활용하여 엔트로피를 구하는 예제를 살펴보겠습니다. 아래는 경사, 표면, 속도 제한을 기준으로 속도가 느린지 빠른지 분류해놓은 표입니다.

경사 표면 속도 제한 속도 steep bumpy yes slow steep smooth yes slow flat bumpy no fast steep smooth no fast

첫줄을 보면 경사가 가파르고(steep), 표면이 울퉁불퉁하고(bumpy), 속도 제한이 있다면(yes) 속도가 느리다(slow)라고 분류했습니다. X variables가 경사, 표면, 속도 제한이고, Y variable이 속도입니다. 이때 엔트로피를 기반으로 결정 트리 모델링 해보겠습니다.

속도 라벨에는 slow, slow, fast, fast로 총 4개의 examples가 있습니다. Pi는 한 영역 안에 존재하는 데이터 가운데 범주 i에 속하는 데이터의 비율이라고 했습니다. i를 slow라고 했을 때, P_slow = 0.5입니다. slow 라벨 갯수 = 2개, 전체 examples 수 = 4개이기 때문에 P_slow = 2/4 = 0.5입니다. 마찬가지로, P_fast도 0.5입니다. fast 라벨 갯수가 2개이기 때문에 2/4로 0.5입니다. 즉 P_slow = 0.5, P_fast = 0.5입니다.

그렇다면 현재 범주 전체의 엔트로피는 얼마일까요? 바로 1입니다. 서로 다른 데이터가 정확히 반반 있기 때문입니다. 위에서 봤던 엔트로피 공식에 그래도 대입을 해 엔트로피를 구해보겠습니다.

엔트로피 공식

Entropy = – P_slow * log₂(P_slow) – P_fast * log₂(P_fast) = – 0.5 * log₂(0.5) – 0.5 * log₂(0.5) = 1

공식에 의해서도 1이라고 구할 수 있고, 데이터가 정확히 반반 (slow 2개, fast 2개)이므로 1이라고 할 수 있습니다.

정보 획득 (Information gain)

엔트로피가 1인 상태에서 0.7인 상태로 바뀌었다면 정보 획득(information gain)은 0.3입니다. 분기 이전의 엔트로피에서 분기 이후의 엔트로피를 뺀 수치를 정보 획득이라고 합니다. 정보 획득은 아래와 같이 공식화할 수 있습니다.

Information gain = entropy(parent) – [weighted average] entropy(children)

entropy(parent)는 분기 이전 엔트로피이고, entropy(children)은 분기 이후 엔트로피입니다. 이때, [weighted average] entropy(children)는 entropy(childeren)의 가중 평균을 뜻합니다. 분기 이후 엔트로피에 대해 가중 평균을 하는 이유는 분기를 하면 범주가 2개 이상으로 쪼개지기 때문입니다. 범주가 하나라면 위 엔트로피 공식으로 바로 엔트로피를 구할 수 있습니다. 하지만 범주가 2개 이상일 경우 가중 평균을 활용하여 분기 이후 엔트로피를 구하는 것입니다.

결정 트리 알고리즘은 정보 획득을 최대화하는 방향으로 학습이 진행됩니다. 어느 feature의 어느 분기점에서 정보 획득이 최대화되는지 판단을 해서 분기가 진행됩니다.

위에서 예로 든 속도 문제를 다시 보겠습니다. 맨 처음 전체 엔트로피 = 1 이라고 했습니다. 즉, entropy of parent = 1입니다.

entropy(parent) = 1

경사 기준 분기

우선, 경사(grade)를 기준으로 첫 분기를 해보겠습니다. 전체 데이터 중 steep는 3개, flat는 1개 있습니다. steep와 flat을 기준으로 분기를 해주면 결과는 아래와 같습니다. steep에 해당하는 데이터는 총 3개이며, 이때의 속도는 slow, slow, fast입니다. 반면 flat에 해당하는 데이터는 1개이며, 이때의 속도는 fast입니다. flat에 해당하는 노드의 엔트로피는 얼마일까요? (아래 그림에서 오른쪽 노드) 한 노드에 fast라는 하나의 데이터만 존재하므로 엔트로피는 0입니다.

따라서, entropy(flat) = 0 입니다.

출처: Udacity

이제 entropy(steep). 즉, steep로 분기했을 때의 엔트로피를 구해보겠습니다. 이는 위 그림의 왼쪽 노드에 해당합니다. slow가 2개, fast가 1개이므로 P_slow = 2/3, P_fast = 1/3입니다. 따라서 엔트로피 공식에 의해

entropy(steep) = – P_slow * log2(P_slow) – P_fast * log2(P_fast) = – (2/3) * log2(2/3) – (1/3) * log2(1/3) = 0.9184 입니다.

entropy(flat) = 0 이고, entropy(steep) = 0.9184입니다. 이제 분기 이후 노드에 대한 가중 평균을 구해보겠습니다.

[weighted average] entropy(children) = weighted average of steep * entropy(steep) + weighted average of flat * entropy(flat) = 3/4 * (0.9184) + 1/4 * (0) = 0.6888

(weighted average of steep = 3/4인 이유는 4개의 데이터 중 steep에 해당하는 데이터는 3개이기 때문입니다. 마찬가지로 weighted average of flat = 1/4인 이유는 4개의 데이터 중 flat에 해당하는 데이터는 1개이기 때문입니다.)

따라서, 경사(grade)를 기준으로 분기한 후의 엔트로피는 0.6888 입니다. 이제 정보 획득 공식을 통해 정보 획득량을 구해보겠습니다.

information gain = entropy(parent) – [weighted average] entropy(children) = 1 – 0.6888 = 0.3112

경사 feature를 기준으로 분기를 했을 때는 0.3112만큼의 정보 획득(information gain)이 있다는 뜻입니다.

표면 기준 분기

표면(bumpiness)을 기준으로 분기했을 때는 bumpy에는 slow, fast, smooth에도 slow, fast가 있습니다. 하나의 범주에 서로 다른 데이터가 정확히 반반 있습니다. 이럴 때 엔트로피는 1입니다. 공식으로 계산을 해보겠습니다.

entropy(bumpy) = – P_slow * log2(P_slow) – P_fast * log2(P_fast) = – (1/2) * log2(1/2) – (1/2) * log2(1/2) = 1

entropy(smooth) = – P_slow * log2(P_slow) – P_fast * log2(P_fast) = – (1/2) * log2(1/2) – (1/2) * log2(1/2) = 1

입니다. 따라서,

information gain = entropy(parent) – [weighted average] entropy(children) = 1 – (2/4) * 1 – (2/4) * 1 = 0 입니다.

표면을 기준으로 분기했을 때는 정보 획득이 전혀 없다는 뜻입니다.

속도제한 기준 분기

출처: Udacity

마찬가지로 계산을 해보면

entropy(yes) = – P_slow * log2(P_slow) – P_fast * log2(P_fast) = – (1) * log2(1) – (0) * log2(0) = 0

entropy(no) = – P_slow * log2(P_slow) – P_fast * log2(P_fast) = – (0) * log2(0) – (1) * log2(1) = 0

입니다. 따라서,

information gain = 1 – (2/4) * 0 – (2/4) * 0 = 1입니다.

경사, 표면, 속도제한 기준으로 분기 했을 때 정보 획득은 각각 0.3112, 0, 1입니다. 결정트리는 정보 획득이 가장 많은 방향으로 학습이 진행된다고 했습니다. 따라서 첫 분기점을 속도제한 기준으로 잡습니다. 이런식으로 max_depth나 min_sample_split으로 설정한 범위까지 분기를 하게 됩니다. 이것이 바로 결정트리의 전체적인 알고리즘입니다.

실습

사이킷런에서 제공하는 유방암 데이터를 활용하여 결정트리 분류 실습을 해보겠습니다. 전반적인 방식은 지금까지 했던 다른 머신러닝 모델과 유사합니다. Classifier를 만들고, fitting한 뒤, Test해보는 식입니다. Classifier만 DecisionTreeClassfier를 사용한다는 것을 제외하고는 다른게 거의 없습니다.

from sklearn.tree import DecisionTreeClassifier from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split cancer = load_breast_cancer() X_train, X_test, y_train, y_test = train_test_split( cancer.data, cancer.target, stratify=cancer.target, random_state=42) tree = DecisionTreeClassifier(random_state=0) tree.fit(X_train, y_train) print(“훈련 세트 정확도: {:.3f}”.format(tree.score(X_train, y_train))) print(“테스트 세트 정확도: {:.3f}”.format(tree.score(X_test, y_test))) >>> 훈련 세트 정확도: 1.000 >>> 테스트 세트 정확도: 0.937

결정 트리의 default는 max_depth, min_sample_split 제한이 없으므로 한 범주에 한 종류의 데이터가 남을 때까지 가지를 칩니다. 따라서 훈련 세트의 정확도는 100%인데 테스트 세트의 정확도는 93.7%입니다.

tree = DecisionTreeClassifier(max_depth=4, random_state=0) tree.fit(X_train, y_train) print(“훈련 세트 정확도: {:.3f}”.format(tree.score(X_train, y_train))) print(“테스트 세트 정확도: {:.3f}”.format(tree.score(X_test, y_test))) >>> 훈련 세트 정확도: 0.988 >>> 테스트 세트 정확도: 0.951

반면, max_depth=4로 설정해주면 오버피팅을 막아 훈련 세트 정확도는 아까보다 떨어지지만 테스트 세트 정확도가 더 높아졌습니다.

References

Reference1: 텐서 플로우 블로그 (결정 트리)

Reference2: ratsgo’s blog (의사결정나무)

분류(Classifier)/회귀(Regressor)/가지치기(Pruning)

의사결정 나무는 간단하게 말해서 if~else와 같이 특정 조건을 기준으로 O/X로 나누어 분류/회귀를 진행하는 tree구조의 분류/회귀 데이터마이닝 기법이다.

이해도가 매우 높고 직관적이라는 장점이 있다. 그렇기에 많이 사용되며, 의사결정나무도 많은 머신러닝 기법과 동일하게 종속변수의 형태에 따라 분류와 회귀 문제로 나뉜다.

종속변수가 범주형일 경우 Decision Tree Classification으로 분류를 진행하고, 종속변수가 연속형일 경우 Decision Tree Regression으로 회귀를 진행한다.

상세한 원리와 수학적/직관적 이해는 아래 링크를 통해서 학습하길 바란다.

https://todayisbetterthanyesterday.tistory.com/39

1. 기본적인 의사결정나무의 형태

# sklearn 모듈의 tree import from sklearn import tree # 간단한 데이터셋 생성 X = [[0, 0], [1, 1]] Y = [0, 1] # 의사결정나무 적합 및 학습데이터 예측 clf = tree.DecisionTreeClassifier() clf = clf.fit(X, Y) clf.predict([[1, 1]])

위의 코드블럭은 가장 간단한 의사결정나무의 예시이다. 학습데이터에 2개의 설명변수만 사용하여 2행짜리 데이터를 생성하였다. 종속변수 또한 설명변수가 2행이기에 2개밖에 안된다.

이제 sklearn.datasets에 존재하는 iris 데이터를 통해서 구현하고 시각화 작업을 함께 해보자.

2. 라이브러리 import & 실습 데이터 로드

# sklearn 모듈의 tree import from sklearn import tree from sklearn.datasets import load_iris from os import system # graphviz 라이브러리 설치를 위함 # graphviz 라이브러리 설치 // 아래 예제에서 오류나는 경우 anaconda prompt에서 설치바람 system(“pip install graphviz”) # graphviz 사용에 있어서 error발생원인이 환경변수일 경우 환경변수 추가 필요 # 환경변수 추가 후 환경변수 설정 아래코드 # os.environ[“PATH”] += os.pathsep + ‘C:\\Program Files (x86)\\Graphviz2.38\\bin\\’ # iris 실습데이터 로드 iris = load_iris()

iris 데이터는 이전 게시글의 예제에서 계속적으로 다루어왔다. 4개의 feature 변수가 있으며, 3개의 target 변수가 있다.

자세하게 알아보는 과정은 그렇기에 생략하겠다.

2. Decision Tree Classifier ( 의사결정분류나무 )

기본적인 의사결정 나무 : Information Gain – Gini

# 의사결정나무 분류 clf = tree.DecisionTreeClassifier() # 종속변수가 현재 범주형 clf = clf.fit(iris.data, iris.target) # feature, target

이것은 의사결정나무 분류모형을 적합시킨 것이다. tree를 시각화시켜서 자세하게 살펴보자

# 시각화 dot_data = tree.export_graphviz(clf, # 의사결정나무 모형 대입 out_file = None, # file로 변환할 것인가 feature_names = iris.feature_names, # feature 이름 class_names = iris.target_names, # target 이름 filled = True, # 그림에 색상을 넣을것인가 rounded = True, # 반올림을 진행할 것인가 special_characters = True) # 특수문자를 사용하나 graph = graphviz.Source(dot_data) graph

graphviz는 tree를 도식화하는 라이브러리이다. 각 매개변수에 대한 설명을 적어놓았으니 읽어보길 바란다.

의사결정나무는 맨 위의 1개 root노드부터 맨 아래 여러가지 노드들 즉, leaf노드들로 구성된다. 그리고 Decision Tree를 이용할 때, 가장 기본적인(아무런 매개변수를 주지 않았을 때) Information Gain 방식은 지니계수를 이용한다. 도식화된 Tree를 살펴보면 gini = xxx 가 써있음을 통해 알 수 있다. 이 gini계수는 엔트로피와 마찬가지로 낮을 수록 분류가 잘 된것으로 판단하며 기본적으로 의사결정나무는 이 Information Gain을 낮추는 방향으로 분류를 진행한다.

Information Gain – entropy 의사결정나무

# 의사결정나무 분류 clf2 = tree.DecisionTreeClassifier(criterion = “entropy”) # Information Gain – entropy clf2 = clf2.fit(iris.data, iris.target) # feature, target

# 시각화 dot_data2 = tree.export_graphviz(clf2, # 의사결정나무 모형 대입 out_file = None, # file로 변환할 것인가 feature_names = iris.feature_names, # feature 이름 class_names = iris.target_names, # target 이름 filled = True, # 그림에 색상을 넣을것인가 rounded = True, # 반올림을 진행할 것인가 special_characters = True) # 특수문자를 사용하나 graph2 = graphviz.Source(dot_data2) graph2

과정은 지니와 모두 동일하나 DecisionTreeClassifier()을 생성할 때 매개변수로 criterion = “entropy”만 추가하였다.

즉, 의사결정나무의 분류 기준을 entropy로 한다는 것이다. 이제 위의 결과표를 보면 gini가 아니라 entropy가 쓰인 것을 확인할 수 있다.

위의 두 의사결정나무 모형은 너무 많은 노드들이 존재한다. 게다가 마지막 노드에서 gini와 entropy 모두 0.0을 출력한다. 이는 완벽하게 분리시켰다고 말할 수 도 있지만, 사실 억지로 분류시킨 것에 가깝다. 그렇기에 과적합(Overfitting)이 발생한 것이다.

추가적으로 한 가지 더 알아야 할 것이 있다. 위의 색은 3가지 색의 계열로 이루어져 있다. 같은 색 계열이면 같은 집단으로 분류를 한 것이며, 색이 진할수록 Information Gain(entropy, gini .. )이 낮은 것이다. 즉, 정확하게 분류를 했다는 것이다. 이는 상대적이기에 depth가 작으면 entropy가 높아도 진하게 출력될 수 있다.

이제 pruning(가지치기)라는 기법을 배워서 과적합을 방지하도록 학습해보자.

Pruning – 가지치기

# Pruning clf3 = tree.DecisionTreeClassifier(criterion = “entropy”, max_depth = 2) clf3.fit(iris.data, iris.target)

이번 실습에서는 가지치기를 최대 깊이를 제한시켜서 실습하고자 한다. 의사결정나무에서 깊이란 맨 마지막 leaf노드들이 root노드까지 바로 가는데 걸리는 조건(edge)의 개수이다. 이번 실습에서는 깊이제한을 2로 하였다.

사실 이 실습의 가지치기의 기준은 올바른 방식이 아니다. 가지치기를 진행하는 방법은 여러 기준이 있는데

1) 지니계수/엔트로피와 같은 Information Gain의 값이 일정 수준 이하로 안내려가도록

2) 가지의 개수 자체를 제한하는 방법

3) 이 실습과 같이 깊이를 제한하는 방법

등이 있다.

실제 통계용 언어로 많이 사용되는 R의 함수에서는 내부적으로 1)을 활용하여 가지치기를 진행한다. 그리고 이번 실습과 같이 3)을 활용하여 가지치기를 진행할 경우 Cross Validation등을 통해서 보다 정확한 깊이를 찾아낼 수 있다. 그렇기에 일반적으로 X라고 단정짓는 것은 좋은 방법은 아니다.

여튼 시각화를 통해서 확인해보자.

# 시각화 dot_data3 = tree.export_graphviz(clf3, # 의사나무 모형 대입 out_file = None, # file로 변환할 것인가 feature_names = iris.feature_names, # feature 이름 class_names = iris.target_names, # target 이름 filled = True, # 그림에 색상을 넣을것인가 rounded = True, # 반올림을 진행할 것인가 special_characters = True) # 특수문자를 사용하나 graph3 = graphviz.Source(dot_data3) graph3

가지치기의 기준으로 max_depth를 2로 주었더니, 트리의 깊이가 2로 변했다. 그리고 entropy 또한 0.4/0.151로 많이 높아졌다. 위의 DecisionTree의 gini/entropy는 0.0이었는데 분류가 너무 안된 것이 아닌가? 라고 생각할 수 있다.

하지만, 우리는 여태 train데이터를 예측했다. 그렇기에 학습데이터의 경우 가지가 무한정 많아지면 정확해질 수 밖에 없다. 만약 새로운 test데이터가 주어진다면, 오히려 과적합된 DecisionTree가 학습데이터 내에서 너무 이상값들에 집중해서 일반적인 새로운 test데이터를 제대로 예측하지 못할 수도 있다.

게다가 이 짧은 트리가 거창한 트리보다 훨씬 직관적이고 이해도가 높다. Decision Tree를 사용하는 가장 큰 이유중 하나가 바로 “직관적인 이해”인데 가지치기를 하지않고 무한한 가지를 만들면 Decision Tree를 사용하는 의미 또한 퇴색된다.

이제 Confusion matrix를 활용하여 3가지 분류기의 학습데이터를 분류하는 정확도를 확인해보자.

Confusion matrix를 활용한 정확도 비교

from sklearn.metrics import confusion_matrix # 1번 의사결정나무 – 지니계수 활용 confusion_matrix(iris.target, clf.predict(iris.data))

# 2번 의사결정나무 – entropy 활용 confusion_matrix(iris.target, clf2.predict(iris.data))

# 3번 의사결정나무 – 가지치기 작업 confusion_matrix(iris.target, clf3.predict(iris.data))

위의 결과를 보면 가지치기를 한 의사결정나무의 정확도가 가장 떨어진다. 하지만 학습데이터를 분류한 것이라는 사실을 염두해 두어야 한다. 만약 새로운 데이터가 들어오면 말했듯이 맨 마지막 가지치기의 의사결정나무가 일반화된 특징을 잡을 가능성이 높다.

Traing Set / Test Set 구분

여태까진 전부 학습데이터로 분류를 진행했지만 실제 데이터가 주어졌을 때, 데이터는 Train/(Validation)/Test로 나누어 학습할 가능성이 크다. 그렇기에 Train set과 Test set을 나누어 실습해보자.

# 데이터셋 분리 함수 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(iris.data #feature , iris.target #target , stratify = iris.target #층화추출법 , random_state = 1) #난수고정

위의 train_test_split의 매개변수중 stratify매개변수가 들어간 것을 볼 수 있다. 이것은 필수적으로 들어가야할 요소는 아니다. iris데이터셋의 경우 150개의 데이터밖에 없기에 무작위 추출이 진행된다면 target데이터가 치우쳐질 수도 있다.

그리고 이러한 사용은 제약/임상실험에서도 마찬가지다.(병에 안걸린사람이 걸린사람보다 훨씬 많으니 과소평가될 가능성이 있음). 그렇기에 데이터가 적은 이유로 고루고루 데이터를 추출시키기 위해 층화추출법을 사용한 것이다.

# train dataset clf4 = tree.DecisionTreeClassifier(criterion = “entropy”) clf4.fit(X_train, y_train) # test set predict confusion matrix confusion_matrix(y_test,clf4.predict(X_test))

위의 confusion matrix결과를 보면 가지치기를 하지 않았는데도 불구하고 3번에서 한 경우에 오분류가 발생했다. 이는 train data set과 test data set의 특성이 어느정도 달라서 학습의 분류결과가 완전하게 맞을 순 없다는 것을 보여준다.

3. Decision regression Tree ( 의사결정회귀나무 )

의사결정 회귀나무는 종속변수가 연속형 변수일때 진행한다. 기본적인 방식은 의사결정 분류나무와 동일하나 사용하는 함수가 다르다. 실습을 통해서 알아보자.

# 필요 라이브러리 import numpy as np from sklearn.tree import DecisionTreeRegressor # 회귀나무 함수 import matplotlib.pyplot as plt # 실습용 데이터셋 만들기 rng = np.random.RandomState(1) X = np.sort(5 * rng.rand(80, 1), axis=0) y = np.sin(X).ravel() # sin함수의 예측을 목표로한다 y[::5] += 3 * (0.5 – rng.rand(16)) # 이상치를 발생시킨다.

위의 코드를 통해 필요한 라이브러리를 로드하고, 실습에서 사용할 학습용 데이터셋을 만들었다. 기본적인 target변수의 형태는 sin함수를 따르도록 만들었으나, 이상치를 주었다.

# X_test set 생성 X_test = np.arange(0.0,5.0,0.01)[:,np.newaxis]

test를 진행하기 위해 X_test셋을 만들었다. 이를 가지고 예측을 하는 작업을 진행할 것이다.

Regression Tree 구축

# 깊이가 다른 두 Regression 나무 생성 regr1 = tree.DecisionTreeRegressor(max_depth = 2) regr2 = tree.DecisionTreeRegressor(max_depth = 5) # 두 가지 회귀나무 적합 regr1.fit(X,y) regr2.fit(X,y)

# 예측 y_1 = regr1.predict(X_test) y_2 = regr2.predict(X_test)

# 예측 결과물 y_1

# depth가 다른 두 회귀나무 도식화 plt.figure() plt.scatter(X, y, s=20, edgecolor=”black”, c=”darkorange”, label=”data”) plt.plot(X_test, y_1, color=”cornflowerblue”, label=”max_depth=2″, linewidth=2) plt.plot(X_test, y_2, color=”yellowgreen”, label=”max_depth=5″, linewidth=2) plt.xlabel(“data”) plt.ylabel(“target”) plt.title(“Decision Tree Regression”) plt.legend() plt.show()

위의 그림을 확인해보면 max_depth = 5인 의사결정회귀나무는 이상값에 영향을 더 크게 받았음을 확인할 수 있다. 오히려 max_depth = 2의 의사결정회귀나무가 이상값을 무시하고 전체적인 추세를 더 잘 잡는 것을 확인할 수 있다.

하지만 만약 sin함수에서 떨어져있는 점들이 이상값이 아니었다면, 저런 점들 또한 고려할 필요가 생긴다. 그렇기에 가지치기의 적절한 기준을 찾는 것 또한 분석가의 안목에 달려있다.

# depth = 5 의사결정 회귀나무 시각화 dot_data4 = tree.export_graphviz(regr2, out_file=None, filled=True, rounded=True, special_characters=True) graph4 = graphviz.Source(dot_data4) graph4

위를 보면 depth가 5인 의사결정나무의 부분이다. 이미지가 너무 커서 짤렸으나 한 가지 확인하고 가야할 것이 있다.

위에서 보면 value가 낮을 수록 같은 색계열에서 연한 색을 띈다. 그리고 value값이 높을 수록 진한 색을 띈다.

# depth =2 의사결정 회귀나무 시각화 dot_data5 = tree.export_graphviz(regr1, out_file=None, filled=True, rounded=True, special_characters=True) graph5 = graphviz.Source(dot_data5) graph5

위의 경우는 depth = 2의 의사결정 회귀나무이다. 회귀나무에서 보았듯이 분류나무와 분류하는 기준이 다르다. 분류나무에서는 Information Gain으로 entropy/gini지수를 사용했다면, 회귀나무에서는 회귀에 많이 사용하는 mse(mean squared error)가 기본적인 기준으로 작동한다.

즉, 회귀나무는 mse를 낮추는 방향으로 가지를 뻗어나아간다는 것이다.

이상 파이썬을 활용하여 의사결정분류나무(DecisionTreeClassifier)와 의사결정회귀나무(DecisionTreeRegression)에 대해서 알아보았고 이를 데이터 셋을 나누며 실습을 진행하고 시각화 또한 진행해 보았다.

키워드에 대한 정보 의사 결정 나무 예제

다음은 Bing에서 의사 결정 나무 예제 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기

  • 머신러닝
  • 의사결정트리
  • Decision Tree
  • 알고리즘
  • machine learning
  • algorithm
  • data structure
  • python
  • imagineer
  • 상상개발자
  • 쉽게
  • 쉬운
  • 기계학습
  • 의사결정나무
  • 디시전트리
[머신러닝] #의사결정트리 #(Decision #Tree) #알고리즘 #쉽게 #이해하기


YouTube에서 의사 결정 나무 예제 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 [머신러닝] 의사결정트리 (Decision Tree) 알고리즘 쉽게 이해하기 | 의사 결정 나무 예제, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

See also  밝은 아이 안과 | 미소아이안과 빠른 답변

Leave a Comment