机器学习(三)——决策树
目录
什么是决策树
决策树的节点
决策树的特点
决策树的基本算法
决策树的划分选择
如何选择最优划分属性
方法一:信息增益
信息熵的python代码实现:
用信息增益划分最优属性的python代码:
方法二:增益率
小总结
方法三:基尼指数
决策树相关代码理解
递归创键决策树:
按照给定特征值划分数据集:
统计并返回出现次数最多的类别:
创建决策树:
实现决策树可视化:
输出结果:
决策树的储存:
测试:
我的实验:
总结:
利用信息增益生成决策树:
利用增益率生成决策树:
利用基尼指数生成决策树:
什么是决策树
分类决策树模型是一种描述对实例进行分类的树形结构,它由节点和有向边组成
决策树的节点
分为内部节点和叶节点
内部节点——代表一个特征或属性
叶节点——代表一个类
如下图 ,黑色的为内部节点,红色的为叶节点:
决策树的特点
1.对属性进行“测试”即对就决策过程中的问题进行判定
2.决策树上的每条路径都是一个判定测试序列
决策树的最终学习目的是为了产生一颗泛化能力强即能够有效处理未知样本的决策树
决策树的基本算法
注意:当某一判定测试序列的结果为空(无对应样本),将当前节点标记为叶子节点并进行回溯——回到父节点,以父节点的测试结果为准
决策树的划分选择
优秀的决策树应该具备优秀的划分属性、高纯度的节点
如何选择最优划分属性
方法一:信息增益
信息熵——度量样本集合纯度的指标之一,信息熵(Ent(D))的值越小,则样本集合(D)的纯度越高,Ent(D)最小为0,最大值为log2|y|
Ent(D)计算公式:
PS:Pk为第k类样本在样本集合D中所占的比例
当p=0或p=1时,样本集合纯度百分百。
原因:
信息熵的python代码实现:
该函数可计算数据集的香农熵,将数据集的最后一列设为键值,并为所有可能的分类创建字典,其中若发现新分类(新键值),则扩展字典并加入新键值。
def calcShannonEnt(dataSet):numEntries = len(dataSet)labelCounts = {}#字典for featVec in dataSet: #the the number of unique elements and their occurancecurrentLabel = featVec[-1]#扩展字典添加新键值if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1#统计键值出现的次数shannonEnt = 0.0for key in labelCounts:prob = float(labelCounts[key])/numEntries#统计某一键值出现频率shannonEnt -= prob * log(prob,2) #log base 2return shannonEnt
信息增益基于信息熵(Ent(D))
用信息增益划分最优属性的python代码:
以下代码为信息增益公式的代码形式,是实现信息增益划分属性的核心:
baseEntropy = calcShannonEnt(dataSet)#得到总熵值
#得到每种决策属性的比例
prob = len(subDataSet)/float(len(dataSet))
#得到各个特征值对应的熵
newEntropy += prob * calcShannonEnt(subDataSet)
从下列代码我学习到了:
1.python的列表推导式:[表达式 for 变量 in 列表];形如:[example[i] for example in dataSet]
以行的形式遍历数据集,并取出第i列;
2.利用set集合的性质取出列表中的重复元素,得到某一属性的分类标签列表。
def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #the last column is used for the labelsbaseEntropy = calcShannonEnt(dataSet)#得到总熵值bestInfoGain = 0.0; bestFeature = -1for i in range(numFeatures): #iterate over all the featuresfeatList = [example[i] for example in dataSet]#create a list of all the examples of this feature#利用set内值不重复的性质得到某一属性的分类标签列表uniqueVals = set(featList) #get a set of unique valuesnewEntropy = 0.0#遍历特征值得到相应的数据集for value in uniqueVals:subDataSet = splitDataSet(dataSet, i, value)#得到每种决策属性的比例prob = len(subDataSet)/float(len(dataSet))#得到各个特征值对应的熵newEntropy += prob * calcShannonEnt(subDataSet)#计算信息增益 infoGain = baseEntropy - newEntropy #calculate the info gain; ie reduction in entropyif (infoGain > bestInfoGain): #compare this to the best gain so farbestInfoGain = infoGain #if better than current best, set to bestbestFeature = i#返回最优划分属性的索引值return bestFeature #returns an integer
ID3决策树学习算法就是以信息增益为准则来划分属性的算法
特点:一般而言,信息增益越大,则意味着该属性越适合作为划分属性;信息增益对可取值数目较多的属性有所偏好。
方法二:增益率
IV(a):属性a的固有值,属性a的可能取值数目越大,IV(a)的值通常越大
增益率基于IV(a)
公式如下:
特点:增益率准则对可取数目较少的属性有所偏好。
小总结
以上两种方法各有特点亦为缺点,故可采用一个启发式方法:先从候选划分属性中找到信息增益高于平均水平的属性,再从中选取增益率最大的。
方法三:基尼指数
分类问题中,设D中有K个类,样本点属于第K类的概率为Pk,则概率分布的基尼值定义为:
给定数据集D,属性a的基尼指数定义为:
故知Gini(D)越小,数据集D的纯度越高,因此在选择划分属性时,选择那个使得划分后基尼指数最小的属性作为最优划分属性。
决策树相关代码理解
递归创键决策树:
通过一些方法得到最优划分属性后,我们需要建造决策树;一棵决策树里包含很多分支,为了得到这些分支,我们需要一个可以按照给定特征值划分数据集的函数,如下列代码:
按照给定特征值划分数据集:
def splitDataSet(dataSet, axis, value):retDataSet = []for featVec in dataSet:if featVec[axis] == value:
#判断featVec数组的第axis个是否等于valuereducedFeatVec = featVec[:axis]
#从0到axis #chop out axis used for splittingreducedFeatVec.extend(featVec[axis+1:])
#从axis到最后 所以reducedFeatVec就是一列除去特征值的数组retDataSet.append(reducedFeatVec)
#retDataSet就是符合条件featVec[axis] == value:并所有除去特征值的数组的集合return retDataSet
我学到的点:
1.代码中extend函数的作用是直接将需要添加的部分拆解后逐个扩展在列表最后:
>>>a=[1,2,3]
>>>b=[4,5,6]
>>>a.extend(b)
>>>a
[1,2,3,4,5,6]
而append函数的作用是把需要添加的部分完整地以列表形式添加在列表最后:
>>>a=[1,2,3]
>>>b=[4,5,6]
>>>a.append(b)
>>>a
[1,2,3,[4,5,6]]
2. featVec[:axis]代表featvec数组从头到第axis个元素;featVec[axis:]代表featvec数组从第axis个元素到尾;featVec[-1]代表数组的最后一个元素。
统计并返回出现次数最多的类别:
在创建决策树时,若遇到无特征可继续划分的情况,将该节点标记为叶子节点,并将其类别标记为出现样本数中最多的类,这时我们需要一个可以统计样本出现次数的函数;这个函数需要具备创建字典,储存、统计字典内每个类标签出现的频率并进行排序,返回频率最高的类别标签,代码如下:
#返回出现次数最多的类别名称
def majorityCnt(classList):classCount={}for vote in classList:if vote not in classCount.keys(): classCount[vote] = 0classCount[vote] += 1sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)return sortedClassCount[0][0]
我学到的点:
1、可以利用operator操作键值排序字典。
创建决策树:
决策树中最基本的元素就是标签和数据集,其为以下函数的输入参数,该函数用到了列表推导式取出dateSet的最后一列(所有的样本值),目的是判别何时进行叶节点的创建(也就是何时停止函数的递归调用),其中有两个停止条件:一、当样本类别完全相同则停止并将该类别标记为该叶节点的类别;二、若已经没有特征可以继续划分样本集,则对剩余样本集进行统计,将出现次数最多的样本类别标记为该叶节点的类别,这就用到了上面的majorityCnt(classList)函数。
def createTree(dataSet,labels):#遍历取出数据集的最后一列,即取出每条数据的类别 [example[-1]代表每行的的最后一个元素classList = [example[-1] for example in dataSet]#若classList[0]类别的个数等于总类别个数,则代表每条数据的类别都相等(类别完全相同)if classList.count(classList[0]) == len(classList): #返回该类别,停止继续划分return classList[0]#stop splitting when all of the classes are equal#若dateset的每条数据只剩一个元素则代表已经没有特征可以继续划分if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet#返回出现次数最多的类别return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet)#获取最优划分属性bestFeatLabel = labels[bestFeat]#最优划分属性的标签myTree = {bestFeatLabel:{}}del(labels[bestFeat])#取出最优划分属性对应的所有的值featValues = [example[bestFeat] for example in dataSet]#利用set内值不重复的性质得到最优划分属性对应的的互不相同的值uniqueVals = set(featValues)for value in uniqueVals:subLabels = labels[:] #copy all of labels, so trees don't mess up existing labels# print(subLabels)#递归调用createTree函数 splitDataSet(dataSet, bestFeat, value)为符合条件并除去特征值的所有数组的集合myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)return myTree
以上代码就是根据树逐层规律深入的特点进行函数的递归调用,同样用到了set集合内元素不重复的特点对featVals列表进行去重 ,featVals列表保证了后续分支的创建。
实现决策树可视化:
以下代码是用来实现决策树的可视化:
getnumleafs()函数和gettreedepth()函数是获取叶节点的个数和决策树的层数,在结构和方法上都比较类似,同样用到了递归调用的思想,在每次循环中都先找到该字典中的第一个字符,然后判断在该键值下的第二个字符是不是还是键,是的话说明还有深度或者层数,不是的话说明已经找完了;
遇到的问题:TypeError: 'dict_keys' object does not support indexing
解决:是版本的问题,2.x的版本中是firststr=mytree.keys()[],但是dict_keys型的数据不支持索引,所以强制转换成list即可,即 firststr=list(mytree.keys())[0]。
然后还有一些是用来生成决策树图形的函数。
'''
Created on Oct 14, 2010@author: Peter Harrington
'''
import matplotlib.pyplot as pltdecisionNode = dict(boxstyle="sawtooth", fc="0.8")
leafNode = dict(boxstyle="round4", fc="0.8")
arrow_args = dict(arrowstyle="<-")def getNumLeafs(myTree):numLeafs = 0firstStr = list(myTree.keys())[0]secondDict = myTree[firstStr]for key in secondDict.keys():if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodesnumLeafs += getNumLeafs(secondDict[key])else: numLeafs +=1return numLeafsdef getTreeDepth(myTree):maxDepth = 0firstStr = list(myTree.keys())[0]secondDict = myTree[firstStr]for key in secondDict.keys():if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodesthisDepth = 1 + getTreeDepth(secondDict[key])else: thisDepth = 1if thisDepth > maxDepth: maxDepth = thisDepthreturn maxDepthdef 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):#if the first key tells you what feat was split onnumLeafs = getNumLeafs(myTree) #this determines the x width of this treedepth = getTreeDepth(myTree)firstStr = list(myTree.keys())[0] #the text label for this node should be thiscntrPt = (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.totalDfor key in secondDict.keys():if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodes plotTree(secondDict[key],cntrPt,str(key)) #recursionelse: #it's a leaf node print the leaf nodeplotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD
#if you do get a dictonary you know it's a tree, and the first element will be another dictdef createPlot(inTree):fig = plt.figure(1, facecolor='white')fig.clf()axprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #no ticks#createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses 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()#def createPlot():
# fig = plt.figure(1, facecolor='white')
# fig.clf()
# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses
# plotNode('a decision node', (0.5, 0.1), (0.1, 0.5), decisionNode)
# plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode)
# plt.show()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]#createPlot(thisTree)
输出结果:
在生成图形化决策树的过程中还会出现一个问题:ValueError: 'no surfacing' is not in list
原因:在调用createTree函数时对labels里的标签进行回删,导致labels标签不完整
解决:再次新建,补全labels数组。
决策树的储存:
利用pickle模块存储决策树:
def storeTree(inputTree,filename):import picklefw = open(filename,'wb')pickle.dump(inputTree,fw)fw.close()
def grabTree(filename):import picklefr = open(filename,'rb')return pickle.load(fr)
遇到的问题:TypeError: write() argument must be str, not bytes
原因:Python3给open函数添加了名为encoding的新参数,而这个新参数的默认值却是‘utf-8’。这样在文件句柄上进行read和write操作时,系统就要求开发者必须传入包含Unicode字符的实例,而不接受包含二进制数据的bytes实例。
解决方法:使用二进制写入模式(‘wb’)来开启待操作文件,而不能像原来那样,采用字符写入模式(‘w’)。
fw = open(filename,'w')
改为
fw = open(filename,'wb')
同样的,文件读取数据的时候也有类似的问题。解决这种问题的办法也相似:用'rb'模式(二进制模式)打开文件。
测试:
从txt文本中读取数据并生成决策树,以下为调用过程:
fr=open('lenses.txt')
lenses=[inst.strip().split('\t') for inst in fr.readlines() ]
lensesLabels=['age','prescript','astigmatic','taerRate']
lensesTree = trees.createTree(lenses,lensesLabels)
treePlotter.createPlot(lensesTree)
结果:
如图所示,我们最多只要问四个问题就能确定需要佩戴哪种隐形眼镜,上面这颗决策树完整地匹配了数据,但某些节点的匹配结果太多了,产生了许多非必要的叶子节点,为了减少这种现象的出现,我们需要剪裁决策树,这需要进行更深一步的学习。
我的实验:
我参考了某平台的奶茶订单月售量收集了100条数据,六个属性分别是:知名度,价格,地理位置 ,是否赠送准时宝,是否连锁,有无特色;决策属性为是否畅销。
将其转换为代码可读的milkTea.txt:
输出结果:
文本形式:
{'chain': {'no': {'distinctive': {'no': {'price': {'20+': 'popular','10-': {'location': {'good': {'free insurance': {'popular': 'popular','unpopular': 'unpopular'}},'bad': {'free insurance': {'popular': 'popular','unpopular': 'unpopular'}}}},'10-20': 'unpopular'}},'no ': 'unpopular','yes': {'free insurance': {'no': {'price': {'20+': 'unpopular','10-20': {'location': {'popular': 'popular','unpopular': 'unpopular'}}}},'yes': 'popular'}}}},'yes': {'distinctive': {'no': {'profile': {'known': {'price': {'20+': 'popular','10-20': {'location': {'good': {'free insurance': {'popular': 'popular','unpopular': 'unpopular'}},'bad': {'free insurance': {'popular': 'popular','unpopular': 'unpopular'}}}}}},'un': 'popular'}},'yes': {'profile': {'known': 'popular', 'un': 'unpopular'}}}}}}
可视化:
可以发现这颗决策树有一些问题,我也找不到原因,或许是代码中使用的算法是ID3,还不够完美,而如果存在太多特征划分,就会出现问题,也或许是我的txt文件排版问题,反正目前是还没解决,希望在接下来的学习中能有机会解决它,而且这颗决策树会有过度匹配(overfitting)的问题,解决方法是剪裁决策树,去掉一些不必要的叶子节点,比如叶子节点只能增加少许信息,则可以删除该节点,将其并入到其他叶子节点中。
总结:
利用信息增益生成决策树:
缺点:信息增益偏向取值较多的特征
原因:当特征的取值较多时,根据此特征划分更容易得到纯度更高的子集,因此划分之后的熵更低,由于划分前的熵是一定的,因此信息增益更大,因此信息增益比较 偏向取值较多的特征。
利用增益率生成决策树:
缺点:信息增益比偏向取值较少的特征
原因: 当特征取值较少时HA(D)的值较小,因此其倒数较大,因而信息增益比较大。因而偏向取值较少的特征
综上:以上两种方法各有特点亦为缺点,故可采用一个启发式方法:先从候选划分属性中找到信息增益高于平均水平的属性,再从中选取增益率最大的。
利用基尼指数生成决策树:
这个方法没有什么特别的偏向
特点:
对于一个具有多个取值(超过2个)的特征,需要计算以每一个取值作为划分点,对样本D划分之后子集的纯度Gini(D,Ai),(其中Ai 表示特征A的可能取值), 然后从所有的可能划分的Gini(D,Ai)中找出Gini指数最小的划分,这个划分的划分点,便是使用特征A对样本集合D进行划分的最佳划分点。
机器学习(三)——决策树相关推荐
- gini系数 决策树_案例7:机器学习--使用决策树实现泰坦尼克号乘客生存率预测...
一.决策树简介 1.1 什么是决策树? 决策树:是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果,本质是一颗由多个判断节点组成的树. ...
- 机器学习三个部分:输入、算法、输出 资料收集
机器学习三个部分:输入.算法.输出. 输入:驱动机器学习的数据 输入是训练和算法需要的数据集.从源代码到统计数据,数据集可以包含任何东西: GSA / data(美国总务管理局数据):https:// ...
- 机器学习中决策树的随机森林_决策树和随机森林在机器学习中的使用
机器学习中决策树的随机森林 机器学习 (Machine Learning) Machine learning is an application of artificial intelligence ...
- 机器学习之决策树原理
机器学习之决策树原理 1 决策树简介 2 数学知识 ① 信息熵 ② 条件熵 ③ 信息增益 ④ 信息增益比(信息增益率) ⑤ 基尼指数(基尼系数) 3 决策树的构建过程 4 三种决策树算法 ① ID3 ...
- [机器学习数据挖掘]机器学习实战决策树plotTree函数完全解析
[机器学习&数据挖掘]机器学习实战决策树plotTree函数完全解析 http://www.cnblogs.com/fantasy01/p/4595902.html点击打开链接 import ...
- 机器学习实战--决策树ID3的构建、画图与实例:预测隐形眼镜类型
声明 本文参考了<机器学习实战>书中代码,结合该书讲解,并加之自己的理解和阐述 机器学习实战系列博文 机器学习实战--k近邻算法改进约会网站的配对效果 机器学习实战--决策树的构建.画图与 ...
- 机器学习实战-决策树(二)Python实现
转载请注明作者和出处: http://blog.csdn.net/c406495762 运行平台: Windows Python版本: Python3.x IDE: Sublime text3 一 前 ...
- 机器学习三要素之数据、模型、算法
参考:https://gitbook.cn/gitchat/column/5ad70dea9a722231b25ddbf8/topic/5b1086eccad6fe44db4c1268 1. 机器学习 ...
- Scikit-Learn 机器学习笔记 -- 决策树
Scikit-Learn 机器学习笔记 – 决策树 参考文档: handson-ml import numpy as np# 加载鸢尾花数据集 def load_dataset():from skle ...
- 机器学习实战-决策树-22
机器学习实战-决策树-叶子分类 import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplo ...
最新文章
- 站长之家html视频播放,HTML5视频发展状况
- 教你做一个优秀的项目经理
- Android 使用PDF.js浏览pdf
- 面试官:你说对 MySQL 事务很熟?那我问你 10 个问题
- c语言的高级编程,C语言高级编程
- 持久内存服务器大多数数据库管理系统,内存数据库VS传统数据库:如何在多个任务之间共享内存中的数据?...
- esp8266应用教程——TFT LCD显示
- 移动端使用二倍图比一倍图有什么好处
- RAID磁盘阵列与阵列卡
- proposal中文翻译_PROPOSAL 是什么意思_ PROPOSAL 的翻译_音标_读音_用法_例句_爱词霸在线词典...
- 什么是群发单显和分别发送,有什么区别,发客户邮件忘记群发单显
- 如何在微信中使用企业邮箱,企业微信邮箱密码是什么?
- html5批量修改本地文件名,文件名批量更名技巧;将文件夹名添加到文件名上-批量修改文件名...
- 国开1253c语言程序设计,人教版三年级数学下册单元测试题全套
- 【移动通信】5GC:5G的QoS (Quality of Service) 控制 服务质量管理
- WPF(3)----多窗口的实现
- C语言学习(三)数据-浮点类型
- E1/CE1/T1/PRI/BRI知识介绍和配置(转)
- 教师计算机基础知识培训简报,信息技术能力提升培训简报.doc
- SPPID开发-开发简介及VB代码转C#