(本来打算在看完决策树这一章的时候就写一篇博客,但是各种事情耽误吧,一个月之后的今天才开始写这篇博客,就当是在一遍的复习下决策树吧。)
我会根据《机器学习实战》书中决策树讲解的顺序进行讲解,包括代码和概念。因为本人也是一个奔波在学习机器学习道路上的一个小白,所以有什么错误或者疑问,欢迎各位在下面留言。废话不多说下面开始进入正题。
一、什么是决策树?
书中给出了一个小例子很好的解释了决策树
(如果你没玩过这个游戏我建议你下次和朋友聚会时可以试着玩一下,很不错的。)
ok,看过上面的例子,估计你也应该看出来了,决策树就是通过你要分析的数据中的关键“点”,然后对数据进行一步一步的划分,说白了决策树就是一种分类器。
二、决策树的优缺点?
(优缺点都是抄袭书上的)
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值型和标称型。
数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)
标称型:标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)
三、决策树的构造
(书中构造的决策树算法能够读取数据集合,决策树很多任务都是为了数据中所蕴含的知识信息, 因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,机器学习算法最终将使用这些机器从数据集中创造的规则)
1.使用信息论划分数据集
在构造决策树的时,我们解决的第一个问题就是数据集上哪一个特征在划分数据分类时起决定性作用,也就是说根据哪个特征划分数据集我们可以得到最好的结果,最好的结果就是,举个极端的例子要是我们根据一个特征就可以把数据按照不同的类别划分开,那么这个特征就是最好的,现实是没有这样的特征,那么我们就应该找到最大程度化划分开数据集的特征。所以在开始之前我们就要有一个办法来评估每个特征。
划分数据集大原则是:将无序的数据变得更加有序。划分数据集之前之后信息发生的变化称为信息增益。集合信息的度量方式称为香浓熵或者简称为熵。
(1)关于信息熵的问题:
(我对这方面的接触的比较少,可能给大家解释的不会太清楚,你们可以通过这个网址了解下:
https://www.zhihu.com/question/22178202)
信源中所有发生情况的平均不确定性可以称为信息熵,数据集就可以看成是一个信源,每个特征可以看成是不同的情况,每种情况的不确定性可以用下面的式子进行计算(也可以说是每种特征所拥有的信息):
p(x)代表每种类型在数据集中出现的概率
下面是数据集的信息熵的计算公式:
(2)信息熵的代码实现
from math import log
def calcshannonEnt(dataSet):
numEntries = len(dataSet) #获得数据集的大小
labelCounts = {} #创建字典类型
for featVec in dataSet:
currentLabel = featVec[-1] #每条数据的最后一列是其标签
if currentLabel not in labelCounts.keys(): #如果标签不在字典中,创建这个标签
labelCounts[currentLabel] = 0 #标签出现次数初始化0
labelCounts[currentLabel] += 1 #出现次数加1
shannonEnt = 0.0
for key in labelCounts: #整个循环是求数据集的信息熵
prob = float(labelCounts[key]) / numEntries #求数据集中某个类别出现的概率
shannonEnt -= prob * log(prob,2) #求数据集的信息熵
return shannonEnt
(3)根据特征划分数据集代码实现
#(待划分的数据集,划分数据集特征的所在列,该列特征的划分值)
def splitDataSet(dataSet,axis,value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
(4)找到对数据集划分最好的特征
def chooseBestFeatureToSplit(dataSet): #选择最好的数据集划分方式
numFeatures = len(dataSet[0]) - 1 #求一条数据的长度(这里是求特征数量)
#print(dataSet[0])
#print(numFeatures)
baseEntropy = calcShannonEnt(dataSet) #计算划分前数据的原始香农熵
bestInfoGain = 0.0; bestFeature = -1 #
for i in range(numFeatures):
featList = [example[i] for example in dataSet] #遍历dataSet的每条数据的第i列,并保存到featuList中
#print(featList)
uniqueVals = set(featList) #去掉featList列表中重复的特征值
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 #这里返回的是第几个特征,也就是第i列的特征才是最好的划分特征
return bestFeature #返回划分数据集最好的特征
2.决策树的创建
(1)计算数据集中每个数据标签的出现次数
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] #返回列表
(2)根据上面的子函数构建决策树
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: #数据集的每条记录长度为1,也就是使用完了所有的特征,也是递归函数第二个停止条件
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) #返回第i列特征是最好的划分特征
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet] #返回该第i个特征下的所有特征值
uniqueVals = set(featValues) #去掉重复的特征值
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
到这里,决策树基本上就讲完了,书中之后用Matplotlib模块绘制决策树部分各位就自己去完成吧,鉴于我个人水平,一定有很多部分没有讲明白或者我理解不到位的地方,如果发现这些问题,希望各位在下面留言部分写下了,和博主一起探讨