决策树算法

一、什么是决策树?

  ①决策树概念:

    决策树是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。决策树其实也就是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果,本质是一颗由多个判断节点组成的树。决策树由下面几种元素构成:

    1、根节点:包含样本的全集

    2、内部节点:对应特征属性测试

    3、叶节点:代表决策的结果

  预测时,在树的内部节点处用某一属性值进行判断,根据判断结果决定进入哪个分支节点,直到到达叶节点处,得到分类结果。

  ②决策树学习的三大步骤:

    1、特征选择

      特征选择即是通过对于某一或某些特征的选择而筛选出和分类结果相关性较高的特征,我们在特征的选择中通常使用的判别准则是信息增益。

    2、决策树生成

      通过特征的选择之后,就从根节点出发,去计算所有特征的信息增益,并从中选出信息增益最大的特征作为结点特征,并根据各个特征的不同值去建立字子节点,如此对每个子节点使用相同的方式生成新的子节点,直到信息增益很小或没有特征可选为止。

    3、决策树剪枝

      剪枝是通过主动去掉部分分支来降低过拟合的风险。

   ③三种典型的决策树算法

      1、ID3算法:通过信息增益去选择特征。

      2、C4.5算法:通过信息增益比去选择特征。

      3、CART:并没有采用信息熵模型而是使用了基尼系数代之。

二、决策树与信息增益

  ①决策树与信息增益的关系:

    在决策树算法的学习过程中,信息增益是特征选择的一个重要指标,它定义为一个特征能够为分类系统带来多少信息,带来的信息越多,说明该特征越重要,相应的信息增益也就越大。

  ②信息熵与信息增益:

     什么是信息熵?

     信息熵描述了一个事件的不确定性。熵是表示随机变量不确定的度量,是对所有可能发生的事件产生的信息量的期望。在信息论与概率统计中,熵是表示随机变量不确定性的度量。设X是一个取有限个值的离散随机变量,其概率分布为:

      

      则随机变量X的熵定义为:

         

      用python实现:

      

#计算熵
def CalculateEntropy(datasets):  #datasets为处理过的数据集
    LabelCount = {}
    DataLength = len(datasets)
    for i in range(DataLength):
        label = datasets[i][-1]
        if label not in LabelCount:
            LabelCount[label] = 0
        LabelCount[label] += 1
    entropy = -sum ( [(j / DataLength) * log(j / DataLength, 2)
            for j in LabelCount.values()])
    return entropy

     什么是条件熵?

    

    设有随机变量(X,Y),其联合概率分布为:

    

    条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性。随机变量X给定的条件下随机变量Y的条件熵H(Y|X),定义为X给定条件下Y的条件概率分布的熵对X的数学期望

    当熵和条件熵中的概率由数据估计得到时,所对应的熵与条件熵分别称为经验熵和经验条件熵

    用python实现计算经验条件熵:

def CalculateConditionalEntropy(datasets, axis=0):    #datasets同为处理过的数据集
    CharacteristicSet = {}
    DataLength = len(datasets)
    for i in range(DataLength):
        Characteristic = datasets[i][axis]
        if Characteristic not in CharacteristicSet:
            CharacteristicSet[Characteristic] = []
        CharacteristicSet[Characteristic].append(datasets[i])
    ConditionalEntropy = sum(
        [(len(j) / DataLength) * ConditionalEntropy(j) for j in CharacteristicSet.values()])
    return ConditionalEntropy

     什么是信息增益?

    信息增益表示得知特征X的信息而使得类Y的信息的不确定性减少的程度。

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

      

    用python代码实现计算信息增益:

    

def InfoGain(entropy, ConditionalEntropy):
    return entropy - ConditionalEntropy
def InfoGainTrain(datasets):
    count = len(datasets[0]) - 1
    entropy = CalculateEntropy(datasets)
    BestCharacteristic = []
    for s in range(count):
        SInfoGain = InfoGain(entropy, ConditionalEntropy(datasets, axis=s))
        BestCharacteristic.append((s, SInfoGain))
        print('特征({}) - InfoGain - {:.3f}'.format(labels[s], SInfoGain))
        
    Best= max(BestCharacteristic, key=lambda x: x[-1])
    return '特征({})的信息增益最大,选择为根节点特征'.format(labels[Best[0]])

三、ID3算法的实现

  针对以下给定的房贷数据集实现ID3算法。

  1.   年龄 有工作 有自己的房子 信贷情况 类别
    0 青年 一般
    1 青年
    2 青年
    3 青年 一般
    4 青年 一般
    5 中年 一般
    6 中年
    7 中年
    8 中年 非常好
    9 中年 非常好
    10 老年 非常好
    11 老年
    12 老年
    13 老年 非常好
    14 老年 一般

    拿到数据集先对数据集进行标注,我们记

    1. 年龄:0代表青年,1代表中年,2代表老年;
    2.  有工作:0代表否,1代表是;
    3. 有自己的房子:0代表否,1代表是;
    4. 信贷情况:0代表一般,1代表好,2代表非常好;
    5. 类别:no代表否,yes代表是。
def createDataSet():        #创建测试数据集
    dataSet = [[0, 0, 0, 0, 'no'],         
               [0, 0, 0, 1, 'no'],
               [0, 1, 0, 1, 'yes'],
               [0, 1, 1, 0, 'yes'],
               [0, 0, 0, 0, 'no'],
               [1, 0, 0, 0, 'no'],
               [1, 0, 0, 1, 'no'],
               [1, 1, 1, 1, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [2, 0, 1, 2, 'yes'],
               [2, 0, 1, 1, 'yes'],
               [2, 1, 0, 1, 'yes'],
               [2, 1, 0, 2, 'yes'],
               [2, 0, 0, 0, 'no']]
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']        
    return dataSet, labels

完整代码实现:

 

from math import log

def createDataSet():
    dataSet = [[0, 0, 0, 0, 'no'],  # 数据集
               [0, 0, 0, 1, 'no'],
               [0, 1, 0, 1, 'yes'],
               [0, 1, 1, 0, 'yes'],
               [0, 0, 0, 0, 'no'],
               [1, 0, 0, 0, 'no'],
               [1, 0, 0, 1, 'no'],
               [1, 1, 1, 1, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [1, 0, 1, 2, 'yes'],
               [2, 0, 1, 2, 'yes'],
               [2, 0, 1, 1, 'yes'],
               [2, 1, 0, 1, 'yes'],
               [2, 1, 0, 2, 'yes'],
               [2, 0, 0, 0, 'no']]
    labels = ['年龄', '有工作', '有自己的房子', '信贷情况']  # 分类属性
    return dataSet, labels  # 返回数据集和分类属性


"""
函数说明:计算给定数据集的经验熵(香农熵)
Parameters:
    dataSet - 数据集
Returns:
    shannonEnt - 经验熵(香农熵)
"""


def calcShannonEnt(dataSet):
    numEntires = len(dataSet)  # 返回数据集的行数
    labelCounts = {}  # 保存每个标签(Label)出现次数的字典
    for featVec in dataSet:  # 对每组特征向量进行统计
        currentLabel = featVec[-1]  # 提取标签(Label)信息
        if currentLabel not in labelCounts.keys():  # 如果标签(Label)没有放入统计次数的字典,添加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1  # Label计数
    shannonEnt = 0.0  # 经验熵(香农熵)
    for key in labelCounts:  # 计算香农熵
        prob = float(labelCounts[key]) / numEntires  # 选择该标签(Label)的概率
        shannonEnt -= prob * log(prob, 2)  # 利用公式计算
    return shannonEnt  # 返回经验熵(香农熵)


"""
函数说明:按照给定特征划分数据集
Parameters:
    dataSet - 待划分的数据集
    axis - 划分数据集的特征
    value - 需要返回的特征的值
"""


def splitDataSet(dataSet, axis, value):
    retDataSet = []  # 创建返回的数据集列表
    for featVec in dataSet:  # 遍历数据集
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]  # 去掉axis特征
            reducedFeatVec.extend(featVec[axis + 1:])  # 将符合条件的添加到返回的数据集
            retDataSet.append(reducedFeatVec)
    return retDataSet  # 返回划分后的数据集


"""
函数说明:选择最优特征
Parameters:
    dataSet - 数据集
Returns:
    bestFeature - 信息增益最大的(最优)特征的索引值
"""


def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1  # 特征数量
    baseEntropy = calcShannonEnt(dataSet)  # 计算数据集的香农熵
    bestInfoGain = 0.0  # 信息增益
    bestFeature = -1  # 最优特征的索引值
    for i in range(numFeatures):  # 遍历所有特征
        # 获取dataSet的第i个所有特征
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)  # 创建set集合{},元素不可重复
        newEntropy = 0.0  # 经验条件熵
        for value in uniqueVals:  # 计算信息增益
            subDataSet = splitDataSet(dataSet, i, value)  # subDataSet划分后的子集
            prob = len(subDataSet) / float(len(dataSet))  # 计算子集的概率
            newEntropy += prob * calcShannonEnt(subDataSet)  # 根据公式计算经验条件熵
        infoGain = baseEntropy - newEntropy  # 信息增益
        if i == 0:
            print("’年龄‘特征的信息增益为%.3f" % ( infoGain))  # 打印每个特征的信息增益
        if i == 1:
            print("‘有工作’特征的信息增益为%.3f" % ( infoGain))
        if i == 2:
            print("’有自己的房子‘特征的信息增益为%.3f" % ( infoGain))
        if i == 3:
            print("‘信贷情况’特征的信息增益为%.3f" % (infoGain))
        if (infoGain > bestInfoGain):  # 计算信息增益
            bestInfoGain = infoGain  # 更新信息增益,找到最大的信息增益
            bestFeature = i  # 记录信息增益最大的特征的索引值
    return bestFeature  # 返回信息增益最大的特征的索引值


if __name__ == '__main__':
    dataSet, features = createDataSet()
    entropy = calcShannonEnt(dataSet)
    bestfeature = chooseBestFeatureToSplit(dataSet)
    print("训练集的熵为:%f" % (entropy))
    if bestfeature==0:
        print("最优特征索引值: ’年龄‘ " )
    if bestfeature==1:
        print("最优特征索引值: ‘有工作’ " )
    if bestfeature==2:
        print("最优特征索引值: ’有自己的房子‘ " )
    if bestfeature==3:
        print("最优特征索引值: ’信贷情况‘ " )

运行结果:

      

四、sklearn库中的决策树算法

   在 sklearn 库中与决策树相关的算法都存放在sklearn.tree模块里,该模块提供了 4 个决策树算法,下面对这些算法做简单的介绍:

    ① DecisionTreeClassifier()

      这是一个经典的决策树分类算法,它提供了许多有用的参数,比如criterion,该参数有两个参数值,分别是 gini(基尼指数)和 entropy(信息增益),默认情况下使用“基尼指数”,其中“gini”用于创建 CART 分类决策树,而“entropy”用于创建 ID3 分类决策树。

    ② DecisionTreeRegressor()

      它表示用决策树算法解决回归问题。

    ③ ExtraTreeClassifier()

      该算法属于决策树分类算法,但又不同于.DecisionTreeClassifier()算法,因为.ExtraTreeClassifier()选择“特征维度”作为判别条件时具有随机性,它首先从特征集合中随机抽取 n 个特征维度来构建新的集合,然后再从新的集合中选取“判别条件”。

    ④ ExtraTreeRegressor()

      该算法同样具有随机性,它与.ExtraTreeClassifier()随机过程类似,它主要解决机器学习中的回归问题。

五、应用sklearn的决策树算法对iris数据集进行类别预测

    ①sklearn决策树算法对于iris的分类:

from sklearn.datasets import load_iris   #导入数据集iris
iris = load_iris()  #载入数据集
print iris.data
from sklearn import datasets
iris = datasets.load_iris()
print(iris.data)
iris.target
from IPython.display import Image  
from sklearn import tree
import pydotplus 
dot_data = tree.export_graphviz(clf, out_file=None, 
                         feature_names=iris.feature_names,  
                         class_names=iris.target_names,  
                         filled=True, rounded=True,  
                         special_characters=True)  
graph = pydotplus.graph_from_dot_data(dot_data)  
Image(graph.create_png()) 

      使用决策树对iris分类结果:

    ②sklearn决策树算法对iris的预测

    

#导入包
# 数据集
from sklearn import datasets
# 分类器
from sklearn import tree
# 训练集测试集分割模块
from sklearn.model_selection import train_test_split
# 绘制决策树
import graphviz

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

 

# 导入数据集函数
def get_data(total_data):
    # 显示total_data包含的内容
    print("传入数据集包含内容有:", [x for x in total_data.keys()])
    # 样本
    x_true = total_data.data
    # 标签
    y_true = total_data.target
    # 特征名称
    feature_names = total_data.feature_names
    # 类名
    target_names = total_data.target_names
    
    return x_true, y_true, feature_names, target_names
# 定义主函数
def main():
    # 利用自定义函数导入Iris数据集
    total_iris = datasets.load_iris()
    x_true, y_true, feature_names, target_names = get_data(total_iris)
    
    # 分割数据集
    rate_test = 0.2  # 训练集比例
    x_train, x_test, y_train, y_test = train_test_split(x_true,
                                                        y_true,
                                                        test_size= rate_test)
    print("\n训练集样本大小:", x_train.shape)
    print("训练集标签大小:", y_train.shape)
    print("测试集样本大小:", x_test.shape)
    print("测试集标签大小:", y_test.shape)

    # 设置决策树分类器
    clf = tree.DecisionTreeClassifier(criterion="entropy")
    # 训练模型
    clf.fit(x_train, y_train)
    # 评价模型
    score = clf.score(x_test, y_test)
    print("\n模型测试集准确率为:", score)
    
    # 绘制决策树模型
    clf_dot = tree.export_graphviz(clf,
                                   out_file= None,
                                   feature_names= feature_names,
                                   class_names= target_names,
                                   filled= True,
                                   rounded= True)
    # 显示绘制的模型,在当前目录下,保存为png模式
    graph = graphviz.Source(clf_dot, 
    						filename= "iris_decisionTree.gv", 
    						format= "png")
    graph.view()
    
    # 显示特征重要程度
    print("\n特征重要程度为:")
    info = [*zip(feature_names, clf.feature_importances_)]
    for cell in info:
        print(cell)
    
    
# 调用主函数
if __name__ == "__main__":
    main()

预测结果:

六、决策树剪枝

    ①什么是决策树的剪枝?

    对比日常生活中,环卫工人在大街上给生长茂密的树进行枝叶的修剪。在机械学习的决策树算法中,有对应的剪枝算法。将比较复杂的决策树,化简为较为简单的版本,并且不损失算法的性能。

     ②决策树为什么要剪枝?

    如果一棵决策树完全生长,那么这棵决策树就很有可能面临过拟合的问题。完全生长的决策树所对应的每个叶节点中只会包含一个样本,这样决策树就是过拟合的,所以我们就需要对这种决策树进行剪枝操作来提升我们决策树模型的泛化能力。

   ③剪枝分为什么?

      1、预剪枝

        预剪枝的思想是在树种节点进行生长之前,先计算当前的划分是否能提升模型的泛化能力,如果不能的话,那么就不在进行子树的生长。预剪枝对于何时停止决策树的生长有下面这几种方法:
          1.当决策树达到一定的深度的时候,停止生长;
          2.当到达当前节点的样本数量小于某个阈值的时候,停止树的生长;
          3.计算决策树每一次分裂对测试集的准确度是否提升,当没有提升或者提升程度小于某个阈值的时候,则停止决策树的生长;

      2、后剪枝

        后剪枝的思想其实就是让算法生成一棵完全生长的决策树,然后从底层向上计算是否剪枝,如果需要剪枝,剪枝过程就是把子树删除,用一个叶子节点替代,该节点的类别按照多数投票的方法进行判断。同样的,后剪枝也可以通过在测试集上的准确率进行判断,如果剪枝后的准确率有所提升或者没有降低,那么我们就可以进行剪枝。

七、ID3、5算法的应用场景

    ①ID3算法:

       ID3算法理论清晰、使用简单、学习能力较强,且构造的决策树平均深度较小,分类速度较快,特别适合处理大规模的学习问题,同时ID3的简洁和强大之处,它已经广泛的应用在数据挖掘和人工智能中。

    ②C4.5算法:

      C4.5是ID3算法的一种改进,C4.5算法针对处理连续型属性比较耗时的改进、利用数学上的等价无穷小提高信息增益率的计算效率等等方面较ID3都得到了改进,同时C4.5的最大特点是建树规则易手理解,建树者不需要了解任何挖掘对象所在领域的专业知识,并且分类速度快,分类器淮确率高,C4.5 算法现在己经被广泛应用到经济、工业、医药、农业等各个领域。

    ③C5算法:

      C5.0算法则是C4.5算法的修订版,适用于处理大数据集,采用Boosting方式提高模型准确率,又称为BoostingTrees,在软件上计算速度比较快,占用的内存资源较少。

      

      

posted @ 2022-10-25 20:59  1nfinite  阅读(308)  评论(0编辑  收藏  举报