Анализ дерева решений

Beginner

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

Введение

Дерево решений классификатор - это популярный алгоритм машинного обучения, используемый для задач классификации и регрессии. Это модель на основе дерева, которая разбивает пространство признаков на набор непересекающихся регионов и предсказывает целевую величину для каждого региона. В этом лабе мы узнаем, как анализировать структуру дерева решений, чтобы получить более глубокое понимание связи между признаками и целевым значением для предсказания.

Советы по работе с ВМ

После запуска ВМ нажмите в левом верхнем углу, чтобы переключиться на вкладку Notebook и получить доступ к Jupyter Notebook для практики.

Иногда вам может потребоваться подождать несколько секунд, пока Jupyter Notebook не загрузится полностью. Валидация операций не может быть автоматизирована из-за ограничений Jupyter Notebook.

Если вы сталкиваетесь с проблемами при обучении, не стесняйтесь обращаться к Labby. Оставьте отзыв после занятия, и мы оперативно решим проблему для вас.

Обучить классификатор дерева решений

Во - первых, нам нужно обучить классификатор дерева решений, используя датасет load_iris из scikit - learn. Этот датасет содержит 3 класса по 50 экземпляров каждый, где каждый класс относится к определенному типу ириса. Мы разделим датасет на обучающую и тестовую выборки и обучим классификатор дерева решений с максимальным количеством 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)]  ## start with the root node id (0) and its depth (0)
while len(stack) > 0:
    ## `pop` ensures each node is only visited once
    node_id, depth = stack.pop()
    node_depth[node_id] = depth

    ## If the left and right child of a node is not the same we have a split
    ## node
    is_split_node = children_left[node_id]!= children_right[node_id]
    ## If a split node, append left and right children and depth to `stack`
    ## so we can loop through them
    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(
    "The binary tree structure has {n} nodes and has "
    "the following tree structure:\n".format(n=n_nodes)
)
for i in range(n_nodes):
    if is_leaves[i]:
        print(
            "{space}node={node} is a leaf node.".format(
                space=node_depth[i] * "\t", node=i
            )
        )
    else:
        print(
            "{space}node={node} is a split node: "
            "go to node {left} if X[:, {feature}] <= {threshold} "
            "else to node {right}.".format(
                space=node_depth[i] * "\t",
                node=i,
                left=children_left[i],
                feature=feature[i],
                threshold=threshold[i],
                right=children_right[i],
            )
        )

Визуализировать дерево решений

Мы также можем визуализировать дерево решений с использованием функции plot_tree из модуля tree в scikit - learn.

from sklearn import tree
import matplotlib.pyplot as plt

tree.plot_tree(clf)
plt.show()

Получить путь решения и листовые узлы

Мы можем получить путь решения для интересующих нас образцов с использованием метода decision_path. Этот метод выводит индикаторную матрицу, которая позволяет нам получить узлы, через которые проходят интересующие нас образцы. Идентификаторы листьев, достигнутые интересующими нас образцами, можно получить с использованием метода apply. Это возвращает массив идентификаторов узлов листьев, достигнутых каждым интересующим нас образцом. С использованием идентификаторов листьев и decision_path мы можем получить условия разделения, которые были использованы для предсказания одного образца или группы образцов. Ниже приведен код для получения пути решения и листовых узлов для одного образца:

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

sample_id = 0
## obtain ids of the nodes `sample_id` goes through, i.e., row `sample_id`
node_index = node_indicator.indices[
    node_indicator.indptr[sample_id] : node_indicator.indptr[sample_id + 1]
]

print("Rules used to predict sample {id}:\n".format(id=sample_id))
for node_id in node_index:
    ## continue to the next node if it is a leaf node
    if leaf_id[sample_id] == node_id:
        continue

    ## check if value of the split feature for sample 0 is below threshold
    if X_test[sample_id, feature[node_id]] <= threshold[node_id]:
        threshold_sign = "<="
    else:
        threshold_sign = ">"

    print(
        "decision node {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]
## boolean array indicating the nodes both samples go through
common_nodes = node_indicator.toarray()[sample_ids].sum(axis=0) == len(sample_ids)
## obtain node ids using position in array
common_node_id = np.arange(n_nodes)[common_nodes]

print(
    "\nThe following samples {samples} share the node(s) {nodes} in the tree.".format(
        samples=sample_ids, nodes=common_node_id
    )
)
print("This is {prop}% of all nodes.".format(prop=100 * len(common_node_id) / n_nodes))

Резюме

В этом практическом занятии мы научились анализировать структуру дерева решений, чтобы получить более глубокое понимание связи между признаками и целевым значением для предсказания. Мы увидели, как получить структуру двоичного дерева, визуализировать дерево решений и получить путь решения и листовые узлы для одного или группы образцов. Эти методы могут помочь нам лучше понять, как классификатор дерева решений делает предсказания, и могут помочь нам настроить модель для улучшения ее производительности.