通过前面的一些练习,我们已经学习了支持向量机、 回归、 鸢尾花模型 、卷积、 知识图谱、 生成式对抗网络、 K近邻、 等AI算法的基本概念,熟悉了一些常用的AI库,并且使用PYTHON大法进行了一些实战练习。接下来,我们向更深一层的概念进军啦!今天我们来学习的是:决策树。
决策树(Decision Tree) 是一种常用的监督学习算法,用于分类和回归问题。它通过一系列的决策规则将数据集划分成不同的类别或数值预测。决策树模型的主要特点是其结构类似于树形图,每个节点表示对特征的测试,每条分支代表测试结果,而叶节点则是最终的预测结果。
以下视图生动展示了什么叫做决策树:
看起来决策树就是一系列 if -else ?
python
if 今天下雨:
去公园 = "不去"
else:
if 今天温度 > 30:
去公园 = "不去"
else:
去公园 = "去"
不不不,没有这么简单,虽然基本的逻辑是这样。
决策树 的工作原理确实可以理解为一系列的 if-else 语句,但是它的优势在于 自动选择最佳判断条件 和 高效的决策过程。
决策树与 if-else
的区别:
-
自动化选择条件 :在传统的
if-else
代码中,我们通常需要手动编写每个条件。但在决策树中,算法会根据数据自动选择最能区分不同样本的条件(特征)。例如,算法会选择"温度"还是"湿度"作为条件,取决于哪个特征对预测更有效。 -
树结构 :决策树不仅是一个简单的条件判断,它有很多层次,每个层次的
if-else
语句都代表不同的决策。这样,决策树通过递归方式逐步做出更细致的判断,直到最后给出结果。 -
可解释性和透明性:决策树结构简单,易于理解,可以清晰地显示每个判断过程。你可以通过查看树的结构,了解模型是如何做出决策的。
理解了吧? 现在我们继续我们的决策树!
一、决策树原理
(1)决策树的基本构建原理:
-
根节点:树的最顶部节点,表示整个数据集。通过选择一个最优的特征来将数据集划分为两个或多个子集。
-
内部节点:每个内部节点表示根据某个特征做出的决策。例如,特征值是否大于某个阈值。
-
叶节点:树的末端节点,表示分类标签或回归预测值。
(2)决策树的工作原理:
决策树的构建基于一个递归的过程,不断地选择特征来划分数据。具体步骤如下:
-
选择最优特征:在每个节点,选择一个特征,使得该特征能够最有效地将数据集划分成不同的子集。选择标准一般基于以下几个准则:
- 信息增益(用于ID3算法):衡量特征对数据集的划分效果,信息增益越大,说明该特征越好。
- 基尼指数(用于CART算法):度量数据集的不纯度,基尼指数越小,表示数据集越纯。
- 卡方检验:用于衡量特征对类别的相关性,通常用于分类问题。
-
递归划分:一旦选择了一个特征,就根据特征的不同值将数据集划分成多个子集,并递归地对每个子集进行相同的操作,直到满足停止条件(例如,节点中样本已经完全属于同一类,或者达到了预设的树深度等)。
-
停止条件:
- 所有样本都属于同一类。
- 没有更多的特征可以用来划分数据。
- 达到最大树深度。
- 每个节点的样本数少于预设的最小样本数。
(3)优点:
- 易于理解和解释:决策树可以生成直观的树状图,容易理解和可解释,是一种透明的模型。
- 不需要特征缩放:决策树不受特征的尺度影响,不需要像某些算法那样对数据进行标准化或归一化。
- 处理非线性关系:决策树能够有效地处理非线性数据,能够学习到特征之间复杂的关系。
- 可以处理分类和回归任务:决策树不仅可以用于分类问题,也可以用于回归问题。
(4)缺点:
- 容易过拟合:决策树可能会非常复杂,容易在训练数据上过拟合,尤其是树的深度较大时。
- 不稳定:小的变化可能会导致决策树结构发生很大变化,因此对数据噪声较为敏感。
- 偏向于选择取值较多的特征:某些决策树算法(如ID3)可能会偏向于选择取值较多的特征,从而导致不理想的树结构。
(5)剪枝(Pruning):
为了解决过拟合问题,可以对决策树进行剪枝。剪枝是在树构建完成后,通过去除一些不重要的分支来简化树的结构。剪枝可以分为:
- 前剪枝:在树构建过程中,就停止某些不重要的分裂。
- 后剪枝:在树构建完成后,通过评估每个分支的效用来决定是否剪掉。
(6)决策树的变种:
- 随机森林(Random Forest):一种集成学习方法,通过构建多个决策树并结合它们的预测结果来提高模型的稳定性和准确性。
- 梯度提升树(Gradient Boosting Tree):通过逐步构建决策树,并让每一棵树尽量纠正上一棵树的错误来提高预测性能,常见的实现有 XGBoost 和 LightGBM。
决策树的特征图
接下来我将带你一起使用 Python 来实现一个简单的决策树模型。我们将使用 scikit-learn
库,这个库提供了非常简便的决策树实现。
步骤 1: 安装必要的库
首先,确保你已经安装了 scikit-learn
和 matplotlib
(用于绘图)。可以使用以下命令来安装:
pip install scikit-learn matplotlib
步骤 2: 导入库
我们需要导入一些必要的库和模块:
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn import metrics
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
scikit-learn
我们之前已经多次用过了,它支持监督学习和无监督学习算法,包括分类、回归、聚类、降维等。此外,它还提供了数据处理功能(如标准化、特征选择、数据分割)和模型评估工具(如交叉验证、网格搜索)。Scikit-learn 的优点是易于使用、文档丰富、社区活跃,广泛应用于学术研究、工业界和数据科学项目中。
它是学习和实现机器学习算法的理想工具,尤其适合初学者和快速原型开发。
步骤 3: 加载数据集
我们将使用 鸢尾花(Iris)数据集
,它是一个常见的机器学习数据集,包含了三种不同的鸢尾花的样本。
鸢尾花(Iris)数据集 是一个经典的机器学习数据集,常用于分类算法的测试和教学。它由著名植物学家 Fisher 于 1936 年发布,包含 150 个样本,分为三类鸢尾花:Setosa、Versicolor 和 Virginica,每类 50 个样本。每个样本有 4 个特征:花萼长度、花萼宽度、花瓣长度和花瓣宽度,都是数值型数据。
这个数据集广泛用于演示和验证分类算法的性能,如决策树、支持向量机、K-近邻等。由于其简单而直观的结构,鸢尾花数据集成为了机器学习领域的标准测试集,帮助研究人员比较不同算法的效果。此外,它也非常适合用作机器学习入门练习和模型评估的基础
# 加载Iris数据集
iris = load_iris()
X = iris.data # 特征
y = iris.target # 标签
步骤 4: 数据集划分
我们将数据集分为训练集和测试集,以便训练和评估模型。
# 划分数据集为训练集和测试集(80% 训练,20% 测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
上面这行代码用于将数据集划分为训练集和测试集,目的是为了训练模型并评估其性能。以下是逐个部分的详细解释:
4.1. train_test_split
函数
train_test_split
是 Scikit-learn 中的一个函数,用于随机地将数据集划分为训练集和测试集。它从传入的 X
(特征)和 y
(标签)中,按照指定的比例随机划分数据。
X
:输入特征(通常是一个二维数组,每一行代表一个样本,每一列代表一个特征)。y
:目标标签(通常是一维数组,包含每个样本的对应标签)。
4.2. test_size=0.2
test_size
参数指定测试集占总数据集的比例。在这里,test_size=0.2
表示 80% 的数据用于训练,20% 的数据用于测试。
- 训练集:80% 的数据
- 测试集:20% 的数据
我们可以通过调整 test_size
来控制训练集和测试集的划分比例,例如设置 test_size=0.3
会让测试集占 30%,训练集占 70%。
4.3. random_state=42
random_state
用于控制划分数据时的随机性。它是一个种子值,确保每次运行时,数据集的划分结果相同,从而保证实验的可重复性。设置为一个固定的数值(如 42
)可以确保每次运行时得到相同的训练集和测试集划分结果。
- 如果
random_state
设置为None
或不指定,train_test_split
每次运行时都会产生不同的划分,这样每次训练的结果可能会有所不同。 - 设置一个固定的
random_state
(如42
)确保每次运行结果一致,这对于模型调试、对比实验很重要。
4.4. 返回值
train_test_split
返回四个变量,分别是:
X_train
:训练集的特征(80%)。X_test
:测试集的特征(20%)。y_train
:训练集的标签(80%)。y_test
:测试集的标签(20%)。
这行代码的作用是将数据集按 80% 和 20% 的比例划分为训练集和测试集。训练集用于训练模型,测试集用于评估模型性能。random_state=42
保证了每次划分的结果一致,使得实验结果可复现。
步骤 5: 创建和训练决策树模型
现在我们创建一个决策树分类器并用训练数据训练它。
# 创建决策树模型
clf = DecisionTreeClassifier(random_state=42)
# 训练模型
clf.fit(X_train, y_train)
在这段代码中,random_state=42
是一个用于控制随机数生成的种子(seed)值。
解释:
- 随机性:许多机器学习算法(包括决策树)在训练过程中涉及随机过程,比如在数据集划分、特征选择、训练集抽样等环节中都会有随机性。为了确保结果的可重复性,通常需要设置一个固定的随机种子。
- random_state 参数 :这个参数用于控制决策树在训练过程中涉及的随机数生成。通过设置
random_state
,我们可以保证每次运行代码时,模型的训练过程(包括数据划分等)都是一样的,从而确保每次得到相同的结果。 - 数字 42 :这里的
42
只是一个固定的整数种子,数字本身并没有特殊含义。任何整数都可以作为random_state
,
作用:
- 设置
random_state=42
可以确保在多次运行时,模型训练过程中的随机部分(如样本分割)保持一致,保证实验结果可复现。如果没有设置random_state
或将其设为None
,每次运行代码时,训练过程中的随机部分可能会有所不同,导致结果不一致。
所以,42
只是一个约定俗成的种子值,任何其他整数值(如 random_state=0
或 random_state=123
)也能达到相同的效果。
另外 这里的42 和稍微前面代码(数据集划分)中的42 ,可以保持一致,也可以不一致:
- 如果我们希望每次执行代码时,训练集和测试集的划分、以及模型训练过程中的随机性完全相同,那么**
train_test_split
和`所以DecisionTreeClassifier
中的random_state
应该保持一致**。这样可以确保数据划分与模型训练的完全一致性,得到相同的结果。 - 如果这两个
random_state
的值不同,不会影响模型的运行或结果的有效性,只是每次运行时,模型训练和数据划分的随机部分可能不同。
因为大多数情况下,我么使用ai 是应用,而不是基础开发,所以调试参数在这个过程中非常关键,当我们懂得算法的基本逻辑和原理,我们就可以通过调参来训练模型变得更加精准,所以这里我特地来对这个参数做了一个比较详细的说明。
DecisionTreeClassifier
是 Scikit-learn 库中用于分类任务的决策树模型。它实现了分类算法中的 CART(分类)
DecisionTreeClassifier
的工作原理:
- 分裂节点:在每一层,决策树通过选择一个特征并根据该特征的值来分裂数据集,使得每个子集尽可能"纯净"。这里的"纯净"通常是指子集中的样本尽可能属于同一个类别。
- 划分准则 :决策树通常使用 基尼指数 (Gini Impurity)或 信息增益(Entropy)来选择最优的划分特征。
- 递归构建:从根节点开始,递归地对数据进行划分,直到满足停止条件(如节点中的样本属于同一类别,或者达到树的最大深度)。
步骤 6: 预测和评估模型
训练完模型后,我们可以在测试集上进行预测,并评估模型的性能。
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 评估模型
print(f"准确率: {metrics.accuracy_score(y_test, y_pred)}")
print(f"混淆矩阵:\n{metrics.confusion_matrix(y_test, y_pred)}")
print(f"分类报告:\n{metrics.classification_report(y_test, y_pred)}")
我们来逐行解读上面的代码:
6.1. y_pred = clf.predict(X_test)
- 解释 :这行代码使用训练好的
DecisionTreeClassifier
模型(存储在clf
中)对测试集X_test
进行预测,并将预测的结果保存在y_pred
中。 - 过程 :
predict()
方法接受测试数据X_test
(该数据是模型之前未见过的数据),并基于训练过程中学到的规则进行预测,返回对应的类别标签(如0
,1
,2
等)。
6.2. print(f"准确率: {metrics.accuracy_score(y_test, y_pred)}")
- 解释 :这行代码计算并输出模型在测试集上的 准确率。
- 过程 :
metrics.accuracy_score(y_test, y_pred)
计算预测结果y_pred
与真实标签y_test
之间的准确率。准确率是正确预测的数量占总预测数量的比例,即:
- 它反映了模型分类的整体表现。
6.3. print(f"混淆矩阵:\n{metrics.confusion_matrix(y_test, y_pred)}")
-
解释 :这行代码计算并打印 混淆矩阵,用于评估分类模型的性能。
-
过程 :
metrics.confusion_matrix(y_test, y_pred)
生成一个矩阵,其中:- 对角线上的元素表示正确分类的样本数。
- 非对角线上的元素表示分类错误的样本数,表明哪些类别被误分为其他类别。
例如,一个简单的 3 类问题的混淆矩阵可能是:
[[50, 2, 1], # 类别 0 被正确分类为 50 个,错误地分为 2 个类别 1,1 个类别 2 [3, 45, 4], # 类别 1 被正确分类为 45 个,错误地分为 3 个类别 0,4 个类别 2 [0, 5, 47]] # 类别 2 被正确分类为 47 个,错误地分为 5 个类别 1
混淆矩阵是模型性能分析中非常重要的工具,尤其是对于多分类任务。
6.4. print(f"分类报告:\n{metrics.classification_report(y_test, y_pred)}")
-
解释 :这行代码生成并打印 分类报告,它提供了更多关于模型性能的详细信息。
-
过程 :
metrics.classification_report(y_test, y_pred)
生成一个报告,其中包含以下几项指标:- 精确度(Precision):每个类别被预测为该类别的样本中,实际属于该类别的比例。
- 召回率(Recall):每个类别中实际属于该类别的样本中,被正确分类为该类别的比例。
- F1 分数(F1-Score):精确度和召回率的调和平均数,常用于不平衡数据集中的性能评估。
- 支持(Support):每个类别的样本数。
分类报告的一个例子可能是:
precision recall f1-score support class 0 0.91 0.96 0.94 50 class 1 0.89 0.85 0.87 50 class 2 0.93 0.94 0.93 50 avg / total 0.91 0.91 0.91 150
精简理解:
y_pred = clf.predict(X_test)
:对测试集进行预测,得到预测的类别标签。accuracy_score
:计算并打印准确率,评估整体预测正确性。confusion_matrix
:生成混淆矩阵,分析模型在不同类别上的表现,包括误分类的情况。classification_report
:提供精确度、召回率、F1 分数等详细的分类评估指标,用于多类别问题的深入分析。
这些评估方法有助于全面了解模型在不同分类上的表现,帮助我们发现问题并进行改进。
步骤 7: 可视化决策树
我们还可以可视化训练后的决策树,看看它是如何做出分类决策的。
# 绘制决策树
plt.figure(figsize=(12,8))
plot_tree(clf, filled=True, feature_names=iris.feature_names, class_names=iris.target_names)
plt.show()
完整代码示例:
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn import metrics
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
# 加载Iris数据集
iris = load_iris()
X = iris.data # 特征
y = iris.target # 标签
# 划分数据集为训练集和测试集(80% 训练,20% 测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建决策树模型
clf = DecisionTreeClassifier(random_state=42)
# 训练模型
clf.fit(X_train, y_train)
# 在测试集上进行预测
y_pred = clf.predict(X_test)
# 评估模型
print(f"准确率: {metrics.accuracy_score(y_test, y_pred)}")
print(f"混淆矩阵:\n{metrics.confusion_matrix(y_test, y_pred)}")
print(f"分类报告:\n{metrics.classification_report(y_test, y_pred)}")
# 绘制决策树
plt.figure(figsize=(12,8))
plot_tree(clf, filled=True, feature_names=iris.feature_names, class_names=iris.target_names)
plt.show()
结果分析
上述代码运行后结果如下:
结果表明,模型在测试集上的表现非常好,准确率为 1.0,这意味着模型在所有测试样本中都预测正确了。
详细解读:
1. 准确率: 1.0
准确率是指模型正确分类的样本占总样本的比例。在这个结果中,准确率是 1.0,表示模型完全正确地分类了所有测试样本。也就是说,测试集中的 30 个样本,模型没有任何错误预测。
2. 混淆矩阵:
混淆矩阵如下:
[[10 0 0]
[ 0 9 0]
[ 0 0 11]]
- 行表示实际类别,列表示模型预测的类别。
- 每个数字表示实际类别和预测类别的匹配情况。
- 第一行:实际类别 0 的 10 个样本被正确预测为类别 0,0 个被误预测为类别 1 或类别 2。
- 第二行:实际类别 1 的 9 个样本被正确预测为类别 1,0 个被误预测为其他类别。
- 第三行:实际类别 2 的 11 个样本被正确预测为类别 2,0 个被误预测为其他类别。
从混淆矩阵可以看出,模型在每个类别上的预测都非常准确,没有误分类的情况。
3. 分类报告:
分类报告显示了每个类别的 精确度(Precision) 、召回率(Recall) 、F1 分数(F1-Score) 和 支持(Support):
precision recall f1-score support
0 1.00 1.00 1.00 10
1 1.00 1.00 1.00 9
2 1.00 1.00 1.00 11
accuracy 1.00 30
macro avg 1.00 1.00 1.00 30
weighted avg 1.00 1.00 1.00 30
- 精确度(Precision):每个类别的预测中,模型预测为该类别的样本中有多少是正确的。所有类别的精确度都是 1.00,表示模型完全没有误报。
- 召回率(Recall):每个类别中,实际属于该类别的样本中有多少被正确分类。所有类别的召回率也是 1.00,表示模型完全没有漏报。
- F1 分数(F1-Score):精确度和召回率的调和平均,所有类别的 F1 分数也是 1.00,表明模型的分类性能非常平衡且完美。
- 支持(Support):每个类别的样本数量,分别是 10、9 和 11。
4. 宏平均(Macro Avg)与加权平均(Weighted Avg):
- 宏平均(Macro Avg):计算所有类别的精确度、召回率和 F1 分数的简单平均,不考虑每个类别的样本数量。由于每个类别的评分都为 1.00,宏平均也为 1.00。
- 加权平均(Weighted Avg):根据每个类别的样本数量加权平均精确度、召回率和 F1 分数。加权平均也是 1.00,因为每个类别的表现都非常好。
从输出结果来看,模型的表现是完美的,准确率、精确度、召回率、F1 分数等指标均为 1.00。此结果通常意味着以下几种情况:
- 数据集非常简单或干净:例如,数据集中的特征很容易区分不同类别,或者数据没有噪声。
- 过拟合:如果模型在训练集和测试集上都能获得非常高的准确率,可能存在过拟合的情况,即模型在训练时记住了过多的细节,导致它对新的数据泛化能力差。我们可以通过调整模型的超参数(如树的最大深度)或使用交叉验证来检查是否发生了过拟合。
总的来说,模型在这个测试集上表现得非常好。
最后我们得到一张决策树的图:
绘制的 决策树 是根据决策树模型的训练结果自动生成一个树状结构,显示每个决策节点和分支。由于我们使用的数据集(如鸢尾花数据集)通常具有清晰的类别分界,所以绘制出的决策树会比较简单和清晰。
决策
-
根节点:根节点是决策树的起点,表示对数据集的第一个分割条件。通常,它会选择一个最能区分不同类别的特征进行分裂。
-
内部节点:每个内部节点表示根据某个特征做出的决策。
-
叶子节点:叶子节点表示最终的分类结果(或回归值)。对于分类问题,叶子节点会显示某一类别(例如类别 0, 1, 2)。对于回归问题,叶子节点会显示预测的连续数值。
-
分支:从根节点到叶子节点的每条路径都代表了一组特征条件的判断过程。每个分支代表一个决策,即通过比较特定特征的值,数据将被分到不同的子节点。
具体绘制的决策树结构:
对于鸢尾花数据集,通常包括特征如 sepal length
(花萼长度)、sepal width
(花萼宽度)、petal length
(petal width
(花瓣宽度)。决策树会根据这些特征的值进行划分。
进阶探索
- 剪枝 :默认情况下,决策树会一直分裂直到所有叶子节点都纯净(即每个节点只包含一个类的样本)。可以通过设置
max_depth
或min_samples_split
等参数来避免过拟合。 - 调参 :尝试调整决策树的其他超参数,如
max_features
、min_samples_leaf
等,来优化模型。
今天练习的这是一个简单的入门教程,用来帮助我们来理解决策树的基本使用和原理。如果你有任何问题,或者想了解更复杂的部分,可以在留言区告诉我哦 谢谢了!