机器学习(四)—决策树

  摘要:本文首先浅谈了自己对决策树的理解,进而通过Python一步步构造决策树,并通过Matplotlib更直观的绘制树形图,最后,选取相应的数据集对算法进行测试。

  最近在看《机器学习实战》这本书,因为一直想好好了解机器学习方面的算法,加之想学Python,就在朋友的推荐之下选择了这本同等定位的书。今天就来学习一下决策树,所有的代码均python3.4实现,确实与2.7有很多不同。

  决策树和KNN一样,都是处理分类问题的算法。对于决策树的定义不计其数,就我个人而言,首先单看名字,就想到了最小生成树,猜想图解的话这个算法会是一棵树,在机器学习这个层面,将所要处理的数据看做是树的根,相应的选取数据的特征作为一个个节点(决策点),每次选取一个节点将数据集分为不同的数据子集,可以看成对树进行分支,这里体现出了决策,直到最后无法可分停止,也就是分支上的数据为同一类型,可以想象一次次划分之后由根延伸出了许多分支,形象的说就是一棵树。

  在机器学习中,决策树是一个预测模型,它代表的是对象属性与对象值之间的一种映射关系,我们可以利用决策树发现数据内部所蕴含的知识,比如在本文的最后我们选取隐形眼镜数据集根据决策树学习到眼科医生是如何判断患者佩戴眼镜片的过程,而K近邻算法虽与决策树同属分类,却无从得知数据的内在形式。下面我们就一步步的学习决策树:

1. 构造决策树

  基于之前的了解,在构造决策树首先需要选取特征将原始数据划分为几个数据集,那么第一个问题就是当前数据的哪个特征在划分数据分类时起决定性作用,所以必须评估每个特征。进而通过特征将原始数据就被划分为几个数据子集,这些数据子集分布在第一个决策点的所有分支上,如果分支上的所有数据为同一类型,则划分停止,若分支上的所有数据不是同一类型,则还需要继续划分,直到所有具有相同类型的数据均在一个数据子集中。在用决策树进行划分时,关键是每次划分时选取哪个特征进行划分,在划分数据时,我们必须采用量化的方法判断如何划分数据。

(1)信息增益

  划分数据时是根据某一原则进行划分,使得划分在同一集合中的数据具有共同的特征,据此,我们可以理解为划分数据的原则就是是无序的数据变得有序。当然划分数据有很多种方法,在此选用信息论度量信息,划分组织杂乱无章的数据。

信息论是量化处理信息的分支科学,可以在数据划分之前或之后使用信息论量化度量信息的内容。其中在划分数据集之前之后信息发生的变化称为信息增益,计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择

首先我们需要知道怎么计算信息增益,集合信息的度量方式称为香农熵或者简称为熵,熵定义为信息的期望值,那么信息是什么?xi的信息可定义为:L(xi) = -log(p(xi)),其中p(xi)是选择该分类的概率。

熵指的是所有类别所有可能值包含的信息期望值,可表示为:

熵越高,表明混合的数据越多,则可以在数据集中添加更多的分类。基于上述的分析,编程计算给定数据集的香农熵,代码如下:

from math import log
###计算香农熵(为float类型)
def calShang(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}##创建字典
    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)
    return shannonEnt

对此我们可以输入数据集测试:

def creatDataSet():
    dataSet = [[1,1,‘yes‘],
               [1,1,‘yes‘],
               [1,0,‘no‘],
               [0,1,‘no‘],
               [0,1,‘no‘]]
    labels = [‘no surfacing‘,‘flippers‘]
    return dataSet,labels
‘‘‘
#测试
myData,labels = creatDataSet()
print("原数据为:",myData)
print("标签为:",labels)
shang = calShang(myData)
print("香农熵为:",shang)
‘‘‘

得到熵之后,我们就可以按照获取最大增益的办法划分数据集。

(2)划分数据集

  基于之前的分析,信息增益表示的是信息的变化,而信息可以用熵来度量,所以我们可以用熵的变化来表示信息增益。而获得最高信息增益的特征就是最好的选择,故此,我们可以对所有特征遍历,得到最高信息增益的特征加以选择。

  首先,我们按照给定特征划分数据集并进行简单的测试:

###划分数据集(以指定特征将数据进行划分)
def splitDataSet(dataSet,feature,value):##传入待划分的数据集、划分数据集的特征以及需要返回的特征的值
    newDataSet = []
    for featVec in dataSet:
        if featVec[feature] == value:
            reducedFeatVec = featVec[:feature]
            reducedFeatVec.extend(featVec[feature + 1:])
            newDataSet.append(reducedFeatVec)
    return newDataSet

‘‘‘
#测试
myData,labels = creatDataSet()
print("原数据为:",myData)
print("标签为:",labels)
split = splitDataSet(myData,0,1)
print("划分后的结果为:",split)
‘‘‘

接下来我们遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的划分方式并简单测试:

##选择最好的划分方式(选取每个特征划分数据集,从中选取信息增益最大的作为最优划分)在这里体现了信息增益的概念
def chooseBest(dataSet):
    featNum = len(dataSet[0]) - 1
    baseEntropy = calShang(dataSet)
    bestInforGain = 0.0
    bestFeat = -1##表示最好划分特征的下标

    for i in range(featNum):
        featList = [example[i] for example in dataSet] #列表
        uniqueFeat = set(featList)##得到每个特征中所含的不同元素
        newEntropy = 0.0
        for value in uniqueFeat:
            subDataSet = splitDataSet(dataSet,i,value)
            prob = len(subDataSet) / len(dataSet)
            newEntropy += prob * calShang(subDataSet)
        inforGain = baseEntropy - newEntropy
        if (inforGain > bestInforGain):
            bestInforGain = inforGain
            bestFeature = i#第i个特征是最有利于划分的特征
    return bestFeature

‘‘‘
##测试
myData,labels = creatDataSet()
best = chooseBest(myData)
print(best)
‘‘‘

(3)递归构建决策树

基于之前的分析,我们选取划分结果最好的特征划分数据集,由于特征很可能多与两个,因此可能存在大于两个分支的数据集划分,第一次划分之后,可以将划分的数据继续向下传递,如果将每一个划分的数据看成是原数据集,那么之后的每一次划分都可以看成是和第一次划分相同的过程,据此我们可以采用递归的原则处理数据集。递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都有相同的分类。编程实现:

##递归构建决策树
import operator
#返回出现次数最多的分类名称
def majorClass(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    #降序排序,可以指定reverse = true
    sortedClassCount = sorted(classcount.iteritems(),key = operator.itemgetter(1),reverse = true)
    return sortedClassCount[0][0]

#创建树
def creatTree(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 majorClass(classList)
    bestFeat = chooseBest(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] = creatTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree

‘‘‘
#测试
myData,labels = creatDataSet()
mytree = creatTree(myData,labels)
print(mytree)
‘‘‘

2.使用matplotlib注解绘制树形图

 之前我们已经从数据集中成功的创建了决策树,但是字典的形式非常的不易于理解,因此本节采用Matplotlib库创建树形图。

首先,使用文本注解绘制树节点:

##采用matplotlib绘制树形图
import matplotlib.pyplot as plt

decisionNode = dict(boxstyle="sawtooth", fc="0.8")
leafNode = dict(boxstyle="round4", fc="0.8")
arrow_args = dict(arrowstyle="<-")

 #绘制树节点
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    createPlot.ax1.annotate(nodeTxt, xy=parentPt,  xycoords=‘axes fraction‘,
             xytext=centerPt, textcoords=‘axes fraction‘,
             va="center", ha="center", bbox=nodeType, arrowprops=arrow_args )

获得叶节点的数目和树的层数,并进行测试:

##获取节点的数目和树的层数
def getNumLeafs(myTree):
numLeafs = 0
#firstStr = myTree.keys()[0]
firstSides = list(myTree.keys())
firstStr = firstSides[0]#找到输入的第一个元素
secondDict = myTree[firstStr]
for key in secondDict.keys():
if type(secondDict[key]) == dict:
numLeafs += getNumLeafs(secondDict[key])
else: numLeafs += 1
return numLeafs

def getTreeDepth(myTree):
maxDepth = 1
firstSides = list(myTree.keys())
firstStr = firstSides[0]#找到输入的第一个元素
#firstStr = myTree.keys()[0]
secondDict = myTree[firstStr]
for key in secondDict.keys():
if type(secondDict[key]) == dict:
thisDepth = 1 + getTreeDepth(secondDict[key])
else: thisDepth = 1
if thisDepth > maxDepth: maxDepth = thisDepth
return maxDepth

def retrieveTree(i):
listOfTrees =[{‘no surfacing‘: {0: ‘no‘, 1: {‘flippers‘: {0: ‘no‘, 1: ‘yes‘}}}},
{‘no surfacing‘: {0: ‘no‘, 1: {‘flippers‘: {0: {‘head‘: {0: ‘no‘, 1: ‘yes‘}}, 1: ‘no‘}}}}
]
return listOfTrees[i]

#测试
mytree = retrieveTree(0)
print(getNumLeafs(mytree))
print(getTreeDepth(mytree))

在此,我们说明一下Python2.7和3.4在实现本段代码的区别:

在2.7中,找到key所对应的第一个元素为:firstStr = myTree.keys()[0],这在3.4中运行会报错:‘dict_keys‘ object does not support indexing,这是因为python3改变了dict.keys,返回的是dict_keys对象,支持iterable 但不支持indexable,我们可以将其明确的转化成list,则此项功能在3中应这样实现:

firstSides = list(myTree.keys())
firstStr = firstSides[0]#找到输入的第一个元素

绘制树:

def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    createPlot.ax1.annotate(nodeTxt, xy=parentPt,  xycoords=‘axes fraction‘,
             xytext=centerPt, textcoords=‘axes fraction‘,
             va="center", ha="center", bbox=nodeType, arrowprops=arrow_args )

def plotMidText(cntrPt, parentPt, txtString):
    xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0]
    yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]
    createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)

def plotTree(myTree, parentPt, nodeTxt):
    numLeafs = getNumLeafs(myTree)
    depth = getTreeDepth(myTree)
    firstSides = list(myTree.keys())
    firstStr = firstSides[0]#找到输入的第一个元素
    cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff)
    plotMidText(cntrPt, parentPt, nodeTxt)
    plotNode(firstStr, cntrPt, parentPt, decisionNode)
    secondDict = myTree[firstStr]
    plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD
    for key in secondDict.keys():
        if type(secondDict[key]).__name__==‘dict‘:
            plotTree(secondDict[key],cntrPt,str(key))
        else:
            plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW
            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
    plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD

def createPlot(inTree):
    fig = plt.figure(1, facecolor=‘white‘)
    fig.clf()
    axprops = dict(xticks=[], yticks=[])
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plotTree.totalW = float(getNumLeafs(inTree))
    plotTree.totalD = float(getTreeDepth(inTree))
    plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0;
    plotTree(inTree, (0.5,1.0), ‘‘)
    plt.show()

#测试
mytree = retrieveTree(0)
print(mytree)
createPlot(mytree)

测试之后结果如下:   

这样相比于字典形式确实清晰了很多。

3.测试算法

在本章中,我们首先使用决策树对实际数据进行分类,然后使用决策树预测隐形眼镜类型对算法进行验证。

(1)使用决策树执行分类

在使用了训练数据构造了决策树之后,我们便可以将它用于实际数据的分类:

###决策树的分类函数,返回当前节点的分类标签
def classify(inputTree,featLabels,testVec):##传入的数据为dict类型
    firstSides = list(inputTree.keys())
    firstStr = firstSides[0]#找到输入的第一个元素
    ##这里表明了python3和python2版本的差别,上述两行代码在2.7中为:firstStr = inputTree.key()[0]
    secondDict = inputTree[firstStr]##建一个dict
    #print(secondDict)
    featIndex = featLabels.index(firstStr)#找到在label中firstStr的下标
    for i in secondDict.keys():
        print(i)

    for key in secondDict.keys():
        if testVec[featIndex] == key:
            if type(secondDict[key]) == dict:###判断一个变量是否为dict,直接type就好
                classLabel = classify(secondDict[key],featLabels,testVec)
            else:
                classLabel = secondDict[key]
    return classLabel   ##比较测试数据中的值和树上的值,最后得到节点

#测试
myData,labels = creatDataSet()
print(labels)
mytree = retrieveTree(0)
print(mytree)
classify = classify(mytree,labels,[1,0])
print(classify)

(2)使用决策树预测隐形眼镜类型

  基于之前的分析,我们知道可以根据决策树学习到眼科医生是如何判断患者需要佩戴的眼镜片,据此我们可以帮助人们判断需要佩戴的镜片类型。

  在此从UCI数据库中选取隐形眼镜数据集lenses.txt,它包含了很多患者眼部状况的观察条件以及医生推荐的隐形眼镜类型。我们选取此数据集,结合Matplotlib绘制树形图,进一步观察决策树是如何工作的,具体的代码如下:

fr = open(‘lenses.txt‘)
lenses = [inst.strip().split(‘\t‘) for inst in fr.readlines()]
lensesLabels = [‘ages‘,‘prescript‘,‘astigmatic‘,‘tearRate‘]
lensesTree = creatTree(lenses,lensesLabels)
print(lensesTree)
createPlot(lensesTree)

得到的树形图:

沿着决策树的不同分支,我们可以得到不同患者需要佩戴的隐形眼镜类型,从该图中我们可以得到,只需要问四个问题就可以确定出患者需要佩戴何种隐形眼镜。

本章主要使用的是ID3算法,自身也存在着很多不足。当然还有其它的决策树构造算法,比如C4.5和CART,以后有机会了再好好看看。

以上是我自己的一些理解与总结,难免有错,望大家不吝指教~

时间: 2024-11-05 16:11:59

机器学习(四)—决策树的相关文章

[转载]简单易学的机器学习算法-决策树之ID3算的

一.决策树分类算法概述 决策树算法是从数据的属性(或者特征)出发,以属性作为基础,划分不同的类.例如对于如下数据集 (数据集) 其中,第一列和第二列为属性(特征),最后一列为类别标签,1表示是,0表示否.决策树算法的思想是基于属性对数据分类,对于以上的数据我们可以得到以下的决策树模型 (决策树模型) 先是根据第一个属性将一部份数据区分开,再根据第二个属性将剩余的区分开. 实现决策树的算法有很多种,有ID3.C4.5和CART等算法.下面我们介绍ID3算法. 二.ID3算法的概述 ID3算法是由Q

【火炉炼AI】机器学习006-用决策树回归器构建房价评估模型

[火炉炼AI]机器学习006-用决策树回归器构建房价评估模型 (本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 ) 最近几十年,房价一直是中国老百姓心中永远的痛,有人说,中国房价就像女人的无肩带文胸,一半人在疑惑:是什么支撑了它?另一半人在等待:什么时候掉下去? 而女人,永不可能让它掉下来.就算快掉下来了,提一提还是又上去了..... 虽然我们不能预测中国房价什么时候崩盘,但是却可以用机器学

【Python数据挖掘课程】四.决策树DTC数据分析及鸢尾数据集分析

今天主要讲述的内容是关于决策树的知识,主要包括以下内容:        1.分类及决策树算法介绍        2.鸢尾花卉数据集介绍        3.决策树实现鸢尾数据集分析        前文推荐:       [Python数据挖掘课程]一.安装Python及爬虫入门介绍       [Python数据挖掘课程]二.Kmeans聚类数据分析及Anaconda介绍       [Python数据挖掘课程]三.Kmeans聚类代码实现.作业及优化        希望这篇文章对你有所帮助,尤其

机器学习---算法---决策树

转自:https://blog.csdn.net/qq_43208303/article/details/84837412 决策树是一种机器学习的方法.决策树的生成算法有ID3, C4.5和CART等.决策树是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果.决策树是一种十分常用的分类方法,需要监管学习(有教师的Supervised Learning),监管学习就是给出一堆样本,每个样本都有一组属性和一个分类结果,也就是分类结果已

机器学习技法-决策树和CART分类回归树构建算法

课程地址:https://class.coursera.org/ntumltwo-002/lecture 重要!重要!重要~ 一.决策树(Decision Tree).口袋(Bagging),自适应增强(AdaBoost) Bagging和AdaBoost算法再分类的时候,是让所有的弱分类器同时发挥作用.它们之间的区别每个弱分离器是否对后来的blending生成G有相同的权重. Decision Tree是一种有条件的融合算法,每次只能根据条件让某个分类器发挥作用. 二.基本决策树算法 1.用递

[机器学习&amp;数据挖掘]机器学习实战决策树plotTree函数完全解析

在看机器学习实战时候,到第三章的对决策树画图的时候,有一段递归函数怎么都看不懂,因为以后想选这个方向为自己的职业导向,抱着精看的态度,对这本树进行地毯式扫描,所以就没跳过,一直卡了一天多,才差不多搞懂,才对那个函数中的plotTree.xOff的取值,以及计算cntrPt的方法搞懂,相信也有人和我一样,希望能够相互交流. 先把代码贴在这里: import matplotlib.pyplot as plt #这里是对绘制是图形属性的一些定义,可以不用管,主要是后面的算法 decisionNode

机器学习二 -- 决策树学习

决策树学习 从今天开始,坚持每天学习一个机器学习的新知识,加油! 决策树学习是应用最广的归纳推理算法之一,是一种逼近离散值目标函数的方法,在这种方法中学习到的函数被表示为一颗决策树. 决策树表示法 决策树通过把实例从根结点排列到某个叶子结点来分类实例,叶子结点即为实例所属的分类.树上的每一个结点指定了对实例的某个属性的测试,并且该结点的每一个后继分支对应于该属性的一个可能值.分类实例的方法是从这棵树的根节点开始,册数这个结点指定的属性,然后按照给定实例的该属性对应的树枝向下移动,然后这个过程再以

机器学习四 -- 基于概率论的分类方法:朴素贝叶斯

基于概率的分类方法:朴素贝叶斯 贝叶斯决策理论 朴素贝叶斯是贝叶斯决策理论的一部分,所以在讲解朴素贝叶斯之前我们先快速简单了解一下贝叶斯决策理论知识. 贝叶斯决策理论的核心思想:选择具有最高概率的决策.比如我们毕业选择就业方向,选择C++方向的概率为0.3,选择Java的概率为0.2,选择机器学习的概率为0.5.那么我们就把这样的一位毕业生就业方向归类为机器学习方向. 条件概率 什么是条件概率?事件A在另一个事件B已知发生条件下的发生概率,记为P(A|B),读作“在B条件下A的概率”. 例子1:

机器学习之决策树(一)

1.算法介绍决策树是一种基本的分类和回归方法,决策树模型呈树形结构,在分类问题中,表示基于特征对实例进行分类的过程.决策树学习通常包括三个步骤:特征选择.决策树的生成和决策树的修剪.决策树的本质是从训练数据集中归纳出一组分类规则.本文主要是对决策树的ID3算法的介绍,后文会介绍C4.5和CART算法. 2.算法优缺点优点:计算复杂度不高,结果易于理解,对于中间值的缺失不敏感,可以处理不相关特征.缺点:可能会产生过拟合问题.适用于标称型(ID3和C4.5)和数值型(CART算法) 3.特征选择熵:

机器学习_02_决策树

决策树也是一种基础的机器学习模型 比如预测今天小明是否出去打球, 那么我们知道一些特征, 通过对特征的划分,我们可以做出一颗树, 就是决策树, 其实决策树在管理学也用的很多, 主要是对每种情况给出一个概率,然后判断情况的优劣, 这样我们可以通过这棵树来判断当前的情况 如何判断哪个特征进行划分呢, 我们用熵来划分,通过信息增益我们可以选出最优的特征来进行划分. 熵是不确定性的度量, 熵越大, 不确定性越大, 否则越小 ID3算法 对当前的样本集合, 计算所有的信息增益 选择信息增益最大的属性作为测