机器学习实战-决策树

2018-12-10  本文已影响0人  投篮手型差

决策树(DecisionTree)

优点:

缺点:

决策树算法主要有3个步骤:1.特征选择;2.构造决策树;3.决策树剪枝

构造决策树

构造过程需要解决以下几个问题:

可以发现,决策树过程是一个递归步骤,基线条件是:子集内所有数据都是同一类型;
关于递归,可以复习之前的笔记递归笔记


一般步骤


ID3算法

对于每次划分数据集,第一次划分的特征选择如何来选呢?需要引入以下几个概念来量化特征选择的过程:1.信息增益、2.香侬熵。

信息增益(information gain)

对于划分数据集的大原则就是:将无序变有序

所以,信息增益越大,说明用这个特征来划分前后数据集信息变化越大,即这个特征的区分度越高(这个特征带来的信息越多,越重要),适合用来作为划分数据集的特征。

关于信息熵及信息增益的公式如下:摘自《机器学习》和《统计学习方法》

信息熵 信息增益

信息增益也可以从概率的角度来理解,就是信息熵减去条件熵

image.png

计算信息熵

dataSet = [[1, 1, 'yes'],
           [1, 1, 'yes'],
           [1, 0, 'no'],
           [0, 1, 'no'],
           [0, 1, 'no']]
labels = ['不浮出水面是否生存','是否有蹼']

from math import log

def Entropy(dataSet):
    num = len(dataSet)
    labelCounts = {}
    for featVec in dataSet: 
        currentLabel = featVec[-1]
        labelCounts[currentLabel] = labelCounts.get(currentLabel,0) +1
        #统计类别出现的次数,使用get方法,出现加1,没有添加新类别+1
    entropy = 0.0
    for key in labelCounts:  
        prob = float(labelCounts[key])/num
        entropy -= prob * log(prob,2)
        #计算类别概率以及香侬熵
    return entropy

划分数据集

def SplitDataSet(dataSet,axis,value): 
    '''
    dataSet:待划分的数据集
    axis:用来划分数据集的特征(最佳划分特征)-特征在列表的位置,第一个第二个...分别对应0,1,2....
    value:特征返回值-特征的结果只能是0,1(是,否)-->(1,0)
    '''
    split_set = []#设置一个空list,用于存放划分结果
    for vector in dataSet:
        result_axis = vector[axis]
        if result_axis == value:
            tempV = vector[:]#copy,复制向量为了下一步pop方法时,数据集内容保持不变
            tempV.pop(axis)
            split_set.append(tempV)
    return split_set

选择最佳划分特征

idea:

def chooseBestFeatureToSplit(dataSet):
    '''
    根据信息增益来选择最佳特征的划分数据集
    '''
    featureNum = len(dataSet[0])-1#特征个数,list长度-1,最后一个是分类标签
    initEntropy = Entropy(dataSet)#初始信息熵
    infoGain = []
    conditionEntropy = 0
    for axis in range(featureNum):
        conditionEntropy = 0
        class_value = set([x[axis] for x in dataSet])#当前特征的类别集合
        for value in class_value:
            splitSet = SplitDataSet(dataSet,axis,value)
            prob = len(splitSet)/float(len(dataSet))
            conditionEntropy += prob*Entropy(splitSet)
            #计算条件熵
        splitInfoGain = initEntropy-conditionEntropy
        #一次划分后的信息增益
        infoGain.extend([splitInfoGain])
        gianMax = max(infoGain)
        infoGain.index(gianMax)
        print('the index of best feature to split is : %d'%infoGain.index(gianMax))
        return infoGain.index(gianMax)


递归构建决策树

递归结束的基线条件:

叶子结点/终止块:实例具有相同分类


决策树的步骤:1.从数据集中选择一个最佳特征,按其进行数据划分;2.如果划分的数据集类别相同,或者用来划分的特征已经用完,则无需再划分,否则重复1,对剩下的数据集进行划分。划分数据集方法是相同的,等于采用了递归方法。

idea:

def majorityCnt(classList):
    '''
    用来统计到达叶子结点的类别
    '''
    classCount={}
    for vote in classList:
        classCount[vote] = classCount.get(vote,0) +1 
    sortedClassCount = sorted(classCount.items(), key=lambda x:x[1], reverse=True)
    return sortedClassCount[0][0] #返回多数的那个类别


def creatTree(dataSet,labels):
    '''
    创建决策树
    dataSet:数据集
    labels:标签类别集
    '''
    classList = [x[-1] for x in dataSet]#叶子节点类别(yes/no)
    classListType = set(classList)
    if len(classListType) == 1:
        return classList[0]
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)
    #递归基线条件:特征用光/划分子集内类别都相同
    else:
        
        #划分数据集的过程
        bestFeature_index = chooseBestFeatureToSplit(dataSet)#最佳特征索引
        bestlabels = labels[bestFeature_index]#最好特征对应的名称
        
        #创建树结构
        myTree = {bestlabels:{}}
        
        #每使用一次最佳特征,特征数量-1
        subLabels=labels[:]
        del subLabels[bestFeature_index]
        
        #划分数据集
        featureType = set([x[bestFeature_index] for x in dataSet])#最好特征对应的列的类别
        for value in featureType:
            myTree[bestlabels][value] = creatTree(SplitDataSet(dataSet,bestFeature_index,value),subLabels)#最佳特征划分后对应的子集
    return myTree

书上代码跑完一次会清空labels,每次都需要重新读取数据集,不方便,原因是对labels数据集直接进行del操作

关于决策树画图的部分,用书上的代码太繁琐,可以参考调用sklearn的例子,唯一要考虑的就是将数据集处理成函数能接受的参数即可,效果如下。

dataSet数据集分类结果

小结


推荐阅读

上一篇 下一篇

猜你喜欢

热点阅读