《机器学习实战》——决策树

基本原理:每个数据集都有一个或者多个特征作为已知条件,现在根据这些特征分类。K-近邻算法是已知部分分类,通过比较特征距离来将新的数据划分到旧的分类,但是你不知道它是如何划分,也就不知道分类是什么含义。决策树则是将一堆全新的数据,通过一个一个问答的形式,逐步缩小范围,给出最终分类,行成一个树形图。它通过遍历比较信息熵信息增益选出当前最适合分类的特征,从而得出一系列规则。

(参考博客园描述)因此先回忆一下信息论中有关信息量(就是“熵”)的定义。说有这么一个变量X,它可能的取值有n多种,分别是x1,x2,……,xn,每一种取到的概率分别是P1,P2,……,Pn,那么X的熵就定义为:

意思就是一个变量可能的变化越多(反而跟变量具体的取值没有任何关系,只和值的种类多少以及发生概率有关),它携带的信息量就越大。
对分类系统来说,类别C是变量,它可能的取值是C1,C2,……,Cn,而每一个类别出现的概率是P(C1),P(C2),……,P(Cn),因此n就是类别的总数。此时分类系统的熵就可以表示为:

信息增益是针对一个一个的特征而言的,就是看一个特征t,系统有它和没它的时候信息量各是多少。于是有了条件熵:(1)系统不包含特征t;(2)系统虽然包含特征t,但是t已经固定了,不能变化。

特征X被固定为值xi时的条件熵:

特征X被固定时的条件熵:

注意区别。从刚才计算均值的讨论可以看出来,第二个式子与第一个式子的关系就是:

因此固定t时系统的条件熵就有了,为了区别t出现时的符号与特征t本身的符号,我们用T代表特征,而用t代表T出现,那么:

与刚才的式子对照一下,含义很清楚对吧,P(t)就是T出现的概率,就是T不出现的概率。这个式子可以进一步展开,其中的

另一半就可以展开为:

因此特征T给系统带来的信息增益就可以写成系统原本的熵与固定特征T后的条件熵之差:

关键词:信息熵; 条件熵; 信息增益; 树形图

案例:

信息增益Gain(R)表示属性R给分类带来的信息量,我们寻找Gain最大的属性,就能使分类尽可能的纯,即最可能的把不同的类分开。不过我们发现对所以的属性Info(D)都是一样的,所以求最大的Gain可以转化为求最新的InfoR(D)。这里引入Info(D)只是为了说明背后的原理,方便理解,实现时我们不需要计算Info(D)。举一个例子,数据集D如下:

记录ID 年龄 输入层次 学生 信用等级 是否购买电脑
1 青少年 一般
2 青少年 良好
3 中年 一般
4 老年 一般
5 老年 一般
6 老年 良好
7 中年 良好
8 青少年 一般
9 青少年 一般
10 老年 一般
11 青少年 良好
12 中年 良好
13 中年 一般
14 老年 良好

  这个数据集是根据一个人的年龄、收入、是否学生以及信用等级来确定他是否会购买电脑,即最后一列“是否购买电脑”是类标。现在我们用信息增益选出最最佳的分类属性,计算按年龄分裂后的信息量:

  整个式子由三项累加而成,第一项为青少年,14条记录中有5条为青少年,其中2(占2/5)条购买电脑,3(占3/5)条不购买电脑。第二项为中年,第三项为老年。类似的,有:

  可以得出Info年龄(D)最小,即以年龄分裂后,分得的结果中类标最纯,此时已年龄作为根结点的测试属性,根据青少年、中年、老年分为三个分支:

  注意,年龄这个属性用过后,之后的操作就不需要年龄了,即把年龄从attributeList中删掉。往后就按照同样的方法,构建D1,D2,D3对应的决策子树。ID3算法使用的就是基于信息增益的选择属性方法。


算法实施:

1. 创建数据集,给出特征标签——createDataSet()

2. 计算香农熵——getShannonEnt()

3. 根据特征、特征值抽出符合条件的分类结果——splitDataSet()

4. 选择最佳划分的特征:每个特征按不同的值划分类别,分别计算各个类别香农熵并相加作为该特征划分的香农熵。用初始香农熵减去求出的新熵即为信息增益。信息增益越大,说明熵越小,包含信息种类越少,该特征就越好。遍历所有特征,选出最大的信息增益,选择该增益对应的特征作为返回值.调用2,3.

——chooseBestFeatureToSplit()

5. 选择最后一个特征内部的最大类返回:作为终止条件——majorityCnt()

6. 创建树:两个终止条件——类别完全相同 or 到达最后一个特征。首先选择最佳划分特征,打印出它的label;删除该label,剩余的标签作为新的递归标签组;针对之前选好的特征,遍历其取值划分出一个类别,在此基础上建立子树。递归调用形成整棵决策树。——createTree()

# coding:utf-8
from math import log
def getDataSet():
	dataSet = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
	labels = ['no surfacing', 'flippers']
	return dataSet, labels
def getShangnonEnt(dataset):
	####### 得到信息熵,首先统计实例总数 #######
	N = len(dataset)
	labelCounts = {}
	for item in dataset:
		nowLabel = item[-1]
		if nowLabel not in labelCounts.keys():
			labelCounts[nowLabel] = 0
		labelCounts[nowLabel] += 1
	# 开始求熵
	shannonEnt = 0.0
	for key in labelCounts:
		prob = float(labelCounts[key])/N
		shannonEnt -= prob * log(prob, 2)
	return shannonEnt
def splitDataSet(dataSet, axis, value):
	####### 根据特征项axis的value值划分类,并返回该分类 #######
	newData = []
	for item in dataSet:
		if item[axis] == value:
			reducePart = item[:axis]
			reducePart.extend(item[axis+1:])
			newData.append(reducePart)
	return newData
def getBestFeatureToSplit(dataSet):
	###### 计算最大的信息增益,选出最佳划分特征。信息增益:基本熵减新熵######
	numFeature = len(dataSet[0])-1	# 特征总数
	baseEntropy = getShangnonEnt(dataSet)
	bestInfoGain = 0.0
	bestFeature = -1
	# 选择所有特征,逐个计算
	for i in range(numFeature):
		myList = [item[i] for item in dataSet]
		uniqueList = set(myList)
		newEntropy = 0.0
		# 计算当前特征的所有可能值的信息熵
		for value in uniqueList:
			subDataSet = splitDataSet(dataSet, i, value)
			prob = len(subDataSet)/float(len(dataSet))
			newEntropy += prob * getShangnonEnt(subDataSet)
		infoGain = baseEntropy - newEntropy
		if (infoGain > bestInfoGain):
			bestInfoGain = infoGain
			bestFeature = i
	print("bestFeature",bestFeature)
	return bestFeature
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
def createTree(dataSet, labels):
	classList = [item[-1] for item in dataSet]
	if classList.count(classList[0]) == len(classList):
		# 类别完全相同则停止划分
		return classList[0]
	if len(dataSet[0]) == 1:
		# 遍历玩所有特征,返回出现次数最多的
		return majorityCnt(classList)
	bestFeat = getBestFeatureToSplit(dataSet)
	bestFeatLabel = labels[bestFeat]
	myTree = {bestFeatLabel:{}}
	del(labels[bestFeat])
	# 得到列表包含的所有属性值
	featValues = [item[bestFeat] for item in dataSet]
	uniqueVals = set(featValues)
	for value in uniqueVals:
		subLabels = labels[:]
		myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
	return myTree
dataSet, labels = getDataSet()
print("dataSet:", dataSet, labels)
tree = createTree(dataSet, labels)
print("myTree:", tree)


发布了392 篇原创文章 · 获赞 492 · 访问量 241万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览