• 【机器学习实战学习笔记(2-2)】决策树python3.6实现及简单应用



    通过决策树原理及相关概念细节我们知道,决策树的学习算法主要包括3个步骤:特征选择、决策树生成算法、决策树剪枝,我们按照这个思路来一一实现相关功能。

    本文的实现目前主要涉及特征选择、ID3及C4.5算法。剪枝及CART算法暂未涉及,后期补上

    1.ID3及C4.5算法基础

    前面文章我们提到过,ID3与C4.5的主要区别是特征选择准则的不同:

    • ID3:信息增益
    • C4.5:信息增益比

    1.1 计算香农熵

    不管是这两者的哪一种,都涉及到信息增益的计算,而计算信息增益的基础又是计算香农熵。所以我们先来实现计算香农熵的代码。

    from math import log
    import operator
    
    # 计算给定数据集的香农熵
    def calcShannonEnt(dataSet):
        numEntries = len(dataSet)
        labelCounts = {}
        # 为所有可能分类创建字典
        for featVec in dataSet:
            currentLabel = featVec[-1]
            if currentLabel not in labelCounts.keys():
                labelCounts[currentLabel] = 0
            labelCounts[currentLabel] += 1
        shannonEnt = 0.0
        for key in labelCounts:
            prob = float(labelCounts[key])/numEntries
            shannonEnt -= prob * log(prob,2) # 以2为底求对数
        return shannonEnt
    

    然后创建书中的数据集,并计算该数据集的香农熵:

    # 创建自己的数据集
    def createDataSet():
        dataSet = [[1, 1, 'yes'],
                  [1, 1, 'yes'],
                  [1, 0, 'no'],
                  [0, 1, 'no'],
                  [0, 1, 'no']]
        labels = ['no surfacing','flippers']
        return dataSet, labels
    
    myDat,labels=createDataSet()  
    myDat  #  [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
    
    calcShannonEnt(myDat)  #  0.9709505944546686
    

    1.2 按照给定特征划分数据集

    # 按照给定特征划分数据集
    def splitDataSet(dataSet, axis, value):
        retDataSet = []
        for featVec in dataSet:
            if featVec[axis] == value:
                reducedFeatVec = featVec[:axis]
                reducedFeatVec.extend(featVec[axis+1:])
                retDataSet.append(reducedFeatVec)
        return retDataSet
    

    测试:

    splitDataSet(myDat,0,1)  #  [[1, 'yes'], [1, 'yes'], [0, 'no']]
    

    1.3 选择最优特征

    # 选择最好的数据集划分方式
     # 选择最好的数据集划分方式
    def chooseBestFeatureToSplit(dataSet):
        numFeatures = len(dataSet[0]) - 1
        baseEntropy = calcShannonEnt(dataSet)
        bestInfoGain = 0.0; bestFeature = -1
        for i in range(numFeatures):
            # 创建唯一的分类标签列表
            featList = [example[i] for example in dataSet]
            uniqueVals = set(featList)
            newEntropy = 0.0
            # 计算每种划分方式的信息熵
            for value in uniqueVals:
                subDataSet = splitDataSet(dataSet, i, value)
                prob = len(subDataSet)/float(len(dataSet))
                newEntropy += prob * calcShannonEnt(subDataSet)
            infoGain = baseEntropy - newEntropy  # ID3
            #  infoGain = baseEntropy - newEntropy  #  C4.5 
            # 计算最好的信息增益
            if (infoGain > bestInfoGain):
                bestInfoGain = infoGain
                bestFeature = i
        return bestFeature         
    

    1.4 多数表决实现

    在ID3、C4.5算法的停止条件之一是:没有特征可以选择时停止算法,但如果这时该结点类标签依然不是唯一的,此时我们需要决定如何定义该叶子结点。在这种情况下,通常采用多数表决的方法决定该叶子结点的分类。

    # 多数表决实现
    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(1),reverse=True)
        # Python3中不再支持iteritems(),将iteritems()改成items()
        return sortedClassCount[0][0]
    

    对于“多数表决实现”函数的注释:

    • 1.dict.items()
      作用:是可以将字典中的所有项,以列表方式返回。因为字典是无序的,所以用items方法返回字典的所有项,也是没有顺序的。
    • 2.operator.itemgetter()
      operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号.
    • 3.sorted()函数,排序
      list.sort()是对已经存在的列表进行操作,进而可以改变进行操作的列表;
      sorted返回的是一个新的list,而不是在原来的基础上进行的操作

    2.基于ID3、C4.5生成算法创建决策树

    这里主要介绍基于ID3生成算法创建决策树,C4.5只需要在ID3生成决策树代码上将chooseBestFeatureToSplit(dataSet)函数中infoGain = baseEntropy - newEntropy换成infoGain = baseEntropy - newEntropy即可 。

    # 创建树的函数代码
    def creatTree(dataSet,labels):
        classList = [example[-1] for example in dataSet]
        labels2 = labels[:]
        # 类别完全相同则停止继续划分
        if classList.count(classList[0]) == len(classList):
            return classList[0]
        # 遍历完所有特征时返回出现次数最多的类别
        if len(dataSet[0]) == 1:
            return majorityCnt(classList)
        bestFeat = chooseBestFeatureToSplit(dataSet)
        bestFeatLabel = labels2[bestFeat]
        myTree = {bestFeatLabel:{}}
        del (labels2[bestFeat])
        # 得到列表包含的(选定为最佳特征的)所有属性值
        featValues = [example[bestFeat] for example in dataSet]
        uniqueVals = set(featValues)
        for value in uniqueVals:
            subLabels = labels2[:] # 复制类标签
            # 递归
            myTree[bestFeatLabel][value] = creatTree(splitDataSet(dataSet, bestFeat, value),subLabels)
        return myTree
    

    对于“creatTree”函数的注释:

    • 1.list.count(obj)
      统计某个元素在列表中出现的次数

    • 2.del,list.remove(),list.pop()

      del:根据索引位置来删除单个值或指定范围内的值;

      list.remove():删除单个元素,删除首个符合条件的元素,按值删除,返回值为;

      list.pop():删除索引位置元素,无参情况下删除最后一个元素,返回删除的元素值;

    测试:

    myTree = creatTree(myDat, labels)
    myTree  #  {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
    

    3.使用决策树进行分类

    # 使用决策树的分类函数
    def classify(inputTree, featLabels, testVec):
        firstStr = list(inputTree.keys())[0]
        secondDict = inputTree[firstStr]
        featIndex = featLabels.index(firstStr)
        for key in secondDict.keys():
            if testVec[featIndex] == key:
                if type(secondDict[key]).__name__=='dict':
                #if isinstance(secondDict[key], dict): 这个也可以
                    classLabel = classify(secondDict[key],featLabels,testVec)
                else:
                    classLabel = secondDict[key]
        return classLabel
    

    对于“classify”的注释:

    • 1.type(a).name == ‘dict’:
      可判断a的类型是否类型为dict,list tuple 这些也适用

    • 2.也可以用isinstance(变量名,类型)判断类型:
      判断该变量是否是该类型,或者是否是该类和该类的父类类型;

      小注:
      type(变量名):获取该变量名的类型,结合==判断该变量的类型是否等于目标类型(等号右边value值)
      
      比如:a类继承b类,实例c=a()
      isinstance(c,a)和isinstance(c,b)都是True
      type(c)的value值是a,a是不等于b的,所以a==b为False即:type(c)==b为False
      
    • 3.==和is
      ==:变量名的value值是否相等
      is:变量名的id(地址)是否相等(数字类型的value值相等则id相等)

    测试:

    classify(myTree, labels, [1,0])  #  'no'
    classify(myTree, labels, [1,1])  #  'yes'
    

    4.存储决策树

    import pickle
    
    # 使用pickle模块存储决策树
    def storeTree(inputTree, filename):
            with open(filename, 'wb') as f:
                pickle.dump(inputTree, f)
    #  加载决策树
    def grabTree(filename):
        with open(filename, 'rb') as f:
            return pickle.load(f)
    

    测试:

    storeTree(myTree,'classifierStorage.txt')
    grabTree('classifierStorage.txt')  #  {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
    

    参考资料:
    《机器学习实战》第三章

  • 相关阅读:
    java日期格式转换工具类
    关于IOS给我的启发
    (转载)小米手机给我的启发和思考
    ElasticDownload
    ShopMall
    CycleViewPager
    MaterialImageView
    ZrcListView
    superCleanMaster
    QuickNewsLibs
  • 原文地址:https://www.cnblogs.com/siplifyit/p/12109218.html
Copyright © 2020-2023  润新知