机器学习--决策树

决策树

  决策树是一类常见的机器学习方法,决策树是基于树结构来进行决策的。其学习目的是为了产生一棵泛化能力强,即处理未见未示例能力强的决策树,其基本流程遵循简单且直观的 “分而治之” 策略

决策树流程示意图

不是
不是
发送邮件域名地址为:
myEmployer.com
无聊时
需要阅读邮件
包含单词
曲棍球的邮件
需要及时
处理朋友邮件
无需阅读
的垃圾邮件

工作原理

1.一棵决策树包含一个根结点,若干个内部结点和若干个叶结点;
2.根结点包含样本全集
3.叶结点对应策略结果,其他每个结点对应一个属性测试;
4.每个结点包含的样本集合根据属性测试的结果被分化到子节点中
5.从根结点到每个叶结点的路径对应一个判定测试序列

伪代码实现决策树基本算法

输入: 训练集: D = {(x1, y1), (x2, y2), …, (xm, ym)}
    属性集:A = {a1, a2,…,am}
过程: def TreeGenerate(D,A):
1.生成结点node;
2.if D中样本全属于同一个类别C:
  将node 标记为C类结点; return
3.if A=∅ OR D 中样本在A上取值相同:
  将node标记为叶结点,其类别标记为D样本数最多的类;return
4.从 A 中选择最优划分属性a*
5.for a*的每一个值a*v
  为node生成一个分支;另分支Dv表示D中在a*上取值为a*v的样本子集
  if Dv为空:
    将分支结点标记为叶结点,其类别标记为D中样本最多的类;return
  else
    以TreeGenerate(Dv, A \ {a*})为分支结点

伪代码分析:

根据伪代码可以看出决策树是一个递归过程。在决策树基本算法中,有三种情形会造成递归返回:

  1. 当前结点包含的样本同属于同一类别,无需划分;
  2. 当前属性集为空,或是多有样本在所有属性上取值相同,无法划分;
  3. 当前结点包含的样本集合为空,不能划分;

*针对情形(2)我们把当前结点标记为叶结点,并将其类别设为该节点所含样本最多的类别
*针对情形(3)同样将当前结点标记为叶结点,但将其类别设定为其父节点所含样本最多的类别

划分选择

 通过伪代码,我们可以看出决策树学习的关键是在如何选择最优划分属性。随着划分过程不断进行,我们希望决策树的分支结点所包含的样本尽可能属于同一类别,即结点的 ‘‘纯度’’ 越来越高。
 划分数据集的大原则是:将无序的数据变得更加有序,而划分数据集之前之后信息发生的变化称之为信息增益

信息增益
信息熵(shang)

‘信息熵’ 是度量样本集合纯度最常用的一种指标,假定当前样本集合D中第K类样本所占的比例为pk(k=1,2,…,|y|),则D的信息熵定义为:

  Ent(D) = - ∑ k = 1 ∣ y ∣ p k l o g 2 p k \displaystyle\sum_{k=1}^{|y|}p_{k}log_{2}p_{k} k=1ypklog2pk
|y|: 样本集合D中的有y个类别
k: 第k类样本

 Ent(D)信息熵的值越,则D的纯度越

信息增益

 假定离散属性a有V个可能的取值 {a1, a2,…,av} ,若使用a来对样本集D进行划分,则会产生V个分支点,其中第v个分支结点饱含了D中所有在属性a上取值为 av的样本,记为Dv
 可以计算出Dv的信息熵Ent(Dv)。再考虑到不同分支结点所包含的样本数不同,给分支结点赋予权重 |Dv| / |D|,则可以计算出用 属性a样本集D进行划分所获得的 ‘信息增益’:

Gain(D,a) = -Ent(D) - ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) \displaystyle\sum_{v=1}^V\frac{|D^v|}{|D|}Ent(D^v) v=1VDDvEnt(Dv)
D: 样本集
V: 离散属性a可能取值的个数
|Dv|: 第v 个分支结点饱含了D中所有在属性a上取值为 av 的样本个数
|D|: 样本集个数
信息增益越,则意味使用属性a来划分所获得的‘纯度提升’越

增益率

 信息增益准则其实对可取数目较多属性有所偏好,所以信息增益并不是一个很好的特征选择度量,故引出了信息增益率:

Gain_ratio(D,a)= G a i n ( D , a ) I V ( a ) \frac{Gain(D,a)}{IV(a)} IV(a)Gain(D,a)

IV(a)=- ∑ v = 1 V ∣ D v ∣ ∣ D ∣ l o g 2 ∣ D v ∣ ∣ D ∣ \displaystyle\sum_{v=1}^V\frac{|D^v|}{|D|}log_2\frac{|D^v|}{|D|} v=1VDDvlog2DDv

IV(a): 称为属性a的 ‘固有值’,属性a取值越多,则固有值越大

增益率准则对可取数目较少的属性有所偏好,所以C4.5算法并不是直接选择增益率最大的候选划分属性,而是先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的

核心代码

计算给定数据集的信息熵:

from match import log
def calcshannonEnt(dataSet):
	numEntries = len(dataSet)
	labelCount = {}
	for featVec in dataSet:
		currentLabel = featVec[-1]
		if currentLabel not in labelCount.keys():
			labelCount[currentLabel] = 0
		labelCount[currentLabel] += 1
	shannonEnt = 0.0
	for key in labelCount:
		pro = float(labelCount[key]) / numEnries
		shannonEnt -= pro * log(pro, 2)
	return shannonEnt

代码分析:

  1. 计算出集中实例的总数:numEntries = len(dataSet)
  2. 创建一个数据字典,它的键值是实例数据最后一列的数值:currentLabel = featVec[-1]
  3. 如果当前键值不存在,扩展字典并将当前键值加入字典:if currentLabel not in labelCount.keys()
  4. 每个键值都记录当前类别出现次数:labelCount[currentLabel] += 1
  5. 最后我们根据信息熵公式,计算出当前出入的实例集的信息熵:shannonEnt -= pro * log(pro, 2)


    划分数据集,按照给定特征划分数据集
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

代码分析:

  1. 输入三参数:待划分数据集、划分数据集的特征,需要返回的特征的值
  2. 创建一个新的列表对象,避免修改原始数据集:retDataSet = {}
  3. 遍历数据集每个元素,根据输入的划分数据集的特征查找符合的特征值:if featVec[axis] == value
  4. 当按照某个特征划分数据集时,将符合要求的元素抽取出来放入新的列表对象

选择最好的数据集划分方式

1.通过信息增益,选出最优数据集划分(ID3):

def chooseBestFeatureToSplit(dataSet):
	numFeatures = len(dataSet[0]) - 1 #计算特征个数,dataSet最后一个默认为标签属性,不算特征向量
	baseEntropy = calcshannonEnt(dataSet)#计算传入的数据集的信息熵
	baseInfoGain = 0.0 #信息增益
	beastFeature = -1 #最佳特征值
	for i in range(numFeatures):
		featList = [example[i] for example in dataSet] #获取每一列数据
		uniqueVals = set(featList) #去重每一列数据
		newEntropy = 0.0
		for value in uniqueVals:
			subDataSet = splitDataSet(dataSet, i, value)
			prob = len(subDataSet) / float(len(dataSet)) #计算条件概率|Dv| / |D|
			newEntropy += prob * calcshannonEnt(subDataSet)#计算信息熵
		infoGain = baseEntropy - newEntropy #计算信息增益
		if(infoGain > bestInfoGain): #如果新的信息增益大于之前最好的信息增益
			bestInFoGain = infoGain #更新信息增益
			bestFeatureIndex = i #更新最新特征标签
	return bestFeatureIndex

2.通过信息增益率,选出最优数据集划分(C4.5)
def chooseBestFeatureToSplit(dataSet):
	numFeature = len(data[0]) - 1  #计算特征个数,dataSet最后一个默认为标签属性,不算特征向量
	baseEntropy = calcshannonEnt(dataSet)#计算传入的数据集的信息熵
	baseInfoGainRatio = 0.0 #信息增益率
	beastFeature = -1 #最佳特征值
	for i in range(numFeatures):
		faetEntropy = calcshannonEnt(i, dataSet) #此处注意使用了i,这个值作为第一个变量传入。原因是将原来calcshannonEnt中的-1变为了i的值. 实际作用是计算固有值
		featList = [example[i] for example in dataSet]#获取每一列数据
		uniqueVals = set(featList) #去重每一列数据
		newEntropy = 0.0
		for value in uniqueVals:
			subDataSet = splitDataSet(dataSet, i, value)
			prob = len(subDataSet) / float(len(dataSet)) #计算条件概率|Dv| / |D|
			newEntropy += prob * calcshannonEnt(subDataSet)#计算信息熵
		infoGain = baseEntropy - newEntropy #计算信息增益
     	infoGainRatio = infoGain/float(featEntropy)   #计算信息增益率
     	if(infoGainRatio > bestInfoGainRatio):
           bestInfoGainRatio = infoGainRatio
           bestFeatureIndex = i
     return bestFeatureIndex



递归构建决策树

 递归结束条件:

  1. 程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类
  2. 递归使用完了所有的特征,任然不能是数据划分成仅包含了数据集的所有分类

预防出现第2种递归结束,需要以下代码处理,使列中相同值数量最多为结果

def majorityCnt(classList):
	classCount = {}
	for vote in classList:
		if vote not in classCount.keys():
			classCount[vote] = 0
		classCount[vote] += 1
	sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = Ture)
	return sortedClassCount

创建数的函数代码

def createTree(dataSet, labels):
	classList = [example[-1] for example in dataSet]
	if classList.count(classList[0] == len(classList)) #判定为类别完全相同符合递归结束条件1
		return classList[0]
	if(len(dataSet[0])) == 1 #判定遍历完所有特征时返回出现次数最多的类别
		return majorityCnt(classList)
	bestFeat = chooseBestFeatureToSplit(dataSet) #获取最优特征索引
	bestFeatLabel = labels[bestFeat] #获取最优索引对应的标签名称
	myTree = {bestFeatLabel:{}} #创建根节点
	del(labels[bestFeat]) #删除最优对应的标签名,使labels能进行下一层遍历
	featValue = [example[bestFeat] for example in dataSet] #获取最优列
	uniquesValue = set(featValue)
	for value in uniquesVals:
		subLabels = labels[:] #子标签集合
		myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value, subLabels)) #递归生成树
	return myTree


决策树算法

优点: 计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据
缺点: 可能会产生过度匹配问题
适用数据类型: 数值型和标称型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值