Vizuara-从零开始的决策树笔记-全-

Vizuara 从零开始的决策树笔记(全)

001:什么是决策树?

在本节课中,我们将要学习决策树的基本概念。决策树是机器学习中一项重要且直观的技术,常用于解决分类和回归问题。我们将从泰坦尼克号生存预测这个经典案例入手,了解决策树的应用场景和基本结构。

泰坦尼克号沉船事故中,许多人因船只沉没而不幸丧生。这也是一个非常流行的机器学习项目,初学者通常在Kaggle等网站上完成这个项目,并将其添加到简历中。

用于解决这个特定问题的技术就是决策树。

问题本身相当简单。船上有各种各样的人,他们属于不同的年龄、性别。不仅如此,人们还属于不同的经济阶层,例如有些人富有,有些人贫穷。基于所有这些变量,目标是预测哪些人会在泰坦尼克号灾难中幸存,哪些人不会。

决策树是机器学习中最重要的技术之一。我正在创建的这套名为“从零开始构建决策树”的系列教程,将教你关于决策树所需了解的一切。

让我们开始吧。我是Raj Daneer博士,于2022年从麻省理工学院毕业,获得机器学习博士学位。从那时起,我们的使命就是让所有人都能接触到人工智能,这套系列教程是该使命的一部分。

这套系列教程将全部围绕决策树展开,但我会以正确的方式教你。首先,让我稍微激发一下你对决策树的兴趣。如果你访问Kaggle这个网站并搜索决策树数据集,你会发现使用决策树可以解决各种各样的问题,例如使用传感器数据进行机器故障预测、银行营销、员工离职预测、乳腺癌诊断、银行营销活动数据集、药物分类、员工满意度调查数据、亚马逊商业研究、员工流失、酒店预订取消等。事实上,还有大量其他数据集,例如我提到的泰坦尼克号数据、二手车价格预测等。许多机器学习问题都可以使用决策树来解决。

不仅在项目中,即使在工业界,决策树算法也被广泛应用。我问过ChatGPT,决策树算法通常在哪些行业实施。你会发现它们被用于金融行业进行信用评分、欺诈检测;用于医疗保健进行诊断、治疗和患者结果预测;用于市场营销和销售进行客户细分、预测客户是否会购买产品;并且还用于许多其他行业。事实上,我有一些在初创公司工作的朋友,他们经常告诉我,无论生成式人工智能多么热门,为公司带来收入的机器学习算法是决策树、随机森林和梯度提升。因此,我相信这足以激发你对这套系列教程的好奇心,并从这个介绍视频开始。在这个视频中,我将简要概述我们将在本系列中学到什么,并涵盖第一讲,专门介绍什么是决策树。

在你产生兴趣之后,下一个问题是如何开始学习决策树?许多学生的做法是直接从TensorFlow开始,他们使用现成的TensorFlow代码,可能只需10行代码,甚至在泰坦尼克号等项目上就能获得很好的结果。

问题在于,通过这种方法,你将无法了解决策树的基础构建模块。有许多重要的构建模块,例如基尼不纯度是一个构建模块,熵和误分类错误是其他构建模块。我们将以正确的方式学习决策树,一步步构建决策树。首先,我们将从零开始构建,然后我也会向你展示Python的实践演示。

我关注了一个名为Stat Quest的频道,这是一个非常出色的频道,也是制作本系列讲座的灵感来源之一。

让我们开始今天的讲座。在今天的讲座中,我们的议程是看一个简单的概念,即决策树到底是什么。

然后,在本讲座结束时,我也会告诉你,你可以从这套系列教程中期待什么,以及我们将遵循的议程。

让我们看看最简单的决策树。决策树从一个陈述开始,然后有一些决策点。陈述是:你想学习决策树吗?如果是,那么你可以继续观看视频;如果否,则不要观看这个视频。

这就是最简单的决策树。😊,从这个决策树中,你可以看到决策树的构建模块。通常有一个陈述,例如“你想学习决策树吗?”,然后有一个决策,因此会分支成两个部分:如果是,则执行这个动作(观看此视频);如果否,则不要观看此视频。

这是机器学习中最简单、最直观的概念之一。所以,这是一个简单的决策树。很好,但决策树本身有两种类型。第一种决策树是分类树,第二种决策树是回归树。许多学生犯的另一个错误是,他们认为决策树只有一种类型,即分类树,但实际上回归问题也可以用决策树来解决。

这两者有什么区别?我们之前看到的例子,“你想学习决策树吗?如果是,观看此视频;如果否,不要观看此视频”,这是关于分类到不同类别的。因此,这是一个分类决策树的例子。另一方面,在回归决策树中,你预测的是数值。例如,这是我构建的一个样本,陈述仍然是“你想学习决策树吗?”,如果是,那么我预测你的年龄在15到40岁之间;如果否,那么我预测你的年龄小于15岁。

你可以看到,这里也有一个陈述,并分支成两个部分。但在这里,我实际上是在预测数值,而不仅仅是“是”或“否”的分类,对吧?所以这就是为什么它是一个回归问题的例子。事实上,你看到的所有这些数据集上的例子,可能是分类或回归问题,而决策树是解决这两类机器学习问题的强大技术。因此,了解这两种决策树之间的区别对你来说很重要。

然后,让我实际演示一个我构建的简单分类树,用于预测患者患糖尿病的风险。我从一个陈述开始:如果年龄小于或等于45岁。你考虑一个人,查看他们的年龄。如果年龄小于或等于45岁(如果你选择“是”),那么你检查他们的体重。这是一个年轻人,你检查他们的体重。如果体重小于60公斤,很好,这是低风险。但如果是一个年轻人,体重大于60公斤,那么这个人有中度糖尿病风险。另一方面,让我们看看决策树的另一边。如果我对“年龄小于或等于45岁”的回答是“否”,这意味着这个人年龄较大。现在已经确定这个人年龄较大。如果他们的体重小于或等于70公斤(这意味着我们走这条路径),那么他们处于中度风险。但如果这个人年龄较大,并且体重大于或等于70公斤,那么这意味着这个人有更高的糖尿病风险。

所以,分类决策树就是这样构建的,它们可以尽可能长。这被称为决策树的深度,即我进行多少次分割。

在查看一个非常好的实际例子之前,我想介绍一些术语,以便这些术语能一直伴随着你。这是“从零开始构建决策树”系列中一个相当简单的术语,但重要的是现在就要正确理解它,以免以后犯错。

在开始时提出的陈述,例如“年龄小于或等于45岁吗?”或者在这个例子中“你想学习决策树吗?”,这被称为根节点,即开始时提出的陈述。然后,这里所有用蓝色显示的块被称为内部节点。为什么?因为内部节点有一个传入箭头和一个传出箭头。如果你看所有这些蓝色方框,它们都有一个传入箭头和一个传出箭头。看这个,它有一个传入箭头和一个传出箭头;看这个,它有一个传入箭头和一个传出箭头。所以这些既有信息传入也有信息传出的节点被称为内部节点或分支节点。

然后是第三个术语。看那些用红色显示的块,它们都只有传入箭头,没有传出箭头。看这个。

本节课中我们一起学习了决策树的基本概念。我们了解到决策树是一种用于分类和回归的机器学习技术,结构上由根节点、内部节点和叶节点组成。我们从泰坦尼克号预测的例子出发,看到了决策树的实际应用价值,并区分了分类树与回归树的不同。在接下来的课程中,我们将深入了解决策树的构建模块,并学习如何从零开始实现它。

002:开始构建决策树 🚀

在本节课中,我们将开始完全从零构建一个分类决策树。我们将使用一个包含离散和连续数据的示例数据集,并学习如何构造一个能完美分类数据的决策树。

概述

上一节我们介绍了决策树的定义、基本结构(根节点、内部节点、叶节点)以及分类树与回归树的区别。本节中,我们来看看如何从一份具体的训练数据出发,动手构建一个分类决策树。

数据集介绍

以下是用于构建决策树的训练数据。我们收集了7个人的信息,包含三个特征和一个目标变量:

人员 爱看电影? 爱看卡通? 年龄 爱蝙蝠侠?
1 7
2 18
3 50
4 12
5 35
6 83
7 38

我们的目标是:当遇到一个新的人,已知他/她是否爱看电影、是否爱看卡通以及年龄时,预测此人是否爱蝙蝠侠。

这个数据集混合了离散数据(是/否)和连续数据(年龄)。接下来,我们将探讨为什么传统的逻辑回归方法在此可能不适用,并展示决策树如何能更好地解决这个问题。

为何不使用逻辑回归?

逻辑回归是一种常用于分类任务的回归技术。但在这个例子中,它可能表现不佳。

让我们以“年龄”特征为例进行说明。将年龄绘制在X轴,将“爱蝙蝠侠”的概率(0为否,1为是)绘制在Y轴。根据训练数据,我们得到以下点:

  • 年龄7、12、50、83 → 概率为0(不爱蝙蝠侠)。
  • 年龄18、35、38 → 概率为1(爱蝙蝠侠)。

如果尝试用逻辑回归拟合一条S型曲线,它可能如下图所示。虽然能正确分类年轻和中年群体,但对于年龄50和83的样本,曲线预测的概率接近1,这与事实(他们不爱蝙蝠侠)完全不符。

逻辑回归曲线无法很好地拟合这种非单调、分段的数据模式,导致对部分样本分类错误。

决策树解决方案

与逻辑回归不同,决策树可以构建出完美分类此训练数据的模型。以下是一个最优的决策树结构:

如果 (爱看卡通? == 是) {
    如果 (年龄 < 12.5) {
        预测:不爱蝙蝠侠
    } 否则 {
        预测:爱蝙蝠侠
    }
} 否则 {
    预测:不爱蝙蝠侠
}

让我们验证这个决策树:

  1. 人员1:爱卡通=是,年龄=7 (<12.5) → 预测“否”,匹配。
  2. 人员2:爱卡通=是,年龄=18 (>12.5) → 预测“是”,匹配。
  3. 人员3:爱卡通=否 → 直接预测“否”,匹配。

作为练习,你可以用这个树结构验证数据集中所有7个人的结果,会发现它都能正确分类。

核心问题

现在,我们面临构建决策树的核心问题:如何自动地从数据中得出这样的树结构?
具体需要思考:

  1. 根节点应该选择哪个特征进行提问?(本例中是“爱看卡通?”)
  2. 对于连续特征(如年龄),如何确定最佳的分割点(如12.5)?
  3. 构建树的停止条件是什么?

这些正是我们接下来几节要解决的关键问题。

总结

本节课中我们一起学习了:

  1. 介绍了一个用于构建分类决策树的示例数据集。
  2. 通过图示说明了逻辑回归在处理此类混合数据时的局限性。
  3. 展示了一个能完美分类数据的决策树结构,并进行了验证。
  4. 提出了构建决策树过程中需要解决的核心问题,为后续学习如何自动选择特征和分割点奠定了基础。

在下一节,我们将深入探讨决策树构建的核心算法:如何量化特征的好坏并选择最佳分割点。

003:基尼不纯度是什么? 🤔

在本节课中,我们将深入学习基尼不纯度。我们也会简要了解熵,但课程重点将放在基尼不纯度上。我们将学习如何量化决策树中节点的“不纯度”,从而科学地选择最佳分割问题。

概述

在上一节中,我们开始构建一个分类树,并提出了一个关键问题:如何决定在根节点应该问哪个问题? 我们有一个包含“是否爱看电影”、“是否爱看卡通”、“年龄”和“是否爱蝙蝠侠”的数据集。我们的目标是构建一棵树,能根据前三个特征准确预测一个人是否爱蝙蝠侠。

直观上,我们比较了“爱看电影”和“爱看卡通”这两个问题。我们发现,询问“爱看卡通”能得到一个“纯”的叶子节点(所有回答“否”的人都不爱蝙蝠侠),而询问“爱看电影”得到的两个叶子节点都是“不纯”的(混合了爱和不爱蝙蝠侠的人)。因此,“爱看卡通”似乎是更好的根节点问题。

但是,直觉需要量化。本节我们将学习两种量化不纯度的方法:基尼不纯度。我们将重点学习基尼不纯度,并计算两个问题的基尼不纯度值,数值更低的问题将被选为根节点。

基尼不纯度详解

基尼不纯度衡量的是一个数据集中类别混合的程度。一个完全“纯”的节点(所有样本都属于同一类别)的基尼不纯度为0。一个类别均匀混合的节点具有最高的基尼不纯度。

基尼不纯度的计算公式如下:

Gini = 1 - Σ (p_i)²

其中,p_i 是数据集中第 i 个类别出现的概率。

让我们将这个公式应用到我们的例子上。

计算“爱看电影”问题的基尼不纯度

“爱看电影”这个问题将数据集分成了两个子集(叶子节点)。

对于第一个叶子节点(回答“是”):

  • 总人数:4人
  • 爱蝙蝠侠(是):1人
  • 不爱蝙蝠侠(否):3人
  • 概率 p_yes = 1/4 = 0.25
  • 概率 p_no = 3/4 = 0.75

计算该叶子节点的基尼不纯度:
Gini_leaf1 = 1 - (0.25² + 0.75²) = 1 - (0.0625 + 0.5625) = 1 - 0.625 = 0.375

对于第二个叶子节点(回答“否”):

  • 总人数:3人
  • 爱蝙蝠侠(是):2人
  • 不爱蝙蝠侠(否):1人
  • 概率 p_yes = 2/3 ≈ 0.667
  • 概率 p_no = 1/3 ≈ 0.333

计算该叶子节点的基尼不纯度:
Gini_leaf2 = 1 - (0.667² + 0.333²) ≈ 1 - (0.444 + 0.111) ≈ 1 - 0.555 ≈ 0.445

现在,我们需要计算整个“爱看电影”问题的加权平均基尼不纯度。权重是每个叶子节点样本数占总样本数的比例。

  • 总样本数 N = 7
  • 叶子节点1的权重 = 4/7 ≈ 0.571
  • 叶子节点2的权重 = 3/7 ≈ 0.429

“爱看电影”问题的总基尼不纯度为:
Gini_movies = (0.571 * 0.375) + (0.429 * 0.445) ≈ 0.214 + 0.191 ≈ 0.405

计算“爱看卡通”问题的基尼不纯度

接下来,我们用同样的方法计算“爱看卡通”问题的基尼不纯度。

对于第一个叶子节点(回答“是”):

  • 总人数:4人
  • 爱蝙蝠侠(是):3人
  • 不爱蝙蝠侠(否):1人
  • 概率 p_yes = 3/4 = 0.75
  • 概率 p_no = 1/4 = 0.25

计算该叶子节点的基尼不纯度:
Gini_leaf3 = 1 - (0.75² + 0.25²) = 1 - (0.5625 + 0.0625) = 1 - 0.625 = 0.375

对于第二个叶子节点(回答“否”):

  • 总人数:3人
  • 爱蝙蝠侠(是):0人
  • 不爱蝙蝠侠(否):3人
  • 概率 p_yes = 0/3 = 0
  • 概率 p_no = 3/3 = 1

计算该叶子节点的基尼不纯度:
Gini_leaf4 = 1 - (0² + 1²) = 1 - (0 + 1) = 0

这是一个完全纯的节点。

计算加权平均基尼不纯度:

  • 叶子节点3的权重 = 4/7 ≈ 0.571
  • 叶子节点4的权重 = 3/7 ≈ 0.429

“爱看卡通”问题的总基尼不纯度为:
Gini_cartoons = (0.571 * 0.375) + (0.429 * 0) ≈ 0.214 + 0 = 0.214

比较与选择

现在我们已经得到了两个问题的量化不纯度:

  • “爱看电影”的基尼不纯度:0.405
  • “爱看卡通”的基尼不纯度:0.214

基尼不纯度越低,说明分割效果越好。 比较两个数值,0.214 < 0.405,这证实了我们的直觉:“爱看卡通”是比“爱看电影”更好的根节点分割问题。

通过计算基尼不纯度,我们将选择最佳分割点的过程从直觉判断转变为可量化的数学比较。

关于熵的简要说明

除了基尼不纯度,熵是另一种常用的不纯度度量方法。熵的概念来源于信息论,它衡量的是系统的混乱程度。一个纯节点的熵为0。熵的计算公式为:

Entropy = - Σ (p_i * log₂(p_i))

在实践中,基尼不纯度和熵通常能产生非常相似的树。选择哪一种有时取决于具体实现或微小的性能差异。基尼不纯度的计算稍快一些,因为它不涉及对数运算。

总结

在本节课中,我们一起学习了如何量化决策树中的不纯度。

  1. 我们首先回顾了需要量化比较不同分割点(问题)的必要性。
  2. 我们深入学习了基尼不纯度,其公式为 Gini = 1 - Σ (p_i)²,用于衡量一个节点中类别的混合程度。
  3. 我们通过代码演示般的步骤,计算了“爱看电影”和“爱看卡通”两个问题的加权平均基尼不纯度。
  4. 通过比较数值(0.405 vs 0.214),我们科学地得出结论:“爱看卡通”应作为根节点的问题,因为它能带来更低的不纯度,即更好的分类效果。
  5. 最后,我们简要介绍了另一种度量方法——熵,并指出它与基尼不纯度在实践中的相似性。

现在,我们已经掌握了选择根节点问题的关键工具。在下一节中,我们将探讨如何处理像“年龄”这样的连续数值特征,并完成我们决策树的构建。

004:数值数据的基尼不纯度

在本节课中,我们将学习如何为数值型数据计算基尼不纯度。上一节我们介绍了基尼不纯度的数学定义和可视化,并将其与熵进行了比较。本节我们将重点探讨当特征(如年龄)是数值时,如何应用基尼不纯度来选择最佳分割点。

首先,让我们回顾一下我们试图解决的问题。我们有一个包含七个人的训练数据集,记录了他们对三个问题(是否爱看电影、是否爱看卡通片、年龄)的回答,以及他们是否喜欢蝙蝠侠。我们的目标是根据这些信息,构建一个决策树来预测一个新来的人是否喜欢蝙蝠侠。

在之前的课程中,我们已经为“爱看电影”和“爱看卡通片”这两个分类问题计算了基尼不纯度,并得出结论:“爱看卡通片”作为根节点比“爱看电影”更优。然而,我们还没有考虑数值特征“年龄”。本节我们将学习如何为“年龄”这个数值特征计算基尼不纯度。

处理数值特征与处理分类特征的过程有所不同。以下是计算数值特征基尼不纯度的步骤。

步骤一:排序数值特征

首先,需要将数值特征的值从低到高进行排序。在我们的数据中,年龄列已经是升序排列:7, 12, 18, 35, 38, 50, 83。如果你的数据未排序,这是必须完成的第一步。

步骤二:计算相邻行的平均值

接下来,计算排序后相邻数据点的平均值。这为我们提供了潜在的分割阈值。

以下是计算过程:

  • 7 和 12 的平均值是 9.5
  • 12 和 18 的平均值是 15
  • 18 和 35 的平均值是 26.5
  • 35 和 38 的平均值是 36.5
  • 38 和 50 的平均值是 44
  • 50 和 83 的平均值是 66.5

因此,我们得到六个潜在的分割点:9.5, 15, 26.5, 36.5, 44, 66.5。

步骤三:为每个分割点计算基尼不纯度

现在,我们需要为每一个潜在的分割点计算基尼不纯度。基尼不纯度的公式为:

Gini = 1 - (p_yes)² - (p_no)²

其中,p_yesp_no 分别代表在给定分割下,节点中“是”和“否”类别的比例。

我们将以第一个分割点 9.5 为例进行演示。我们提出的问题是:“年龄是否小于 9.5?”

根据这个条件,数据被分为两部分:

  • 左分支 (年龄 < 9.5):只有年龄为7的数据点。对应的“爱蝙蝠侠”标签是“否”。所以,左分支的类别分布是:p_yes = 0/1 = 0, p_no = 1/1 = 1
  • 右分支 (年龄 >= 9.5):包含年龄为12, 18, 35, 38, 50, 83的数据点。对应的标签是:否, 是, 是, 是, 否, 否。所以,右分支的类别分布是:p_yes = 3/6 = 0.5, p_no = 3/6 = 0.5

现在计算每个分支的基尼不纯度:

  • 左分支基尼不纯度Gini_left = 1 - (0)² - (1)² = 0
  • 右分支基尼不纯度Gini_right = 1 - (0.5)² - (0.5)² = 1 - 0.25 - 0.25 = 0.5

一个分割点的总体基尼不纯度是左右分支基尼不纯度的加权平均,权重是各分支样本数占总样本数的比例。

总基尼不纯度 = (左分支样本数 / 总样本数) * Gini_left + (右分支样本数 / 总样本数) * Gini_right

对于分割点9.5:

  • 总样本数 = 7
  • 左分支样本数 = 1
  • 右分支样本数 = 6
  • 总基尼不纯度 = (1/7)0 + (6/7)0.5 ≈ 0.429

我们需要对步骤二中得到的每一个潜在分割点(15, 26.5, 36.5, 44, 66.5)重复上述计算过程。

步骤四:比较并选择最佳分割点

完成所有分割点的计算后,我们会得到每个分割点对应的总体基尼不纯度。基尼不纯度越低,意味着该分割点产生的子集“纯度”越高,分类效果越好。

因此,我们会选择能产生最小总体基尼不纯度的那个分割点,作为对“年龄”这个特征的最佳分割方式。

通过比较“爱看电影”(基尼不纯度0.405)、“爱看卡通片”(基尼不纯度0.214)以及“年龄”的最佳分割点所对应的基尼不纯度,我们就能最终确定哪个特征应该放在决策树的根节点。数值最小的特征将获得最高优先级。

本节课中,我们一起学习了为数值型特征计算基尼不纯度的完整流程。这个过程包括排序数据、计算相邻值平均值作为候选分割点、为每个分割点计算加权基尼不纯度,并最终选择不纯度最低的分割点。掌握了这个方法,我们就能公平地比较分类特征和数值特征,从而科学地构建决策树的每一个节点。

005:完成分类决策树的构建 🎯

在本节课中,我们将完成分类决策树的构建。这是一个从两到三节课前开始的系列过程的收尾工作。

概述

我们将使用一个具体的训练数据集,通过计算基尼不纯度来决定决策树每个节点的最佳划分问题,从而完成整个决策树的构建。

问题回顾

我们收集了七个人的数据,询问了三个问题:

  1. 你喜欢看电影吗?
  2. 你喜欢看卡通吗?
  3. 你的年龄是多少?

基于这三个问题的答案,我们的目标是预测一个人是否喜欢蝙蝠侠。这是一个分类问题,我们将通过从头构建一个决策树来解决它。

上一节我们介绍了基尼不纯度的概念,并确定了决策树的根节点。本节中我们来看看如何确定后续的内部节点和叶节点。

当前进度

在上一讲中,我们通过计算基尼不纯度,确定了第一个问题(根节点)应该是“喜欢卡通吗?”。其基尼不纯度最低,为 0.214

决策树目前的状态如下:

  • 如果回答“是”,则有4个人进入左分支(其中3人喜欢蝙蝠侠,1人不喜欢)。
  • 如果回答“否”,则有3个人进入右分支(所有人都不喜欢蝙蝠侠)。

右分支的叶节点已经是“纯”的(类别一致),因此无需进一步划分。左分支的叶节点是“不纯”的(类别混合),需要继续划分。

确定下一个内部节点

现在,我们需要决定在左分支(回答“喜欢卡通”)的4个人中,接下来应该问哪个问题:“喜欢电影吗?”还是“年龄?”。

我们将再次使用基尼不纯度作为衡量标准,为这两个候选问题分别计算基于当前4人子集的基尼不纯度。

以下是计算过程:

1. 基于“喜欢电影吗?”的划分

我们为这4个人创建决策树映射。

  • “喜欢电影”为“是”的分支:有2人。其中1人喜欢蝙蝠侠,1人不喜欢。

    • 基尼不纯度计算公式为:Gini = 1 - (p_yes^2 + p_no^2)
    • 此处,p_yes = 0.5, p_no = 0.5
    • 计算得:Gini_yes = 1 - (0.5^2 + 0.5^2) = 0.5
  • “喜欢电影”为“否”的分支:有2人。其中2人喜欢蝙蝠侠,0人不喜欢。

    • 此处,p_yes = 1, p_no = 0
    • 计算得:Gini_no = 1 - (1^2 + 0^2) = 0

接下来,计算该问题(“喜欢电影吗?”)的加权基尼不纯度
Weighted Gini = (分支样本数 / 总样本数) * Gini_分支1 + (分支样本数 / 总样本数) * Gini_分支2
Weighted Gini_movies = (2/4)*0.5 + (2/4)*0 = 0.25

2. 基于“年龄”的划分(简化示例)

为了简化教程,我们假设对“年龄”这个连续变量进行了二分处理(例如,以某个阈值为界)。假设经过计算,其加权基尼不纯度为 0.3(此处为示例值,实际计算需确定最佳分割点)。

比较与选择

比较两个候选问题的加权基尼不纯度:

  • “喜欢电影吗?”:0.25
  • “年龄”:0.3

“喜欢电影吗?”这个问题的加权基尼不纯度更低。因此,我们选择它作为决策树的下一个内部节点(在“喜欢卡通=是”的分支上)。

构建决策树

现在我们可以扩展我们的决策树:

  1. 根节点:喜欢卡通吗?
    • -> 叶节点:预测为“不喜欢蝙蝠侠”。
    • -> 进入下一个节点。
  2. 内部节点:喜欢电影吗?
    • -> 叶节点:预测为“喜欢蝙蝠侠”(根据当前子集)。
    • -> 叶节点:预测结果混合(1喜欢,1不喜欢)。

注意,在“喜欢电影=是”的分支,我们得到了一个不纯的叶节点(1是,1否)。理论上,如果我们需要一个完全纯的树,可以继续用“年龄”等剩余特征对这个节点进行划分,直到所有叶节点纯或达到停止条件(如最大深度)。本例中,我们仅演示核心构建过程,在此停止。

总结

本节课中我们一起学习了如何完成决策树的构建。我们回顾了问题,确认了根节点,并重点演示了如何为不纯的分支确定下一个最佳划分特征。通过计算和比较不同特征划分后的加权基尼不纯度,我们选择了能使不纯度降低最多的特征(“喜欢电影吗?”)作为内部节点,从而逐步完善了决策树的结构。这个过程可以递归进行,直到构建出完整的分类模型。

006:分类树剪枝概述 🌳

在本节课中,我们将学习决策树中的一个关键概念——剪枝。我们将探讨为何一个在训练数据上表现完美的模型,在测试数据上可能表现糟糕,并理解如何通过剪枝来避免过拟合,提升模型的泛化能力。

问题背景与回顾

上一节我们介绍了如何从零构建一个完整的分类决策树。本节中,我们来看看这个看似完美的模型在实际应用中可能遇到的问题。

我们一直使用的训练数据集如下,它收集了7个人的信息,目标是基于他们是否爱看电影、是否爱看卡通以及年龄,来预测他们是否喜欢蝙蝠侠。

爱看电影? 爱看卡通? 年龄 喜欢蝙蝠侠?
7
12
18
35
38
50
83

基于此数据,我们之前构建了最优分类树,其结构如下:

  1. 首先询问:“你是否爱看卡通?”
    • 如果回答“否”,则预测为“不喜欢蝙蝠侠”。
    • 如果回答“是”,则进入下一步。
  2. 接着询问:“你的年龄是多少?”
    • 如果年龄 < 12.5,则预测为“不喜欢蝙蝠侠”。
    • 如果年龄 >= 12.5,则预测为“喜欢蝙蝠侠”。

此决策树在训练数据上达到了100%的准确率。

泛化问题:训练 vs. 测试

然而,机器学习模型的核心目标不仅是拟合训练数据,更要能泛化到未见过的测试数据上。

现在,让我们用以下三个新的测试样本来检验模型的泛化能力:

测试人员 爱看电影? 爱看卡通? 年龄 真实喜好
1 6 喜欢
2 7 喜欢
3 9 不喜欢

以下是模型对测试数据的预测过程与结果:

  • 测试人员1:爱卡通(是)→ 年龄6岁(<12.5)→ 预测为“不喜欢”。(预测错误
  • 测试人员2:爱卡通(是)→ 年龄7岁(<12.5)→ 预测为“不喜欢”。(预测错误
  • 测试人员3:不爱卡通(否)→ 直接预测为“不喜欢”。(预测正确

模型在三个测试样本中错了两个,测试准确率仅为33%,这与100%的训练准确率形成了巨大反差。

过拟合分析

为什么会出现如此严重的性能下降?让我们分析决策树的结构。

问题出在“爱卡通=是 且 年龄<12.5”这个叶节点上。回顾训练数据,只有一个样本(年龄7岁,不喜欢蝙蝠侠)落入了这个分支。模型仅仅依据这一个样本,就为所有符合该条件的人做出了“不喜欢蝙蝠侠”的预测。

这导致了过拟合:模型过度学习了训练数据中的细节(甚至可能是噪声),而未能捕捉到更普遍的规律。因此,当遇到同样符合该条件但真实喜好不同的新样本(测试人员1和2)时,模型做出了错误判断。

解决方案:剪枝

为了解决过拟合问题,我们需要引入剪枝技术。剪枝的核心思想是:简化模型,牺牲一部分训练精度以换取更好的泛化能力

具体到我们的例子,一个直接的剪枝策略是:剪掉那个基于单个样本的、不可靠的叶节点

剪枝后的决策树如下:

  1. 首先询问:“你是否爱看卡通?”
    • 如果回答“否”,则预测为“不喜欢蝙蝠侠”。
    • 如果回答“是”,则直接预测为“喜欢蝙蝠侠”

我们移除了对年龄的判断,整个“爱卡通=是”的分支现在直接输出一个统一的预测。

剪枝效果验证

让我们用剪枝后的树重新评估测试数据:

  • 测试人员1:爱卡通(是)→ 预测为“喜欢”。(预测正确
  • 测试人员2:爱卡通(是)→ 预测为“喜欢”。(预测正确
  • 测试人员3:不爱卡通(否)→ 预测为“不喜欢”。(预测正确

剪枝后的模型在测试集上达到了100%的准确率!虽然它在训练集上可能不再完美(例如,会错误预测那个7岁的训练样本),但其泛化到新数据的能力得到了显著提升。

以下是剪枝前后的核心逻辑对比:

  • 剪枝前(过拟合):
    if loves_cartoon == “否”:
        prediction = “不喜欢”
    else: # loves_cartoon == “是”
        if age < 12.5:
            prediction = “不喜欢” # 仅基于1个样本
        else:
            prediction = “喜欢”
    
  • 剪枝后(泛化更好):
    if loves_cartoon == “否”:
        prediction = “不喜欢”
    else: # loves_cartoon == “是”
        prediction = “喜欢” # 合并分支,简化规则
    

总结

本节课中我们一起学习了决策树剪枝。我们首先看到了一个在训练集上完美但在测试集上表现很差的模型,这揭示了过拟合问题。通过分析,我们发现问题源于模型基于极少样本(本例中为1个)做出了过于具体的决策。为了解决这个问题,我们引入了剪枝,通过移除不可靠的分支来简化模型。最终,剪枝后的模型虽然训练精度有所下降,但测试精度(泛化能力) 得到了根本性改善。记住,一个好的机器学习模型的目标是泛化,而非仅仅记忆训练数据。

007:使用Python从头构建完整分类树 🚀

在本节课中,我们将学习如何从零开始,使用Python构建一个完整的分类决策树。我们将以一个真实且有影响力的医学问题——预测心脏病——作为贯穿始终的案例,将之前学习的基尼不纯度、熵、剪枝等概念付诸实践。


理解问题与数据 📊

上一节我们介绍了本系列的目标,本节中我们来看看我们将要解决的具体问题。

我们的核心目标是:使用决策树模型,根据患者的13项特征,预测其是否患有心脏病。这意味着,对于任何新患者,只要我们收集到这13项数据,模型就能给出患病风险的预测。

在着手解决任何机器学习问题之前,充分理解问题本身和数据至关重要。我们的数据来源于UCI机器学习仓库中的克利夫兰心脏病数据集,最初记录于1989年的一篇医学论文中。

以下是数据集中每个变量的含义。理解这些变量有助于后续处理分类数据、调试问题,并增加对问题的直觉。

  • age: 患者年龄(岁)。
  • sex: 患者性别(1=男性,0=女性)。
  • cp: 胸痛类型(1-4,值越高表示越典型的心绞痛)。
  • trestbps: 静息血压(毫米汞柱)。
  • chol: 血清胆固醇水平(毫克/分升)。
  • fbs: 空腹血糖是否 > 120 mg/dl(1=是,0=否)。
  • restecg: 静息心电图结果(0-2)。
  • thalach: 运动达到的最大心率。
  • exang: 运动是否诱发心绞痛(1=是,0=否)。
  • oldpeak: 运动相对于休息引起的ST段压低。
  • slope: 峰值运动时ST段的斜率(0-2)。
  • ca: 荧光透视显影的主要血管数量(0-3)。
  • thal: 铊扫描结果(3=正常,6=固定缺陷,7=可逆缺陷)。
  • hd (目标变量): 是否患有心脏病(0=无,1=有)。

我们的任务就是基于前13个特征,预测最后一个目标变量 hd


第一步:导入必要的库 📦

在Python中查看或处理数据之前,我们首先需要导入必要的工具库。以下是构建决策树项目常用的库。

import pandas as pd      # 用于数据处理和分析
import numpy as np       # 用于数值计算
import matplotlib.pyplot as plt # 用于数据可视化
from sklearn.model_selection import train_test_split # 用于拆分训练集和测试集
from sklearn.tree import DecisionTreeClassifier, plot_tree # 用于构建和可视化决策树
from sklearn.metrics import accuracy_score, classification_report # 用于评估模型性能

pandasnumpy 是处理数据的基石。sklearn 提供了构建和评估决策树模型的完整工具链。


第二步:加载与探索数据 🔍

库准备就绪后,下一步就是将数据加载到Python环境中并进行初步探索,以了解数据的基本情况。

# 假设数据文件名为 'heart_disease.csv',并位于当前目录
data = pd.read_csv('heart_disease.csv')

# 查看数据的前几行,了解数据结构
print("数据前5行:")
print(data.head())

# 查看数据的基本信息,包括列名、非空值数量和数据类型
print("\n数据信息:")
print(data.info())

# 查看数据的统计摘要
print("\n数据统计描述:")
print(data.describe())

运行这些代码可以帮助我们确认数据是否成功加载,检查是否有缺失值,并了解各特征的数值分布范围。


第三步:数据预处理 🧹

原始数据通常不能直接用于模型训练。数据预处理的目标是清理数据并将其转换为适合算法的格式。

以下是常见的预处理步骤:

  1. 处理缺失值:检查并决定是删除缺失行,还是用均值、中位数或众数填充。

    # 检查缺失值
    print(data.isnull().sum())
    # 假设我们用该列的均值填充缺失值(以‘age’列为例)
    # data['age'].fillna(data['age'].mean(), inplace=True)
    
  2. 处理分类变量:决策树算法通常需要数值输入。对于像 sexcp 这样的分类变量,如果它们已经是数字编码(如0/1),则可以保留。对于字符串类型的分类变量,需要使用pd.get_dummies()进行独热编码。

    # 如果‘thal’是字符串类型,进行独热编码
    # data = pd.get_dummies(data, columns=['thal'], drop_first=True)
    
  3. 分离特征与目标变量:将我们要预测的列(目标变量)与其他列(特征)分开。

    X = data.drop('hd', axis=1)  # 特征矩阵
    y = data['hd']               # 目标变量向量
    
  4. 划分训练集与测试集:将一部分数据留出,用于最终评估模型的泛化能力,避免过拟合。

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    

    这里,test_size=0.2 表示20%的数据用作测试集,random_state 确保每次运行拆分结果一致。


第四步:构建初步决策树 🌱

数据准备完成后,我们可以构建第一个决策树模型。这是一个基线模型,帮助我们了解数据的基本可分性。

# 初始化决策树分类器,先使用默认参数
clf_baseline = DecisionTreeClassifier(random_state=42)

# 在训练集上训练模型
clf_baseline.fit(X_train, y_train)

# 在训练集和测试集上进行预测
y_train_pred = clf_baseline.predict(X_train)
y_test_pred = clf_baseline.predict(X_test)

# 计算准确率
train_accuracy = accuracy_score(y_train, y_train_pred)
test_accuracy = accuracy_score(y_test, y_test_pred)

print(f"训练集准确率: {train_accuracy:.4f}")
print(f"测试集准确率: {test_accuracy:.4f}")

如果训练集准确率远高于测试集准确率,说明模型可能过拟合了,它过于复杂,记住了训练数据的噪声,而非一般规律。


第五步:优化决策树 ✂️

初步的树往往过于复杂。优化(或“剪枝”)的目的是简化模型,提高其泛化到新数据的能力。主要方法是通过超参数控制树的生长。

以下是关键的超参数及其作用:

  • max_depth: 树的最大深度。限制深度能有效防止过拟合。
  • min_samples_split: 节点分裂所需的最小样本数。值越大,树越简单。
  • min_samples_leaf: 叶节点所需的最小样本数。可以平滑模型。
  • max_leaf_nodes: 最大叶节点数量。
  • criterion: 分裂标准,可选 'gini'(基尼不纯度)或 'entropy'(信息增益)。

我们可以尝试不同的参数组合,并选择在测试集上表现最好的一个。

# 尝试一个经过剪枝的树
clf_pruned = DecisionTreeClassifier(max_depth=5, min_samples_split=10, random_state=42)
clf_pruned.fit(X_train, y_train)

y_test_pred_pruned = clf_pruned.predict(X_test)
test_accuracy_pruned = accuracy_score(y_test, y_test_pred_pruned)

print(f"剪枝后测试集准确率: {test_accuracy_pruned:.4f}")

# 可以查看更详细的评估报告
print("\n分类报告:")
print(classification_report(y_test, y_test_pred_pruned))

通过比较剪枝前后的测试集准确率,我们可以判断优化是否有效。


第六步:可视化决策树 📈

可视化决策树能帮助我们理解模型是如何做出决策的,哪些特征最重要。

plt.figure(figsize=(20,10))  # 设置画布大小
plot_tree(clf_pruned,
          feature_names=X.columns,  # 特征名称
          class_names=['No Disease', 'Disease'], # 类别名称
          filled=True,        # 用颜色填充节点
          rounded=True,       # 圆角节点
          fontsize=10)
plt.title("优化后的心脏病预测决策树")
plt.show()

从树形图中,我们可以清晰地看到从根节点开始,模型根据哪些条件(如 thalach > 152.5)将患者划分到不同的分支,直至得出最终诊断。


总结 🎯

本节课中,我们一起完成了一个完整的机器学习项目流程,使用Python从头构建了一个心脏病预测的分类决策树。我们经历了以下关键步骤:

  1. 理解问题与数据:明确了预测目标,并深入了解了13个临床特征的含义。
  2. 环境准备:导入了必要的Python库。
  3. 数据加载与探索:将数据载入程序并查看了其概况。
  4. 数据预处理:为模型训练准备好了干净、格式正确的数据。
  5. 构建基线模型:用默认参数建立了第一棵树,作为性能基准。
  6. 优化模型:通过调整超参数(如 max_depth)对树进行剪枝,以提升泛化能力。
  7. 评估与可视化:评估了模型性能,并可视化树结构以解释其决策过程。

这个过程不仅适用于心脏病预测,也是解决大多数分类问题的通用蓝图。核心思想是:从理解业务开始,用代码实现数据处理和模型构建,并通过迭代优化使模型既准确又可靠。

008:什么是回归树? 🌳

在本节课中,我们将要学习决策树的另一个重要应用——回归树。我们将了解它与分类树的区别,探索其适用场景,并通过一个药物剂量与效果的例子来直观理解其工作原理。

在过去的五到六节课中,我们深入探讨了分类树。我们不仅从零开始构建了完整的分类树,还用Python将其代码实现。本节中,我们来看看决策树的另一个强大分支:回归树。

我曾一度认为分类是决策树的唯一用途,并不知道决策树同样可以用于回归任务。实际上,回归树在许多现实世界的回归问题中非常有用且具有影响力。让我们看一些例子。

这是著名的约翰·霍普金斯大学COVID-19病例数据仪表盘。观察图中的红点,可以看到感染病例的集中区域。

事实上,包括世界经济论坛在内的许多知名机构都将其转化为动态动画,以展示感染的传播过程。观察这个动画,可以看到红点如何演变:从中国开始,然后世界各地逐渐出现大片红色区域,这意味着感染规模在全球范围内缓慢扩大。


《纽约时报》为此维护了一个非常出色的数据集。在这里,你可以看到感染病例在世界各大洲的变化情况,存在多个峰值。图中颜色最深的热点区域表示该地区病例数最多。你甚至可以绘制人均病例数或人均死亡数,会发现某些区域颜色非常深,这表明COVID-19对该地区产生了巨大影响。

当COVID-19蔓延时,政府机构和政策制定者迫切需要预测本周、下周以及未来几周的感染病例数,因为这个预测将直接影响他们实施的政策,例如口罩强制令、封锁政策、封锁持续时间以及哪些区域必须佩戴口罩。因此,预测变得至关重要,而Kaggle平台在其中扮演了重要角色。在疫情期间,Kaggle发起了多项挑战。

这里你可以看到“COVID-19全球预测挑战”,许多人提交了他们的解决方案。这个挑战本质上是一个预测任务:目标是预测随时间变化的感染病例数。挑战描述是:预测全球不同地区累计确诊的COVID-19病例数。

他们提供了训练数据和用于提交的测试数据,参赛者需要提交自己的预测方案。人们提出了多种解决方案,但非常有趣的是,回归树也被用于构建这些解决方案。我原本以为人们会使用最小二乘法、梯度提升或神经网络等复杂的机器学习算法,但回归树同样被用于提出解决方案。你可以看到这个解决方案被标记为星标,意味着它被许多人引用和赞赏。

我会在视频的信息区分享这个笔记本文档链接。这里我只想说明,回归树甚至可以用于解决像COVID-19大流行这样具有重大影响的现实问题。因此,你在本节课中学到的知识将对你极为有益。

不仅仅是这个问题,在GitHub和Kaggle上,你还会发现许多使用回归树完成的项目。例如,有一个著名的汽油消耗数据集。

该数据集包含诸如汽油税、高速公路里程、人口等不同特征,最终目标是预测汽油消耗量。事实证明,回归树被广泛用于解决这个问题。正如我现在展示的,它在Kaggle上也可用,人们为此编写了许多代码。

如果你现在在GitHub上搜索“回归树”,无论是在本讲座之后还是期间,你都会找到大量使用回归树本身完成的项目。

因此,本节课的目的是首先让你从根本上理解什么是回归树,然后,类似于我们处理分类树的方法,我们将从零开始构建一个完整的回归树,并用Python编写代码。

那么,让我们开始今天的课程。首先,我想告诉你为什么回归树是有益的,尤其是在存在许多其他回归方法(如最小二乘法、普通最小二乘法等)的情况下。我发现的一个主要原因是,回归树处理非线性数据的能力远优于其他回归方法。

让我解释一下我的意思。看这个数据集,它看起来几乎是线性的,虽然有一些噪声,但我们可以清楚地看到有一条直线可以拟合这些数据。对于这样的数据集,传统的回归方法非常有效,你可以直接使用它们。但是,非线性数据呢?如果数据是这样的:这里有一簇点,那里有一簇点,然后这里和那里也各有一簇。尝试在这里拟合一条直线,无论你用哪条直线,都无法很好地拟合这种非线性数据。正是在这类数据中,回归树实际上极其有益和有用。

在今天的讲座中,让我们理解什么是回归树,以及它们能解决什么类型的问题。从下一讲开始,我们将着手构建回归树。

再次说明,我参考了StatsQuest频道的讲座材料。我将这些材料提炼成了构建模块,但我非常喜欢这个频道,所以在此注明出处。

我们要解决的问题是这样的:我们有一个训练数据,其中Y轴是药物有效性,X轴是药物剂量。想象一个数据,其中药物剂量以百分比表示(如10%、20%、30%、40%等),Y轴是药物有效性百分比。目标是量化剂量水平与药物有效性之间的关系,这样,每当遇到一个新的人(例如,给一个新的人服用药物时),你能否根据他们服用的药物剂量来预测药物的有效性?这就是我们试图解决的主要问题。

假设数据集看起来像这样。现在请专注于黄色的点,暂时忽略橙色的线或橙色文字,只看黄色的点。

这就是我们当前的数据集。在X轴上,我们有药物剂量,在Y轴上,我们有药物有效性百分比。

如果你专注于黄色的数据点,首先可以看到它们似乎是成簇的,看起来完全不像线性数据。事实上,如果你试图用一条直线来拟合这些数据,那将是一个非常糟糕的主意,因此你也不能使用传统的回归方法。

让我擦掉这条直线,以便我能更清楚地描述这个数据集的更多特征。

我已经擦掉了它,这样我们可以更清晰地可视化数据集。显然,传统的标准线性回归将是一个糟糕的选择。另一种思考方式是:无论在哪里出现高数值的点(比如这些点),其左右两侧总是伴随着低数值的点。这是数据集不适合线性回归的完美指示或完美体现,你可以看到这种以非线性方式呈现的簇状分布。

为了解决这个问题,我们将使用回归树。事实证明,对于解决这类问题,回归树比传统的最小二乘法要好得多。

回归树的另一个优点是它们也非常易于解释和使用。

我告诉你的关于回归树的第一个优点是,与其他回归方法相比,它们能以更好的方式处理非线性数据。但还有另一个优点,那就是:与所有决策树一样,它们在本质上非常易于解释。我真正尊重和喜欢决策树的一点是,与神经网络等其他方法不同,回归树具有高度的可解释性。你可以查看一棵回归树,并提出诸如“为什么答案是这样?”或“为什么解决方案的行为是这样?”的问题。这就像照亮了通常被视为“黑箱”的机器学习。

很好,那么让我们使用回归树。我们的目标是:给定一个新的剂量,我们能否实际找出该剂量的有效性?我们拥有这个训练数据。如果我们遇到一个不在训练集中的新数据点...

本节课中我们一起学习了回归树的基本概念、其相对于传统线性回归在处理非线性数据方面的优势,以及它在现实世界问题(如疫情预测)中的应用价值。我们还通过一个具体的药物剂量预测例子,直观地理解了回归树要解决的问题类型。在接下来的课程中,我们将开始动手从零构建回归树。

009:确定根节点 🎯

在本节课中,我们将开始学习如何从零开始构建一个完整的回归树。我们将从理解问题开始,逐步学习确定回归树根节点的具体步骤。

问题概述

我们面临一个经典的回归问题。数据如下:

  • X轴:药物剂量
  • Y轴:药物有效性百分比
  • 数据:我们收集了大约10到15名患者的数据,记录了在不同药物剂量下的有效性百分比。
  • 目标:基于这些训练数据,构建一个回归树模型。该模型能够根据新患者的药物剂量,预测其药物有效性百分比。

最终构建完成的优化决策树如下图所示,它包含根节点、分支和叶节点。我们的任务就是一步步构建出这棵树。

确定根节点

上一节我们介绍了回归树的应用和最终目标,本节中我们来看看如何开始构建的第一步:确定回归树的根节点。

根节点是决策树的起点,它决定了数据的第一次分割。在我们的例子中,根节点显示为“剂量 < 14.5?”。为什么是14.5而不是其他值?这就是我们今天要解决的问题。

以下是确定根节点的具体步骤:

步骤一:计算前两个剂量值的平均值

首先,我们需要从数据中找出第一个分割点。一个初始的方法是查看数据的前两个剂量值(在图中用橙色圆圈标出),并计算它们的平均值。

设第一个点的剂量为 x1,第二个点为 x2。计算公式如下:
平均值 = (x1 + x2) / 2
在本例中,计算得到平均值为 3

我们将以 3 作为候选根节点进行测试。

步骤二:以平均值作为分割点构建临时树

接下来,我们以“剂量 < 3?”作为根节点,构建一个临时的决策树。

  • 如果剂量小于3(位于图中橙色虚线的左侧),则将该区域所有点的药物有效性平均值作为预测值。左侧只有一个点,其值为0,因此预测值为 0
  • 如果剂量大于等于3(位于橙色虚线右侧),则将该区域所有点的药物有效性平均值作为预测值。计算右侧所有点的平均值,得到 38.8

因此,这个临时树规则是:

  • 若剂量 < 3,预测有效性 = 0
  • 若剂量 >= 3,预测有效性 = 38.8

步骤三:定性评估临时树

现在,我们直观地评估这个以“剂量=3”为根节点的树是否优秀。

  • 左侧:只有一个数据点,预测值(0)与其真实值(0)完全吻合,表现完美。
  • 右侧:包含了大部分数据点。预测值是一个固定的38.8。从图中可以明显看出,许多点的真实值远高于或低于38.8,导致预测误差很大。

由于大部分数据点都集中在右侧,且预测误差显著,我们可以定性地判断:选择“剂量=3”作为根节点并不是一个好的选择。

步骤四:量化评估——残差平方和

仅凭定性判断不够精确。在分类树中,我们使用基尼不纯度等指标;在回归树中,我们使用残差平方和来量化模型的误差。

残差平方和 的计算方法是:对于每一个数据点,计算其真实值与模型预测值之差的平方,然后将所有点的平方差求和。

公式表示为:
SSR = Σ(真实值_i - 预测值_i)²

这个指标越小,说明模型的预测整体上越接近真实值,模型越好。

让我们应用这个指标来量化评估我们的临时树:

  • 对于左侧唯一的点:真实值=0,预测值=0,残差平方为 (0-0)² = 0
  • 对于右侧每一个点:真实值各不相同,预测值均为38.8。每个点都会产生一个 (真实值 - 38.8)² 的平方差。

将所有点的残差平方相加,就得到了这个以“剂量=3”为根节点的树的SSR。这个SSR值会很大,这从数学上证实了我们之前的定性判断——这不是一个优秀的根节点选择。

总结

本节课中我们一起学习了构建回归树的第一步:确定根节点。

  1. 我们从一个简单的启发式方法开始:取前两个数据点特征值的平均值作为候选分割点。
  2. 我们根据这个分割点构建了一个临时的决策树,并进行了定性分析。
  3. 我们引入了残差平方和这个关键指标,用于量化回归树的预测误差。

我们发现,第一个候选根节点(剂量=3)效果不佳。那么,如何找到最优的根节点呢?下一节课我们将介绍,通过系统地尝试所有可能的分割点,并选择那个能使残差平方和最小的分割点,来确定最佳的根节点。

010:完成回归树的构建

在本节课中,我们将继续从零开始构建回归树。上一节我们确定了决策树的根节点,本节我们将完成整个决策树的构建,学习如何为每个分支节点选择最佳分割阈值。

回顾与目标

上一节我们开始构建一个回归树来解决一个具体问题:根据药物剂量预测药效百分比。我们收集了约20-25名患者的数据,这些数据点呈现非线性关系,因此传统的回归模型(如普通最小二乘法)难以处理,而回归树是解决此类问题的理想算法。

我们的目标是:给定训练数据,构建一个使用药物剂量预测药效的回归树。最终,我们希望得到一棵优化后的树,其结构如下:首先根据“剂量是否小于14.5”进行判断,然后根据“剂量是否大于等于25”和“剂量是否大于等于23.5”进行后续判断。这棵树能很好地拟合图中的所有数据点。

上一节我们通过计算残差平方和,确定了根节点的最佳分割阈值为 剂量 < 14.5。残差平方和的计算公式为:
SSR = Σ(实际值 - 预测值)²
其中,预测值在节点内是数据点目标值的平均值。SSR值越低,代表该分割产生的误差越小。

目前,决策树仅确定了根节点,我们尚不清楚“是”和“否”分支之后的结构。本节课将完成这些部分的构建。

构建左分支(剂量 < 14.5)

当根据根节点(剂量 < 14.5)进行分割后,数据点被分为左右两部分。我们将左侧部分标记为 L,右侧部分标记为 R。首先,我们处理左侧分支(L)。

左侧分支包含6个数据点,其剂量值均小于14.5。为了进一步划分这个分支,我们需要像为根节点所做的那样,为这6个点寻找最佳分割阈值。

以下是具体步骤:

  1. 我们依次考虑不同的候选阈值(例如,剂量 < 3, < 5, < 7, ..., < 11.5)。
  2. 对于每个候选阈值,我们计算其对应的残差平方和。
  3. 选择使残差平方和最小的阈值作为该节点的分割点。

我们对这6个点计算了不同阈值下的SSR,并绘制了SSR随阈值变化的曲线。结果显示,当阈值为 剂量 < 11.5 时,SSR值最低,即误差最小。

因此,在左侧分支(“是”分支)中,下一个分类问题应为“剂量是否小于11.5”。如果答案为“是”,则进入一个叶节点,其预测值为该分支内数据点药效的平均值(计算得1);如果答案为“否”,则进入另一个叶节点,其预测值为对应数据点药效的平均值(计算得20)。

识别问题与剪枝

然而,这里存在一个问题。让我们仔细查看由“剂量 < 11.5”为“否”(即剂量在11.5到14.5之间)所定义的数据桶。在原始数据图中,这个区间内实际上只有一个数据点。

当一个节点分割后产生的子节点包含的数据点非常少(例如只有1个)时,继续分割可能导致过拟合——模型过度学习了训练数据中的噪声,而降低了泛化到新数据的能力。为了避免这种情况,我们通常需要设置一个停止条件,例如最小叶子节点样本数。这意味着,如果分割后某个子节点包含的数据点数量低于预设的最小值(例如5个),我们就停止在该节点继续分割,并将其直接作为叶节点。

因此,对于当前情况,由于“剂量在11.5到14.5之间”这个分支只包含1个数据点,少于最小叶子节点样本数,我们不应该在此处继续分割。这个分支应直接成为一个叶节点,其预测值就是该单一数据点的药效值。

构建右分支(剂量 >= 14.5)

现在,让我们回到根节点的右侧分支(R),即那些剂量大于等于14.5的数据点。

我们采用相同的流程来构建这个分支:

  1. 聚焦于右侧分支的所有数据点。
  2. 为这些点计算不同分割阈值下的残差平方和。
  3. 选择SSR最小的阈值作为该节点的分割点。

计算结果表明,对于右侧分支,最佳分割点是 剂量 < 25。在此阈值下,我们可以进一步将数据分为两部分。

完成决策树构建

遵循上述原则(寻找最小SSR的分割点,并应用最小叶子节点样本数规则以防止过拟合),我们最终可以构建出完整的决策树。

其最终结构如下:

  1. 根节点:剂量 < 14.5?
    • -> 进入左分支。
      • 由于左分支在进一步分割时,其中一个子节点数据量过少,因此根据剪枝原则,左分支最终形成一个叶节点(或经过适当合并后的节点)。
    • -> 进入右分支。
  2. 右分支节点:剂量 < 25?
    • -> 进入分支。
      • 在此分支内,继续寻找最佳分割点,发现是 剂量 < 23.5
      • 根据“剂量 < 23.5”再次分割,直到所有分支满足停止条件(如达到最小叶子节点样本数),形成叶节点。
    • -> 进入分支。
      • 此分支数据也可能继续分割或直接成为叶节点,取决于其数据量和SSR计算。

通过递归地应用“计算SSR -> 选择最佳分割 -> 检查停止条件”这一过程,我们最终得到了那棵能够有效拟合数据、同时通过剪枝避免过拟合的优化回归树。

总结

本节课中,我们一起完成了回归树的构建。我们学习了如何为决策树的每个节点选择最佳分割阈值,核心方法是最小化残差平方和。同时,我们引入了剪枝的概念,通过设置如最小叶子节点样本数这样的停止条件来防止模型过拟合。整个过程从根节点开始,递归地分割数据,直到满足停止条件,最终形成一棵完整的、可用于预测的回归决策树。

011:多特征回归树 🌳

在本节课中,我们将学习如何构建一个包含多个特征的回归树。我们将使用一个包含药物剂量、患者年龄和性别三个特征的数据集,来预测药物的有效性百分比。

概述

在之前的课程中,我们学习了如何基于单一特征(药物剂量)构建回归树来预测药物有效性。然而,现实世界的数据分析通常涉及多个特征。本节课,我们将扩展这一概念,探讨当输入特征不止一个时,如何构建回归树模型。

问题背景与数据

我们面临的问题是:当一位新患者来到诊所时,我们已知其药物剂量、年龄和性别,需要基于已有的训练数据预测其药物有效性百分比。

以下是训练数据的一个样本:

剂量 年龄 性别 药物有效性 (%)
10 35 45
20 28 60
15 50 30
25 40 70

我们的目标是建立一个模型,能够根据新患者的这三个特征,输出一个预测的药物有效性百分比。

构建多特征回归树的步骤

上一节我们明确了问题与数据。本节中,我们来看看构建多特征回归树的具体步骤。核心思想是依次评估每个特征,找到最佳分割点。

第一步:评估第一个特征(剂量)

首先,我们暂时忽略年龄和性别特征,仅使用药物剂量来构建树。我们将剂量与药物有效性的关系绘制成散点图。

为了决定树的根节点(即第一个分割点),我们使用残差平方和作为评估指标。其公式为:

SSR = Σ(实际值 - 预测值)²

以下是确定最佳剂量分割阈值的步骤:

  1. 对排序后的剂量值,依次计算相邻数据点剂量值的平均值,作为候选阈值(例如:剂量 < 5, 剂量 < 7, 剂量 < 14.5 等)。
  2. 对于每个候选阈值,将数据分为“左”(小于阈值)和“右”(大于等于阈值)两组。
  3. 分别计算左右两组数据药物有效性的平均值,作为该组的预测值。
  4. 计算每组中所有数据点的实际值与组预测值之差的平方和,并将左右两组的SSR相加,得到该阈值下的总SSR。
  5. 选择总SSR最小的那个剂量阈值作为根节点的分割点。

在本例中,计算发现阈值 剂量 < 14.5 能产生最小的残差平方和,因此被选为基于剂量特征的根节点分割条件。

第二步:纳入第二个特征(年龄)

在根据剂量完成第一次分割后,我们得到两个数据子集。接下来,我们需要在每个子集上重复上述过程,但此时可以考虑所有特征(剂量、年龄、性别)。

对于“剂量 < 14.5”这个左子集:

  1. 我们分别评估用“年龄”和“性别”进行进一步分割的效果。
  2. 计算使用不同年龄阈值或按性别(男/女)分割后产生的SSR。
  3. 同样,选择能使该子集SSR降低最多的特征和分割点。

对于“剂量 >= 14.5”的右子集,也进行完全相同的操作。

第三步:纳入第三个特征(性别)及后续分割

性别是一个分类变量,其分割方式与数值型特征(剂量、年龄)不同。对于性别,分割直接基于类别,例如“性别 == 男”和“性别 == 女”。

在每个节点选择分割时,算法会在所有可用特征(剂量、年龄、性别)的所有可能分割点中,挑选出能够最大程度降低SSR的那一个。这个过程会在每个新生成的子节点上递归进行,直到满足停止条件(例如,节点中数据点少于某个最小值,或SSR的减少量小于某个阈值)。

总结

本节课中,我们一起学习了如何从零开始构建一个多特征回归树。关键步骤包括:

  1. 依次评估特征:从第一个特征开始,寻找最佳分割点以最小化残差平方和。
  2. 递归分割:在生成的每个数据子集上,重复评估所有特征,选择最佳分割。
  3. 处理混合类型特征:同时处理数值型特征(如剂量、年龄)和分类型特征(如性别)。
  4. 以误差最小化为目标:整个构建过程的核心是不断寻找能够最大程度降低预测误差(SSR)的分割方式。

通过这种方法,我们可以构建出一个能够综合考虑多个特征、并捕捉它们与目标变量之间复杂非线性关系的决策树模型。

012:在Python中构建完整的回归决策树 🚀

在本节课中,我们将学习如何从零开始,使用Python代码构建一个完整的回归决策树。我们将回顾回归树的基础概念,并逐步实现代码,最终可视化生成的决策树。

概述

在之前的几节课中,我们学习了回归决策树的基础知识,理解了诸如残差平方和等核心概念,并了解了回归树如何处理单特征和多特征数据。本节课我们将把这些理论知识付诸实践,用Python编写一个完整的回归决策树模型。

第一步:导入必要的包

在开始编写代码之前,我们需要导入必要的Python库。以下是构建决策树所需的核心包。

from sklearn.tree import DecisionTreeRegressor
import numpy as np
import matplotlib.pyplot as plt

我们主要使用三个库:

  • sklearn.tree:用于创建决策树模型。
  • numpy:用于数学计算和数组操作。
  • matplotlib.pyplot:用于数据可视化。

第二步:准备数据集

我们将使用一个带有噪声的二次函数数据集来演示。这样做的目的是模拟现实世界中数据的复杂性。

# 生成带有噪声的二次函数数据
np.random.seed(42)
m = 200
X = np.random.rand(m, 1)
y = 4 * (X - 0.5) ** 2
y = y + np.random.randn(m, 1) / 10

这段代码生成了200个数据点。其基础关系是 y = 4 * (x - 0.5)^2,然后我们添加了高斯噪声 np.random.randn(m, 1) / 10 来模拟真实数据的不确定性。

我们可以将数据可视化:

plt.figure(figsize=(6, 4))
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", fontsize=18, rotation=0)
plt.title("带有噪声的二次函数训练集")
plt.axis([0, 1, -0.2, 1])
plt.grid(True)
plt.show()

第三步:拟合决策树模型

使用scikit-learn库,拟合一个回归决策树模型非常简单。核心是使用 DecisionTreeRegressor 类。

以下是构建和训练模型的关键代码:

# 创建决策树回归器实例,并设置最大深度为2
tree_reg = DecisionTreeRegressor(max_depth=2, random_state=42)
# 使用数据拟合模型
tree_reg.fit(X, y)

在这两行代码中:

  1. DecisionTreeRegressor(max_depth=2) 创建了一个回归决策树对象,并限制其最大深度为2,以防止过拟合。
  2. tree_reg.fit(X, y) 命令模型根据提供的 X(特征)和 y(目标值)学习决策规则。

DecisionTreeRegressor 有许多参数可以调整模型行为,例如:

  • criterion: 分裂标准,默认为 'squared_error'(均方误差)。
  • max_depth: 树的最大深度。
  • min_samples_split: 内部节点再分裂所需的最小样本数。
  • min_samples_leaf: 叶节点所需的最小样本数。

如果不指定这些参数,scikit-learn 将使用默认值。例如,不指定 max_depthmin_samples_leaf 可能导致树生长得非常深,直到每个叶节点只包含一个样本,这通常会导致过拟合。

第四步:可视化决策树

模型训练完成后,理解其决策过程非常重要。我们可以使用 sklearn.tree 模块中的 plot_tree 函数来可视化树结构。

from sklearn.tree import plot_tree

plt.figure(figsize=(10,6))
plot_tree(tree_reg, feature_names=["x1"], rounded=True, filled=True)
plt.show()

生成的树形图将显示:

  • 根节点和内部节点:显示分裂条件(例如 x1 <= 0.197)。
  • 叶节点:显示该节点最终的预测值(value)。
  • 颜色深浅:通常表示节点的“纯度”或样本浓度。

根据我们设置的 max_depth=2,这棵树将包含一个根节点、两个内部节点和三个叶节点。第一层是深度1,其子节点是深度2,由于深度限制,树在此停止生长。

总结

本节课我们一起学习了构建回归决策树的完整流程:

  1. 导入库:引入了 sklearn, numpy, matplotlib
  2. 准备数据:创建了一个带有噪声的模拟数据集。
  3. 训练模型:使用 DecisionTreeRegressor 拟合数据,并理解了关键参数如 max_depth 的作用。
  4. 可视化树:通过 plot_tree 函数直观地查看模型的决策路径。

通过这个实践,你不仅掌握了用Python快速构建回归树的方法,也加深了对树如何通过一系列规则分割数据来做出预测的理解。这是理解更复杂集成模型(如随机森林、梯度提升树)的重要基础。

013:回归树与分类树总结 📊

在本节课中,我们将回顾并总结本系列课程中学习的所有关键概念。我们将快速梳理分类树与回归树的核心知识,为面试或复习提供一个清晰的概览。

决策树概述 🌳

决策树可以看作由两部分组成:一个陈述和一个需要做出的决策。这个定义会递归应用。例如,基于一个陈述做出决策后,根据该决策的结果,又会引出进一步的决策,如此循环,树的结构会随着决策的增多而变得越来越深。

事实上,生活中的许多决策都可以构建成决策树。例如,“我想看这部电影吗?”(是/否),如果选择“是”,那么“这部电影是什么类型?”,之后可以在每个类别下继续细分。这就是决策树最简单的定义。

决策树的类型 📝

决策树主要有两种类型:分类树回归树。许多学生在学习决策树时,可能认为它只用于分类任务,这是一个误解。实际上,决策树在回归任务中也是非常强大的技术。

分类树

分类决策树的主要目的是将数据划分到不同的类别中。例如,一个简单的问卷问题:“你想学习决策树吗?”,它可以分成两个分支:“是”或“否”。如果选择“是”,你可以继续观看本视频;如果选择“否”,你可以暂停或不观看。这就是一个最简单的分类树例子。

理解了这一点,你可以联想到许多现实生活中的分类问题。例如,根据患者数据判断其是否患有心脏病;区分图片中是猫还是狗;判断脑部扫描中是否存在肿瘤;自动驾驶汽车判断前方物体是人还是车。所有这些都可以看作是分类问题,并可以使用分类决策树来解决。

回归树

另一方面,回归决策树用于预测数值。例如,“你想学习决策树吗?”如果“是”,那么你的年龄可能在15到40岁之间;如果“否”,那么你的年龄可能小于15岁。在这个例子中,我们预测的是年龄这个数值。

同样,理解了回归树的概念,你可以想到其广泛的应用场景。例如,根据药物剂量、患者年龄、性别等多个特征来量化药物的有效性水平,这就是在预测一个数值。股票市场是另一个例子,X轴是时间,Y轴是股价(一个数值),回归决策树也可以用于此类任务。

在本系列课程中,前6-7讲我们深入探讨了分类树,而后6-7讲则详细介绍了回归树。

分类树的核心概念 🔑

在学习分类树时,我们首先花了大量时间理解树是如何分裂的。例如,如何决定根节点、中间节点和叶节点?为此,分类树使用了一些关键指标。

以下是分类树中使用的两个主要指标:

  1. 基尼不纯度:这是分类错误的一种度量。其核心目标是最小化基尼不纯度。其公式为:
    Gini = 1 - Σ(p_i²),其中 p_i 是第 i 个类别的概率。

  2. :这是另一种衡量系统混乱度或不确定性的指标。其核心目标同样是最小化熵。其公式为:
    Entropy = -Σ(p_i * log2(p_i))

这两种度量都可以用于分类任务。在Python代码中,构建分类树时你可以选择使用哪种指标。虽然两者都有人使用,但通常更倾向于使用基尼不纯度。两者有很多相似之处,例如,当真实概率为0.5时,两者的值都达到最高。

构建分类树的主要目的(或者说优化的主要目标),就是在建树过程中尽可能降低基尼不纯度或熵。

在Python中,构建分类树非常简单,使用 DecisionTreeClassifier 命令。你需要指定X数据和y数据来拟合这个分类树。在底层,Python使用的是CART算法(分类与回归树)。对于分类树,该算法试图最小化基尼不纯度或熵;对于回归树,则最小化另一个指标(稍后会提到)。

在Python的 DecisionTreeClassifier 函数中,你可以通过 criterion 参数指定标准为“gini”(基尼不纯度)或“entropy”(熵),还有一个选项是“log_loss”。通常,基尼不纯度是最常用的。

有时面试中可能会问到基尼不纯度和熵的区别。答案是:它们都是用于寻找误差度量的优秀指标,有很多相似之处,但基尼不纯度通常更受青睐,因为它基于概率的平方计算,不涉及对数运算,有时能带来更好的结果(尽管并非所有问题都如此)。通常可以两种方法都尝试一下。

分类树的进阶主题 🚀

随着课程的深入,在分类树部分我们还探讨了两个非常重要的主题。

第一个是代价复杂度剪枝。剪枝,顾名思义就是“修剪”或“减少”。代价复杂度剪枝旨在减少树的长度或深度。

你可能会想,为什么剪枝是必要的?为了回答这个问题,在本系列课程的实践部分,我们使用Python解决了一个实际问题,并完整地构建和可视化了分类树。在那个问题中,我们使用了303名患者的真实数据,包含13个新变量,来演示整个过程。

第二个重要主题是交叉验证与测试。这是评估模型性能、防止过拟合的关键步骤。

总结 📚

本节课我们一起回顾了决策树系列的核心内容。我们首先明确了决策树的基本结构,然后区分了其两大类型:用于预测类别的分类树和用于预测数值的回归树。在分类树部分,我们重点学习了决定树如何分裂的关键指标——基尼不纯度,以及如何在Python中实现。最后,我们还简要提到了代价复杂度剪枝交叉验证这两个构建稳健模型的重要技术。希望这个总结能帮助你巩固知识,为后续学习或面试做好准备。

posted @ 2026-03-26 12:20  布客飞龙III  阅读(25)  评论(0)    收藏  举报