决策树分析

Machine LearningMachine LearningBeginner
立即练习

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

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

决策树分类器是一种用于分类和回归问题的流行机器学习算法。它是一种基于树的模型,将特征空间划分为一组不重叠的区域,并预测每个区域的目标值。在本实验中,我们将学习如何分析决策树结构,以进一步深入了解特征与要预测的目标之间的关系。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签,以访问Jupyter Notebook进行练习。

有时,你可能需要等待几秒钟让Jupyter Notebook完成加载。由于Jupyter Notebook的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,请随时向Labby提问。课程结束后提供反馈,我们将立即为你解决问题。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL ml(("Machine Learning")) -.-> ml/FrameworkandSoftwareGroup(["Framework and Software"]) sklearn(("Sklearn")) -.-> sklearn/CoreModelsandAlgorithmsGroup(["Core Models and Algorithms"]) sklearn(("Sklearn")) -.-> sklearn/ModelSelectionandEvaluationGroup(["Model Selection and Evaluation"]) sklearn(("Sklearn")) -.-> sklearn/UtilitiesandDatasetsGroup(["Utilities and Datasets"]) sklearn/CoreModelsandAlgorithmsGroup -.-> sklearn/tree("Decision Trees") sklearn/ModelSelectionandEvaluationGroup -.-> sklearn/model_selection("Model Selection") sklearn/UtilitiesandDatasetsGroup -.-> sklearn/datasets("Datasets") ml/FrameworkandSoftwareGroup -.-> ml/sklearn("scikit-learn") subgraph Lab Skills sklearn/tree -.-> lab-49325{{"决策树分析"}} sklearn/model_selection -.-> lab-49325{{"决策树分析"}} sklearn/datasets -.-> lab-49325{{"决策树分析"}} ml/sklearn -.-> lab-49325{{"决策树分析"}} end

训练决策树分类器

首先,我们需要使用scikit-learn中的load_iris数据集来拟合一个决策树分类器。这个数据集包含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)]  ## 从根节点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(
    "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],
            )
        )

可视化决策树

我们还可以使用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))

总结

在本实验中,我们学习了如何分析决策树结构,以便更深入地了解特征与要预测的目标之间的关系。我们已经了解了如何获取二叉树结构、可视化决策树,以及获取单个样本或一组样本的决策路径和叶节点。这些技术可以帮助我们更好地理解决策树分类器是如何进行预测的,并指导我们对模型进行微调以提高其性能。