决策树

作者: CSTDOG | 来源:发表于2019-03-05 17:29 被阅读0次

    决策树定义

    • 概念:决策树可以近似于一个流程图,长方形代表判断模块,椭圆形代表终止模块,表示已经得出结论,可以终止运行。从判断模块引出的左右箭头称作分支,它可以到达另一个判断模块或者终止模块。


      决策树
    • 优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据

    • 缺点:可能会产生过度匹配问题

    • 应用难点:很难确定特征

    • 适用数据类型:数值型和标称型

    • 如何创建树的分支?

    Function createBranch():
    检测数据集中的每个子集是否属于同一分类
        If so return 类标签;
        Else
            寻找划分数据集的最好特征
            划分数据集
            创建分支节点
                for 每个划分的子集
                    调用函数createBranch()并增加返回结果到分支节点中
            return 分支节点
    
    • 信息增益:划分数据之前之后的变化称为信息增益,通过计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。香农提出的了熵用于计算信息增益,信息增益=原来的熵-变化后的熵
    • 信息的定义:l(xi)=-log2p(xi),p(xi)表示选择xi类别的概率
    • 熵:信息的期望值,计算一个样本集合中的数据是纯洁的还是不纯洁的,公式(n是分类的数目):
    H=-\sum_{i=1}^{n}p(x_i)log_2p(x_i)
    

    决策树的程序实现

    • 计算给定数据集的香农熵的实现代码:
    from math import log
    def calcShannonEnt(dataset):
        numEntries = len(dataset)
        labelCounts ={}
        # 求出各个类别的样本总数,用来计算p(xi)
        for featVec in dataset:
            currentLabel = featVec[-1]
            if currentLabel not in labelCounts.keys():
                labelCounts[currentLabel] = 0
            labelCounts[currentLabel] += 1
        shannoEnt = 0.0
        #利用公式计算熵
        for key in labelCounts:
            prob = float(labelCounts[key])/numEntries
            shannoEnt -= prob*log(prob,2)
        return shannoEnt
    
    #按照给定特征划分数据集
    #extend() 函数用于在列表末尾一次性追加另一个序列中的多个值
    #输入:待划分的数据集,划分数据集的特征,需要返回的特征的值
    #a[i:j]:表示取从第i+1位到第j个元素
    def splitDataSet(dataSet,axis,value):
        retDataSet =[]
        for featVec in dataSet:
            if featVec[axis] == value:
                # 从数据元组去掉该特征值
                reduceFeatVec =featVec[:axis]
                reduceFeatVec.extend(featVec[axis+1:])
                retDataSet.append(reduceFeatVec)
        return retDataSet
    
    • 选择最好的数据集划分方法
    #选择最好的数据集划分方式
    def chooseBestFeatureToSplit(dataSet):
        #计算列数得到特征数
        numFeatures = len(dataSet) - 1
        baseEntropy = calcShannonEnt(dataSet)
        bestInfoGain = 0.0
        bestFeatures = -1
        for i in range(numFeatures):
            #循环取出dataset的元组,然后取出元组中的第i列的所有取值
            featList = [example[i] for example in dataSet]
            uniqueVals = set(featList)
            #计算每个特征的所有特征值作为划分子节点时的熵
            for values in uniqueVals:
                subDataSet =splitDataSet(dataSet, i ,value)
                prob = len(subDataSet)/float(len(dataSet))
                newEntropy += prob*calcShannonEnt(subDataSet)
            infoGain = baseEntropy - newEntropy
            #找到最好的特征值的,增益值越大分类效果越好
            if(infoGain>bestInfoGain):
                bestInfoGain = infoGain
                bestFeatures = i
        return bestFeatures
    
    • 数据具有多个标签时如何处理?
      • 采用多数表决的方法决定该子节点的分类
    #当出现多个类标签结果时,采用投票表决的方法决定最终类标签
    def majorityCnt(classList):
        classCount = {}
        for vote in classList:
            if vote not in classCount.keys():
                classCount[vote]=0
            classCount[vote] += 1
            sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(i),reverse=True)
        return sortedClassCount[0][0]
    
    • 构建决策树
    #创建树的函数代码
    def createTree(dataSet, labels):
        classList=[example[-1]  for example in dataSet]
        #观察当前分组的分类标签是否一致
        if(classList.count(classList[0])==len(classList)):
            return classList[0]
        #特征已经遍历完
        if(len(dataSet[0]) == 1):
            return majorityCnt(classList)
        bestFeat =chooseBestFeatureToSplit(dataSet)
        bestFeatLabel = labels[bestFeat]
        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)
        return myTree
    
    • 使用前面构造好的算法得到决策树,对测试数据执行分类:
    #使用决策树执行分类
    #输入:决策树,特征表,测试数据
    def classify(inputTree, feaLabels, testVec):
        #取出第一个键值,即根
        firstStr = inputTree.keys()[0]
        #获得根对应的子节点
        secondDict = inputTree[firstStr]
        #找到第一个键值在实际数据存储中的列位置
        featIndex = feaLabels.index(firstStr)
        for key in secondDict.keys():
            #找到下一步要到达的节点
            if testVec[featIndex]==key:
                if type(secondDict[key])._name_== 'dict':
                    classLabel=classify(secondDict[key],feaLabels,testVec)
                else:
                    classLabel=secondDict[key]
        return classLabel
    
    • 如何存储决策树?使用pickle包,pickle提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。
    def storeTree(inputTree, filename):
        import pickle
        fw = open(filename, 'w')
        pickle.dump(inputTree, fw)
        fw.close()
    
    
    def grabTree(filename):
        import pickle
        fr = open(filename)
        return pickle.load(fr)
    

    相关文章

      网友评论

        本文标题:决策树

        本文链接:https://www.haomeiwen.com/subject/nxvouqtx.html