【非常详细】通俗易懂的讲解决策树(Decision Tree)

前言:本文章参考了前辈的文章并结合了自己的思考,前辈的参考链接会在文末或者文中给出。如果对本文内容有指正,欢迎留言。

决策树是一种机器学习的方法,它是一种树形结构(可以是二叉树或者非二叉树),其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果。其实决策树的分类和人在生活中的决策很相似,举个栗子:

今天我想网购台电脑,刷到一台看着挺带劲的机子,在决定买不买之前,我心路历程是这样的:

  1. 这台笔记本价格还可以,7000 多,没超过 8000 ,在我的可接受范围内
  2. emmm,牌子是 xxx ,名牌值得信赖。。再看看配置
  3. i7,固态+机械,显存 8G,内存16G。这个配置一看就是游戏本,我喜欢。看看评价如何?
  4. woc,这么多差评,差评率也太高了。。。告辞告辞

看到没,刚刚的心路历程就是一个决策过程。我通过品牌、价格、配置、差评率等属性来决定“买还是不买 ”。再举一个经典的例子:给出如下的一组数据,一共有十个样本(学生数量),每个样本有分数,出勤率,回答问题次数,作业提交率四个属性,最后判断这些学生是否是好学生。最后一列给出了人工分类结果。

然后用这一组附带分类结果的样本可以训练出多种多样的决策树,这里为了简化过程,我们假设决策树为二叉树,且类似于下图:

通过学习上表的数据,可以设置A,B,C,D,E的具体值,而A,B,C,D,E则称为阈值。当然也可以有和上图完全不同的树形,比如下图这种的:

其中非叶子节点表示判断条件,叶子节点表示决策结果,箭头表示在一个判断条件在不同情况下的决策路径。

构建决策树

决策树是一种十分常用的分类方法,需要监管学习(Supervised Learning),监管学习就是给出一堆样本,每个样本都有一组属性和一个分类结果,也就是分类结果已知,那么通过学习这些样本得到一个决策树,这个决策树能够对新的数据给出正确的分类。具体过程如下:

  1. 训练阶段:从给定的训练数据集DB,构造出一颗决策树

�����=������������(��)

  1. 分类阶段:从根开始,按照决策树的分类属性,从上往下,逐层划分。直到叶子节点,便能获得结果。

�=������������(�)

但是决策树是怎样构造的呢?我们继续拿上图中的例子讲解,判断一个学生是不是好学生,首先看这个学生的分数是不是大于A,如果大于那就是好学生。如果不大于A,其次再看该学生的出勤率,接下来才看回答次数,最后才看作业提交率。但是为什么要把学生的分数当作第一个判断的节点呢?为什么要把作业提交率作为最后一个判断的节点呢?如果弄清这个问题,便自然而然构建出了一颗决策树。

为了弄清决策树的构建过程,需要先了解一个概念:。看到熵这个字大部分人会觉得很深奥,其实不难,熵表征的是一个东西的混乱程度、不确定性,该物体越混乱、越不确定,它的熵越大,越整齐、越确定,熵越小。我摘抄知乎的一个高赞回答:

通俗的理解熵

举个栗子,对于这样一堆沙子,我们可以随意的更改沙堆的形状,甚至可以组成数万亿种形状,但不管哪种形状,构成沙子的“结构”不会发生任何改变,从熵的意义上讲,这个沙堆的熵值很高,因为沙堆不正确、不确定性比较强(这里的沙堆泛指一切自然形成的沙堆,大同小异)。

但是,当我们把沙堆弄成这样一个沙堡:

这个时候,让一堆沙组成图中这种有规则形状的沙堡的组合就会骤降,甚至只有几种组合能让一堆沙看起来和图中的沙堡特别相似(沙子的结构仍然不会发生任何变化)。从熵的意义上讲,这个沙堡的熵值很低,因为有了规则,不确定就很弱。

再举一个栗子,看下面两个集合,相比于 B 集合,A 集合就比较混乱、不确定性比较大。 B 集合中大部分都是 ① ,比较整齐,确定性较强,所以 B 集合的熵就比较小, A 集合混乱则熵比较大。

①②⑤⑥⑦①②④�=[ ① ② ⑤ ⑥ ⑦ ① ② ④ ]①①①①⑦①①④�=[ ① ① ① ① ⑦ ① ① ④ ]

熵的表达式为:

=�������=−∑�=1��(��)���2�(��)

其中 �(��) 为 �� 出现的概率。假如是 2 分类问题,当 A 类和 B 类各占 50% 的时候,

(�������=−(0.5∗���2(0.5)+0.5∗���2(0.5))=1

当只有 A 类,或只有 B 类的时候,

())�������=−(1∗���2(1)+0)=0

所以当 Entropy 最大为1的时候,是分类效果最差的状态,当它最小为0的时候,是完全分类的状态。

再回到刚刚 A、B 集合的例子中去。对于集合 A ,出现 ①②④⑤⑦⑥ 的概率都比较低,因为 A 中元素比较杂,当出现的概率比较低的时候 �(��) 比较小,当把每个元素出现的概率值带入熵的表达式中,得到的熵是比较大的。而对于集合 B,其中 ① 比较多,该元素出现的概率比较大,当把 B 中每个元素的出现的概率带入熵的表达式,得到的熵值比较小。

决策树构造实例

现在理解了熵的概念,我就可以使用熵的值作为一个标准来构建决策树。熵在理想状态下等于零,一般实际情况下,熵介于0和1之间。熵的不断最小化,实际上就是提高分类正确率的过程。

比如上表中的4个属性:单一地通过以下语句分类:

  1. 分数小于70为【不是好学生】:分错1个
  2. 出勤率大于70为【好学生】:分错3个
  3. 问题回答次数大于9为【好学生】:分错2个
  4. 作业提交率大于80%为【好学生】:分错2个

最后发现分数小于 70 为【不是好学生】这条分错最少,也就是说此时不确定性比较弱,既熵最小,所以应该选择这条为父节点进行树的生成,当然分数也可以选择大于 71,大于 72 等等,出勤率也可以选择小于 60,65 等等,总之会有很多类似上述 1 ~ 4 的条件,最后选择分类错最少即熵最小的那个条件。而当分裂父节点时道理也一样,分裂有很多选择,针对每一个选择,与分裂前的分类错误率比较,留下那个提高最大的选择,即熵减最大的选择。

因为构建树的基本想法是随着树的深度的增加,节点的熵迅速的降低。熵降低的速度越快越好,这样就能得到一颗较矮的决策树(能 3 步判断出来就不用 5 步判断),用一个例子讲解决策树的构建:

根据当天的状况,来决定今天出不出去玩耍。例如第一条数据,今天阳光明媚,温度比较热,空气湿度比较高,没风。今天没有出去玩耍。上面 14 条数据,我们可知新的一天打球的概率是 9/14 ,不打的概率是 5/14。此时的熵为:

−914���2914−514���2514=0.940

0.940 就是我们不构建决策树,该数据集固有的熵值:0.940,接下来我们需要一步步构建决策树,使得熵值不断减小,并且还需寻找下降最快的办法。

选取根节点

构建决策树第一个问题就是该把那个属性当作根节点?outlook?temperature?windy?首先,把这四个属性当作根节点分别构建四棵树,结果如下:

紧接着分别计算四种划分的熵值。outlook 当作根节点的时候,熵的计算方法为:

  1. outlook有三个分支,需要分别计算三个分支的熵
  2. 对于第一个分支:sunny 的计算方式为:

������=−25���225=0.971

  1. 第二个分支的熵为 0 (全部是 yes ,节点确定性非常强,熵为 0), 第三个分支:rainy 的熵为:

������=−35���235=0.971

  1. outlook 当作根节点时,14 条数据显示 5/14 的情况下是 sunny, 4/14 情况下是 overcast,5/15 的情况下为 rainy。
  2. outlook 当作根节点时,熵的值为:

515∗0.971+414∗0+514∗0.971=0.693

这样的话,如果选择 outlook 当作根节点,熵就原始的 0.940 下降到了 0.693,信息熵增益 gain(outlook)为:0.940-0.693=0.247。

信息增益的公式为:

����(�,�)=�������(�)−∑�=1�|��||�|�������(��)

其中 D 是数据集,既 14条样本,a 是选择的属性 ( 此处 a 为 outlook ),a 中一共有 V 个取值 (此例子中 V 为等于3,分别为 sunny,humidity,temperature)。用这个 V 取值去划分数据集 D,分别得到数据集 �1 到 ��,分别求这 V 个数据集的信息熵,并将其求加权平均。两者的差得到的就是信息增益,公式中减号之前的值 �������(�) 为划分前的熵值。

同理可得:gain(temperature) = 0.029 、 gain(humidity)=0.152、gain(windy)=0.048。

其中 gain(outlook) 最大,说明熵下降的最快,该属性当作根节点能够分的更好。所以根节点就选取 outlook。

递归构建决策树

通过上面的例子选择出了根节点,紧接着可以采用相同的办法继续划分,既基于什么样的划分可以使得信息熵增益的值最大、熵下降的最快。比如现已知 outlook 作为根据点,剩余的属性还有 temperature、humidity、windy。分别以此三个属性为根节点构建决策树,取 gain 信息熵增益最大的属性,紧接着递归进行下去就可以构建出一颗决策树。

常见的决策树构建算法

上一小节的例子中,基于信息增益的决策树构建算法叫作 ID3。比较常用还有 C4.5 和 Classification And Regression Tree ( CART ),CART 的分类效果一般优于其他决策树。在介绍 C4.5 和 CART 之前,先来看一下 ID3 有什么缺点。

上图是刚刚 ID3 构建算法用的例子,在前面加了另外一个特征 ID,值就是样本的 ID 序号。每条数据的特征有 5 个,分别是 outlook、temperature、humidity、windy、ID。如果用信息增益最大化的方法挑选根节点会发现,当 ID 此属性作为根节点的时候信息增益值是最大的,但这样划分显然不正确,因为 ID 的值和是否取 “play” 没有任何关系。

利用 ID 作为根节点会划分出很多分支,而每个分支中的值很少。这种情况的出现会导致信息增益非常大。所以用信息增益作为评判划分属性的方法有一定的缺陷的:信息增益准则对那些属性的取值比较多的属性有所偏好,也就是说,采用信息增益作为判定方法,会倾向于去选择属性取值比较多的属性。如上图中每条样本的 ID 都是不相同的,此时属性取值很多。如果用 ID 这个属性去划分原数据集,那么原数据集中有多少个样本,就会被划分为多少个子集,每个子集只有一条样本,这种极端情况下每个子集的信息熵就是0了,就是说此时每个子集都特别纯。这样的话,会导致信息增益公式的第二项 ∑�=1�|��||�|�������(��) 整体为0,这样导致的结果是,信息增益计算出来的特别大,然后决策树会用 ID 这个属性来划分原数据集D,其实这种划分毫无意义。因此,为了改变这种不良偏好带来的不利影响,提出了 C4.5 算法。

C4.5

为了解决上方 ID3 提到的问题,引入了新的概念信息增益率。信息增益率:

����������(�,�)=�����(�,�)��(�)��(�)=−∑�=1�|��||�|���2|��||�|

��(�) 被称为是的“固有值”,仔细观察该式子,可以发现改值的大小取决于属性 a 的纯度,如果 a 只含有少量的取值的话,那么 a 的纯度就比较高,否则的话,a 的取值越多,a 的纯度越低,的 ��(�) 值也就越大,因此,最后得到的信息增益率就越低。

所以信息增益率是信息增益的值除以固有值,虽然以 ID 为根节点得到的信息增益非常大,但是此时固有值也非常大。除以一个非常大的值会使得比值变小,从而得到一个较小的信息增益率。如果将信息增益率作为划分标准会得到更好的划分效果。

虽然提高了分类效果,C4.5还是有一些缺点:

  • C4.5生成的是多叉树,在计算机中二叉树模型会比多叉树运算效率高
  • C4.5只能用于分类任务
  • 熵模型里面含有大量的对数运算, 是非常耗时的。

CART

C4.5 生成算法基于较为复杂的熵来度量,使用的是多叉树(二叉树效率更高),只能处理分类不能处理回归。对这些问题,Classification And Regression Tree 做了改进,可以处理分类,也可以处理回归。

ID3 中基于信息增益选择特征,选择增益大的特征作为当前树的根节点。C4.5中,采用信息增益率选择特征,减少因特征值多导致信息增益大的问题。CART 分类树算法使用基尼系数来代替信息增益率,基尼系数代表了模型的不纯度,基尼系数越小,不纯度越低,特征越好。这和信息增益(比)相反。

假设K个类别,第k个类别的概率为 ��,概率分布的基尼系数表达式:

����(�)=∑�=1���(1−��)=1−∑�=1���2

如果是二分类问题,第一个样本输出概率为p,概率分布的基尼系数表达式为:

����(�)=2�(1−�)

对于样本D,个数为|D|,假设K个类别,第k个类别的数量为|��|,则样本D的基尼系数表达式:

����(�)=1−∑�=1�(∣��∣∣�∣)2

对于样本D,个数为|D|,根据特征A的某个值a,把D分成|�1|和|�2|,则在特征A的条件下,样本D的基尼系数表达式为:

����(�,�)=∣�1∣∣�∣����(�1)+∣�2∣∣�∣����(�2)

比较基尼系数和熵模型的表达式,二次运算比对数简单很多。尤其是二分类问题,更加简单。

和熵模型的度量方式比,基尼系数对应的误差有多大呢?对于二类分类,基尼系数和熵之半的曲线如下:

基尼系数和熵之半的曲线非常接近,仅在45度角附近误差稍大。因此,基尼系数可以做为熵模型的一个近似替代。CART分类树算法每次仅对某个特征的值进行二分,而不是多分,这样CART分类树算法建立起来的是二叉树,而不是多叉树。

CART分类树算法具体流程

CART分类树建立算法流程,之所以加上建立,是因为CART分类树算法有剪枝算法流程。算法输入训练集D,基尼系数的阈值,样本个数阈值。输出的是决策树T。

算法从根节点开始,用训练集递归建立CART分类树。

  1. 对于当前节点的数据集为 D,如果样本个数小于阈值或没有特征,则返回决策子树,当前节点停止递归。
  2. 计算样本集 D 的基尼系数,如果基尼系数小于阈值,则返回决策树子树,当前节点停止递归。
  3. 计算当前节点现有的各个特征的各个特征值对数据集 D 的基尼系数,对于离散值和连续值的处理方法和基尼系数的计算见第二节。缺失值的处理方法和 C4.5 算法里描述的相同。
  4. 在计算出来的各个特征的各个特征值对数据集 D 的基尼系数中,选择基尼系数最小的特征A和对应的特征值a。根据这个最优特征和最优特征值,把数据集划分成两部分�1和 �2,同时建立当前节点的左右节点,做节点的数据集 D 为 �1,右节点的数据集 D 为 �2。
  5. 对左右的子节点递归的调用 1-4 步,生成决策树。

对生成的决策树做预测的时候,假如测试集里的样本 A 落到了某个叶子节点,而节点里有多个训练样本。则对于 A 的类别预测采用的是这个叶子节点里概率最大的类别。

离散型问题

如上图涉及的例子属于离散型问题,其离散的属性可以列举处理进行划分。比如 temperature 有 hot、mild、cool 而不是 30°,20°,5° 这样的连续值。所以处理离散型问题不需要考虑属性值的划分。

连续型的属性

上方例子中,属性基于离散变量的,然而我们现实的机器学习任务中也会遇到连续属性,如何处理连续属性呢?连续属性的可取值数目是无限的,因此不能像前面处理离散属性枚举离散属性取值来对结点进行划分。因此需要连续属性离散化,常用的离散化策略是二分法,这个技术也是 C4.5 中采用的策略。下面来具体介绍下,如何采用二分法对连续属性离散化:

下面举个具体的例子,来看看到底是怎样划分的。给定数据集如下(数据集来自周志华《机器学习》)

对于数据集中的属性“密度”,决策树开始学习时,根节点包含的17个训练样本在该属性上取值均不同。我们先把“密度”这些值从小到大排序:

根据上面计算 �� 的公式,可得:

下面开始计算 t 取不同值时的信息增益:

可以发现,当 t = 0.381 时, ����(�,�,�) 最大为 0.263 因此选择此处为划分点。

剪枝操作

首先剪枝(pruning)的目的是为了避免决策树模型的过拟合。因为决策树算法在学习的过程中为了尽可能的正确的分类训练样本,不停地对结点进行划分,因此这会导致整棵树的分支过多,也就导致了过拟合。分支过多在分类时考虑数据的信息就更为丰富(每次分支都会考虑一种特征信息),这就导致了能拟合住噪声信息。所以,我们要尽可能生成一个较矮的决策树。让树长不高,那就需要剪枝。

决策树的剪枝策略最基本的有两种:预剪枝(pre-pruning)和后剪枝(post-pruning):

  • 预剪枝(pre-pruning):在构建决策树过程时,提前停止。预剪枝就是在构造决策树的过程中,先对每个结点在划分前进行估计,若果当前结点的划分不能带来决策树模型泛华性能的提升,则不对当前结点进行划分并且将当前结点标记为叶结点。
  • 后剪枝(post-pruning):决策树构建完毕后,然后开始剪枝。先把整颗决策树构造完毕,然后自底向上的对非叶结点进行考察,若将该结点对应的子树换为叶结点能够带来泛华性能的提升,则把该子树替换为叶结点。

通过剪枝操作,可以降低树的高度,降低过拟合的风险。如果树非常高,叶子节点可能就比较多,叶子节点过多对模型的泛化能力是有影响的,举个极端的例子,叶子的个数等于样本的个数,每个叶子节点可以划分到 1 个样本,这样叶子节点非常纯,但是也失去分类的意义。所以树矮,叶子节点少才是我们追求的目标。

衡量决策树算法的效果

衡量决策树算法的效果常常使用评价函数:

�(�)=∑�∈������⋅�(�)

我们希望此评价函数越小越好。其中 t 为叶子节点, 计算所有叶子节点的 ��⋅�(�) , 然后求和。其中 �(�) 为当前叶子计算出来的熵值或者基尼系数的值, 而 �� 是代表有几个样本归到当前叶子节点中, 作为权重。我们希望每个叶子节点划分到的样本类别是非常"纯"的,不混乱的, 这样才代表分类效果好, 分类效果稳定。所以, 越纯 �(�) 越小, �(�) 越小, 故使评价函数尽可能小是我们的目标。

��(�)=�(�)+�⋅∣�����∣

上面也是一个评价函数,该评价函数多了另一项 �⋅∣�����∣ ,其中 ∣�����∣ 代表叶子节点的个数。意思很容易明白,叶子节点个数越多,损失越大。我们目的是使得 ��(�) 越来越小, � 代表叶子个数对评价结果的影响程度。

随机森林

森林:刚刚提到的是如何构建一棵树,而很多树组合在一起便是一个森林。构建出了很多决策树,综合这些决策树的决策结果得出最后的分类或者回归结果。

随机:比如我们有 10 个样本,用这些样本构造决策树。第一棵决策树建立的时候不全部使用这 10 个样本,而是使用 0.6 * 10 = 6 个样本 (0.6相当于一个参数)。在建立第二棵树是还是原始 10 个样本中进行采用 6 个样本(有放回采样)。这样一棵棵决策树构建过程中采用的 6 个样本可能是各不相同的,引入随机性可增加模型的健壮性,使得模型泛化能力更强。

刚刚是随机去除一部分样本来构建决策树,除此之外,一个样本还有很多特征,而随机剔除一些特征也能够增加模型的泛化能力。因为不知道哪些特征对模型泛化能力更有用,所以进行随机的剔除。这类似于深度学习中的 Dropout。

使用sklearn构建决策树

在Jupyter Notebook导入相关库:

from sklearn.datasets import load_iris
from sklearn import tree
import sys
import os  
from IPython.display import Image  
import pydotplus 
import pandas as pd

IPython.display 的 Image 和 pydotplus是为了可视化生成的决策树。没安装 pydotpplus 可以使用 pip3 命令安装。

接着用sklearn自带的鸢尾花数据:

iris = load_iris()

table = pd.DataFrame(iris['data'])
table['target'] = iris['target']    # target => ['setosa', 'versicolor', 'virginica']
table.columns = ['sepal length (cm)','sepal width (cm)','petal length (cm)','petal width (cm)', 'target']
print(table)

数据集总共150个花,总共三种花,target表示了种类号,每个花有四个特征。下面利用这个数据构建决策树

# 创建决策树对象,使用信息熵作为依据
clf = tree.DecisionTreeClassifier(criterion='entropy')
# fit方法分类。features为iris.data,labels为iris.target
clf = clf.fit(iris.data, iris.target)

接着可视化一下这个树,看看它是什么样子的

# 可视化
dot_data = tree.export_graphviz(clf, feature_names=iris.feature_names,   graph = pydotplus.graph_from_dot_data(dot_tree)
img = Image(graph.create_png())
graph.write_png("out.png")

如果报错:GraphViz's executables not found

你需要使用apt或者yum安装GraphViz。Windows 需要下载一个工具,具体请百度一下。并且在代码中加入以下代码:

os.environ["PATH"] += os.pathsep + 'D:\Program files\graphviz\\release\\bin'

这就是生成的决策树,entropy表示的是熵,samples表示该节点所含样本数量,value表示不同类别的个数有多少。

这样,就完成了决策树的搭建

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值