• 机器学习实践之决策树算法学习


    关于本文说明,本人原博客地址位于http://blog.csdn.net/qq_37608890,本文来自笔者于2017年12月06日 18:06:30所撰写内容(http://blog.csdn.net/qq_37608890/article/details/78731169)。

      本文根据最近学习机器学习书籍 网络文章的情况,特将一些学习思路做了归纳整理,详情如下.如有不当之处,请各位大拿多多指点,在此谢过.

    一、决策树(decision tree)概述

    1、决策树概念

           决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。其每个非叶节点表示一个特征属性上的测试,每个分支代表这个特征属性在某个值域上的输出,而每个叶节点存放一个类别。使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。

    2 工作原理

          在构造决策树时,需要解决的第一个问题就是,当前数据集上哪个特征在划分数据分类时起到来决定性的作用。为了找到决定性的特征,我们需要对每个特征都要进行评估.完成测试后,原始数据就被划分为几个数据子集.这些数据子集会分布在第一个决策点的所有分支上.若某一分支下的数据属于同一类型,则当前无需阅读的垃圾邮件已经被正确地划分数据分类,没必要再对数据集进行分类.否则,则需要重复划分数据子集的过程.这里划分子集的算法和划分原始数据集的方法相同,直至所有具有相同类型的数据都进入一个数据子集内.构造决策树伪代码函数createBranch()如下:

     

        检测数据集中的每个子项是否属于同一分类:  
              IF so return 类标签  
          
              Else  
                      寻找划分数据集的最好特征  
                      划分数据集  
                       创建分支节点  
                                 for 每个划分的子集  
                                         调用函数createBranch()并增加返回结果到分支节点中  
                        return 分支节点  
                        
    

            一旦我们构造了一个决策树模型,以它为基础来进行分类将是非常容易的。具体做法是,从根节点开始,对实例的某一特征进行测试,根据测试结构将实例分配到其子节点(也就是选择适当的分支);沿着该分支可能达到叶子节点或者到达另一个内部节点时,那么就使用新的测试条件递归执行下去,直到抵达一个叶子节点。当到达叶子节点时,我们便得到了最终的分类结果。下面介绍一个小例子。

        通俗来说,决策树分类的思想类似于找对象。现想象一个女孩的母亲要给这个女孩介绍男朋友,于是有了下面的对话:

    女儿:多大年纪了?
    母亲:26。
    女儿:长的帅不帅?
    母亲:挺帅的。
    女儿:收入高不?
    母亲:不算很高,中等情况。
    女儿:是公务员不?
    母亲:是,在税务局上班呢。
    女儿:那好,我去见见。


          这个女孩的决策过程就是典型的分类树决策。相当于通过年龄、长相、收入和是否公务员对将男人分为两个类别:见和不见。假设这个女孩对男人的要求是:30岁以下、长相中等以上并且是高收入者或中等以上收入的公务员,那么这个可以用下图表示女孩的决策逻辑:

     

            上图完整表达了这个女孩决定是否见一个约会对象的策略,其中绿色节点表示判断条件,橙色节点表示决策结果,箭头表示在一个判断条件在不同情况下的决策路径,图中红色箭头表示了上面例子中女孩的决策过程。
    这幅图基本可以算是一颗决策树,说它“基本可以算”是因为图中的判定条件没有量化,如收入高中低等等,还不能算是严格意义上的决策树,如果将所有条件量化,则就变成真正的决策树了。

    3、决策树的相关特性

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

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

    •    使用数据类型: 数值型和标称型。

    4、 一般流程

        (1) 收集数据: 可以使用任何方法.

        (2) 准备数据: 构造算法只适用于标称型数据,因此数值型数据必须离散化.

        (3) 分析数据: 可以使用任何方法,构造树完成之后,应该检查图形是否符合预期.

        (4) 训练算法: 构造树的数据结构.

        (5) 测试算法: 使用经验树计算错误率.

        (6) 使用算法: 此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义.

     

    二 决策树场景

           假设,现在有一个叫做 "十五个问题" 的游戏,游戏的规则很简单:参与游戏的一方在脑海中想某个事物,其他参与者向他提问,只允许提 15个问题,问题的答案也只能用对或错回答。问问题的人通过推断分解,逐步缩小待猜测事物的范围,最后得到游戏的答案。决策树的工作原理与15个问题类似,用户输入一系列数据后给出游戏答案。

          下图给出了一个假想的邮件分类系统,它首先检测发送邮件域名.如果地址为myEmployer.com,则将其放在"无聊时需要阅读的邮件"中。否则,则需要检查邮件内容中是否包含单词 曲棍球 ,若包含则将邮件归入"需要及时处理的朋友邮件",否则则归类到"无需阅读的垃圾邮件"。

     

       

     

       决策树一个很重要的任务就是为了理解数据中所蕴含的知识信息(这与K-近邻算法无法给出数据的内在含义有着显著不同),因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,这些机器根据数据集创建规则的过程,就是机器学习的过程。

    三 决策树项目案例一  对海洋生物进行鱼和非鱼判断

    1 项目情况

          下表中的数据包含5个海洋生物,特征: 不浮出水面是否可以生存和是否有脚蹼.现将动物划分为两类: 鱼和非鱼.如果想依据给出的特征选出一个来划分数据,就涉及到要将划分数据的依据进行量化后才可以判断出来.

        

      我们先构造进行数据输入的createDataSet()函数和计算给定数据集的香农熵函数calcShannonEnt()

       

        def createDataSet():  
            dataSet = [[1,1,'yes'],  
                      [1,1,'yes'],  
                      [1,0,'no'],  
                      [0,1,'no'],  
                      [0,1,'no']]  
            labels=['no surfacing','flippers']  
            # change to discrete values  
            return dataSet,labels  
        #信息增益  
        #计算给定数据的香农熵  
          
        def calcShannonEnt(dataSet): #the the number of unique elements and their occurance  
            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.00000  
            for key in labelCounts:  
                prob = float(labelCounts[key]) /numEntries  
                shannonEnt -= prob * log(prob,2)   #log base 2  
                  
            return shannonEnt  
    

     执行

        myDat,labels=createDataSet()  
        myDat  
    

     得到 

    [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
    

     执行

    calcShannonEnt(myDat)  
    

    得到

    0.9709505944546686

    熵越高,则混合的数据越多,我们可用在数据集中添加更多的分类,观察熵是如何变化的.

    按照给定特征划分数据集,将指定特征的特征值等于 value 的行剩下列作为子数据集。

     

        def splitDataSet(dataSet, index, value):  
            """splitDataSet(通过遍历dataSet数据集,求出index对应的colnum列的值为value的行) 
                就是依据index列进行分类,如果index列的数据等于 value的时候,就要将 index 划分到我们创建的新的数据集中 
            Args: 
                dataSet 数据集                 待划分的数据集 
                index 表示每一行的index列        划分数据集的特征 
                value 表示index列对应的value值   需要返回的特征的值。 
            Returns: 
                index列为value的数据集【该数据集需要排除index列】 
            """  
            retDataSet = []  
            for featVec in dataSet:   
            
                if featVec[index] == value:  
                 
                    reducedFeatVec = featVec[:index]  
                     
                    reducedFeatVec.extend(featVec[index+1:])  
                
                    retDataSet.append(reducedFeatVec)  
            return retDataSet  
    

     选择最好的数据集划分方式:

     

        def chooseBestFeatureToSplit(dataSet):  
            """chooseBestFeatureToSplit(选择最好的特征) 
         
            Args: 
                dataSet 数据集 
            Returns: 
                bestFeature 最优的特征列 
            """  
            # 求第一行有多少列的 Feature, 最后一列是label列嘛  
            numFeatures = len(dataSet[0]) - 1  
    
            baseEntropy = calcShannonEnt(dataSet)  
          
            bestInfoGain, bestFeature = 0.0, -1  
            # iterate over all the features  
            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)  
                # gain[信息增益]: 划分数据集前后的信息变化, 获取信息熵最大的值  
             
                infoGain = baseEntropy - newEntropy  
                print 'infoGain=', infoGain, 'bestFeature=', i, baseEntropy, newEntropy  
                if (infoGain > bestInfoGain):  
                    bestInfoGain = infoGain  
                    bestFeature = i  
            return bestFeature  
    

    训练算法:构造树的数据结构

      创建树的函数

        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  
            myTree = {bestFeatLabel: {}}  
          
            del(labels[bestFeat])  
            # 取出最优列,然后它的branch做分类  
            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)  
                # print 'myTree', value, myTree  
            return myTree  
    

       测试算法:使用决策树执行分类

        def classify(inputTree, featLabels, testVec):  
            """classify(给输入的节点,进行分类) 
         
            Args: 
                inputTree  决策树模型 
                featLabels Feature标签对应的名称 
                testVec    测试输入的数据 
            Returns: 
                classLabel 分类的结果值,需要映射label才能知道名称 
            """  
    
            firstStr = inputTree.keys()[0]  
    
            secondDict = inputTree[firstStr]  
     
            featIndex = featLabels.index(firstStr)  
    
            key = testVec[featIndex]  
            valueOfFeat = secondDict[key]  
            print '+++', firstStr, 'xxx', secondDict, '---', key, '>>>', valueOfFeat  
         
            if isinstance(valueOfFeat, dict):  
                classLabel = classify(valueOfFeat, featLabels, testVec)  
            else:  
                classLabel = valueOfFeat  
            return classLabel  
    

     

    三  项目案例2: 使用决策树预测隐形眼镜类型

    项目概述

    隐形眼镜类型包括硬材质、软材质以及不适合佩戴隐形眼镜。我们需要使用决策树预测患者需要佩戴的隐形眼镜类型。
    开发流程

    (1)收集数据: 提供的文本文件。
    (2)解析数据: 解析 tab 键分隔的数据行
    (3)分析数据: 快速检查数据,确保正确地解析数据内容,使用 createPlot() 函数绘制最终的树形图。
    (4)训练算法: 使用 createTree() 函数。
    (5)测试算法: 编写测试函数验证决策树可以正确分类给定的数据实例。
    (6)使用算法: 存储树的数据结构,以便下次使用时无需重新构造树。

    收集数据:提供的文本文件

    文本文件数据格式如下:

      

    young   myope   no  reduced no lenses  
    pre myope   no  reduced no lenses  
    presbyopic  myope   no  reduced no lenses 
    

      解析数据:解析 tab 键分隔的数据行

        lecses = [inst.strip().split('	') for inst in fr.readlines()]  
        lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']  
    

    分析数据:快速检查数据,确保正确地解析数据内容,使用 createPlot() 函数绘制最终的树形图。

    treePlotter.createPlot(lensesTree)  
    

     
    训练算法:使用 createTree() 函数

        >>> lensesTree = trees.createTree(lenses, lensesLabels)  
        >>> lensesTree  
    

     
    得到

        {'tearRate': {'reduced': 'no lenses', 'normal': {'astigmatic':{'yes':  
        {'prescript':{'hyper':{'age':{'pre':'no lenses', 'presbyopic':  
        'no lenses', 'young':'hard'}}, 'myope':'hard'}}, 'no':{'age':{'pre':  
        'soft', 'presbyopic':{'prescript': {'hyper':'soft', 'myope':  
        'no lenses'}}, 'young':'soft'}}}}}  
    

    五 小结

    其实决策树跟带终止块的流程图类似,所以这里的终止块就是分类结果.当我们进行数据处理时,首先要对集合中数据的不一致性进行测量评估,也就是计算香农熵,下一步才可以寻找最有方案划分数据,最终实现所有具有相同类型的数据都划分到同一个数据子集里面.在构建数据树时,我们一般采用递归方把数据集转化为决策树.多数情况下,我们不构造新的数据结构,而是采用Python语言内嵌的数据结构字典存储树节点信息.每一步选择信息增益最大的特征作为决策块,最终来完成决策树的生成.


    Matplotlib的注解功能,可以让将存储的树结构转化为容易理解的图形.隐形眼镜的例子说明决策树可能会产生过多的数据集划分,结果导致过度匹配数据集的问题.当然可以通过裁剪决策树,合并相邻的不能产生信息增益的叶节点,来解决这个问题(过度匹配).


    关于决策树的构造算法,这里本文只是用了ID3算法,当然还有C4.5和CART算法.对于决策树的完整工作过程而言,包括三部分:


    1 特征选择;


    2 生成决策树;


    3 剪枝部分


    而除去ID3算法,其他两个算法都有剪枝部分过程.所以这也迎合来隐形眼镜过拟合的问题.

      关于决策树部分,笔者先整理到这里,后续有机会会针对C4.5和CART算法做些归纳整理.有不足之处,请各位同仁多多指导.

  • 相关阅读:
    JS 格林威治时间格式(GMT)格式化
    SQL Server各个版本功能比较
    SQL Server各个版本功能比较
    SQL Server各个版本功能比较
    SQLl中的left join、right join、inner join详解
    SQLl中的left join、right join、inner join详解
    SQLl中的left join、right join、inner join详解
    【必备】jQuery性能优化的38个建议
    Metasploit笔记
    SpringBoot开发二十-Redis入门以及Spring整合Redis
  • 原文地址:https://www.cnblogs.com/georgeli/p/8087660.html
Copyright © 2020-2023  润新知