决策树模型
重要任务:是为了理解数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,这些机器根据数据集创建规则的过程就是机器学习的过程。
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。缺点:可能会产生过度匹配问题。
范例:专家系统。
如何构造决策树:
编写构造树的python代码度量算法成功率递归建立分类器Matplotlib绘制决策树图
利用信息论划分数据集,伪代码:
IF (整个数据集属于同一个类型) return 类标签Else 寻找划分数据集的最好特征划分数据集创建分支节点for 每个划分的子集递归继续划分return 分支节点
划分数据集的大原则是:将无序的数据变得更加有序。
信息增益
定义:在划分数据集之前之后的信息发生的变化称为信息增益。
可以用于评价一次信息划分的优劣。
集合信息的度量方式称为:香农熵。
熵定义为信息的期望值。
如果待分类的事务可能划分在多个分类之中,则符号 x i x_i xi的信息定义为:
l ( x i ) = − l o g 2 p ( x i ) l(x_i)=-log_2p(x_i) l(xi)=−log2p(xi)
p ( x i ) p(x_i) p(xi)是选择该分类的概率。
根据期望的公式,得
H = − ∑ i = 1 n p ( x i ) l o g 2 p ( x i ) H=-\sum^n_{i=1}p(x_i)log_2p(x_i) H=−i=1∑np(xi)log2p(xi)
计算给定那个数据集的香农熵:
from math import logdef calcShannonEnt(dataSet):numEntries = len(dataSet)labelCounts = {}for featVec in dataSet:#统计每个标签出现的频次,利用统计数currentLabel = featVec[-1]if currentLabel not in labelCounts.keys():labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1ShannonEnt = 0.0for key in labelCounts:prob = float(labelCounts[key])/numEntries#求概率ShannonEnt -= prob *log(prob,2)#求期望return ShannonEnt
基尼不纯度:另一种度量集合无序程度的方法。
决策树的构建过程实际上就是降低香农熵(无序度量值)的过程。
对于一个特征集合我们可以选择的划分方式就是各个特征,选择哪个特征能够更加快的降低数据集的无序度?
我们只需要尝试按照每一种方式划分,看看哪种划分更快的降低无序度,那么便选择它,这也就是上面伪代码讲的如何划分。
如何理解以下这段代码(由于书中并没有对这段代码解释,以下属于笔者的猜测):
for value in uniqueVals:subDataSet = splitDataSet(dataSet, i, value)prob = len(subDataSet)/float(len(dataSet))newEntropy += prob * calcShannonEnt(subDataSet)
我们可以知道信息熵的定义是期望,我们想要得到的是按照某一特征划分之后整体的期望。
那么可以得到一个新的公式,根据期望的计算 E = ∑ i = 1 n p ( x i ) ∗ x i E=\sum_{i=1}^{n}p(x_i)*x_i E=∑i=1np(xi)∗xi。
其中 x i x_i xi表示的便是划分后的第i个集合的香农熵的值, p ( x i ) p(x_i) p(xi)表示选择该数据集的概率。
最终建立的树是用字典表示的,例如:
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
若字典中的关键字的值对印的是类标签,则表示这是一个叶子结点,若为仍是字典,则表示这是一个判断结点。
利用matplotlib注释绘制属性图。
直接上结果吧
trees.py:
from math import logimport operator#------------计算给定数据集的香农熵-------------def calcShannonEnt(dataSet):numEntries = len(dataSet)labelCounts = {}for featVec in dataSet:#统计每个标签出现的频次,利用统计数currentLabel = featVec[-1]if currentLabel not in labelCounts.keys():labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1ShannonEnt = 0.0for key in labelCounts:prob = float(labelCounts[key])/numEntries#求概率ShannonEnt -= prob *log(prob,2)#求期望return ShannonEnt#-------------生成测试的数据集-----------------def createDataSet():dataSet = [[1,1,'maybe'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]labels = ['no surfacing','flippers']return dataSet,labels#-------------划分数据集------------------#代码思路就是将axis维的特征值为value的放到一个新的数据集中def splitDataSet(dataSet,axis,value):retDataSet = []for featVec in dataSet:if featVec[axis] == value:reducedFeatVec = featVec[:axis]reducedFeatVec.extend(featVec[axis+1:])#a.extend(b) [a,b]retDataSet.append(reducedFeatVec)#a.append(b) [a,[b]]return retDataSet#------------选择最好的数据集划分方式---------------def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1#the last column is used for the labelsbaseEntropy = calcShannonEnt(dataSet)bestInfoGain = 0.0; bestFeature = -1#找到的是使得香农熵减少最多的划分方式for i in range(numFeatures): #iterate over all the featuresfeatList = [example[i] for example in dataSet]#create a list of all the examples of this featureuniqueVals = set(featList) #get a set of unique valuesnewEntropy = 0.0for 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 entropyif (infoGain > bestInfoGain): #compare this to the best gain so farbestInfoGain = infoGain #if better than current best, set to bestbestFeature = ireturn bestFeature #returns an integer#------------获取出现次数最多的分类名称--------------#代码实现,利用map先计数后排序def majorityCnt(classList):classCount={}for vote in classList:if vote not in classCount.keys(): classCount[vote] = 0classCount[vote] += 1sortedClassCount = sorted(classCount.items(), 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]#递归结束条件之一,整个集合属于同一个类别if len(dataSet[0]) == 1: #递归结束条件二,已经没有特征可供划分,并且集合中的数据也没有被分为一类,那么就返回出现次数最多的类别,向现实低头了属于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 labelsmyTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)return myTree
main.py
import treePlotterimport treesmyDat,labels=trees.createDataSet()print(myDat)print(trees.calcShannonEnt(myDat))print(trees.splitDataSet(myDat,0,1))print(trees.splitDataSet(myDat,1,1))print(trees.chooseBestFeatureToSplit(myDat))mytree=trees.createTree(myDat,labels)print(mytree)
结语:
决策树的工作原理就是将原本无序的数据集变得有序,并且这种有序是用树的方式呈现的,该模型的核心便是如何指导模型,使得数据集向有序变化。
决策树使用的无序度,尽量让每一次的划分都能最大化的降低整体的无序度,通过这种方式一步一步的走向有序。
同时,信息熵的概念同样让人着迷,尽管不知道为什么是 − l o g 2 ( p ( x ) ) -log_2(p(x)) −log2(p(x)),但他确实好用,类似的还有交叉熵,等等,这些度量函数值得了解和学习。
今天也是的最后一天,在过去的一年,acm参加了七个赛站,最后以两铜结束,参加了数学建模没能完成论文,混了省一。高中初中小学,都有一点点的竞赛经验,但是都没有获奖,也不是十分重视,到了大学acm也算是圆了竞赛梦,尽管现实残酷。过去这一年的种种遗憾都将随时间流走,希望在新的一年里向着新的方向前行。
如果觉得《《machine learning in action》机器学习 算法学习笔记 决策树模型》对你有帮助,请点赞、收藏,并留下你的观点哦!