决策树
1.基础知识:
决策树理解起来是很浅显易懂的一种分类算法,它从数据集中提取出一系列的规则。每一条达到叶节点的路径都是一条规则,用于决策所属类别。将特征作为树的非叶节点;分支是对应特征的每类属性值(已给每条数据都有属性值以及类别);叶节点是所属类别。
如图:
熵的计算:那么怎么选择具有决定性的特征呢?为了找到决定性特征,划分出最好的结果,我们必须评估每个特征。
熵定义为信息的期望值,先看看信息的计算。如果 xi 类别被划分在含有x1,x2…xn 类的集合中(混合类), xi 的信息定义为如下:
其中,p(xi)是选择该分类的概率。可以看出 p(xi) 越大,信息越小。也就是集合中类的种类越单一,信息也就越小。
熵的计算如下,集合中 所有类别对应的信息*概率 的和即为信息的期望:
2.1信息增益(ID3),越大越好
D:数据集
A:特征A
K:类别的集合 k~K
D的经验熵:
(表示数据集D的纯度,H越小,纯度越高)
特征A将数据集D分成N个数据集,特征A对数据集D的经验条件熵:
(即给定特征A,计算每个子数据集的纯度再求和,表示给定A后数据集的纯度,数值越小纯度越高)
特征A对数据集的信息增益:
(即特征A帮助提升的纯度的大小,值越大越好)
2.2 信息增益率(C4.5)越大越好
由于信息增益会偏向取值较多的特征(过拟合),解释:当特征A取值很多,则划分出的组数增多,使得H(D|A)减小,则信息增益增大。但是过于精细的划分,会使得分类失去意义。(比如按照身份证号给人分类,则每一个人都是一类)。
特征A对数据集D的信息增益率:
其中,特征A将数据集分成N类,则对于所有特征A相对应的数据的N个类的信息经验熵为(即表示了特征A为类别标签时,数据D的纯度):
因为当A将数据集D分成太多类时,其纯度降低,H(A)增加,相当于给信息增益添加了一项惩罚项。
2.3 Gini系数(CART)越小越好
基尼指数:从数据集里随机选取子项,度量其被错误分类到其他分组里的概率。基尼系数指数据的不纯度,越小越好。
CART是一个二叉树分类。
K是数据集D的标签集合:k~K
数据D的基尼系数:
若特征A将数据D分成两个数据集
但是若当特征A将数据集D分成超过两个数据集时,需要计算以每一个取值作为划分点,对样本D划分之后子集的纯度Gini(D,Ai),(其中Ai 表示特征A的可能取值),然后从所有的可能划分的Gini(D,Ai)中找出Gini指数最小的划分,这个划分的划分点,便是使用特征A对样本集合D进行划分的最佳划分点。
对各个概念的理解:熵描述了信息的不确定性,我们的目标是划分数据集D,使得划分后的数据集的不确定性降低。信息增益描述了特征A对于降低数据集D的不确定性的帮助,信息增益越大,说明A的引入增加了信息,降低了D的不确定性。然而,由于信息增益会偏向取值较多的特征,为了解决这个特征之间的选择不平衡的现象,我们添加了一项惩罚项,引入了信息增益率。
3.剪枝处理
构建决策树时,根据最优的特征进行构建,往往会使得所得的树分支过多,过拟合。因此需要对树进行剪枝。
预剪枝:边构建树边剪枝。当到某一节点无法达到要求时,即停止分支。
后剪枝:先完整地构建出决策树,然后自下而上地进行剪枝。
判断是否剪枝的标准:剪枝前后的泛化能力,若剪枝后泛化能力增强,则剪枝,否则不剪枝。
泛化能力的判断方法:留出法。即在原数据集中拿出一部分做为验证集,剪枝时,判断该分支分与部分对验证集分类的影响(准确率的影响)。
注意:因为预剪枝含有“贪心”的思想,即一旦某个节点分支不能提高泛化能力,立即停止。此时可能会忽略后续泛化能力高的分支。因此预剪枝可能会造成“欠拟合”。
4.直接上代码
from math import *
import operator
import pickle
def createDataSet():
dataSet = [[1, 1, 1, 'yes'],
[1, 0, 1, 'yes'],
[1, 0, 2, 'no'],
[0, 1, 2, 'no'],
[0, 1, 2, 'no'],
[1, 1, 2, 'yes'],
[0, 0, 2, 'yes'],
[0, 1, 0, 'no'],
]
labels = ['ddddd','fffff','sssss']
#change to discrete values
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[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts: # 计算信息熵
prob = float(labelCounts[key]) / numEntries
shannonEnt -= prob * log(prob, 2)
return shannonEnt
myDat,labels=createDataSet()
shannonEnt=calcShannonEnt(myDat)
# 划分数据集,axis:按第几个属性划分,value:要返回的子集对应的属性值
def splitDataSet(dataSet, axis, value):
retDataSet = []
featVec = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 # 属性的个数。 -1是因为最后一个是分类的标签
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
for i in range(numFeatures): # 对每个属性记录信息增益
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 该属性的取值集合
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
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys(): classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(), #注意python3.x中要改成.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]
uniqueValue = set(featValues) # 该属性所有可能取值,也就是节点的分支
for value in uniqueValue: # 对每个分支,递归构建树
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(
splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
print(createTree(myDat, labels))
流程图如图:
机器学习的结果:
{'sssss': {0: 'no', 1: 'yes', 2: {'ddddd': {0: {'fffff': {0: 'yes', 1: 'no'}}, 1: {'fffff': {0: 'no', 1: 'yes'}}}}}}
我画了一个决策树的图,便于更好的理解机器学习的结果:
如果有问题评论区留言:
有错误我立即改正,你我共同努力。