决策树学习(二)

来源:互联网 发布:哪些明星开了淘宝店铺 编辑:程序博客网 时间:2024/06/05 04:52

本文来源于个人博客:www.chenbiaolong.com

概述

在上一篇文章中我们利用信息熵的概念找到了最优的划分属性,当然也是仅仅找到“一个”属性而已,虽然利用这个属性划分数据集有着最大的熵减。在现实的机器学习中是不可能只用一个属性就能将数据分类的,因此本文将在上一篇文章的基础上真正构建一颗决策树。熵减越大的属性在该树的节点深度越浅。

实现原理

我们最终构建的决策树的形式大概如下:
图1

这张图直接给出了最终结果,但实际上在没有进行计算之前我们并不知道outlook属性是根节点,各个判断节点的顺序需要通过计算熵差决定。只需注意熵减越大的属性在决策树的深度越浅这一原则就行。
决策树的构建可以利用递归的方式实现,通过递归调用上一篇文章中的chooseBestFeatureToSplit函数不断找出剩余的属性中的最优划分属性构建决策树,直到达到停止条件为止。递归调用的结束条件为:

  1. 程序遍历完所有的数据集属性。这个条件没什么可解释的,当属性全部遍历完,无论数据是否完全正确划分都必须退出。现实中可能存在一种情况:当所有的属性全部遍历完后划分的数据集A中同时有2种不同的结果。用我们举的打球例子来说可能考虑了数据中所有属性(天气、气温、湿度)发现在一个应该判断为出去打球的数据集中包含了几个不出去打球的例子。造成这种例外的情况原因有很多,可能是数据记录错误,但更大的可能性是我们的数据属性并没有包括所有影响我们决定的属性。简单的用数学语言表示:
    IsPlayTennis=f(x0,x1,...xn2,xn1)

    决定我们是否打球由n个因素构成,这个n值可能很大(比如包括是否生病,心情等),但我们的数据集中只包含了3个属性,因此就会造成遍历完所有属性依然不能将数据完全划分成功的状况。这种情况下我们采取的方案是采用多数表决的方法,即最终结果是数据集中个数最多的一类结果。
  2. 每个分支下所有的实例都具有相同的分类,即划分后的数据集信息熵为0,数据集完全有序。

程序实现

首先准备测试数据,增加一个lables表示属性名称:

def createDataSet():    dataSet = [[0,0,0,0,'no'],               [0,0,0,1,'no'],                [1,0,0,0,'yes'],                [2,1,0,0,'yes'],                [2,2,1,0,'yes'],                [2,2,1,1,'no'],                [1,2,1,1,'yes'],                [0,1,0,0,'no'],                [0,2,1,0,'yes'],                [2,1,1,0,'yes'],                [0,1,1,1,'yes'],                [1,1,0,1,'yes'],                [1,0,1,0,'yes'],                [2,1,0,1,'no']]    labels = ['outlook', 'temperature','humidity', 'wind', 'playtennis']    return dataSet, labels

构建决策树函数:

def createTree(dataSet,labels):    classList = [example[-1] for example in dataSet] #    if classList.count(classList[0]) == len(classList):         return classList[0]  #stop splitting when all of the classes are equal    if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet        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[:]       #copy all of labels, so trees don't mess up existing labels        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)    return myTree             

这个函数中majorityCnt即是投票选择出个数最多的结果,代码如下:

def majorityCnt(classList):    classCount={}    for vote in classList:        if vote not in classCount.keys(): classCount[vote] = 0        classCount[vote] += 1    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)    return sortedClassCount[0][0]

createTree函数的myTree是个字典,该字典的key是字符串lablevalue则是另外一个字典。代码已经比较清晰,就不多加解释了。
编写测试代码如下:

def main():    dataset,labels = createDataSet()    mytree = createTree(dataset, labels)    print mytree    #print ret_datasetif __name__ == '__main__':    main()

最终结果为

{'outlook': {0: {'humidity': {0: 'no', 1: 'yes'}}, 1: 'yes', 2: {'wind': {0: 'yes', 1: 'no'}}}}

这个结果实际上就是上一篇文章的策略树
此处输入图片的描述
可能你们已经注意到,在我们的属性值中有temperature这一属性,但在构建的策略树中并没有这个属性。原因就是temperature的熵差比较小,排的比较靠后,在运行到temperature这个节点前递归已经结束了,即全部的数据已经被正确划分。

小结

这2篇博文简单介绍了一下决策树的实现原理,主要参考资料为《机器学习》(Tom M.Mitchell著)和《机器学习实战》(Peter Harrington著),python代码均来源于《机器学习实战》。

0 0