2021-04-22

决策树

在这里插入图片描述

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'}}}}}}

我画了一个决策树的图,便于更好的理解机器学习的结果:

在这里插入图片描述
如果有问题评论区留言:
有错误我立即改正,你我共同努力。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值