第八十四篇 决策树

心得:终于可以来总结一下关于决策树的内容了,接下来的几篇来逐一介绍决策树,随即森林,以及xgboos等,希望自己可以对这些知识理解更深刻。

一、简介

什么是决策树,字面意思是用来决策的一种树模型,下图就是一个女生相亲的树形结构图,女生会根据男生的年龄,长相,收入,是否为公务员等来决定自己的相亲标准;所以决策树模型就相当于我们根据已有的样本建立一颗树,来预测未知样本会产生的结果。

什么是树,二叉树:https://blog.csdn.net/l_ppp/article/details/108307532

在这里插入图片描述

二、相关概念

我们想要构建一颗决策树,选取节点的依据是什么呢,年龄重要呢,还是长相重要,用什么可以去衡量这些特征的重要程度呢,以下我们介绍一下,现有的特征选取的几种方式。如下列表有两个特征怎么根据这两个特征来判断性别,那个先判断更好呢!
在这里插入图片描述


  1. 信息熵(Information Entropy):
    信息熵是来计算一个样本集合中的数据是纯洁的还是不纯洁,公式如下:
    在这里插入图片描述
    首先计算未分类前的熵,总共有8位同学,男生3位,女生5位
    熵(总)=-3/8log2(3/8)-5/8log2(5/8)=0.9544

先按照头发来分类:长头发中有1男3女。短头发中有2男2女

熵 (长)= -1/4*log2(1/4)-3/4*log2(3/4) = 0.8113 
熵(短)= -2/4*log2(2/4)-2/4*log2(2/4)=1 
熵(头发) = 4/8*0.8113+4/8*1=0.9057 

按声音特征来分,分类后的结果为:声音粗中有3男3女。声音细中有0男2女。

熵(声音粗)=-3/6*log2(3/6)-3/6*log2(3/6)=1 
熵(声音细)=-2/2*log2(2/2)=0 
熵(声音)=6/8*1+2/8*0=0.75 

信息熵代表的是纯洁程度,显然声音的信息熵更小,说明用声音来做区分的话,样本会分的开一些,更纯洁

  1. 信息增益 ID3
    什么是信息增益,信息增益表示得知特征信息而使得类的信息不确定性减少的程度,那么根据这个描述,我们如果使用信息增益作为决策树最优选择的依据,那么就要选取使得类信息不确定性减少最多的,也就是信息增益最大的特征。

    信息增益越大,该属性分类越好

    信息增益公式:在这里插入图片描述
    上表中:

Gain(头发) = 熵(总)- 熵(头发) = 0.9544-0.9057=0.0487
Gain(声音) = 熵(总)- 熵(声音)= 0.9544-0.75=0.2087

很明显声音的信息增益更大,所以选择声音分的效果会更好

  1. 信息增益率 ID4.5
    信息增益准则对可取值数目较多的属性有所偏好,取一个极端的例子,当我们如果把样本集中的编号也作为一个属性的时候,那么决策树生成的只会是一个深度为2,一个根节点,m个叶节点的树结构,但是这样的结构具有非常大的过拟合特性,根本不是我们想要。所以我们为了避免这种偏好,定义了 信息率
    公式如下:
    在这里插入图片描述
  • ID3算法中,选择的是信息增益来进行特征选择,信息增益大的特征优先选择。
  • 而在C4.5中,选择的是信息增益比来选择特征,以减少信息增益容易选择特征值多的特征的缺点。

离散数据,而且每个类别都有一定数量的样本,这种情况下使用ID3与C4.5的区别并不大。但如果面对连续的数据(如体重、身高、年龄、距离等),或者每列数据没有明显的类别之分(最极端的例子的该列所有数据都独一无二),这时使用信息增益率效果会好一些

Gain_ratio(头发) = Gain(头发) / 熵(总) = 0.0487 / 0.9057 = 0.05377056420448272
Gain_ratio(声音) = 熵(总)- 熵(声音)= 0.2087 / 0.9057 = 0.23042950204261897

显然声音的信息增益率更高

  1. gini指数
    基尼指数的意义是从数据集D中随机抽取两个样本类别标识不一致的概率。基尼指数越小,数据集的纯度越高。
    相比于信息增益,信息增益比等作为特征选择方法,基尼指数省略了对数计算,运算量比较小,也比较容易理解,所以CART树选择使用基尼系数用来做特征选择
    在这里插入图片描述
    先按照头发来分类:长头发中有1男3女。短头发中有2男2女
gini (长)= 1/4*(1-1/4)+3/4*(1-3/4)  = 0.375
gini (短)= 2/4*(1-2/4)+2/4(1-2/4) = 0.5
gini(头发) =4/8*0.375 + 4/8*0.5 = 0.4375

按声音特征来分,分类后的结果为:声音粗中有3男3女。声音细中有0男2女

gini(声音粗)= 1-((3/6)**2+(3/6)**2) = 0.5
gini(声音细)= 1 - (0 + (2/2)**2) = 0
gini(声音)=6/8*0.5+2/8*0=0.375

明显可以看出来声音的gini更小,如果用声音来进行切分的话分的纯度会更高。

三、python快速开始

用熵值来计算

from math import log
import operator


def calcShannonEnt(dataSet):  # 计算数据的熵(entropy)
    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
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries  # 计算单个类的熵值
        shannonEnt -= prob * log(prob, 2)  # 累加每个类的熵值
    return shannonEnt


def createDataSet1():  # 创造示例数据
    dataSet = [['长', '粗', '男'],
               ['短', '粗', '男'],
               ['短', '粗', '男'],
               ['长', '细', '女'],
               ['短', '细', '女'],
               ['短', '粗', '女'],
               ['长', '粗', '女'],
               ['长', '粗', '女']]
    labels = ['头发', '声音']  # 两个特征
    return dataSet, labels


def splitDataSet(dataSet, axis, value):  # 按某个特征分类后的数据
    retDataSet = []
    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
    baseEntropy = calcShannonEnt(dataSet)  # 原始的熵
    bestInfoGain = 0
    bestFeature = -1
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)
        newEntropy = 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):  # 按分类后类别数量排序,比如:最后分类为2男1女,则判定为男;
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.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]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet \
                                                      (dataSet, bestFeat, value), subLabels)
    return myTree


if __name__ == '__main__':
    dataSet, labels = createDataSet1()  # 创造示列数据
    print(createTree(dataSet, labels))  # 输出决策树模型结果

输出:

{'声音': {'粗': {'头发': {'长': '女', '短': '男'}}, '细': '女'}}

四、sklearn决策树

  1. 预剪枝

    就是在构建决策树的时候提前停止。比如指定树的深度最大为3,那么训练出来决策树的高度就是3,预剪枝主要是建立某些规则限制决策树的生长,降低了过拟合的风险,降低了建树的时间,但是有可能带来欠拟合问题。

  2. 后剪枝

    后剪枝是一种全局的优化方法,在决策树构建好之后,然后才开始进行剪枝。后剪枝的过程就是删除一些子树,这个叶子节点的标识类别通过大多数原则来确定,即属于这个叶子节点下大多数样本所属的类别就是该叶子节点的标识。选择减掉哪些子树时,可以计算没有减掉子树之前的误差和减掉子树之后的误差,如果相差不大,可以将子树减掉。一般使用后剪枝得到的结果比较好

  3. 参数介绍

    1. criterion(判断,准则):特征选择标准

    ‘gini’ or ‘entropy’ (default=”gini”),前者是基尼系数,后者是信息熵。两种算法差异不大对准确率无影响,信息墒云孙效率低一点,因为它有对数运算.一般说使用默认的基尼系数”gini”就可以了,即CART算法。除非你更喜欢类似ID3, C4.5的最优特征选择方法。

    2. splitter :特征划分标准

    ‘best’ or ‘random’ (default=”best”) 前者在特征的所有划分点中找出最优的划分点。后者是随机的在部分划分点中找局部最优的划分点。 默认的”best”适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推荐”random” 。

    3. max_depth :决策树最大深度

    int or None, optional (default=None) 一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。常用来解决过拟合

    4. min_impurity_decrease : 节点划分最小不纯度

    float, optional (default=0.) 这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。 sklearn 0.19.1版本之前叫 min_impurity_split

    5. min_samples_split : 内部节点再划分所需最小样本数

    int, float, optional (default=2) 如果是 int,则取传入值本身作为最小样本数; 如果是 float,则去 ceil(min_samples_split * 样本数量) 的值作为最小样本数,即向上取整。

    6. min_samples_leaf : 叶子节点最少样本数

    如果是 int,则取传入值本身作为最小样本数; 如果是 float,则去 ceil(min_samples_leaf * 样本数量) 的值作为最小样本数,即向上取整。 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。

    7. max_leaf_nodes : 最大叶子节点数

    int or None, optional (default=None) 通过限制最大叶子节点数,可以防止过拟合,默认是”None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。

    8. min_impurity_split : 信息增益的阀值

    决策树在创建分支时,信息增益必须大于这个阀值,否则不分裂

    9. min_weight_fraction_leaf : 叶子节点最小的样本权重和

    float, optional (default=0.) 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。

    10.class_weight: 类别权重

    dict, list of dicts, “balanced” or None, default=None 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重,或者用“balanced”,如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果你的样本类别分布没有明显的偏倚,则可以不管这个参数,选择默认的”None” 不适用于回归树 sklearn.tree.DecisionTreeRegressor

  4. sklean 决策树实现

from sklearn.datasets import load_iris
from sklearn import tree
from sklearn.model_selection import train_test_split

#加载数据
iris = load_iris()
X,Y = iris.data,iris.target 
x_train,x_test,y_train,y_test=train_test_split(X,Y,test_size=0.3)

#训练
clf = tree.DecisionTreeClassifier()
clf.fit(x_train,y_train)
clf.score(x_test,y_test)

#画图
import graphviz
dot_data = tree.export_graphviz(clf,out_file=None)
graph = graphviz.Source(dot_data)
#graph.render("iris")
graph  

此处的value代表的是分为几类,每一类当前节点所拥有的的sample数量

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值