机器学习(三):分类算法之决策树算法

一、特点

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

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

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


二、原理与实现


根据某个特征,划分数据集。如果某个分支下的数据属于同一类型,则无需进一步分割;否则需要重复划分数据子集。而其中最重要的问题就是,选取哪个特征进行数据划分。

选取哪个特征进行划分?


划分数据集的最大原则是,将无序的数据变的更加有序。在划分数据集之后信息所发生的变化称为信息增益,计算每个特征值划分数据集获得的信息增益, 获得信息增益最高的特征就是最好的选择。

如何计算信息增益?


集合信息的度量方式称为香农熵或者简称为熵。熵定义为信息的期望值。(没错,就是这么费解,但是多看看就还好)

1. 计算数据集合的熵的方法

一个数据集中,X是这个集合每个数据对应的类别(比如要不要出去玩、是不是鱼等等)。对于变量X,它可能的取值有n多种,分别是x1,x2,……,xn,每一种取到的概率分别是P1,P2,……,Pn,那么集合的熵(对于x而言)就定义为:

一个集合类别可能的变化越多(只和值的种类多少以及发生概率有关,与具体值是多少无关),它携带的信息量就越大

下面给出计算数据集香农熵的python代码:

from math import  log


#伪造一些关于是否为鱼类的特征和标签
def createDataSet():
    dataSet= [
                [1,1,'yes'],    #每个字段分别代表  在水下是否可以生存,  是否有脚蹼,   是否属于鱼类
                [1,1,'yes'],
                [1,0,'no'],
                [0,1,'no'],
                [0,1,'no']
    ]
    labels=['noSurfacing' , 'flippers']
    return dataSet, labels   

#计算香农熵   参数说明:  dataSet是一个数据集合,indexOfLabel是表明每条数据所属类别的下标
def calcShannonEnt(dataSet, indexOfLabel):   
    entriesNum=len(dataSet)   #计算数据总条目数
    labelCounts={}            #每个类别有多少个
    for featVec in dataSet:
        label = featVec[indexOfLabel]
        if label not in labelCounts.keys():
            labelCounts[label]=0
        labelCounts[label] +=1
    shannonEnt=0.0
    for key in labelCounts:   #套用上方的计算式
        prob=float(labelCounts[key])/entriesNum
        shannonEnt -= prob* log(prob,2)
    return shannonEnt
    
    


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


def chooseBestFeatToSplit(dataSet):
    featureNum=len(dataSet[0])-1
    baseEntropy = calcShannonEnt(dataSet,-1)  #原始香农熵
    bestInfoGain=0.0 ; bestFeature=-1
    for i in range(featureNum):
        #创建唯一的分类标签列表
        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,-1)
        infoGain=baseEntropy-newEntropy
        #计算最好的信息增益
        if infoGain>bestInfoGain:
            bestInfoGain=infoGain
            bestFeature=i
    return bestFeature

if __name__=="__main__":
    dataset,labels=createDataSet()
    print  chooseBestFeatToSplit(dataset)

跑出来的结果是 0 ,说明以第一个特征进行划分是最好的,也就是第一个特征是0的放在一组,是1的放在一组。 目测了一下,这的确是最好的划分方式。

3.递归创建决策树

#获取出现次数最多的类别
def getMostType(typeList):
    typeCounts={}            #每个类别有多少个
    for type in typeList:
        if type not in typeCounts.keys():
            typeCounts[type]=0
        typeCounts[type] +=1
    mostCount=0; mostType='';
    for type in typeCounts.keys():
        if typeCounts[type] > mostCount:
            mostCount=typeCounts[type]
            mostType=type
    return mostType

#递归创建
def createTree(dataSet,labels,indexOfType):
    typeList=[example[-1] for example in dataSet]
    #类别完全相同则停止划分
    if typeList.count(typeList[0]) == len(typeList):
        return typeList[0]
    if len(dataSet[0]) ==1:
        return getMostType(typeList)
    bestFeat = chooseBestFeatToSplit(dataSet)
    bestFeatLabel = labels[bestFeat]
    myTree={bestFeatLabel:{}}
    del(labels[bestFeat])
    featValues=[example[bestFeat] for example in dataSet]
    uniqueValues=set(featValues)
    for value in uniqueValues:
        subLabels = labels[:]
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels,indexOfType)
    return myTree


if __name__=="__main__":
    dataset,labels=createDataSet()
    print  createTree(dataset,labels,-1)

递归函数第一个停止的条件是所有类别完全相同,直接返回该类别。第二个停止条件是,所有的特征都用完之后,仍然不能将数据集划分为仅包含唯一类别的分组。


上述代码的输出为:

{'noSurfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

之后拿到新的数据,就可以利用这个结果来判断属于何种类别。可以将结果保存起来,达到一次计算,多次使用的效果。

你可能感兴趣的:(python,机器学习,决策树)