决策树
- 说明:本章节所有代码使用版本为Python3
优点:计算复杂度不高,输出结果易于理解,对中间值缺失不敏感,可以处理不相关特征数据
缺点:可能会产生过度匹配问题
适用数据类型:数值型和标称型
解释:
标称型:标称型目标变量的结果只在有限目标集中取值,比如真与假(标称型目标变量主要用于分类)
数值型(连续型):数值型目标变量则可以从无限的数值集合中取值
决策树经常用于解决处理分类问题,它的一个重要任务是为了理解数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集并从中提取出一系列规则,这些机器根据数据集创建规则的过程,就是机器学习的过程。专家系统中经常使用决策树,而且决策树给出结果往往可以匹敌人类专家。
决策树的构造
决策树的一般流程
- 收集数据:可以使用任何方法
- 准备数据:树构造的算法只适用于标称型数据,因此数值型数据必须离散化
- 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期
- 训练算法:构造树的数据结构
- 测试算法:使用经验树计算错误率
- 使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好的理解数据的内在含义
一些决策树算法采用二分法划分数据,而这里使用ID3算法划分数据集,每一次划分数据集时只选取一个特征属性。
1、信息增益
划分数据集的最大原则:将无序的数据变得有序。组织杂乱无章数据的一种方式就是使用信息论度量信息,量信息是量化处理信息的分支科学。我们可以在划分数据之前或之后使用信息量化度量信息的内容。
划分数据之前之后信息发生的变化称之为信息增益,知道如何计算信息增益,就可以计算每一个特征值划分数据集获得信息增益,获得信息增益最高的特征就是最好的选择。
集合信息的度量方式是香农熵或简称熵。熵定义信息的期望值。若待分类的事务可能划分在多个分类之中,则符号x_i的信息定义为:
l ( x i ) = − l o g 2 p ( x i ) l(x_i)=-log_2 p(x_i) l(xi)=−log2p(xi)
为了计算熵,需要计算所有类别所有可能包含的信息期望,通过下面公式得到:
− ∑ i = 1 n p ( x i ) l o g 2 p ( x i ) -\sum_{i=1}^n p(x_i) log_2 p(x_i) −i=1∑np(xi)log2p(xi)
熵越高,混合的数据也就越多。得到熵之后,可以按照获取最大信息增益的方法划分数据集。
2、划分数据集
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据的熵,以便判断当前是否正确的划分了数据集。我们对每一个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的方式。
3、递归构建决策树
目前我们已经学习了冲数据集构造决策树所需的子功能模块。其工作原理是:
- 得到原始数据集
- 基于最好的属性值划分数据集,由于特征值可能有多于两个,因此可能存在大于两个分支的数据划分
- 第一次划分后,数据将向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据集
- 最后采用递归的原则处理数据集
递归结束的条件是:遍历完所有的数据集的属性,或者每个分支下的所有实例都具有相同的分类。若所有实例都具有相同的分类,则得到一个叶子节点或者终止块。任何到达叶子节点的数据必然是属于叶子节点的分类。
from math import log
import operator
# 自己创建简单的数据集
def createDataSet():
dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [
1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
labels = ['no surfacing', 'flippers']
return dataSet, labels
# 计算给定数据集的香农熵
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
# 获取传来数据集的长度
labelCounts = {
}
for featVec in dataSet:
currentLabel = featVec[-1]
# 获取数据每一行数据集的最后一个元素
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
# 如果最后一个数据的元素不在labelCounts集合中,则添加进去,值为0
labelCounts[currentLabel] += 1
# for循环中是为所有的可能分类创造字典
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
# labelCounts[key]表示字典中key所对应的value值
# prob计算出,每一个key所出现的概率
shannonEnt -= prob*log(prob, 2)
# 以2为底求对数,根据计算香农熵的公式得出的
return shannonEnt
# 给定特征划分数据集
def splitDataSet(dataSet, axis, value):
# dataSet表示待划分的数据集,axis表示划分数据集的特征,value表示需要返回的特征值
retDataSet = []
# 创建list对象
for featVec in dataSet:
if featVec[axis] == value:
# 将符合特征的数据抽取出来
reducedFeatVec = featVec[:axis]
# 指定特征之前的数据
reducedFeatVec.extend(featVec[axis+1:])
# 指定特征之后的数据,extend在已存在的列表中添加新的列表内容
retDataSet.append(reducedFeatVec)
# retDataSet相比于reducedFeatVec没有了特征数据
# 注意extend与append的区别,extend是将每一个元素分别合并到列表中,而append是将一个列表当作一个元素合并到列表中
return retDataSet
# 返回符合要求的元素
# 选择最好的数据划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0])-1
# #统计特征数目(最后一个元素不是特征所以要删去)
baseEntropy = calcShannonEnt(dataSet)
# 计算原始数据的香农熵
bestInfoGain =