【Machine Learning in Action --3】决策树ID3算法
1、简单概念描述
决策树的类型有很多,有CART、ID3和C4.5等,其中CART是基于基尼不纯度(Gini)的,这里不做详解,而ID3和C4.5都是基于信息熵的,它们两个得到的结果都是一样的,本次定义主要针对ID3算法。下面我们介绍信息熵的定义。
p(ai):事件ai发生的概率
I(ai)=-log2(p(ai)):表示为事件ai的不确定程度,称为ai的自信息量
H=sum(p(ai)*I(ai)):称为信源S的平均信息量—信息熵
Gain = BaseEntropy – newEntropy:信息增益
其中N为该特征所取值的个数,比如{rain,sunny},则N即为2
ID3易出现的问题:如果是取值更多的属性,更容易使得数据更“纯”(尤其是连续型数值),其信息增益更大,决策树会首先挑选这个属性作为树的顶点。结果训练出来的形状是一棵庞大且深度很浅的树,这样的划分是极为不合理的。 此时可以采用C4.5来解决,C4.5的思想是最大化Gain除以下面这个公式即得到信息增益率:
其中底为2
2、决策树的优缺点
优点:计算复杂度不高,输出结果易于理解,对中间值缺失不敏感,可以处理不相关特征数据
缺点:可能产生过度匹配问题
适用数据类型:数值型和标称型
3、python代码的实现
以下的代码根据这些数据理解
数据1中包含5个海洋动物,特征包括:不浮出水面是否可以生存,以及是否有脚蹼。我们可以将这些动物分成两类:鱼类和非鱼类。
不浮出水面是否可以生存 | 是否有脚蹼 | 属于鱼类 | |
1 | 是 | 是 | 是 |
2 | 是 | 是 | 是 |
3 | 是 | 否 | 否 |
4 | 否 | 是 | 否 |
5 | 否 | 是 | 否 |
特征[0](no surfacing) | 特征[1](flippers) | 特征[-1]fish | |
dataSet[0] | 1 | 1 | yes |
dataSet[1] | 1 | 1 | yes |
dataSet[2] | 0 | 1 | no |
dataSet[3] | 0 | 1 | no |
dataSet[4] | 0 | 1 | no |
创建名为trees.py的文件,下面代码内容都在此文件中。
(1)计算信息熵
# -*- coding: utf-8 -*-
#计算给定数据集的香农熵
def calcShannonEnt(dataSet):
numEntries=len(dataSet) #数据实例总数
labelCounts={} #对类别数量创建了一个数据字典,键值是最后一列的数值
for featVec in dataSet: #featVec表示特征集
currentLabel=featVec[-1] # currentLabel表示当前键值,featVec[-1]表示数据集中的最后一列
#如果当前键值不存在,扩展字典将当前键值加入字典,设置当前键值表示的类别数量为0
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
(2)创建数据集
#创建数据集
def createDataSet():
dataSet=[[1,1,'yes'],[1,1,'yes'],[0,1,'no'],[0,1,'no'],[0,1,'no']]
labels=['no surfacing','flippers']
return dataSet,labels
在python命令提示符下输入下列命令:
>>> import trees
>>> reload(trees)
<module 'trees' from 'E:\python excise\trees.pyc'>
>>> myDat,labels=trees.createDataSet()
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [0, 1, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.calcShannonEnt(myDat)
0.9709505944546686
>>>
熵越高,则混合的数据越多,在数据集中添加更多的分类,观察熵是如何变化的,这里增加第三个名为maybe的分类,测试熵的变化:
>>> myDat[0][-1]='maybe'
>>> myDat
[[1, 1, 'maybe'], [1, 1, 'yes'], [0, 1, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.calcShannonEnt(myDat)
1.3709505944546687
得到熵后,我们可以按照获取最大信息增益的方法划分数据集
(3)划分数据集
我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式
#按照给定特征划分数据集
#dataSet:待划分的数据集,axis:划分数据集的特征,value:需要返回的特征的值
def splitDataSet(dataSet,axis,value):
retDataSet=[] #为了不修改原始数据dataSet,创建一个新的列表对象
for featVec in dataSet:
if featVec[axis]==value:
reducedFeatVec=featVec[:axis] #获取从第0列到特征列的数据
reducedFeatVec.extend(featVec[axis+1:]) #获取从特征列之后的数据
retDataSet.append(reducedFeatVec) #目前reducedFeatVec表示除了特征列的数据
return retDataSet
>>> reload(trees)
<module 'trees' from 'E:\python excise\trees.pyc'>
>>> myDat,labels=trees.createDataSet()
>>> myDat
[[1, 1, 'yes'], [1, 1, 'yes'], [0, 1, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.splitDataSet(myDat,0,1)
[[1, 'yes'], [1, 'yes']]
>>> trees.splitDataSet(myDat,0,0)
[[1, 'no'], [1, 'no'], [1, 'no']]
(4)选择最好的特征进行划分
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures=len(dataSet[0])-1 #减去类别那一列
baseEntropy=calcShannonEnt(dataSet) #计算整个数据集的原始香农熵
bestInfoGain=0.0;bestFeature=-1 #现在最好的特征是数据集中的最后一列
#i=0,新熵,增益
#i=1,新熵,增益
for i in range(numFeatures): #循环遍历数据集中的所有特征
featList=[example[i] for example in dataSet] #获取第i个特征所有可能的取值,特征0一个列表,特征1一个列表...
uniqueVals=set(featList) #集合数据类型(set)与列表类型相似,不同之处仅在于集合类型中每个值互不相同
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
if(infoGain>bestInfoGain):
bestInfoGain=infoGain
bestFeature=i
return bestFeature
注意:这里数据集需要满足以下两个办法:
<1>所有的列元素都必须具有相同的数据长度
<2>数据的最后一列或者每个实例的最后一个元素是当前实例的类别标签。
>>> reload(trees)
<module 'trees' from 'E:\python excise\trees.pyc'>
>>> myDat,labels=trees.createDataSet()
>>> trees.chooseBestFeatureToSplit(myDat)
0
(5)创建树的代码
Python用字典类型来存储树的结构,返回的结果是myTree-字典
#创建树的函数代码
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
其中递归结束当且仅当该类别中标签完全相同或者遍历所有的特征此时返回次数最多的
>>> reload(trees)
<module 'trees' from 'E:\python excise\trees.pyc'>
>>> myDat,labels=trees.createDataSet()
>>> myTree=trees.createTree(myDat,labels)
>>> myTree
{'no surfacing': {0: 'no', 1: 'yes'}}
其中当所有的特征都用完时,采用多数表决的方法来决定该叶子节点的分类,即该叶节点中属于某一类最多的样本数,那么我们就说该叶节点属于那一类。即为如果数据集已经处理了所有的属性,但是类标签依然不是唯一的,此时我们要决定如何定义该叶子节点,在这种情况下,我们通常采用多数表决的方法来决定该叶子节点的分类。代码如下:
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]
(6)使用决策树执行分类
#测试算法:使用决策树执行分类
def classify(inputTree,featLabels,testVec):
firstStr=inputTree.keys()[0]
secondDict=inputTree[firstStr]
featIndex=featLabels.index(firstStr)
for key in secondDict.keys():
if testVec[featIndex]==key:
if type(secondDict[key]).__name__=='dict':
classLabel=classify(secondDict[key],featLabels,testVec)
else:classLabel=secondDict[key]
return classLabel
>>> import trees
>>> myDat,labels=trees.createDataSet()
>>> labels
['no surfacing', 'flippers']
>>> trees.classify(myTree,labels,[1,0])
'no'
>>> trees.classify(myTree,labels,[1,1])
'yes'
注意递归的思想很重要。
(7)决策树的存储
构造决策树是一个很耗时的任务。为了节省计算时间,最好能够在每次执行分类时调用已经构造好的决策树。为了解决这个问题,需要使用python模块pickle序列化对象,序列化对象可以在磁盘上保存对象,并在需要的时候读取出来。
#使用算法:决策树的存储
def storeTree(inputTree,filename):
import pickle
fw=open(filename,'w')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
import pickle
fr=open(filename)
return pickle.load(fr)
>>> reload(trees)
>>><module 'tree' from 'trees.py'>
>>> trees.storeTree(myTree,'classifierStorage.txt')
>>> trees.grabTree('classifierStorage.txt')
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
classifierStorage.txt如下:
补充:
用matplotlib注解上述形成的决策树
Matplotlib提供了一个注解工具annotations,非常有用,它可以在数据图形上添加文本注释。注解通常用于解释数据的内容。
创建名为treePlotter.py文件,下面代码都在此文件中
其中index方法为查找当前列表中第一个匹配firstStr的元素 返回的为索引。
【Machine Learning in Action --3】决策树ID3算法的更多相关文章
- 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试
机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...
- 《Machine Learning in Action》—— Taoye给你讲讲决策树到底是支什么“鬼”
<Machine Learning in Action>-- Taoye给你讲讲决策树到底是支什么"鬼" 前面我们已经详细讲解了线性SVM以及SMO的初步优化过程,具体 ...
- 《Machine Learning in Action》—— 小朋友,快来玩啊,决策树呦
<Machine Learning in Action>-- 小朋友,快来玩啊,决策树呦 在上篇文章中,<Machine Learning in Action>-- Taoye ...
- 机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集
机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集 关键字:FPgrowth.频繁项集.条件FP树.非监督学习作者:米 ...
- 机器学习实战(Machine Learning in Action)学习笔记————07.使用Apriori算法进行关联分析
机器学习实战(Machine Learning in Action)学习笔记————07.使用Apriori算法进行关联分析 关键字:Apriori.关联规则挖掘.频繁项集作者:米仓山下时间:2018 ...
- 机器学习实战(Machine Learning in Action)学习笔记————06.k-均值聚类算法(kMeans)学习笔记
机器学习实战(Machine Learning in Action)学习笔记————06.k-均值聚类算法(kMeans)学习笔记 关键字:k-均值.kMeans.聚类.非监督学习作者:米仓山下时间: ...
- 机器学习实战(Machine Learning in Action)学习笔记————02.k-邻近算法(KNN)
机器学习实战(Machine Learning in Action)学习笔记————02.k-邻近算法(KNN) 关键字:邻近算法(kNN: k Nearest Neighbors).python.源 ...
- Machine Learning in Action(5) SVM算法
做机器学习的一定对支持向量机(support vector machine-SVM)颇为熟悉,因为在深度学习出现之前,SVM一直霸占着机器学习老大哥的位子.他的理论很优美,各种变种改进版本也很多,比如 ...
- Machine Learning In Action 第二章学习笔记: kNN算法
本文主要记录<Machine Learning In Action>中第二章的内容.书中以两个具体实例来介绍kNN(k nearest neighbors),分别是: 约会对象预测 手写数 ...
- 【机器学习实战】Machine Learning in Action 代码 视频 项目案例
MachineLearning 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远 Machine Learning in Action (机器学习实战) | ApacheCN(apa ...
随机推荐
- MySQL语句相关经验总结
1. 字段自减 UPDATE `table_name` SET `total`=IF(`total` < 1, 0, `total`-1) WHERE `id` = 1; 一般在做字段减法的时候 ...
- CodeForces 645D Robot Rapping Results Report
二分,拓扑排序. 二分答案,然后进行拓扑排序检查,若某次发现存在两个或者两个以上入度为$0$的节点,那么不可行. #pragma comment(linker, "/STACK:102400 ...
- AC日记——【模板】字符串哈希 洛谷 3370
题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转 ...
- java-进程
一个java进程,只有一个入口,就是main方法. tomcat是一个java进程,tomcat只有一个入口,org.apache.catalina.startup.Bootstrap 类的main ...
- Mysql的热备份[转载]
学一点 mysql 双机异地热备份----快速理解mysql主从,主主备份原理及实践 双机热备的概念简单说一下,就是要保持两个数据库的状态自动同步.对任何一个数据库的操作都自动应用到另外一个数据库,始 ...
- 查看Oracle正在执行的任务
select a.program, b.spid, c.sql_text,c.SQL_ID from v$session a, v$process b, v$sqlarea c where a.pad ...
- SQLite模糊查找(like)
select UserId,UserName,Name,Sex,Birthday,Height,Weight,Role from xqhit_Users where UserName like &qu ...
- Struts2中的一个类型转换示例
1.写一个属性文件,里面写好需要转换的类型数据,xwork-conversion.properties,解释: xwork-conversion.properties表示对所有action中的指定数据 ...
- .net C# 苹果消息推送 工具类
public class AppleAPNSMessage { /// <summary> /// 苹果信息推送 证书 路径(注意测试版跟正式发布版证书上不一样) /// </sum ...
- MFC 控件用法
1:IP Control 变量CIPAddressCtrl m_iAddr 关联DDX_Control(pDX,IDC_IPADDRESS1,m_iAddr); 设置地址:m_iAddr.SetAdd ...