决策树

来源:互联网 发布:淘宝cos店便宜 编辑:程序博客网 时间:2024/06/05 14:17

一 、什么是决策树?

决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。

决策树的任务是为了理解数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,并创建规则。

二 、决策树的构建

(1)计算信息增益

划分数据集的最大原则是:将无序的数据变的更加有序;所以我们要计算信息增益。

信息增益:划分数据集之前、之后信息发生的变化,我们计算每个特征值划分的数据集获得熵,与原始的数据熵相比获得增量。信息增量最高的特征就是最好的选择。

信息熵的计算公式为:

其中 D 表示训练数据集,c 表示数据类别数,Pi 表示类别 i 样本数量占所有样本的比例。

数据集样例

Python代码实现如下(计算给定数据集的信息熵):

def calcShannonEnt(dataSet):    numEntries = len(dataSet)#数组长度5    labelCounts = {}    for featVec in dataSet:         #print featVec 分别取出每组数组[1, 1, 'yes'],[1, 1, 'yes'],[1, 0, 'no'],[0, 1, 'no'],0, 1, 'no']        currentLabel = featVec[-1]#取出数组中最后一个元素,yes or no        if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0        labelCounts[currentLabel] += 1#统计yes and no标签的总数    shannonEnt = 0.0    #print labelCounts {'yes': 2, 'no': 3}    for key in labelCounts:      prob = float(labelCounts[key])/numEntries#计算yes和no标签分别占有的比例      shannonEnt -= prob * log(prob,2) #log base 2    return shannonEnt
接下来我们建立测试数据的函数:
def createDataSet():    dataSet = [[1, 1, 'yes'],               [1, 1, 'yes'],               [1, 0, 'no'],               [0, 1, 'no'],               [0, 1, 'no']]    labels = ['no surfacing','flippers']    #change to discrete values    return dataSet, labels
依次输入import tree,myDat,labels=tree.createDataSet(),tree.calcShannonEnt(myDat)测试结果:大概为0.97

证明划分数据集前的信息熵为0.97,熵越高,信息的混合数据越高。

(2)划分数据集

计算出数据集的无序程度后,我们接下来应该划分数据集,然后度量划分数据集的熵。

我们要对每个分类特征划分数据集的结果计算一次信息熵,然后判断哪个特征划分的数据集是最好的划分。代码如下:

def splitDataSet(dataSet, axis, value):    retDataSet = []    for featVec in dataSet:        #print featVec[axis] 取出第axis列数据        if featVec[axis] == value:            #print featVec[:axis] 选取分割轴            reducedFeatVec = featVec[:axis]     #chop out axis used for splitting            reducedFeatVec.extend(featVec[axis+1:])            #print reducedFeatVec[1, 'yes'] [1, 'yes'] [0, 'no']            retDataSet.append(reducedFeatVec)    return retDataSet
其中三个输入的参数分别为数据集,特征(每一列代表一个分类特征),返回的特征值(0或1)

我们用刚才的数据集测试,输入tree.splitDataSet(myDat,0,1),tree.splitDataSet(myDat,0,0)分别计算第一列数据中是,否水面可以生存对应的情况。

(3)选择最好的数据集划分方式

当我们切割好每种特征类别是与否的情况后,我们分别计算每种分类的信息熵,和原始信息熵比较,计算最好的信息增益,作为我们要选取的分类。代码如下:

def chooseBestFeatureToSplit(dataSet):    numFeatures = len(dataSet[0]) - 1      #the last column is used for the labels    baseEntropy = calcShannonEnt(dataSet)    bestInfoGain = 0.0; bestFeature = -1    for i in range(numFeatures):        #iterate over all the features        featList = [example[i] for example in dataSet]#create a list of all the examples of this feature        #print featList得到每一列(每一类)中的元素        uniqueVals = set(featList)       #得到每一列中不同的特征值set([0, 1])        newEntropy = 0.0        for value in uniqueVals:            subDataSet = splitDataSet(dataSet, i, value) #看不同分类下每个特征值的结果            prob = len(subDataSet)/float(len(dataSet))            newEntropy += prob * calcShannonEnt(subDataSet)              #print newEntropy   计算每一类中的新熵值        infoGain = baseEntropy - newEntropy     #计算信息增益,越大越好,代表该分类越明显        #print infoGain        if (infoGain > bestInfoGain):       #compare this to the best gain so far            bestInfoGain = infoGain         #if better than current best, set to best            bestFeature = i    return bestFeature                      #returns an integer

结果表明第0列,也就是不浮出水面是否能生存更适合作为我们的分类标准。

(4)构建决策树

其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,第一次划分后,数据将向下传递到树分支的下一个节点,在这个节点上我们再次划分数据。我们可以采用递归的原则处理数据集。

递归结束的条件为(1)程序遍历完所有划分数据集的属性;(2)每个分支下的实例都有相同的分类

Python代码如下:

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]def createTree(dataSet,labels):    classList = [example[-1] for example in dataSet]  #classList统计分类结果,Yes和No的数量    if classList.count(classList[0]) == len(classList):         return classList[0]#classList.count(classList[0])统计某一类别的数量,类别相同则停止分类    if len(dataSet[0]) == 1:         return majorityCnt(classList) #遍历完所以特征,返回出现次数最多的类别    bestFeat = chooseBestFeatureToSplit(dataSet)        bestFeatLabel = labels[bestFeat]  #print labels[bestFeat]为特征标签    myTree = {bestFeatLabel:{}}    del(labels[bestFeat])   #删除已经构建在树节点上的特征标签    featValues = [example[bestFeat] for example in dataSet] #print featValues(1,1,1,0,0)    uniqueVals = set(featValues)  #print uniqueVals set(1,0)    for value in uniqueVals:        subLabels = labels[:]           #print   subLabels  #copy all of labels, so trees don't mess up existing labels        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)    return myTree   
输出结果为:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

至此,完成决策树的构建。

(5)使用决策树构建分类函数

def classify(inputTree,featLabels,testVec):# 决策树分类函数    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#返回分类标签

(6)决策树的存储

但如果每次构建决策树是很耗时的任务,会花费很多计算空间,所以我们调用创建好的分类将会节省时间。

为此我们使用Python模块pickle序列化对象,并将序列化后的结果保存在磁盘上,在需要的时候都出来即可。

def storeTree(inputTree,filename):    import pickle   #导入模块pickle序列化对象    fw = open(filename,'w')    pickle.dump(inputTree,fw)#决策树序列化     fw.close()    def grabTree(filename):    import pickle    fr = open(filename)    return pickle.load(fr)#从文件中读取决策树,就不用每次每次构建,在需要的时候读取即可

三 、小结

决策树分类器就像带有终止块的流程图,终止块表示分类结果。开始处理数据时,我们首先测量数据的不一致性,也就是熵,最后选择最优的划分数据集的分类特征,使所有数据属于不同的分类中。ID3(决策树)算法用于划分布尔型数据,构建决策树时,我们通常采用递归方法将数据集(使用Python字典存储树的节点信息)转换为决策树。         

此外,我们还能使用Matplotlib的注释功能,将存储的树结构转换为容易理解的图形。Pickle模块则用来存储决策树树的结构。