从零开始实战机器学习(2)—决策树算法

决策树算法听起来很陌生,但是一提到问问题游戏,大家肯定都玩过,游戏规则就是在脑海中想一个事物,其他人向此人提问题,回答也只能是是和否,从而不断缩小范围逼近正确答案。决策树就是这个原理。上一章介绍的K近邻算法能完成多分类问题,但是无法给出数据的内在含义,而决策树算法的优势就在于结果可视化,数据型式非常容易理解。如下图所示就是决策树的流程图型式。
这里写图片描述
优点:计算复杂度不高,结果易于理解,对中间值缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用范围:数值型和标称型
下面,我们就要进入决策树算法的大门了,首先就遇到了一个问题,我们要对一组数据进行分类,按照什么特征进行分类,怎样评判分类的好与坏。这貌似是个大问题,好在已经有人帮我们解决了,也就是信息论之父克劳德香浓提出的香农熵的概念。
香农熵:就是衡量一组数据混乱度的量。
比如说一群动物中基本都是狗,其中混有几只猫,则数据的混乱度很小,数据很有序,而如果一群动物中狗和猫的数目差不多,则这组数据的混乱度很大,很无序。而如何用式子来衡量这个现象呢,香浓直接给了我们一个公式:
这里写图片描述
S代表整个集合,Pi代表第i类出现的概率,m代表共有m个类别。如果一群动物中有29个猫与71个狗,则P1=29/100,P2=71/100。下面要讨论的一个问题是,按哪种特征分类是好的,试想一下,我们分类的目的就是想把每个类别的样本彻底分开,降低样本的香农熵。所以我们是向着熵减的方向进行,所以在每次分类的时候按照每个类别进行分类,找出使数据熵最小的特征。下面就是具体实现的代码。
计算每种分类的香农熵

def calcshannonEnt(dataset):
    numEnt = len(dataset)
    labelCounts1 = {}
    for featVec in dataset:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts1.keys(): labelCounts1[currentLabel] = 0
        labelCounts1[currentLabel] += 1
    ShannonEnt = 0.0
    for key in labelCounts1:
            prob = float(labelCounts1[key])/numEnt
            ShannonEnt -= prob *log(prob,2)
    return ShannonEnt,labelCounts1

对数据进行分类

def splitDataSet(dataSet,axis,value):
    retData = []
    for featvec in dataSet:
        if featvec[axis] == value:           
            reducedFeatVec = featvec[:axis]    #将axis前的的值返回给变量
            reducedFeatVec.extend(featvec[axis+1:]) #将axis后的值接到后面返回给变量,所以用extend函数。也就是将原来数组中的axis那一组值去掉。
            retData.append(reducedFeatVec)   #将分类后的数组赋给数组
    return retData

计算每种分类的香农熵,并返回最大香农熵分类

def chooseBestFeatureToSplit(dataSet):
    numFeature = len(dataSet[0])-1                #计算数据特征的个数
    bestEntropy = calcShannonEnt(dataSet)          #计算这组数据的香农熵
    bestInfoGain = 0.0;bestFeature = -1
    for i in range(numFeature):                  #遍历每个特征进行分类
        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       #信息增益为原香农熵减分类后的香农熵
        if (infoGain > bestInfoGain):       
            bestInfoGain = infoGain         
            bestFeature = i
    return bestFeature                      

多数决策代码,类似于K近邻中的classfy0程序,就是当分类到最后还没有完全分开的话,就取个数多的结果。

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),reversed = True)
        return sortedclassCount[0][0]

创建决策树代码

def createTree(dataSet,label):
    classList = [example[-1] for example in dataSet]     #创建数组存放所有标签值
    if classList.count(classList[0]) == len(classList): #判断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[:]       #将labels赋给sublabels,此时的labels已经删掉了用于分类的特征的标签
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)        #递归创建树直到分类达到上述判断条件
    return myTree

至此,已完成决策树算法的全部代码,下面我们将用数据来测试一下该代码。
数据一:
dataSet =
[[1,1,’yes’],
[1,1,’yes’],
[1,0,’no’],
[0,1,’no’],
[0,1,’no’]]
labels = [‘no surfacing’,’flippers’]
分类结果:
{‘no surfacing’: {0: ‘no’, 1: {‘flippers’: {0: ‘no’, 1: ‘yes’}}}}
数据二:
dataSet =
[[1,1,’yes’],
[1,1,’yes’],
[1,1,’no’]
[1,0,’no’],
[0,1,’no’],
[0,1,’no’]]
labels = [‘no surfacing’,’flippers’]
分类结果:{‘no surfacing’: {0: ‘no’, 1: {‘flippers’: {0: ‘no’, 1: ‘yes’}}}}
可以看出,数据一可以通过决策树将数据完全分开,但是数据二在分到最后一个特征时仍无法将数据完全分开,故最后调用了majorityCnt函数选择个数多的结果。由结果可以看出,以多重字典的表达形式很难懂,很晦涩,所以下面将介绍如何用matplotlib绘制树形图的方式表达结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值