决策树&ID3算法

2024-07-02 1456阅读

文章目录

  • 1.特征选择
    • 1.1熵
    • 1.2信息增益
    • 2.决策树构建
      • 2.1 ID3算法
      • 2.2代码实现决策树构建
      • 2.3决策树可视化
      • 3.参考

        1.特征选择

        • 特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率,如果利用一个特征进行分类的结果与随机分类的结果没有很大差别,则称这个特征是没有分类能力的。经验上扔掉这样的特征对决策树学习的精度影响不大。通常特征选择的标准是信息增益(information gain)或信息增益比

        • 以下表为例

          决策树&ID3算法

        • 特征选择就是决定用哪个特征来划分特征空间。比如,我们通过上述数据表得到两个可能的决策树,分别由两个不同特征的根结点构成。

          决策树&ID3算法

        • 事实上我们可以通过表中数据得到:年龄、有工作、有自己房子、信贷情况共四个特征决定是否给贷款。那么问题在于,哪个特征的决定性更强?下面引入信息熵及信息增益的概念。

          1.1熵

          • 熵的定义式如下:

            决策树&ID3算法

          • 但上图中只计算一种情况下的熵,我们实例中通常有多种结果。我们上面的例子就是存在给贷款和不给贷款两种结果,因此上式推广得到多种结果求熵的公式如下:

            决策树&ID3算法

          • 那么上述例子中15个结果中,9个给贷款,6个不给贷款,则求该例子中的熵如下:

            决策树&ID3算法

            1.2信息增益

            • 在了解信息增益前,需要了解一个概念:条件熵。

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

              决策树&ID3算法

            • 我们get到熵和条件熵的概念,这时候引入信息增益

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

              决策树&ID3算法

            • 我们仍然以贷款的例子解释:我们设年龄特征为A1,那么共有三种情况:青年占5/15,中年占5/15,老年占5/15。其中青年得到贷款情况为2/5,中年得到贷款情况为3/5,老年得到贷款情况为4/5。因此计算式如下:

              决策树&ID3算法

            • 同理另外的特征A2、A3计算如下:

              决策树&ID3算法

            • 最终比较信息增益,特征A3(有自己的房子)信息增益最大。所以A3为最优特征。也就是是否给贷款参考价值最大的特征。

              2.决策树构建

              2.1 ID3算法

              • ID3算法的核心是在决策树各个结点上对应信息增益准则选择特征,递归地构建决策树。具体方法是:从根结点(root node)开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子节点;再对子结点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一个决策树。ID3相当于用极大似然法进行概率模型的选择。

              • 利用之前算出的结果:A3(有自己的房子)信息增益最大,那么将A3作为根结点。会得到两种情况:我们设D1为有自己的房子,D2为没有自己的房子。由于D1只有同一类的样本点,所以它成为一个叶结点,结点的类标记为“是”。那么D2就需要在剩余的几个特征A1、A2、A3中计算在D2条件下的信息增益:

                决策树&ID3算法

              • 由该结果可知,信息增益最大的是A2(有工作)。所以根据A3和A2两个特征得到的决策树如下:

                决策树&ID3算法

                2.2代码实现决策树构建

                # -*- coding: UTF-8 -*-
                from math import log
                import operator
                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                                #返回经验熵(香农熵)
                 
                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                             #返回数据集和分类属性
                 
                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                                      #返回划分后的数据集
                 
                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                     #信息增益
                        # print("第%d个特征的增益为%.3f" % (i, infoGain))            #打印每个特征的信息增益
                        if (infoGain > bestInfoGain):                             #计算信息增益
                            bestInfoGain = infoGain                             #更新信息增益,找到最大的信息增益
                            bestFeature = i                                     #记录信息增益最大的特征的索引值
                    return bestFeature                                             #返回信息增益最大的特征的索引值
                 
                 
                def majorityCnt(classList):
                    classCount = {}
                    for vote in classList:                                        #统计classList中每个元素出现的次数
                        if vote not in classCount.keys():classCount[vote] = 0   
                        classCount[vote] += 1
                    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)        #根据字典的值降序排序
                    return sortedClassCount[0][0]                                #返回classList中出现次数最多的元素
                 
                def createTree(dataSet, labels, featLabels):
                    classList = [example[-1] for example in dataSet]            #取分类标签(是否放贷:yes or no)
                    if classList.count(classList[0]) == len(classList):            #如果类别完全相同则停止继续划分
                        return classList[0]
                    if len(dataSet[0]) == 1 or len(labels) == 0:                                    #遍历完所有特征时返回出现次数最多的类标签
                        return majorityCnt(classList)
                    bestFeat = chooseBestFeatureToSplit(dataSet)                #选择最优特征
                    bestFeatLabel = labels[bestFeat]                            #最优特征的标签
                    featLabels.append(bestFeatLabel)
                    myTree = {bestFeatLabel:{}}                                    #根据最优特征的标签生成树
                    del(labels[bestFeat])                                        #删除已经使用特征标签
                    featValues = [example[bestFeat] for example in dataSet]        #得到训练集中所有最优特征的属性值
                    uniqueVals = set(featValues)                                #去掉重复的属性值
                    for value in uniqueVals:                                   #遍历特征,创建决策树。        
                        subLabels = labels[:]               
                        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels, featLabels)
                    return myTree
                 
                if __name__ == '__main__':
                    dataSet, labels = createDataSet()
                    featLabels = []
                    myTree = createTree(dataSet, labels, featLabels)
                    print(myTree)
                

                2.3决策树可视化

                • 用到Matplotlib:使用的函数为

                  getNumLeafs:获取决策树叶子结点的数目

                  getTreeDepth:获取决策树的层数

                  plotNode:绘制结点

                  plotMidText:标注有向边属性值

                  plotTree:绘制决策树

                  createPlot:创建绘制面板

                  3.参考

                  https://cuijiahua.com/blog/2017/11/ml_3_decision_tree_2.html

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]