决策树—原理简述及代码实现


数据集

决策树常用于处理分类问题,首先对本文实现的决策树训练时所用的数据集做简单介绍。
属性值: 也就是特征,通常是数据集除去最后一列剩余的部分。下面数据集中这部分数据为标称型(有限类别)。
预测目标: 也就是分类任务的目标,即样本的类别,通常是数据集中的最后一列,同样为标称型数据。

具体如下面例子:海洋生物数据

不浮出水面是否可以生存(1是0否)是否有脚撲(1是0否)属于鱼类
111
211
310
401
501

该数据集中前两列即为属性值(有限类别的标称型特征),最后一列“是否属于鱼类”即为所需预测的目标分类。

决策树的构造

决策树的构造过程实际上就是通过递归,不断地在当前节点处选择剩余的属性值(是否可浮出水面,是否有脚)来进行分支,使得最终叶子节点的纯度最优。对上面的数据集来讲:即尽可能地使最终的各个叶子节点均为鱼类,或均不为鱼类。

下面给出决策树构建的伪代码以理解构建过程:

# 创建分支的伪代码函数createBranch()如下所水:
检测数据集中的每个子项是否属于同一分类: 
	If so return 类标签; 
	Else
		寻找划分数据集的最好特征 
		划分数据集
		创建分支节点
			for每个划分的子集
				调用函数createBranch()并增加返回结果到分支节点中 
		return 分支节点

注意createBranch()是一个递归函数,在倒数第二行直接调用了它自己。

信息增益

决策树的关键步骤就是每一步对数据集的划分,也就是在当前的叶子节点上选择属性来将其继续分支。

而分支的最终目标是使得无序的数据变得有序,也就是使最终每个叶子节点的纯度尽可能高,即每个叶子节点都尽可能均为鱼类,或均不为鱼类。

那么,如何在当前的叶子节点上选择剩余属性中最优的属性来进行分支?这就需要定义一个指标来度量当前节点(即当前数据集)的无序程度,下面给出两个常见的度量指标:

  • 信息熵:通过信息熵可以计算分支前后数据集的信息增益,信息增益越大,证明无序程度减小得越多,即能够使得划分后的叶子节点纯度最优
  • 基尼不纯度:此处不讲
信息熵的定义

假设当前数据集(当前节点)为X,针对预测目标有n个分类,如上面数据集,则预测目标为“是否鱼类”,n=2。令其中的一个分类为Xi,P(Xi)为Xi所占的比例,则Xi的信息定义为:
在这里插入图片描述
,而当前数据集的信息熵为:
在这里插入图片描述

信息增益的定义

假设当前数据集(当前节点)的信息熵为H1,数据集中存在属性A,A有n个类别。我们使用属性A来划分当前数据集(分支),设划分后的每个新节点所占比例为Pn,且其熵为hn,令H2 = P1* h1 + P2* h2 + … + Pn* hn。

则有:使用属性A划分后的信息增益为H1-H2

从中我们可以看出,要在当前节点下选择最优属性来划分,则该属性应满足划分后的信息增益值应最大

递归终止条件

  1. 当前节点下的类标签已经完全相同,则该处递归终止,并返回该类别标签。
  2. 当已经使用完所有特征时,该处递归终止。若此时不是唯一类别标签,则采用多数表决的方法,将占比最大的类别标签作为该叶子节点的分类,并返回。

代码实现

计算当前数据集的信息熵

输入的数据结构形如:
[[1, 1, 'yes'],
 [1, 1, 'yes'], 
 [1, 0, 'no'], 
 [0, 1, 'no'],
 [0, 1, 'no']]

from math import log

def calcShannonEnt(dataSet) :
	numEntries = len(dataSet) # 数据集的样本量
	labeXCounts = {}
	for featVec in dataSet: # 迭代数据集的每一行
		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 # 信息熵公式
	return shannonEnt 

按照选定的特征划分数据集

# 得到根据特征axis划分后,值为value的叶子节点的数据集
# axis的值为0~n,即第1个~第n+1个特征
def splitDataSet(dataSet, axis, value):
	retDataSet = []
	for featVec in dataSet:
		if featVec[axis] == valUe: # 当该属性为value时
			# 去掉axis列
			reducedFeatVec = featVec[: axis]
			reducedFeatVec.extend{featVec[axis+l: ])
			retDataSet.append(reducedFeatVec) # 添加到划分后的新数据集内(新节点)
	return retDataSet

选择出划分数据集的最优特征

def chooseBestFeatureToSplit(dataSet):
	numFeatures = len(dataSet[0]) - 1 # 特征数-1
	baseEntropy = calcShannonEnt(dataSet) # 初始信息熵
	bestInfoGain = 0.0; bestFeature = -1 
	for i in range(numFeatures): # 迭代每一个属性
		featList = [example[i] for example in dataSet]
		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 = infoGai
			bestFeature = i
	return bestFeature

叶子节点的多数表决

# 利用多数表决选择该叶子节点应当返回的类别标签
def majorityCnt(classList):
	classCount={} 
	for vote in classList:
		if vote not in classCount.keys(): classCount fvote] = 0 
		classCount[vote] += 1 
	sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) 
	return sortedClassCount[0][0]

递归构建决策树的算法

# 利用了前面的功能函数
# 生成的决策树为字典形式如:{'no surfacing' : {o: 'no', 1: {'flippers': {0: 'no 1, 1: 'yes'}}}} 
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]) # 从labels中先剔除该节点
	featValues = [example[bestFeat] for example in dataSet]
	uniqueVals = set{featValues) 
	for value in uniqueVals:
		subLabels = labels[:] # 切片,使用sublabels时不改变labels的内容
		# 生成当前节点处的子树(进入递归)
		myTree[bestFeatLabel] [value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
	return myTree # 返回当前子树
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值