决策树
决策树是附加概率结果的一个树状的决策图,是直观的运用统计概率分析的图法。机器学习中决策树是一个预测模型,它表示对象属性和对象值之间的一种映射,树中的每一个节点表示对象属性的判断条件,其分支表示符合节点条件的对象。树的叶子节点表示对象所属的预测结果。(==自己理解各位大神勿喷:==决策树主要作用于预测,根据一定的信息数据推断结果,比如:根据一定量的数据【男生,女生,声音粗细,头发长短等】推断此人是男生还是女生。)
信息熵
信息是一种抽象的概念,那么如何对信息进行一个量化的操作呢?1948年,香农提出了“信息熵”的概念。一条信息的信息量大小和它的不确定性有直接的关系,要搞清楚一件非常不确定的事情,或者说我们对一件事情一无所知,就需要了解大量的信息,信息量的度量就等于不确定性的多少。
举个栗子,NBA总决赛的夺冠球队,假设你对NBA球队一无所知,你需要猜多少次?(假设每个球队的夺冠几率都是一样的)这里我们可以给进入季后赛的NBA球队进行编号(NBA季后赛会选出16支球队),然后使用二分法进行猜测(猜测冠军队伍在1-8号球队之间,是的话,在进行二分;不是的话就在9-16号球队之间),这样我们要猜测的次数最多是4次(2^4=16嘛)。
信息熵使用比特(bit)来衡量信息的多少,计算公式如下:
-(P1log2 P1+P2log2 P2+…P16*log2 P16)
计算NBA季后赛总冠军的夺冠球队的信息熵值,含义是每一个球队的夺冠概率乘以,以2为底这个队夺冠的对数。P1、P2…PN表示哪一支球队的夺冠概率,假设每一个球队夺冠的概率都相等的话,那么这里算出的信息熵值就是4,当然这种情况是不太可能存在的,因为每一个球队的实力不一样。
变量的不确定越大,熵的值也就越大。
决策树的优点:
直观,便于理解,小规模数据集有效
决策树的缺点:
处理连续变量不好;类别较多时,错误增加的比较快;可规模性一般。
使用决策树做预测需要以下过程:
1,收集数据:可以使用任何方法。比如想构建一个相亲系统,我们可以从媒婆那里,或者通过参访相亲对象获取数据。根据他们考虑的因素和最终的选择结果,就可以得到一些供我们利用的数据了。
2,准备数据:收集完的数据,我们要进行整理,将这些所有收集的信息按照一定规则整理出来,并排版,方便我们进行后续处理。
3,分析数据:可以使用任何方法,决策树构造完成之后,我们可以检查决策树图形是否符合预期。
4,训练算法:这个过程也就是构造决策树,同样也可以说是决策树学习,就是构造一个决策树的数据结构。
5,测试算法:使用经验树计算错误率。当错误率达到了可接收范围,这个决策树就可以投放使用了。
6,使用算法:此步骤可以使用适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义。
创建决策树算法案例
案例1: 判定鱼类和非鱼类
根据以下 2 个特征,将动物分成两类:鱼类和非鱼类。
特征:
- 不浮出水面是否可以生存
- 是否有脚蹼
计算给定数据集的香农熵的函数
def calcShannonEnt(dataSet):
# 求list的长度,表示计算参与训练的数据量
numEntries = len(dataSet)
# 计算分类标签label出现的次数
labelCounts = {}
# the the number of unique elements and their occurrence
for featVec in dataSet:
# 将当前实例的标签存储,即每一行数据的最后一个数据代表的是标签
currentLabel = featVec[-1]
# 为所有可能的分类创建字典,如果当前的键值不存在,则扩展字典并将当前键值加入字典。每个键值都记录了当前类别出现的次数。
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
# 对于 label 标签的占比,求出 label 标签的香农熵
shannonEnt = 0.0
for key in labelCounts:
# 使用所有类标签的发生频率计算类别出现的概率。
prob = float(labelCounts[key])/numEntries
# 计算香农熵,以 2 为底求对数
shannonEnt -= prob * log(prob, 2)
return shannonEnt
选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
"""chooseBestFeatureToSplit(选择最好的特征)
Args:
dataSet 数据集
Returns:
bestFeature 最优的特征列
"""
# 求第一行有多少列的 Feature, 最后一列是label列嘛
numFeatures = len(dataSet[0]) - 1
# 数据集的原始信息熵
baseEntropy = calcShannonEnt(dataSet)
# 最优的信息增益值, 和最优的Featurn编号
bestInfoGain, bestFeature = 0.0, -1
# iterate over all the features
for i in range(numFeatures):
# create a list of all the examples of this feature
# 获取对应的feature下的所有数据
featList = [example[i] for example in dataSet]
# get a set of unique values
# 获取剔重后的集合,使用set对list数据进行去重
uniqueVals = set(featList)
# 创建一个临时的信息熵
newEntropy = 0.0
# 遍历某一列的value集合,计算该列的信息熵
# 遍历当前特征中的所有唯一属性值,对每个唯一属性值划分一次数据集,计算数据集的新熵值,并对所有唯一特征值得到的熵求和。
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value)
# 计算概率
prob = len(subDataSet)/float(len(dataSet))
# 计算信息熵
newEntropy += prob * calcShannonEnt(subDataSet)
# gain[信息增益]: 划分数据集前后的信息变化, 获取信息熵最大的值
# 信息增益是熵的减少或者是数据无序度的减少。最后,比较所有特征中的信息增益,返回最好特征划分的索引值。
infoGain = baseEntropy - newEntropy
print('infoGain=', infoGain, 'bestFeature=', i, baseEntropy, newEntropy)
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
创建树的函数代码如下:
def createTree(dataSet, labels):
classList = [example[-1] for example in dataSet]
# 如果数据集的最后一列的第一个值出现的次数=整个集合的数量,也就说只有一个类别,就只直接返回结果就行
# 第一个停止条件:所有的类标签完全相同,则直接返回该类标签。
# count() 函数是统计括号中的值在list中出现的次数
if classList.count(classList[0]) == len(classList):
return classList[0]
# 如果数据集只有1列,那么最初出现label次数最多的一类,作为结果
# 第二个停止条件:使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组。
if len(dataSet[0]) == 1:
return majorityCnt(classList)
# 选择最优的列,得到最优列对应的label含义
bestFeat = chooseBestFeatureToSplit(dataSet)
# 获取label的名称
bestFeatLabel = labels[bestFeat]
# 初始化myTree
myTree = {bestFeatLabel: {}}
# 注:labels列表是可变对象,在PYTHON函数中作为参数时传址引用,能够被全局修改
# 所以这行代码导致函数外的同名变量被删除了元素,造成例句无法执行,提示'no surfacing' is not in list
del(labels[bestFeat])
# 取出最优列,然后它的branch做分类
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
# 求出剩余的标签label
subLabels = labels[:]
# 遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数createTree()
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
# print 'myTree', value, myTree
return myTree