机器学习算法——决策树

646 阅读19分钟

一、决策树是什么?

决策树(Decision tree,DT)是一类常见的机器学习方法,属于监督学习的一种。它通过给定的训练数据,计算各种情况发生的概率,在此基础上选择合适的划分并构造决策树,用于数据类别的预测判断,同时也可以进行数据的拟合回归。

1.模型

根据处理数据类型的不同,决策树又分为两类:分类决策树与回归决策树,前者输出的结果为具体的类别,可用于处理离散型数据;后者输出的结果为一个确定的数值,可用于处理连续型数据。

决策树的构建算法主要有ID3、C4.5、CART三种,其中ID3和C4.5是分类树,CART是分类回归树,其中ID3是决策树最基本的构建算法,而C4.5和CART是在ID3的基础上进行优化的算法。

整个模型可以分为三步:选择最优划分特征生成决策树剪枝

1664500408359.png

决策树通过把数据样本分配到某个叶子节点来确定数据集中样本所属的分类。决策树由决策节点,分支共和叶子节点组成;分支表示对于决策节点进行划分的输出;叶子节点表示经过2分支到达的类。

1.1 选择最优划分特征

1.1.1 信息增益

定义: 以某特征划分数据集前后的熵的差值。

信息增益等价于训练数据集中类与特征的互信息

image.png

划分数据集的大原则是:将无序数据变得更加有序,但是各种方法都有各自的优缺点,信息论是量化处理信息的分支科学,在划分数据集前后信息发生的变化称为信息增益,获得信息增益最高的特征就是最好的选择,所以必须先学习如何计算信息增益。集合信息的度量方式称为香农熵,或者简称熵。

在划分数据集之前之后信息发生的变化成为信息增益,可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

信息增益是相对于特征而言的。所以,特征A对训练数据集D的信息增益g(D,A),定义为集合D的经验熵H(D) 与特征A给定条件下D的经验条件熵H(D|A) 之差,即:

image.png=    -  

下面介绍什么是熵、经验熵、条件熵、经验条件熵

定义为信息的期望值,如果待分类的事物可能划分在多个类之中,则符号的信息定义为:

image.png

其中,image.png是选择该分类的概率。

为了计算熵,我们需要计算所有类别所有可能值所包含的信息期望值,通过下式得到: image.png

其中,n 为分类数目,熵越大,随机变量的不确定性就越大。

经验熵:

熵度量的是随机变量的不确定性。熵越大,不确定性越大。

熵中的概率数据估计(特别是最大似然估计)得到时,所对应的熵称为经验熵image.png

条件熵

其中是样本集合D中属于第k类的样本子集,表示该子集的元素个数,表示样本集合的元素个数。 

         某个特征A对于数据集D的经验条件熵H(D|A)为:

                  1664502554135.png 当概率由数据估计(特别是极大似然估计)时,所对应的熵与条件熵分别称为经验熵与经验条件熵。

其中,表示D中特征A取第i个值的样本子集,表示Di中属于第k类的样本子集。

  在熵的理解那部分提到了,熵可以表示样本集合的不确定性,熵越大,样本的不确定性就越大。因此可以使用划分前后集合熵的差值来衡量使用当前特征对于样本集合D划分效果的好坏。

  划分前样本集合D的熵是一定的 ,entroy(前),

  使用某个特征A划分数据集D,计算划分后的数据子集的熵 entroy(后)

                               信息增益 =  entroy(前) -  entroy(后)

对于待划分的数据集D,其 entroy(前)是一定的,但是划分之后的熵 entroy(后)是不定的,entroy(后)越小说明使用此特征划分得到的子集的不确定性越小(也就是纯度越高),因此 entroy(前) - entroy(后)差异越大,说明使用当前特征划分数据集D的话,其纯度上升的更快。

二、ID3算法

2.1 ID3 和 C4.5 决策树的生成和修剪

从数据集构造决策树算法所需要的子功能模块,包括经验熵的计算和最优特征的选择。其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据集被向下传递到树的分支的下一个结点。在这个结点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。

构建决策树的算法有很多,比如C4.5、ID3和CART,这些算法在运行时并不总是在每次划分数据分组时都会消耗特征。由于特征数目并不是每次划分数据分组时都减少,因此这些算法在实际使用时可能引起一定的问题。目前我们并不需要考虑这个问题,只需要在算法开始运行前计算列的数目,查看算法是否使用了所有属性即可。

决策树生成算法递归地产生决策树,直到不能继续下去未为止。这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确,即出现过拟合现象。过拟合的原因在于学习时过多地考虑如何提高对训练数据的正确分类,从而构建出过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化(剪枝)。

 2.1.1 ID3构建决策树

image.png 核心:信息增益

实例:

image.png

(1)计算数据集D的经验商H(D)
由图表中可得知,最终类别有否还有是,占9/15,占6/15
所以,经验熵H(D)为:

image.png

(2)计算特征A 对数据集D的经验条件熵H(D|A)

1664507097053.png

image.png

image.png

image.png

信息增益
g(D,A1)0.083
g(D,A2)0.324
g(D,A3)0.420
g(D,A4)0.363

特征A3(有自己的房子)的信息增益最大,所以选择A3为根节点的特征,它将训练集D划分为两个子集D1(A3取值为“是”)D2(A3取值为“否”)。由于D1只有同一类的样本点,所以它成为一个叶结点,结点的类标记为“是”。对D2则需要从特征A1(年龄),A2(有工作)和A4(信贷情况)中选择新的特征,计算各个特征的信息增益:

image.png

image.png

image.png

  根据计算,选择信息增益最大的A2作为节点的特征,由于其有两个取值可能,所以引出两个子节点:

  ① 对应“是”(有工作),包含三个样本,属于同一类,所以是一个叶子节点,类标记为“是”

  ② 对应“否”(无工作),包含六个样本,输入同一类,所以是一个叶子节点,类标记为“否”

这样就生成一个决策树,该树只用了两个特征(有两个内部节点),生成的决策树如下图所示:

image.png

 2.1.2 ID3算法的不足

ID3没有考虑连续特征,比如长度,密度都是连续值,无法在ID3运用。这大大限制了ID3的用途。 ID3采用信息增益大的特征优先建立决策树的节点。很快就被人发现,在相同条件下,取值比较多的特征比取值少的特征信息增益大。 ID3算法对于缺失值的情况没有做考虑 没有考虑过拟合的问题

三、C4.5算法

image.png

C4.5算法在ID3算法上做了提升,使用信息增益比来构造决策树,且有剪枝功能防止过拟合。

信息增益比 = 惩罚参数 * 信息增益

欠拟合:训练得到的模型在训练集集测试中表现就很差,准确度很低。
过拟合:训练得到的模型在训练集表现很好,但在测试集表现很差。 信息增益比:特征A对训练集D的信息增益比定义为特征A的信息增益与训练集D对于A的信息熵之比。

image.png

注意: 其中的,对于样本集合D,将当前特征A作为随机变量(取值是特征A的各个特征值),求得的经验熵。

      (之前是把集合类别作为随机变量,现在把某个特征作为随机变量,按照此特征的特征取值对集合D进行划分,计算熵

数据集D关于A的取值熵:

image.png

信息增益比本质: 是在信息增益的基础之上乘上一个惩罚参数。特征个数较多时,惩罚参数较小;特征个数较少时,惩罚参数较大。

惩罚参数:数据集D以特征A作为随机变量的熵的倒数,即:将特征A取值相同的样本划分到同一个子集中(之前所说数据集的熵是依据类别进行划分的)

image.png

缺点:信息增益比偏向取值较少的特征

  原因:  当特征取值较少时的值较小,因此其倒数较大,因而信息增益比较大。因而偏向取值较少的特征。

  使用信息增益比:基于以上缺点,并不是直接选择信息增益率最大的特征,而是现在候选特征中找出信息增益高于平均水平的特征,然后在这些特征中再选择信息增益率最高的特征。   

  因此,C4.5寻找最优划分的真实方法是:1)计算所有划分的平均信息增益t;2)从所有信息增益大于t的划分中寻找信息增益率最大的划分,该划分就是最优划分。

实例:

image.png

image.png

image.png

image.png

image.png

经过计算得出结果,”有自己的房子“的信息增益比最大,所以选择特征”有自己的房子“作为根结点的特征。它将训练集D划分为两个子集D1(”有自己的房子“为”是“)和D2(”有自己的房子“为”否“)。

image.png

其中有自己的房子的分支中一共有6个样本,而这六个样本的类别均为,所以定义这个节点为叶子结点,标记类别为,而没有自己的房子的分支中有9个结点,而这9个结点中类别有两种,所以不能标记为叶子结点,需要对这个节点中的9个样本继续计算信息增益比,但是区别在于不是对整个数据,而是对这9个样本点组成的新的数据集计算信息增益比。

1665196841106.png

image.png

image.png

image.png

信息增益比
gR(D2,A1)0.164
gR(D2,A2)1.000
gR(D2,A4)0.340

所得决策树: 1665197214141.png

3.1 ID3和C4.5算法的区别:

1.C4.5是一系列用在机器学习和数据挖掘的分类问题中的算法。它的目标是 监督学习:给定一个数据集,其中的每一个元组都能用一组属性值来描述,每一个元组属于一个互斥的类别中的某一类。

2.C4.5的目标是通过学习,找到一个从属性值到类别的映射关系,并且这个映射能用于对新的类别未知的实体进行分类。

3.ID3算法用来构造决策树。决策树是一种类似流程图的树结构,其中每个内部节点(非树叶节点)表示在一个属性上的测试,每个分枝代表一个测试输出,而每个树叶节点存放一个类标号。一旦建立好了决策树,对于一个未给定类标号的元组,跟踪一条有根节点到叶节点的路径,该叶节点就存放着该元组的预测。决策树的优势在于不需要任何领域知识或参数设置,适合于探测性的知识发现。

四、CART算法

image.png CART,又名分类回归树,是在ID3的基础上进行优化的决策树,为一种二分决策树,是一种树构建算法,这种算法既可以处理离散型的问题,也可以处理连续型的问题。

与ID3算法相反,CART算法正好适用于连续型特征。CART算法使用二元切分法来处理连续型变量。而使用二元切分法则易于对树构建过程进行调整以处理连续型特征。具体的处理方法是:如果特征值大于给定值就走左子树,否则就走右子树。

CART算法有两步:

1.决策树生成:递归地构建二叉决策树的过程,基于训练数据集生成决策树,对回归树用平方误差最小化准则,对分类树用基尼指数最小化准则,进行特征选择,生成二叉树。生成的决策树要尽量大;自上而下从根开始建立节点,在每个节点处要选择一个最好的属性来分裂,使得子节点中的训练集尽量的纯。不同的算法使用不同的指标来定义"最好":
2.决策树剪枝:用验证数据集对已生成的树进行剪枝并选择最优子树,这时损失函数最小作为剪枝的标准。剪枝是决策树学习算法解决过拟合的主要手段。在决策树的学习过程中,为了尽可能地正确分类训练样本,节点划分得不断重复,有时候会造成决策树的分支过多,这时候就是算法在训练样本上学得太好,导致把训练集本身的一些特点作为所有数据所有数据都有的一般性质(实际上新数据中可能没有这些特点),从而导致过拟合。因此可以主动去掉一些分支来降低过拟合的风险

image.png

image.png

学习CART记住以下几个关键点:

(1)CART算法既可以用于创建分类树(Classification Tree),也可以用于创建回归树(Regression Tree)、模型树(Model Tree),两者在建树的过程稍有差异;

(2)当CART是分类树时,采用GINI值作为节点分裂的依据;当CART是回归树时,采用样本的最小方差作为节点分裂的依据;

(3)CART是一棵二叉树。

(4)剪枝策略:CART算法的关键点,也是整个Tree-Based算法的关键步骤。

剪枝过程特别重要,所以在最优决策树生成过程中占有重要地位。有研究表明,剪枝过程的重要性要比树生成过程更为重要,对于不同的划分标准生成的最大树(Maximum Tree),在剪枝之后都能够保留最重要的属性划分,差别不大。反而是剪枝方法对于最优树的生成更为关键。

创建分类树递归过程中,CART每次都选择当前数据集中具有最小Gini信息增益的特征作为结点划分决策树。ID3算法和C4.5算法虽然在对训练样本集的学习中可以尽可能多地挖掘信息,但其生成的决策树分支、规模较大,CART算法的二分法可以简化决策树的规模,提高生成决策树的效率。对于连续特征,CART也是采取和C4.5同样的方法处理。为了避免过拟合(Overfitting),CART决策树需要剪枝。预测过程当然也就十分简单,根据产生的决策树模型,延伸匹配特征值到最后的叶子节点即得到预测的类别。 创建回归树时,观察值取值是连续的、没有分类标签,只有根据观察数据得出的值来创建一个预测的规则。在这种情况下,Classification Tree的最优划分规则就无能为力,CART则使用最小剩余方差(Squared Residuals Minimization)来决定Regression Tree的最优划分,该划分准则是期望划分之后的子树误差方差最小。创建模型树,每个叶子节点则是一个机器学习模型,如线性回归模型。

不考虑剪枝情况下,分类决策树递归创建过程中就是每次选择Gain最小的节点做分叉点,直至子数据集都属于同一类或者所有特征用光了。

基尼指数(Gini) : image.png

例题:

image.png

image.png 2/5表示在5名青年中,有2名青年的类别为“是”;
7/10表示除了这5名青年之外的10名人员中,有7名中年和老年的类别为“是”;
5/15表示一共有15名人员,其中有5名青年;
10/15表示一共有15名人员,除掉5名青年的全部人数。

image.png 有工作与有房子这两个特征中,仅仅是与否,所以计算其一即可。因为两个特征是与否的基尼指数是相同的。
本题中选择了特征信息为"是"计算两个特征的基尼指数
A2,A3表示有工作,有自己的房子,1,2表示有工作和有自己的房子的值为“是”和“否”
而对于值为“是”还是“否”,其基尼指数是相同的,所以仅需要针对两个值的其中一个计算基尼指数即可
本题中,使用了值为“是”计算两个特征的基尼指数
在Gini(D,A2=1)中,表示有工作的特征中,类别为是的基尼指数

image.png

计算信贷情况的基尼指数,假设A4表示信贷情况,以1,2,3表示信贷情况的值为非常好,好,一般。 计算在A4特征中选择类别为"是"的基尼指数,因为类别只有两个,计算一个类别即可。

image.png 在A1,A2,A3,A4几个特征中,Gini(D,A3=1)最小,所以算则特征A3为最优特征,A3=1为其最优切分点。于是根节点生成两个子节点,一个是叶子结点。对另外一个结点继续使用以上方法在A1,A2,A4中选择最优特征以及其最优切分点,结果是A2=1。依次计算得知,所有结点是叶子结点。

image.png

五、ID3,C4.5,CART的区别

1.ID3为最大信息增益,又叫互信息,度量数据集在知道特征之后不确定性减少的程度。 2.C4.5为最大信息增益比——互信息除以数据集关于该特征的取值熵。 3.CART是一颗二叉树,采用二元切割把特征取值切成两份,每一步选择基尼系数(数据不纯度)最小的特征及其对应的划分点进行分类。

ID3倾向于选择取值较多的特征,C4.5一定程度对取值多的特征进行惩罚,提高泛化。CART的二值化分适用于连续变量。一般工业上,优先采用CART,再采用C4.5,然后是ID3。

六、剪枝

为什么要进行剪枝?

  • 预剪枝

预剪枝会使得决策树的很多分支没有展开,也就是没有继续分类下去,这不仅降低了过拟合的风险,还显著减少了决策树的训练时间开销测试时间开销。但是另一方面,有些分支的当前划分虽不能提升泛化性能、甚至可能导致泛化性能暂时下降,但是在其基础上进行的后续划分有可能导致性能显著提升。预剪枝基于’贪心’本质,也就是能多剪枝就多剪枝,使得预剪枝策略给决策树带来了欠拟合的风险。

  • 后剪枝

后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情况下,后剪枝决策树的欠拟合风险很小,泛化性能往往由于预剪枝决策树,但是后剪枝过程是在生成完全决策树后进行的,并且要自下往上地对树中的非叶子节点逐一进行考察计算,因此训练时间的开销比为剪枝和预剪枝决策树都要得多。

关于如何进行剪枝的相关知识,参考:七.4文献

七、参考文献

1.t.csdn.cn/QaAgX
2.t.csdn.cn/wgKPD
3.《机器学习》慕课版
4.读周志华《机器学习》第四章--决策树-pudn.com

八、python代码示例

用sklearn中自带的load_breast_cancer数据集进行决策树模型的训练与评估

from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import pickle#提供了一个简单的持久化功能


class DTTraining:
    """
    Decision Tree Training
    """
    def __init__(self):
        self.clf = DecisionTreeClassifier()

    def train(self, X_train, y_train):
        self.clf.fit(X_train, y_train)

    def save_model(self, path):
        with open(path, 'wb') as f:
            pickle.dump(self.clf, f)#pickle.dump()序列化对象,将结果数据流写入到文件对象中

    def load_model(self, path):
        with open(path, 'rb') as f:
            self.clf = pickle.load(f)#pickle.load()反序列化对象,将文件中的数据解析为一个python对象

    def visualize(self):
        fig, ax = plt.subplots(figsize=(12, 8))
        plot_tree(self.clf, ax=ax)
        plt.show()


class DTEvaluation:
    """
    Decision Tree Evaluation
    """
    def __init__(self, model_path):
        with open(model_path, 'rb') as f:
            self.clf = pickle.load(f)

    def evaluate(self, X_test, y_test):
        score = self.clf.score(X_test, y_test)
        print("Accuracy:", score)


if __name__ == "__main__":
    # 加载数据集并拆分数据
    data = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2)

    # 使用训练集训练新的模型
    trainer = DTTraining()
    trainer.train(X_train, y_train)

    # 将新模型保存到文件
    trainer.save_model("model.pkl")

    # 载入保存的模型
    evaluator = DTEvaluation("model.pkl")

    # 评估载入的模型并输出准确率
    evaluator.evaluate(X_test, y_test)

    # 可视化决策树
    trainer.visualize()


最终输出结果:

Accuracy: 0.9035087719298246

image.png