机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

关键字:决策树、python、源码解析、测试
作者:米仓山下
时间:2018-10-24
机器学习实战(Machine Learning in Action,@author: Peter Harrington)
源码下载地址:https://www.manning.com/books/machine-learning-in-action
git@github.com:pbharrin/machinelearninginaction.git

*************************************************************
一、决策树的原理及源码解析

文件:trees.py,是ID3决策树算法的实现。代码中的主要方法:

createDataSet----创建例子中的数据集5行×3列,即五条数据,前两列为特征,最后一列为是否为鱼类;lable为两个特征的含义'no_surfacing'(不浮出水是否可以生存),'flippers'(是否有脚蹼)

例子:五个海洋生物,两个特征:不浮出水是否可以生存;是否有脚蹼。利用决策树将这些动物分成两类。
数据要求:第一,数据必须是一种列表元素组成的列表,而且所有的列表元素都要具有相同的的数据长度;第二,数据的最后一列或则每个实例的最后一个元素是当前实例的类别标签。

#香农熵的计算
calcShannonEnt----计算给定数据集的香农熵,熵是集合信息的度量方式,公式

#按照信息增益选取最好的特征
chooseBestFeatureToSplit----选择最好的数据集划分方式,返回最好的特征(按照该特征分类的信息增益最大)索引。原理:遍历所有特征,按照当前特征将其划分(splitDataSet)为多个数据集,然后求他们信息熵的和。其中信息增益是熵的减少或则无序减少程度,这里是前原始数据集的信息熵与分类后的信息熵之差。最后选取信息增益最大的特征,返回对应的列索引

#数据集切分
splitDataSet----按照给定特征(axis)的值(value)划分数据集dataset
================================================================
#决策树构建
createTree----决策树构建代码,是一个递归函数

原理:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,再次划分数据。因此我们采用递归的原则处理数据集。递归结束的条件:遍历完所有划分数据集的属性,或则每个分支下的所有实例都具有相同的类(任何到达叶节点的数据必然属于叶节点的分类)。第一种情况,如果遍历完所有的属性,但当前节点类标签不唯一时,通过多数表决的方法(majorityCnt)来确定叶节点的分类。

majorityCnt----多数表决的算法实现返回对应的类别

createTree代码解析:

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:{}} #构建节点(字典,key为最佳的标签)
del(labels[bestFeat]) #删除已采用标签
featValues = [example[bestFeat] for example in dataSet] #获取最佳特征列的值
uniqueVals = set(featValues) #取得其不重复集合
for value in uniqueVals: #遍历
subLabels = labels[:] #copy all of labels, so trees don't mess up existing labels
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels) #在上面节点上增加节点,利用该特征值取值下的数据集,递归
return myTree
>>> import trees
>>> import treePlotter
>>> data,lable=trees.createDataSet()
>>> data
[[, , 'yes'], [, , 'yes'], [, , 'no'], [, , 'no'], [, , 'no']]
>>> lable
['no surfacing', 'flippers']
>>> mytree=trees.createTree(data,lable)
>>> mytree
{'no surfacing': {: 'no', : {'flippers': {: 'no', : 'yes'}}}}

================================================================
#利用构建的树完成测试样本的分类。
#原理:程序比较测试数据与决策树上的数值,递归执行该过程直到进入叶子节点;最后将测试数据定义为叶子节点所属的类型
classify----利用构建的树完成测试样本的分类

def classify(inputTree,featLabels,testVec):
firstStr = inputTree.keys()[0] #获取根节点的key,即采用的第一个特征(只有一个)
secondDict = inputTree[firstStr] #获取根节点的子节点(有多个)
featIndex = featLabels.index(firstStr) #获取获取第一个特征的所在的列索引
key = testVec[featIndex] #取出测试数据对应特征的值
valueOfFeat = secondDict[key] #取出树第二层中key与测试数据相等的节点
if isinstance(valueOfFeat, dict): #判断取出的节点是否是dict对象
classLabel = classify(valueOfFeat, featLabels, testVec) #是,说明还有子节点,继续寻找
else: classLabel = valueOfFeat #否,说明已经到达叶节点,返回其类别
return classLabel

测试:

>>> import trees
>>> import treePlotter
>>> data,lable=trees.createDataSet()
>>> data
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> lable
['no surfacing', 'flippers']
>>> mytree=trees.createTree(data,lable)
>>> mytree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}} >>> data,lable=trees.createDataSet()#重置lable
>>> trees.classify(mytree,lable,[0,0])#利用构建的树对测试数据分类
'no'
>>>

#其他方法
storeTree----利用pickle将构建的树对象序列化保存到本地

grabTree----利用pickle加载保存在本地的树文件,构建树

trees.py源码(测试见后文)

'''
Created on Oct 12, 2010
Decision Tree Source Code for Machine Learning in Action Ch. 3
@author: Peter Harrington
'''
from math import log
import operator 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 def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet: #the the number of unique elements and their occurance
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) #log base 2
return shannonEnt def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis] #chop out axis used for splitting
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet 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
uniqueVals = set(featList) #get a set of unique values
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 #calculate the info gain; ie reduction in entropy
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 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]
if classList.count(classList[0]) == len(classList):
return classList[0]#stop splitting when all of the classes are equal
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]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:] #copy all of labels, so trees don't mess up existing labels
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree 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 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)

****************************************************************
二、利用matplotlib实现tree可视化

文件:treePlotter.py,代码中的主要方法:

其中方法createPlot可以将传给它的tree可视化展现出来

plotMidText----连线中间文字
plotTree----构建树
createPlot----主入口,将传给它的tree可视化展现出来

#获取叶节点的数目和树的层数
getNumLeafs----叶节点的数目
getTreeDepth----树的层数

#创建测试数据
retrieveTree----定义了两个树

#使用文本注解绘制树节点
plotNode----使用文本注解绘制树节点

treePlotter.py源码(测试见后文)
*************************************************************
三、测试实现决策树分类

>>> em=trees.calcShannonEnt(test_tree_data[0])#计算测试数据熵
>>> em
0.9709505944546686
>>> >>> import trees
>>> import treePlotter
>>> test_tree_data=trees.createDataSet()#测试数据
>>> test_tree_data
([[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']], ['no surfacing', 'flippers'])
>>> mytree=trees.createTree(*test_tree_data)#构建决策树
>>> mytree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
>>> treePlotter.createPlot(mytree)#可视化树

aaarticlea/png;base64,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+/Xb3/627PXk5ORKPe+uXbvUqFEj1a9fv1LPA8/BEjAAW+jatavq16+vzz//XJZlVdhxLcvS559/rvr166tr164Vdlx4p44dO6pRo0b67LPP5HK5quSc+fn52rhxo1566SX2r0QZH6si/yYEADc2Z84cjRkzRvfdd5+6dOmili1bKiwsTGFhYapfv76qV69+0/eXlJTo1KlTZXsDpqWlKSUlRd9//70++ugjjR49uoq+E3iyefPmafjw4QoNDVWbNm308MMPq379+qpTp45CQ0MVGhqqWrVqlWsT5+LiYuXn5ys/P19nzpxRfn6+nE6nDhw4oEOHDikkJESpqansX4kyFEAAtrJ582YtXbpUa9euVVZW1lV/du+99151X9YvlZaWqqCg4KrXHnjgAfXs2VMvv/yyfve731VaZnifnTt3atWqVUpJSdGePXuuubZ8fHwUEhJyw+vxl4qLi1VYWHjN+xs1aqTOnTurS5cuevnll9kXEFehAAKwrZKSEjmdTuXk5CgnJ0c//PDDDZeHfXx81LBhQ0VERCgiIkKNGze+5cQQuF1FRUU6efLkVV9nzpy5rWXigIAANWjQ4KqvunXr8hg43BQFEAAAwGb4EAgAAIDNUAABAABshgIIAABgMxRAAAAAm6EAAgAA2AwFEAAAwGYogAAAADZDAQQAALAZCiAAAIDNUAABAABshgIIAABgMxRAAAAAm6EAAgAA2AwFEAAAwGYogAAAADZDAQQAALAZCiAAAIDNUAABAABshgIIAABgMxRAAAAAm6EAAgAA2AwFEAAAwGYogAAAADZDAQQAALAZCiAAAIDNUAABAABshgIIAABgMxRAAAAAm6EAAgAA2AwFEAAAwGYogAAAADZDAQQAALAZCiAAAIDNUAABAABshgIIAABgMxRAAAAAm6EAAgAA2AwFEAAAwGYogAAAADZDAQQAALCZ/w9CwPoz0a+hwAAAAABJRU5ErkJggg==" alt="" width="568" height="426" />

(图Figure_1-1.png)

>>> test_tree_data=trees.createDataSet()#注意上面数据已经改变了test_tree_data[1],即lable
>>> trees.classify(mytree,test_tree_data[1],[0,0])
'no'

--------------------------------------------------------------------------
#测试眼镜的例子:特征包括['age','prescript','astigmatic','tearRate'],类别包括['lenses','soft','hard']

>>> import trees
>>> import treePlotter
>>> fr=open('lenses.txt')
>>> lenses=[inst.strip().split('\t') for inst in fr.readlines()]
>>> lenseslable=['age','prescript','astigmatic','tearRate']
>>> lensestree=trees.createTree(lenses,lenseslable)
>>> lensestree
{'tearRate': {'reduced': 'no lenses', 'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'pre': 'no lenses', 'presbyopic': 'no lenses', 'young': 'hard'}}, 'myope': 'hard'}}, 'no': {'age': {'pre': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'young': 'soft'}}}}}}
>>> treePlotter.createPlot(lensestree)

(图:测试眼)
存在问题:该树的匹配选项过多,过度匹配

最后提醒,使用这里的方法时,注意前文提到的数据要求

机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试的更多相关文章

  1. 学习笔记之机器学习实战 (Machine Learning in Action)

    机器学习实战 (豆瓣) https://book.douban.com/subject/24703171/ 机器学习是人工智能研究领域中一个极其重要的研究方向,在现今的大数据时代背景下,捕获数据并从中 ...

  2. K近邻 Python实现 机器学习实战(Machine Learning in Action)

    算法原理 K近邻是机器学习中常见的分类方法之间,也是相对最简单的一种分类方法,属于监督学习范畴.其实K近邻并没有显式的学习过程,它的学习过程就是测试过程.K近邻思想很简单:先给你一个训练数据集D,包括 ...

  3. 机器学习实战 [Machine learning in action]

    内容简介 机器学习是人工智能研究领域中一个极其重要的研究方向,在现今的大数据时代背景下,捕获数据并从中萃取有价值的信息或模式,成为各行业求生存.谋发展的决定性手段,这使得这一过去为分析师和数学家所专属 ...

  4. java学习笔记之集合—ArrayList源码解析

    1.ArrayList简介 ArrayList是一个数组队列,与java中的数组的容量固定不同,它可以动态的实现容量的增涨.所以ArrayList也叫动态数组.当我们知道有多少个数据元素的时候,我们用 ...

  5. [SpringMVC]SpringMVC学习笔记一: springmvc原理及实例解析.

    前言:今天来回顾下SpringMVC的开发原理, 使用图文并茂的方式 来解析其中的内幕, 我相信懂了其中的运行机制后, 对于面试中SpringMVC大家都可以说so easy了. 一, 图示法 第二张 ...

  6. SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

    系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...

  7. Coursera 机器学习 第6章(下) Machine Learning System Design 学习笔记

    Machine Learning System Design下面会讨论机器学习系统的设计.分析在设计复杂机器学习系统时将会遇到的主要问题,给出如何巧妙构造一个复杂的机器学习系统的建议.6.4 Buil ...

  8. 【python与机器学习实战】感知机和支持向量机学习笔记(一)

    对<Python与机器学习实战>一书阅读的记录,对于一些难以理解的地方查阅了资料辅以理解并补充和记录,重新梳理一下感知机和SVM的算法原理,加深记忆. 1.感知机 感知机的基本概念 感知机 ...

  9. Mybatis学习笔记汇总(包括源码和jar包)

    博客整理 Mybatis学习笔记(一)--对原生jdbc中问题的总结 Mybatis学习笔记(二)--Mybatis框架 Mybatis学习笔记(三)--入门程序 MyBatis学习笔记(四)--入门 ...

随机推荐

  1. qtcreator_process_stub中文输出乱码

    使用qt运行程序输出中文,全都变成了□,让人很头疼,百度了很久,找了一些解决方案都是: 用vim打开x11-common,在控制台输入 vim /etc/X11/Xresources/x11-comm ...

  2. 分布式锁的两种实现方式(基于redis和基于zookeeper)

    先来说说什么是分布式锁,简单来说,分布式锁就是在分布式并发场景中,能够实现多节点的代码同步的一种机制.从实现角度来看,主要有两种方式:基于redis的方式和基于zookeeper的方式,下面分别简单介 ...

  3. redis集群环境的搭建和错误分析

    redis集群环境的搭建和错误分析 redis集群时,出现的几个异常问题 09 redis集群的搭建 以及遇到的问题

  4. How to correctly handle ThreadLocal.get() returning null

    Java's ThreadLocals make certain things easy, but special care must be taken to make sure they are r ...

  5. linux 设置与网络同步的时间

    #设置linux时间 与网络时间同步安装命令yum install -y ntp同步网络时间ntpdate ntp1.aliyun.com

  6. 18-hadoop-weather案例

    weather案例, 简单分析每年的前三个月的最高温即可, 使用自定义的分组和排序 设计分析 设定多个reduce 每年的数据都很多,如果按照默认情况处理,统计性能是非常慢(因为默认只有一个reduc ...

  7. 微信小程序——豆瓣电影——(2):小程序运行部署

    Demo 预览 演示视频(流量预警 2.64MB) GitHub Repo 地址 仓库地址:https://github.com/zce/weapp-demo 使用步骤 将仓库克隆到本地: bash ...

  8. C# 例子1

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. 描述linux系统从开机到登陆界面的启动过程

    简述:1.开机BIOS自检2.MBR引导3.grub引导菜单4.加载内核kernel5.启动init进程6.读取inittab文件,执行rc.sysinit,rc等脚本7.启动mingetty,进入系 ...

  10. HDU 1242 Rescue(BFS+优先队列)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1242 题目描述: Problem Description Angel was caught by t ...