初识决策树:
决策树是一种基本的分类与回归方法,它可以被认为是if-then规则的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。其主要优点是模型具有可读性,分类速度快。
决策树学习通常需要3个步骤:特征选择,决策树的生成,决策树的修剪。主要方法有ID3,C4.5以及CART算法。
1.什么是决策树?
分类决策树模型是一种描述对实例进行分类的树形结构。决策树由节点(node)和有向边(directed edge)组成,节点有两种类型:内部节点和叶节点。内部节点表示一个特征或属性,叶节点表示一个类。
用决策树分类,从根节点开始,对实例的某一特征进行测试,根据测试结果,将实例分配到其子节点,这时,每一个子节点对应着该特征的一个取值,递归的对实例进行测试并分配,直至达到叶节点。最后将实例分到叶节点的类中。
2.如何进行决策树学习?
本质上,是归纳出一组分类规则,由训练数据集估计条件概率模型,我们选择的条件概率模型应该不仅是对训练数据有很好的拟合作用,而且对未知的数据能有很好的预测。
决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得每个子数据集有一个很好地分类过程。这一过程对应着特征空间地划分以及决策树的构建。
开始,构建根节点,将所有训练数据都放在根节点,选择一个最优特征,按照这一特征将训练数据集分割成子集,使得每个子集有一个在当前条件下最好的分类;
如果这些子集已经能够被基本正确分类,那么构建叶节点,并将这些子集分到所对应的叶节点中去;如果还有子集不能被基本正确分类,那么对这些子集选择新的最优特征,继续对其分割,构建相应的节点;
递归,形成决策树。
以上方法可能出现过拟合现象,需要进行自下而上的剪枝,将树变得简单,从而使它具有泛化能力。即:去掉过于细分饿叶节点,使其回退到父节点,甚至更高的叶节点,然后将父节点或者更高的节点改为新的叶节点。
如果特征数量很多,也可以在决策树学习开始的时候,对特征进行选择,只留下对训练数据有足够分类能力的特征。决策树的生成只考虑局部最优,相对的,决策树的剪支则考虑全局最优。
3.特征选择
度量标准是信息增益或者信息增益比。
首先介绍一下熵的概念。
熵是表示随机变量不确定性的度量,定义如下:
条件熵表示在已知随机变量X的条件下,随机变量Y的不确定性,定义如下:
当熵和条件熵中的概率由数据估计得到时,就分别称为经验熵(empirical entropy)和经验条件熵(empirical conditional entropy)。
知道了熵的概念,可以定义信息增益的概念,那么如何利用信息增益来进行特征选择呢?信息增益(information gain)表示得知特征X的信息而使得类Y的信息的不确定性减少程度。对训练数据集(或子集)D,计算其每一个特征的信息增益,比较它们的大小,选择信息增益最大的特征。其中ID3算法使用的是信息增益的方法来进行特征选择。
信息增益比,为什么要用到信息增益比呢?当我们的特征可以选取的值过多的时候,比如选择身份证或者学号来进行分类,这样的分类没有任何意义,我们就应该想到如何对树的分支过多的情况进行惩罚,这样就引入了信息增益比:
在C4.5算法中使用的是信息增益比。
4.决策树的生成
首先介绍ID3算法:
然后介绍C4.5算法,这个算法和ID3类似,只是特征选择的方法不同:
5.决策树的剪枝
决策树的剪枝往往通过极小化决策树整体的损失函数来实现。
剪枝是一个从已生成的树上裁掉一些子树或者叶结点,并将其根结点或父节点作为新的叶结点的过程。
下面介绍一下损失函数:
C(T)表示模型预测误差,衡量了拟合程度;|T|表示模型复杂度。损失函数制衡了拟合程度和模型复杂度之间的平衡。
剪枝的总体目标是令损失函数最小化,因此剪枝算法的基本思想是如果剪枝回缩后损失的函数值比不剪枝更小,那么就剪枝回缩,不然不剪枝。
#计算香农熵
from math import log
import operator
def calcShannonEnt(dataSet):
numEntries=len(dataSet)
labelCounts={}
for featVec in dataSet:#遍历dataSet中的每一行
currentLabel=featVec[-1]#每行中最后一个数表示数据的类标签
if currentLabel not in labelCounts.keys():#如果标签数据中没有该标签,则添加该标签
labelCounts[currentLabel]=0
labelCounts[currentLabel]+=1#如果有,则标签数加1
shannonEnt=0.0
for key in labelCounts: #遍历标签数组
prob =float(labelCounts[key])/numEntries#标签数除以样本矩阵的长度,即概率
shannonEnt-=prob*log(prob,2)#以2为底求对数
return shannonEnt
#获取数据集
def creatDataSet():
dataSet=[[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
labels=['no surfacing','flippers']
return dataSet,labels
myDat,labels=creatDataSet()
calcShannonEnt(myDat)
#按照给定特征划分数据集
#传入的参数,dataSet是样本矩阵,axis是第axis的位置,value是该位置的值
def splitDataSet(dataSet,axis,value):
retDataSet=[]
for featVec in dataSet:#遍历样本矩阵
if featVec[axis]==value:#如果该行中axis位置的值为value
reducedFeatVec=featVec[:axis]#从第0个位置开始截取到axis位置
reducedFeatVec.extend(featVec[axis+1:])#再从第axis+1的位置截取该行,添加属性到之前的属性后面
retDataSet.append(reducedFeatVec) #一个个的添加reducedFeatVec数组
return retDataSet
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures=len(dataSet[0])-1#因为最后一列是类标签,这里是属性数
baseEntropy=calcShannonEnt(dataSet)#原始熵
bestInfoGain=0.0;bestFeature=-1
for i in range(numFeatures):#遍历除了类标签的样本矩阵
featList=[example[i] for example in dataSet]#example[0]=[1,1,1,0,0],example[1]=[1,1,0,1,1]
uniqueVals=set(featList) #set集合有去重作用,所以uniqueVals=[0,1]
#print(uniqueVals)
#chooseBestFeatureToSplit(myDat)
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#返回最大值下的划分属性
return bestFeature #返回信息增益最大的划分属性
#少数服从多数
import operator
def majorityCnt(classList):
classCount={}
for vote in classList:
if vote not in classCount.keys():
classCount[vote]=0
classCount[vote]+=1#该类标签下数据个数+1
sortedClassCount=sorted(classCount.items(),key=operator.items(1),reverse=True)#对标签数据个数降序排序
return sortedClassCount[0][0] #返回数据表最大的那个类标签
#创建一颗决策树
def creatTree(dataSet,Labels):
classList=[example[-1] for example in dataSet]#保存类标签数组,classList=[‘yes’,’yes’,’no’,’no’,’no’]
if classList.count(classList[0])==len(classList): #如果classList全为‘yes’,说明所有数据同属一类
return classList[0]
if len(dataSet[0])==1: #如果dataSet第一维的长度为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[:]#拷贝数组labels,使其不会丢失掉它的属性
myTree[bestFeatLabel][value]=creatTree(splitDataSet(dataSet,bestFeat,value),subLabels)
return myTree
myDat,labels=creatDataSet()
myTree=creatTree(myDat,labels)
print(myTree)
优点
1:理解和解释起来简单,且决策树模型可以想象
2:需要准备的数据量不大,而其他的技术往往需要很大的数据集,需要创建虚拟变量,去除不完整的数据,但是该算法对于丢失的数据不能进行准确的预测
3:决策树算法的时间复杂度(即预测数据)是用于训练决策树的数据点的对数
4:能够处理数字和数据的类别(需要做相应的转变),而其他算法分析的数据集往往是只有一种类型的变量
5:能够处理多输出的问题
6:使用白盒模型,如果给定的情况是在一个模型中观察到的,该条件的解释很容易解释的布尔逻辑,相比之下,在一个黑盒子模型(例如人工神经网络),结果可能更难以解释
7:可能使用统计检验来验证模型,这是为了验证模型的可靠性
8:从数据结果来看,它执行的效果很好,虽然它的假设有点违反真实模型
缺点
1:决策树算法学习者可以创建复杂的树,但是没有推广依据,这就是所谓的过拟合,为了避免这种问题,出现了剪枝的概念,即设置一个叶子结点所需要的最小数目或者设置树的最大深度
2:决策树的结果可能是不稳定的,因为在数据中一个很小的变化可能导致生成一个完全不同的树,这个问题可以通过使用集成决策树来解决
3:众所周知,学习一恶搞最优决策树的问题是NP——得到几方面完全的优越性,甚至是一些简单的概念。因此,实际决策树学习算法是基于启发式算法,如贪婪算法,寻求在每个节点上的局部最优决策。这样的算法不能保证返回全局最优决策树。这可以减轻训练多棵树的合奏学习者,在那里的功能和样本随机抽样更换。
4:这里有一些概念是很难的理解的,因为决策树本身并不难很轻易的表达它们,比如说异或校验或复用的问题。
5:决策树学习者很可能在某些类占主导地位时创建有有偏异的树,因此建议用平衡的数据训练决策树
优缺点原文总结:https://blog.csdn.net/gamer_gyt/article/details/51226904
作者:小新新
来源:CSDN
版权声明:本文为博主原创文章,转载请附上博文链接!欢迎转载!
参考文献:
[1]《机器学习实战》的第三章内容
[2] 《统计学习方法(李航)》