의사결정 트리 분석

Beginner

This tutorial is from open-source community. Access the source code

소개

의사결정 트리 분류기는 분류 및 회귀 문제에 사용되는 인기 있는 머신 러닝 알고리즘입니다. 이는 특징 공간을 중복되지 않는 영역 집합으로 분할하고 각 영역에 대한 대상 값을 예측하는 트리 기반 모델입니다. 이 실습에서는 특징과 예측 대상 간의 관계에 대한 더 깊은 통찰력을 얻기 위해 의사결정 트리 구조를 분석하는 방법을 배울 것입니다.

VM 팁

VM 시작이 완료되면 왼쪽 상단 모서리를 클릭하여 Notebook 탭으로 전환하여 연습을 위한 Jupyter Notebook에 접근합니다.

때때로 Jupyter Notebook 이 완전히 로드되기까지 몇 초 정도 기다려야 할 수 있습니다. Jupyter Notebook 의 제한으로 인해 작업의 유효성 검사는 자동화될 수 없습니다.

학습 중 문제가 발생하면 Labby 에게 문의하십시오. 세션 후 피드백을 제공하면 문제를 신속하게 해결해 드리겠습니다.

의사결정 트리 분류기 학습

먼저, scikit-learn 의 load_iris 데이터셋을 사용하여 의사결정 트리 분류기를 학습해야 합니다. 이 데이터셋은 각각 50 개의 인스턴스를 가진 3 개의 클래스로 구성되어 있으며, 각 클래스는 아이리스 식물의 한 유형을 나타냅니다. 데이터셋을 학습용과 테스트용으로 분할하고 최대 3 개의 잎 노드를 가진 의사결정 트리 분류기를 학습할 것입니다.

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

iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

clf = DecisionTreeClassifier(max_leaf_nodes=3, random_state=0)
clf.fit(X_train, y_train)

이진 트리 구조 분석

의사결정 트리 분류기에는 tree_라는 속성이 있습니다. 이 속성을 통해 노드의 총 개수인 node_count와 트리의 최대 깊이인 max_depth와 같은 저수준 속성에 접근할 수 있습니다. 또한, 여러 개의 병렬 배열로 표현되는 전체 이진 트리 구조를 저장합니다. 이러한 배열을 사용하여 트리 구조를 탐색하여 각 노드의 깊이와 노드가 잎 노드인지 여부와 같은 다양한 속성을 계산할 수 있습니다. 아래는 이러한 속성을 계산하는 코드입니다.

import numpy as np

n_nodes = clf.tree_.node_count
children_left = clf.tree_.children_left
children_right = clf.tree_.children_right
feature = clf.tree_.feature
threshold = clf.tree_.threshold

node_depth = np.zeros(shape=n_nodes, dtype=np.int64)
is_leaves = np.zeros(shape=n_nodes, dtype=bool)
stack = [(0, 0)]  ## 루트 노드 ID(0) 와 깊이 (0) 로 시작
while len(stack) > 0:
    ## `pop` 을 통해 각 노드는 한 번만 방문되도록 합니다.
    node_id, depth = stack.pop()
    node_depth[node_id] = depth

    ## 노드의 왼쪽 및 오른쪽 자식이 같지 않으면 분할 노드입니다.
    is_split_node = children_left[node_id] != children_right[node_id]
    ## 분할 노드인 경우 왼쪽 및 오른쪽 자식과 깊이를 `stack` 에 추가하여 반복할 수 있도록 합니다.
    if is_split_node:
        stack.append((children_left[node_id], depth + 1))
        stack.append((children_right[node_id], depth + 1))
    else:
        is_leaves[node_id] = True

print(
    "이진 트리 구조에는 {n}개의 노드가 있으며 다음과 같은 트리 구조를 갖습니다:\n".format(n=n_nodes)
)
for i in range(n_nodes):
    if is_leaves[i]:
        print(
            "{space}node={node}는 잎 노드입니다.".format(
                space=node_depth[i] * "\t", node=i
            )
        )
    else:
        print(
            "{space}node={node}는 분할 노드입니다: "
            "X[:, {feature}] <= {threshold}이면 노드 {left}로, "
            "그렇지 않으면 노드 {right}로 이동합니다.".format(
                space=node_depth[i] * "\t",
                node=i,
                left=children_left[i],
                feature=feature[i],
                threshold=threshold[i],
                right=children_right[i],
            )
        )

의사결정 트리 시각화

scikit-learn 의 tree 모듈에서 plot_tree 함수를 사용하여 의사결정 트리를 시각화할 수도 있습니다.

from sklearn import tree
import matplotlib.pyplot as plt

tree.plot_tree(clf)
plt.show()

의사결정 경로 및 잎 노드 검색

decision_path 메서드를 사용하여 관심 있는 샘플의 의사결정 경로를 검색할 수 있습니다. 이 메서드는 관심 있는 샘플이 거치는 노드를 검색할 수 있도록 지표 행렬을 출력합니다. 관심 있는 샘플이 도달한 잎 노드 ID 는 apply 메서드를 사용하여 얻을 수 있습니다. 이 메서드는 각 관심 샘플이 도달한 잎 노드의 ID 배열을 반환합니다. 잎 노드 ID 와 decision_path를 사용하여 샘플 또는 샘플 그룹을 예측하는 데 사용된 분할 조건을 얻을 수 있습니다. 아래는 한 샘플에 대한 의사결정 경로 및 잎 노드를 검색하는 코드입니다.

node_indicator = clf.decision_path(X_test)
leaf_id = clf.apply(X_test)

sample_id = 0
## 샘플 `sample_id` 가 거치는 노드의 ID 를 얻습니다 (즉, 행 `sample_id`)
node_index = node_indicator.indices[
    node_indicator.indptr[sample_id] : node_indicator.indptr[sample_id + 1]
]

print("샘플 {id}를 예측하는 데 사용된 규칙:\n".format(id=sample_id))
for node_id in node_index:
    ## 잎 노드이면 다음 노드로 이동합니다.
    if leaf_id[sample_id] == node_id:
        continue

    ## 샘플 0 의 분할 특성 값이 임계값보다 작은지 확인합니다.
    if X_test[sample_id, feature[node_id]] <= threshold[node_id]:
        threshold_sign = "<="
    else:
        threshold_sign = ">"

    print(
        "의사결정 노드 {node} : (X_test[{sample}, {feature}] = {value}) "
        "{inequality} {threshold})".format(
            node=node_id,
            sample=sample_id,
            feature=feature[node_id],
            value=X_test[sample_id, feature[node_id]],
            inequality=threshold_sign,
            threshold=threshold[node_id],
        )
    )

여러 샘플의 공통 노드 확인

여러 샘플에 대해 decision_path 메서드와 toarray 메서드를 사용하여 지표 행렬을 밀집 배열로 변환하여 샘플들이 거치는 공통 노드를 확인할 수 있습니다.

sample_ids = [0, 1]
## 두 샘플 모두 거치는 노드를 나타내는 불리언 배열
common_nodes = node_indicator.toarray()[sample_ids].sum(axis=0) == len(sample_ids)
## 배열 내 위치를 사용하여 노드 ID 를 얻습니다.
common_node_id = np.arange(n_nodes)[common_nodes]

print(
    "\n다음 샘플 {samples}는 트리에서 노드 {nodes}를 공유합니다.".format(
        samples=sample_ids, nodes=common_node_id
    )
)
print("이는 모든 노드의 {prop}%입니다.".format(prop=100 * len(common_node_id) / n_nodes))

요약

이 실습에서는 특징과 예측 대상 간의 관계에 대한 더 깊은 통찰력을 얻기 위해 의사결정 트리 구조를 분석하는 방법을 배웠습니다. 이진 트리 구조를 검색하고, 의사결정 트리를 시각화하고, 샘플 또는 샘플 그룹에 대한 의사결정 경로와 잎 노드를 검색하는 방법을 살펴보았습니다. 이러한 기술은 의사결정 트리 분류자가 예측을 하는 방식을 더 잘 이해하고 모델의 성능을 개선하기 위해 모델을 미세 조정하는 데 도움이 될 수 있습니다.