【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?
  b4DDAHOVqdA1 2023年11月08日 24 0

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集

🤵♂️ 个人主页: @AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!🐱🏍 🙋♂️声明:本人目前大学就读于大二,研究兴趣方向人工智能&硬件(虽然硬件还没开始玩,但一直很感兴趣!希望大佬带带)

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_02

【深度学习 | 核心概念】那些深度学习路上必经的核心概念,确定不来看看? (一)


摘要: 本系列旨在普及那些深度学习路上必经的核心概念,文章内容都是博主用心学习收集所写,欢迎大家三联支持!本系列会一直更新,核心概念系列会一直更新!欢迎大家订阅

该文章收录专栏 [✨--- 《深入解析机器学习:从原理到应用的全面指南》 ---✨]

@toc

决策树算法

决策树是一种基于树形结构的分类模型,它通过对数据属性的逐步划分,将数据集分成多个小的决策单元。每个小的决策单元都对应着一个叶节点,在该节点上进行分类决策。决策树的核心是如何选择最优的分割属性。常见的决策树算法有ID3、C4.5和CART。

以下是各类决策树算法的详细解释以及它们的优点和缺点,制成的表格如下:

算法

说明

优点

缺点

ID3(1986)

基于信息熵选择划分属性,处理离散型数据

- 简单易懂,易于实现

- 对缺失数据支持较好

- 可用于不平衡数据集

- 只处理离散型数据,无法处理连续型特征

- 对于具有大量取值的属性,容易过拟合

- 对于具有较多缺失值的数据,可能会导致偏好选择缺失值较少的属性

C4.5(1993)

在ID3的基础上引入处理连续型属性和缺失数据的能力(信息增益率)

- 支持离散型和连续型属性

- 对缺失数据支持较好

- 采用信息增益率来选择划分属性,减轻了对取值较多的属性的偏好

- 对于具有大量取值的属性,仍可能过拟合

- 对于具有较多缺失值的数据,可能会导致偏好选择缺失值较少的属性

C5.0(1997)

基于信息增益信息增益比选择划分属性,处理离散型和连续型数据

- 高效处理大规模数据集

- 能够处理多类别问题

- 可解释性强

- 对缺失值的处理较为灵活

- 使用剪枝策略降低过拟合风险

- 对噪声和异常值敏感,容易过拟合

- 对连续型特征需要进行离散化处理

- 在处理高维数据时可能会遇到维度灾难

- 生成的树可能过于复杂

- 难以处理类别不平衡的数据集

CART(Classification and Regression Tree) (1984)

用于分类和回归,通过最小化基尼指数或均方差选择划分属性

- 支持离散型和连续型属性

- 可用于分类和回归问题

- 生成的树结构简单明了

- 生成的树倾向于过拟合

- 对于具有大量取值的属性,容易产生过于复杂的树结构

CHAID (1980)

基于卡方检验选择划分属性,用于分类问题

- 对于多分类问题效果较好

- 支持离散型和连续型属性

- 生成的树结构简单明了

- 对于具有大量取值的属性,容易过拟合

- 对于缺失数据的处理能力有限

RandomForest (2001)

基于决策树的集成学习方法,通过随机选择特征子集和样本子集构建多棵决策树

- 具有较高的分类准确性

- 能够处理大量特征和样本

- 对噪声和异常值具有较好的鲁棒性

- 模型的解释性较差

- 训练时间相对较长

- 参数调整较为复杂

Gradient Boosting

基于决策树的集成学习方法,通过迭代训练一系列决策树来纠正前一棵树的预测误差

- 具有较高的分类准确性

- 能够处理大量特征和样本

- 对噪声和异常值具有较好的鲁棒性

- 可以处理各种类型的数据

- 倾向于过拟合

- 训练时间相对较长

- 参数调整较为复杂

以下决策树算法均使用sklearn 的 tree模块实现:

tree模块是scikit-learn(sklearn)库中的一个子模块,提供了决策树算法的实现。它包含了用于分类和回归问题的决策树模型的类和函数。下面我将详细介绍一些tree模块的常用类和函数的使用方法:

  1. 决策树分类器(DecisionTreeClassifier):
    ``tree.DecisionTreeClassifier`是用于分类问题的决策树分类器的类。它的主要参数和方法包括:
  • criterion:分割标准,可以是"gini"(默认)或"entropy"。
  • max_depth:树的最大深度。
  • min_samples_split:分裂内部节点所需的最小样本数。
  • fit(X, y):拟合决策树模型,其中X是特征矩阵,y是目标变量。
  • predict(X):根据训练好的模型进行预测,其中X是特征矩阵。
  • feature_importances_:特征重要性评估结果。

以下是使用DecisionTreeClassifier的示例代码:

from sklearn import tree
X = [[0, 0], [1, 1]]  # 特征矩阵
y = [0, 1]  # 目标变量
clf = tree.DecisionTreeClassifier()
clf.fit(X, y)
prediction = clf.predict([[2., 2.]])
print(prediction)
```
  1. 决策树回归器(DecisionTreeRegressor):
    ``tree.DecisionTreeRegressor是用于回归问题的决策树回归器的类。它的主要参数和方法类似于DecisionTreeClassifier,但适用于连续目标变量。以下是使用DecisionTreeRegressor`的示例代码:
from sklearn import tree
X = [[0, 0], [1, 1]]  # 特征矩阵
y = [0, 1]  # 目标变量
clf = tree.DecisionTreeRegressor()
clf.fit(X, y)
prediction = clf.predict([[2., 2.]])
print(prediction)
```
  1. 可视化决策树(plot_tree):
    ``tree.plot_tree`函数可以用于可视化决策树模型。它的主要参数包括:
  • decision_tree:决策树模型对象。
  • feature_names:特征的名称列表。
  • class_names:类别的名称列表。

以下是使用plot_tree函数可视化决策树的示例代码:

from sklearn import datasets, tree
import matplotlib.pyplot as plt
# 加载数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
class_names = iris.target_names
# 构建决策树模型
clf = tree.DecisionTreeClassifier()
clf.fit(X, y)
# 可视化决策树
plt.figure(figsize=(10, 10))
tree.plot_tree(clf, feature_names=feature_names, class_names=class_names, filled=True)
plt.show()
```

这将显示一个绘制的决策树图形。

除了上述提到的类和函数外,tree模块还包括其他一些用于决策树模型的类和函数,例如export_graphviz函数可将决策树导出为DOT格式,DecisionTreeClassifierDecisionTreeRegressor类还具有许多其他可用于自定义和调整模型的参数和方法。你可以参考scikit-learn的官方文档以了解更多关于tree模块和决策树算法的详细信息和用法示例。

CART算法

CART算法由Leo Breiman等人于1984年提出(最早一批的决策树算法了)。CART算法是一种通用的决策树算法,既可以用于分类问题,也可以用于回归问题。CART算法使用基尼不纯度作为划分准则,并构建二叉决策树。CART算法的灵活性和性能使其成为决策树领域的重要算法之一。

scikit-learn库中的DecisionTreeClassifierDecisionTreeRegressioner 默认实现的是CART(Classification and Regression Trees)算法,

CART(Classification and Regression Trees)是一种常用的决策树算法,可以用于分类和回归任务。下面是CART决策树算法的详细步骤流程:

  1. 特征选择:对于分类任务,常用的特征选择指标有基尼指数(Gini index)和信息增益(Information Gain,后面提出ID3的改进)。对于回归任务,常用的特征选择指标有平方误差和平均绝对误差。根据选择的特征选择指标,计算每个特征的指标值。选择指标值最佳的特征作为当前节点的分裂特征。
  2. 分裂节点:将当前节点的样本根据分裂特征的取值分成多个子节点。对于离散特征,每个取值创建一个子节点;对于连续特征,根据一个阈值将样本分成两个子节点。
  3. 递归构建子树:对于每个子节点,重复步骤1和步骤2,递归地构建子树,直到满足终止条件。终止条件可以是达到最大深度、达到最小样本数、所有样本属于同一类别等。
  4. 剪枝:构建完整的决策树后,对决策树进行剪枝,以避免过拟合。剪枝分为预剪枝和后剪枝两种方法。预剪枝是在构建过程中通过一些预定义的条件进行剪枝;后剪枝是在构建完整的决策树后,根据验证集的表现进行剪枝。
  5. 预测:使用构建好的决策树对新样本进行预测。从根节点开始,根据样本的特征值逐步向下遍历决策树,直到达到叶节点,叶节点的类别或预测值作为最终预测结果。

CART 还可以处理回归任务,根据特征连续值得划分得到阶梯函数,其中得这里虽然不可能出现斜线或者曲线,但只要考虑极限情况,可以逼近一条曲线(即达到均方误差最小),决策树算法相比起使用代数算法来看,更加准确,并且随着问题和数据变复杂,变量越多,算法同样处理,优越性更显著,效果好,符合现实

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_03

ID3(Iterative Dichotomiser 3)

ID3算法最早由Ross Quinlan于1986年提出。它是一种基于信息论的决策树算法,使用信息增益作为划分准则。ID3算法在其提出后的一段时间内被广泛研究和使用。 具体来说,构建决策树的过程可以分为如下几个步骤:

  1. 选择最优特征。在构建决策树时,需要从当前样本集合中选择一个最优的特征作为当前节点的划分属性。通常使用信息增益、信息增益比或基尼指数等指标来评估各个特征的划分能力,并选取最优特征。

基尼指数和信息增益都是用于决策树中特征选择的指标,它们各有优劣。

基尼指数是一种衡量数据集纯度或不确定性的指标,常用于决策树算法中的特征选择。它基于基尼系数的概念,用于度量从数据集中随机选择两个样本,其类别标签不一致的概率,用于衡量选择某个特征进行划分后,数据集的纯度提升程度

每一个数据集基尼指数的计算公式如下: 【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_04

其中,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_05表示数据集【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_06的基尼指数,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_07表示数据集$$D$$中第【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_08个类别的样本所占比例。基尼指数的取值范围为0到1,数值越小表示数据集的纯度越高,即样本的类别越一致。当数据集D中只包含一种类别的样本时,基尼指数为0,表示数据集完全纯净。当数据集D中的样本类别均匀分布时,基尼指数最大(即值越小)为1,表示数据集的不确定性最高。

$gini = (i * gini_{left} + (m - i) * gini_{right}) / m $

其中【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_09是样本总数,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_08是左节点样本数量。通过计算在分支节点中选择最小的加权平均的基尼指数,以达到最大程度地减少数据集的不确定性(提高数据集纯度)。

信息增益同样衡量了在选择某个特征作为节点后,数据集的纯度提高了多少。信息增益的计算基于信息熵的概念。

信息熵是用来衡量数据集的混乱程度或不确定性的度量。对于一个二分类问题(如购买与否),信息熵的计算公式如下 (多分类也一样,每个不题类别求和):

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_11

其中,S是数据集,p(Yes)p(No)分别是购买为"是"和"否"的样本在数据集中的比例。(信息熵公式性质代表了分布越平均,不确定性越大,信息熵越大(如果单单是使用了log函数还无法达到需要加上概率相乘,可以拿一些数据案例测试一下))

信息增益的计算公式如下(不同类别信息熵相加):

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_12

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_13

其中,S是数据集,A是要计算信息增益的特征,Sv是特征A的某个取值对应的子集,|Sv|是子集Sv的样本数量,|S|是数据集S的样本数量。 (通过这个子集数量大小控制影响权重求得每个子集的信息熵求和,然后确定信息增益最大的(即信息熵最小),)

信息增益越大(即信息熵越小->数据集纯度越高,确定性大),意味着使用特征A作为节点可以更好地分割数据集,提高纯度。

在我们的例子中,我们计算了每个特征的信息增益,并选择了具有最大信息增益的特征作为根节点。然后,我们根据根节点的取值将数据集分割成子集,并对每个子集计算信息增益,以选择下一个节点。这个过程一直持续到满足停止条件为止,例如子集中的样本都属于同一类别达到了预定的树的深度

总结以下是基尼指数和信息增益的优缺点(其实二者都是衡量节点分类之后子数据集的纯度提升

基尼指数:

优点:

  1. 它在计算上比信息增益更简单和高效。适合处理处理大规模数据集,基尼指数的计算速度通常比信息增益快。(平方运算要简单于对数运算

缺点:

  1. 基尼指数只关注当前节点上类别标签分布情况,没有考虑到后续划分会如何影响整体数据集纯度。
  2. 对于属性取值较少且不均匀分布的特征,在使用基尼系数进行划分时可能会产生偏向性。
  3. 基尼指数更倾向于选择具有较大数量样本的特征作为划分点,而在多类别问题中可能会忽略少数类别之间的差异

信息增益:

优点:

  1. 信息增益通过熵(Entropy)衡量了一个特征对于分类任务所提供的全部信息,并能够考虑到后续划分对整体数据集纯度改善程度。因此,在一些情况下可以得到更好地结果。
  2. 适用于处理属性取值较少且分布均匀的特征。(毕竟计算量也挺大的)
  3. 它基于信息论的概念,可以更好地处理多分类问题。信息增益在处理不平衡数据集时表现较好(,能够更好地处理类别不均衡的情况。

使用基尼指数还是信息熵取决于具体情况和需求:

  • 如果你想要快速得出结果并处理大规模数据,请使用基尼指数。
  • 如果你关注模型的纯度和分类能力整体数据集纯度改善程度),并且数据集较小,则信息熵可能是更好的选择。且在处理多类别问题时,信息增益是更常用且通常更适合的选择。
  1. 划分子集。根据选取的最优特征,将当前样本集合划分成若干个子集。每个子集对应于一个子节点,且该节点所代表的样本集合与其父节点的样本集合不重复。
  2. 递归构建决策树。对于每个子节点,重复前两个步骤,直到所有的样本都被分配到叶子节点上,并且每个叶子节点对应着一个类别。
  3. 剪枝操作。由于决策树容易出现过拟合,因此需要进行剪枝操作。常用的剪枝方法包括预剪枝和后剪枝。

在进行分类时,对输入测试样本,按照各个属性的划分方式逐步匹配,最终到达某个叶子节点,并将该测试样本归为叶子节点所代表的类别。决策树的输出结果就是针对测试样本的分类结果,即该测试样本所属的类别。

决策树的优点在于易于理解和解释,能够处理不同类型的数据,且不需要对数据进行预处理。但是,决策树容易出现过拟合问题,因此在构建决策树时需要进行剪枝操作。常用的剪枝方法包括预剪枝和后剪枝。

在构建决策树时,在处理特征取值较多的分类问题时表现良好。

考虑这样一个例子:假设我们要构建一个决策树模型来预测天气是否适合进行户外运动。我们可以选择两个特征:温度湿度温度可能只有三个离散取值(低、中、高),而湿度则具有连续范围。如果我们将温度作为划分依据,则每次划分仅能得到三类样本;但如果选择湿度作为划分依据,则可以基于连续范围进行无数次精确地切割,从而获得更加详尽和准确的分类结果。因为它具有更多的取值,这个特征就有潜力提供更多信息来区分不同类别

案例

当然,我可以为您提供一个决策树的详细例子。假设我们要构建一个决策树来预测一个人是否会购买某个产品。我们将使用以下特征来进行预测:

  1. 年龄:年龄范围在18岁到65岁之间。
  2. 性别:男性或女性。
  3. 收入:收入范围在0到100,000之间。

我们有一个包含以下数据的训练集:

编号

年龄

性别

收入

购买

1

25

男性

30,000


2

35

女性

40,000


3

45

女性

80,000


4

20

男性

10,000


5

55

男性

60,000


6

60

女性

90,000


7

30

男性

50,000


8

40

女性

75,000


现在,我们将使用这些数据来构建一个决策树模型。

首先,我们选择一个特征来作为根节点。我们可以使用信息增益或基尼不纯度等指标来选择最佳特征。在这个例子中,我们选择使用信息增益。

计算每个特征的信息增益:

  • 年龄的信息增益:0.029
  • 性别的信息增益:0.152
  • 收入的信息增益:0.048

根据信息增益,我们选择性别作为根节点。

接下来,我们根据性别的取值(男性或女性)将数据集分割成两个子集。

对于男性子集:

编号

年龄

收入

购买

1

25

30,000


4

20

10,000


5

55

60,000


7

30

50,000


对于女性子集:

编号

年龄

收入

购买

2

35

40,000


3

45

80,000


6

60

90,000


8

40

75,000


对于男性子集,我们可以看到购买的结果是"是"和"否"都有,所以我们需要进一步划分。我们选择年龄作为下一个节点。

对于年龄的取值(小于等于30岁和大于30岁):

对于小于等于30岁的子集:

编号

收入

购买

1

30,000


4

10,000


7

50,000


对于大于30岁的子集:

编号

收入

购买

5

60,000


对于小于等于30岁的子集,购买的结果都是"否",所以我们不需要再进行划分。

对于大于30岁的子集,购买的结果都是"是",所以我们不需要再进行划分。

对于女性子集,购买的结果都是"是",所以我们不需要再进行划分。

最终的决策树如下所示:

性别 = 男性:
    年龄 <= 30岁: 否
    年龄 > 30岁: 是
性别 = 女性: 是

这就是一个简单的决策树的例子。根据输入的特征,决策树可以根据特征的取值进行预测。请注意,这只是一个简单的示例,实际上,决策树可以有更多的特征和更复杂的结构。

首先,我们使用scikit-learn库来实现决策树:

from sklearn import tree
import numpy as np

# 数据集
X = np.array([[25, 1, 30000],
              [35, 0, 40000],
              [45, 0, 80000],
              [20, 1, 10000],
              [55, 1, 60000],
              [60, 0, 90000],
              [30, 1, 50000],
              [40, 0, 75000]])

Y = np.array([0, 0, 1, 0, 1, 1, 0, 1])

# 创建决策树模型
clf = tree.DecisionTreeClassifier()

# 训练模型
clf = clf.fit(X, Y)

# 预测
print(clf.predict([[40, 0, 75000],[10, 0, 75000]]))  # 输出:[1, 0]

然后,我们不使用任何机器学习库来实现决策树:

import numpy as np

class Node:
    def __init__(self, predicted_class):
        self.predicted_class = predicted_class  # 预测的类别
        self.feature_index = 0  # 特征索引
        self.threshold = 0  # 阈值
        self.left = None  # 左子树
        self.right = None  # 右子树

class DecisionTree:
    def __init__(self, max_depth=None):
        self.max_depth = max_depth  # 决策树的最大深度

    def fit(self, X, y):
        self.n_classes_ = len(set(y))  # 类别的数量
        self.n_features_ = X.shape[1]  # 特征的数量
        self.tree_ = self._grow_tree(X, y)  # 构建决策树

    def predict(self, X):
        return [self._predict(inputs) for inputs in X]  # 对输入数据进行预测
    
    def _best_gini_split(self, X, y):
        m = y.size  # 样本的数量
        if m <= 1:  # 如果样本数量小于等于1,无法进行分割
            return None, None
        num_parent = [np.sum(y == c) for c in range(self.n_classes_)]  # 每个类别在父节点中的样本数量
        best_gini = 1.0 - sum((n / m) ** 2 for n in num_parent)  # 父节点的基尼指数
        best_idx, best_thr = None, None  # 最佳分割特征索引和阈值
        for idx in range(self.n_features_):  # 遍历每个特征
            thresholds, classes = zip(*sorted(zip(X[:, idx], y)))  # 根据特征值对样本进行排序
            num_left = [0] * self.n_classes_  # 左子节点中每个类别的样本数量
            num_right = num_parent.copy()  # 右子节点中每个类别的样本数量,初始值为父节点的样本数量
            for i in range(1, m):  # 遍历每个样本
                c = classes[i - 1]  # 样本的类别
                num_left[c] += 1  # 更新左子节点中对应类别的样本数量
                num_right[c] -= 1  # 更新右子节点中对应类别的样本数量
                gini_left = 1.0 - sum(
                    (num_left[x] / i) ** 2 for x in range(self.n_classes_)
                )  # 左子节点的基尼指数
                gini_right = 1.0 - sum(
                    (num_right[x] / (m - i)) ** 2 for x in range(self.n_classes_)
                )  # 右子节点的基尼指数
                gini = (i * gini_left + (m - i) * gini_right) / m  # 加权平均的基尼指数
                if thresholds[i] == thresholds[i - 1]:  # 如果特征值相同,则跳过(特征阈值)
                    continue
                if gini < best_gini:  # 如果基尼指数更小,则更新最佳分割特征索引和阈值 (循环每个特征,和每个阈值,以求解最优分类
                    best_gini = gini
                    best_idx = idx
                    best_thr = (thresholds[i] + thresholds[i - 1]) / 2
        return best_idx, best_thr  # 返回最佳分割特征索引和阈值

    def _best_gain_split(self, X, y):
        m = y.size  # 样本的数量
        if m <= 1:  # 如果样本数量小于等于1,无法进行分割
            return None, None
        num_parent = [np.sum(y == c) for c in range(self.n_classes_)]  # 计算每个类别的样本数量
        best_gain = -1  # 初始化最佳信息增益
        best_idx, best_thr = None, None  # 初始化最佳特征索引和阈值
        for idx in range(self.n_features_):  # 遍历每个特征
            thresholds, classes = zip(*sorted(zip(X[:, idx], y)))  # 对每个特征值和类别标签进行排序
            num_left = [0] * self.n_classes_  # 初始化左子树的类别数量 (左边都是0,为0时自动计算为0) 
            num_right = num_parent.copy()  # 右子树的类别数量初始化为父节点的类别数量 (右边是全部)
            for i in range(1, m):  # 遍历每个样本
                c = classes[i - 1]  # 获取当前样本的类别
                num_left[c] += 1  # 左子树的类别数量增加
                num_right[c] -= 1  # 右子树的类别数量减少
                entropy_parent = -sum((num / m) * np.log2(num / m) for num in num_parent if num != 0)  # 计算父节点的熵
                entropy_left = -sum((num / i) * np.log2(num / i) for num in num_left if num != 0)  # 计算左子树的熵
                entropy_right = -sum((num / (m - i)) * np.log2(num / (m - i)) for num in num_right if num != 0)  # 计算右子树的熵
                gain = entropy_parent - (i * entropy_left + (m - i) * entropy_right) / m  # 计算信息增益(分类后左右的信息熵最小)
                if thresholds[i] == thresholds[i - 1]:  # 如果当前样本的特征值和前一个样本的特征值相同,跳过(不一样才能分界)
                    continue
                if gain > best_gain:  # 如果当前的信息增益大于最佳信息增益
                    best_gain = gain  # 更新最佳信息增益
                    best_idx = idx  # 更新最佳特征索引
                    best_thr = (thresholds[i] + thresholds[i - 1]) / 2  # 更新最佳阈值 (循环每个样本的值,根据两份数据均值确定阈值,一直循环)
        return best_idx, best_thr  # 返回最佳特征索引和阈值

    def _grow_tree(self, X, y, depth=0):
        num_samples_per_class = [np.sum(y == i) for i in range(self.n_classes_)]  # 计算每个类别的样本数量
        predicted_class = np.argmax(num_samples_per_class)  # 预测的类别为样本数量最多的类别 (即确定分到该分支样本最多的记为该类)
        node = Node(predicted_class=predicted_class)  # 创建节点
        if depth < self.max_depth:  # 如果当前深度小于最大深度
            idx, thr = self._best_gain_split(X, y)  # 计算最佳分割
            if idx is not None:  # 如果存在最佳分割
                indices_left = X[:, idx] < thr  # 左子树的样本索引 (第 idx特征中小于thr阈值的索引)
                X_left, y_left = X[indices_left], y[indices_left]  # 左子树的样本
                X_right, y_right = X[~indices_left], y[~indices_left]  # 右子树的样本
                node.feature_index = idx  # 设置节点的特征索引
                node.threshold = thr  # 设置节点的阈值
                node.left = self._grow_tree(X_left, y_left, depth + 1)  # 构建左子树
                node.right = self._grow_tree(X_right, y_right, depth + 1)  # 构建右子树
        return node  # 返回节点

    def _predict(self, inputs):
        node = self.tree_  # 获取决策树的根节点
        while node.left:  # 如果存在左子树
            if inputs[node.feature_index] < node.threshold:  # 如果输入样本的特征值小于阈值
                node = node.left  # 到左子树
            else:
                node = node.right  # 到右子树
        return node.predicted_class  # 返回预测的类别

# 数据集
X = [[25, 1, 30000],
     [35, 0, 40000],
     [45, 0, 80000],
     [20, 1, 10000],
     [55, 1, 60000],
     [60, 0, 90000],
     [30, 1, 50000],
     [40, 0, 75000]]

Y = [0, 0, 1, 0, 1, 1, 0, 1]

# 创建决策树模型
clf = DecisionTree(max_depth=2)

# 训练模型
clf.fit(np.array(X), np.array(Y))

# 预测
print(clf.predict([[40, 0, 75000],[10, 0, 75000]]))  # 输出:[1, 0]

ID3 有一个非常严重的缺陷,便是对一个具有着大量的取值的特征,这个时候对于连续化的数据进行离散,这个时候根据信息熵的理论,会更加偏向于此,但实际这个特征所选取的值是没有很好的分类效果的,比如对于同学们进行分类,如果输入的是学号,大量的学号,此时学号是唯一的,信息增益面对该取值信息熵为0(唯一分类)

C4.5算法

C4.5算法是ID3算法的改进版本,由Ross Quinlan于1993年提出。C4.5算法在ID3的基础上引入了信息增益比作为划分准则,考虑了特征的取值数目对信息增益的影响,以避免偏向取值较多的特征。算法使用信息增益比(Gain Ratio)作为特征选择的准则,它对信息增益进行了归一化,解决了ID3算法对具有大量取值的特征的偏好问题。C4.5算法还支持处理缺失数据和连续特征。C4.5算法在提出后成为决策树领域的重要算法,被广泛应用和研究。

下面是C4.5决策树中使用信息增益比的算法步骤:

  1. 计算数据集的熵(Entropy):熵用来衡量数据集的不确定性。假设有一个分类问题,数据集D包含K个类别,每个类别的样本数量分别为【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_14。数据集D的熵可以通过下面的公式计算:
    【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_15
    其中,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_16表示数据集D的样本总数。
  2. 对于每个特征A,计算其对数据集D的信息增益(Information Gain):信息增益衡量了特征A对于减少数据集D的不确定性(熵)的能力。计算信息增益的公式如下:
    【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_17
    其中,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_18表示特征A的所有取值,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_19表示特征A取值为【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_20的样本子集,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_21表示【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_19的样本数量,【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_23表示【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_数据集_19的熵。
  3. 计算特征A的分裂信息(Split Information):分裂信息衡量了特征A的取值数目对信息增益的影响。计算特征A的分裂信息的公式如下(取值结果为负整,取值个数越少或者越多,绝对结果越小,对应的信息增益比越小:
    【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_25
  4. 计算特征A的信息增益比(Gain Ratio):信息增益比是信息增益与分裂信息之比,用来修正信息增益对取值数目的偏好。计算特征A的信息增益比的公式如下:
    【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_决策树_26
  5. 选择信息增益比最大的特征作为节点分裂的依据。

C5.0 算法

C5.0算法是C4.5算法的进一步改进版本,由Ross Quinlan于1997年提出。C5.0算法在C4.5算法的基础上进行了一些优化,提高了算法的效率和准确性。C5.0算法引入了增强学习(boosting)技术,可以构建更强大的决策树模型。C5.0算法在商业软件中得到了广泛应用。

后续将推出ensember method方法,随机森林方法等

【机器学习 | 决策树】万字长文详解决策树前世今生各种算法变体,以ID3、CRAT、C4.5、C5.0为例,确定不来看看?_信息增益_27

🤞到这里,如果还有什么疑问🤞
					🎩欢迎私信博主问题哦,博主会尽自己能力为你解答疑惑的!🎩
					 	 🥳如果对你有帮助,你的赞是对博主最大的支持!!🥳
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
b4DDAHOVqdA1