记>决策树

决策树

决策树的概念:

决策树算法是一种基本的分类与回归方法。在下面举例的两个问题中决策树算法用于分类。

官方课本化点的说辞为:

分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点和有向边组成。结点有两种类型:内部结点和叶结点。内部结点表示一个特征或属性,叶结点表示一个类。

用决策树对需要测试的实例进行分类:从根节点开始,对实例的某一特征进行测试,根据测试结果,将实例分配到其子结点;这时,每一个子结点对应着该特征的一个取值。如此递归地对实例进行测试并分配,直至达到叶结点。最后将实例分配到叶结点的类中。

简单点理解也就是:方法本身如其名,就是不断的决策,就像树一样,会有枝干分叉,不断分叉不断递进直到无法再分叉的叶,叶子就相当于最后的决策类型。最后得出结果。

其他相关:

熵: 熵指的是体系的混乱的程度。
信息论中的熵: 是一种信息的度量方式,表示信息的混乱程度。也就是香农熵。
信息增益: 在划分数据集前后信息发生的变化称为信息增益。

感觉和决策树没有什么关系的几个概念,十分重要。

一些需要在后面找到答案的问题:

1.对于不同的特征如何进行合理的选取,使最后分类结果正确性高?
2.不同特征在不同的分叉点的位置的影响有多大?如果有影响,怎样的放置能使最后分类结果正确性高?
3.决策树算法的核心是什么?

相关实例:

一、判定鱼类和非鱼类

想要处理的问题:

根据两个有关特征,将动物分为两类:鱼类和非鱼类

关于问题的解决:

①数据的收集:
dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 1, 'no']]
labels = ['no surfacing', 'flippers']
#已知的五组数据以及两个特征
②树构造算法(这里使用的是ID3算法,因此数值型数据必须离散化。)

什么是ID3算法?为什么数值型数据必须离散化?

关于ID3算法,先不细想。

③分析数据,可以把树状图画出来
④计算给定数据集的香农熵
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    #下面这个for循环加上if条件语句用于计数,计算数据集中是鱼类与不是鱼类的数量,构成字典。
    所以结果格式为:{'yes':int ,'no',int}与数据集信息有关。
    for featVec in dataSet:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    shannonEnt = 0.0 
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries #计算不同标签出现的频率
        shannonEnt -= prob * log(prob,2) #香农熵的计算公式,以2为底求对数

  return shannonEnt
⑤划分数据集
def splitDataSet(dataSet,index,value):
    retDataSet = []
    for featVec in dataSet:
        if featVec[index] == value: #以一个特征为条件将数据集进行划分。就相当于对树进行分叉。
            reducedFeatVec = featVec[:index]
            reducedFeatVec.extend(featVec[index+1:])
            retDataSet.append(reducedFeatVec)
            #结果格式如下:
            [[1, 'no'], [1, 'no']]
            [[1, 'yes'], [1, 'yes'], [0, 'no']]

    return retDataSet
⑥选择数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1 //特征数
    baseEntropy = calcShannonEnt(dataSet) //基本香农熵
    bestInfoGain,bestFeature = 0.0,-1  
    #以下两个for循环为对单列特征进行操作,第一个for循环为将单个特征的所有值取出,第二个for循环为对单列值对香农熵,为此定义了一个newEntropy.
    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)
        infoGain = baseEntropy - newEntropy
        print("infoGain=",infoGain,'bsetFeature=',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 = {bestFeatLabel:{}}
    del(labels[bestFeat]) //把已经决策了一遍选出的最好的特征去除,即分叉了一次,而后继续进行决策。 
    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)
    return myTree
#利用字典套字典的格式模仿树的结构,以此便于后面决策的运行顺利。
#例:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
⑧运用决策树进行分类
def classify(inputTree,featLabels,testVec):#createTree得到的inputTree已经将最好的决策顺序得到了,此时将测试数据代入进行分类测试
    firstStr = inputTree.keys()[0]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    key = testVec[featIndex]
    valueOfFeat = secondDict[key]
    if isinstance(valueOfFeat,dict):
        classLabel = classify(valueOfFeat,featLabels,testVec)
    else:
        classLabel = valueOfFeat
        return classLabel
⑨测试运行及存储决策树
def fishTest():
    myData,labels = createDataSet()
    myTree = createTree(myData,copy.deepcopy(labels))
    print(myTree)
    print(classify(myTree,labels,[0,1]))

#存储决策树(相关库:pickle)
def storeTree(inputTree, filename):
    import pickle
    fw = open(filename, 'wb')
    pickle.dump(inputTree, fw) //序列化对象,并将结果数据流写入到文件对象中。
    fw.close()

def grabTree(filename):
    import pickle
    fr = open(filename, 'rb')
    return pickle.load(fr)  //反序列化对象。将文件中的数据解析为一个Python对象。

关于上面提出的问题的想法:

关于问题一:对于不同的特征如何进行合理的选取,使最后分类结果正确性高?

A:特征的选取在决策树运行都是能运行的,合理的选取需要考虑实际问题,在算法中主要考虑数据量的大小以及数据是否齐全。在算法构建时要考虑数据的特殊性,把可能性的结果都构想进去,不然算法会报错无法运行。

关于问题二:不同特征在不同的分叉点的位置的影响又大多?如果有影响,怎样的放置能使最后分类的结果正确性高?

A:不同特征在不同的分叉点的位置有较大影响,在上面步骤中的第⑥歩会计算每个特征值单独的香农熵,以此选取出最好的数据集划分方式,也就是决策时的考虑特征的顺序。

关于问题三:决策树算法的核心是什么?

A:ID3算法。也就是计算香农熵以及信息增益。

ID3算法

基本原理及概念:

ID3算法是一种贪心算法,用来构造决策树。就如上面解决问题的想法一样,通过计算每个属性的信息增益(信息增益反映的给定一个条件以后,不确定性减少的程度。),认为信息增益高的是好属性,每次划分选取信息增益最高的属性为划分标准,重复这个过程,直至生成一个能完美分类训练样例的决策树。

缺点:

1.只能处理离散型数据。
……接触时间不长,更多缺点有待后续更新发现。

猜你喜欢

转载自blog.csdn.net/doordiev/article/details/81204028