决策树python实现及常见问题总结

一、概述

决策树是一种基于树结构,使用层层推理来解决分类(回归)问题的算法
决策树由下面几种元素构成:
决策树python实现及常见问题总结_第1张图片
决策树模型的三个步骤

  1. 特征选择
  2. 决策树生成
  3. 决策树剪枝

二、特征选择

根据特征选择不同方法有三种经典的决策树算法:ID3、C4.5、CART
特征选择的目标:决策树分类的目标就是让同类样本最终在同一叶节点中,不同类最终在不同叶节点中,所以在划分过程中,我们希望决策树的分支节点所包含的样本尽可能属于同一类别,即节点的“纯度”越来越高。

1.ID3

采用信息增益作为特征选择的方法。“信息熵”是度量样本纯度的常用的一种指标信息熵越小,纯度越高。

在这里插入图片描述
在这里插入图片描述
G a i n ( D , a ) Gain(D,a) Gain(D,a)可知,信息增益越大,使用属性a来进行划分得到纯度提升越大,所以我们选择信息增益最大的属性来进行划分。
使用信息增益来进行特征选择一个缺点是:该方法对数目较多的属性有所偏好

2、C4.5

C4.5算法采用信息增益率作为特征选择的方法,此方法正是为了解决信息增益准则对可取值较多的属性有所偏好的不利影响。
决策树python实现及常见问题总结_第2张图片
但遗憾的是,采用欣喜增益率准也有缺点:对可取值较少的属性有所偏好

3、CART

CART采用基尼指数作为特征选择的方法。
决策树python实现及常见问题总结_第3张图片
基尼指数表示在数据集中随机抽取两个样本,他们类别不一致的概率,很明显,基尼指数越小,即随机抽取的两个样本类别不一致的概率越小,类别一致的概率就越高,那么纯度就越高。采用基尼指数也减少了大量的对数运算
在这里插入图片描述
另外,CART可以用于回归任务。

三、剪枝处理

剪枝处理是决策树算法解决过拟合的主要手段,基本策略有:

  1. 预剪枝:在决策树生成过程中,对每个进行划分前先进行估计,如果不能带来决策树泛化能力的提升,则停止划分并将当前结点标记为叶节点。预剪枝也带了“欠拟合的风险
  2. 后剪枝:先生成一颗完整的决策树,然后自底而上的对非叶结点进行考察,若将该结点替换成叶结点能带泛化能力的提升,则将该子树替换成叶结点。时间开销大于预剪枝

ID3算法没有剪枝策略,
根据衡量泛化能力的方法有很多不同策略的剪枝,ID3算法采用悲观剪枝策略,CART采用代价复杂度剪枝策略。

**代价复杂度剪枝(后剪枝)**主要包含以下两个步骤:

  1. 从完整决策树 T 0 T_0 T0开始,生成一个子树序列${T_0,T_1,…,T_n},其中 T ( i + 1 ) T_(i+1) T(i+1) T i T_i Ti 生成, T n T_n Tn为根结点。
    如何从从 T i T_i Ti 生成 T ( i + 1 ) T_(i+1) T(i+1)呢?
    裁剪 T i T_i Ti中误差增加率最小的分支节点得到,意思就是计算 T i T_i Ti所有结点进行剪枝前后决策树的误差增加率最小的,误差增加率最小,说明在这个结点进行剪枝处理对决策树的泛化能力影响最小。
    误差增加率定义:
    决策树python实现及常见问题总结_第4张图片
    其中R(t)表示进行剪枝之后的该结点误差, R ( T t ) R(T_t) R(Tt))表示未进行剪枝时子树 T t T_t Tt的误差。考虑到树的复杂性因素,我们用 L ( T t ) L(T_t) L(Tt)|表示子树 T t T_t Tt的叶子结点个数.

四、缺失值处理

1. 如何在特征值缺失的情况下进行划分特征的选择?
2. 选定该划分特征,模型对于缺失该特征值的样本该进行怎样处理?

CART采取的机制是代理分裂,即如果某个存在缺失值的特征是当前最佳的分裂特征,那么我们需要遍历剩余所有特征,选择能与最佳增益的缺失特征分裂之后增益最接近的特征进行分裂,剩余特征中如果也存在缺失值,那这些特征也忽略。选定划分特征之后,当前缺失该特征的样本划分到左右子树取决于代理特征。这种处理缺失值的方法,需要遍历其他所有特征代价太大。Sklearn中决策树模型没有缺失值的处理。

C4.5处理缺失值的方法,是通过计特征上未缺失值的样本的信息增益,然后加上个未缺失样本的权重,选择划分特征之后,对于缺失特征值的样本,让该样本以不同概率划分到不同的节点中去。

五、优缺点及使用场景

1、回答决策树的优缺点?

决策树的优点:
1、计算复杂度不高,输出结果易于理解,
2、对中间值的缺失不敏感(CART、C4.5),比较适合处理有缺失属性的样本
3、可以处理不相关特征数据。
4、可以同时处理标称型和数值型数据
缺点:
1、容易过拟合
2、容易忽略数据集中属性的相互关联
3、使用信息增益和CART的决策树对可取数目较多的属性有所偏好,使用信息增益率对可取值较少的属性有所偏好。

2、三种算法之间的差异

除了特征选择,剪枝处理的差异之外,还可以从以下角度来考虑三者之间的差异。

  1. 从应用角度,ID3和C4.5只能用于分类任务,而CART(Classification and Regression Tree,分类回归树)从名字就可以看出其不仅可以用于分类,也可以应用于回归任务(回归树使用最小平方误差准则)
  2. 从样本类型的角度,ID3只能处理离散型变量,而C4.5和CART都可以处理连续型变量。C4.5处理连续型变量时,通过对数据排序之后找到类别不同的分割线作为切分点,根据切分点把连续属性转换为布尔型,从而将连续型变量转换多个取值区间的离散型变量。而对于CART,由于其构建时每次都会对特征进行 二值划分,因此可以很好地适用于连续性变量。
  3. 从实现细节、优化过程等角度,这三种决策树还有一些不同。比如,ID3对样本特征缺失值比较敏感,而C4.5和CART可以对缺失值进行不同方式的处理;ID3和C4.5可以在每个结点上产生出多叉分支,且每个特征在层级之间不会复用,而CART每个结点只会产生两个分支,因此最后会形成一颗二叉树,且每个特征可以被重复使用;ID3和C4.5通过剪枝来权衡树的准确性与泛化能力,而CART直接利用全部数据发现所有可能的树结构进行对比。

六、CART决策树构建回归树的区别

CART回归树和CART分类树的建立算法大部分都是类似的,所以我们这里只讨论CART回归树和CART分类树之间的建立算法的不同。

回归树和分类树的最主要区别在于样本输出,如果样本输出是离散值,那么这是一颗分类树,如果样本输出是连续值,那么这是一颗回归树。

除了概念上的不同之外,CART回归树和CART分类树的建立和预测的其区别主要有下面两点:

  • 连续值的处理不同:确定特征以及特征值的划分点不同。
  • 决策树建立之后做预测的方式不同

对于连续值的处理,CART分类时是基于基尼系数的大小来度量特征的各个划分点的优劣情况。这比较适合分类树,对于回归模型,我们知道最常用的度量方式是均方误差,这里回归树采用的是和方差的度量方式,对于任意划分特征A,对应的任意划分点s两边划分成的数据集D1和D2,求出使D1和D2各自集合的均方差最小,同时D1和D2的均方差之和最小对应的特征和特征值划分点。表达式为:

决策树python实现及常见问题总结_第5张图片
对于决策树建立后做预测的方式,上面讲到了CART分类树采用叶子节点里概率最大的类别作为当前节点的预测类别。而回归树输出不是类别,它采用的是用最终叶子的均值或者中位数来预测输出结果。

除了上面提到了以外,CART回归树和CART分类树的建立算法和预测没有什么区别。

七、决策树算法小结

优点:

  1. 简单直观,相比较于神经网络之类的黑盒模型,决策树逻辑上可以得到很好的解释。
  2. 基本不需要数据预处理,不需要提前归一化,处理缺失值。
  3. 使用决策树预测代价是O(log2m)。m为样本数
  4. 既可以处理离散值也可以处理连续。
  5. 可以处理多维度输出的分类问题。叶子结点做多分类。
  6. 对异常点的容错能力好,健壮性高

缺点:

  1. 决策树非常容易过拟合,导致泛化能力不强,可以通过剪枝,设置节点最小样本数量和限制树的深度来改进,
  2. 决策树回因为样本一点点变动,就会导致树结构的剧烈改变,这个可以通过集成学习的方法来改进。
  3. 模型的表达能力有限。
  4. 如果某些特征的样本比例过大,生成决策树容易偏向于这些特征。这个可以通过调节样本权重来改善。

八、python简单实现

"""
伪代码:
creteBranch函数
检测数据集中的每个子项是否属于同一分类:
    if so return 类标签;
    else
        寻找划分数据集的最好特征
        划分数据集
        创建分支节点
            for 每个划分子集
                调用函数createBranch并增加返回结果到分支节点中
        return 分支节点
"""
from math import *
import operator
class Tree:

    def clacShannonEnt(self, dataset):
        """
        计算香浓熵的函数
        :param dataset:数据集
        :return: 香浓熵的值
        """
        numEntries = len(dataset)
        labelCounts = {}
        for featVec in dataset:
            currentLabel = featVec[-1]
            labelCounts[currentLabel] = labelCounts.get(currentLabel, 0)+1

        shannonEnt = 0.0
        for key in labelCounts:
            prob = float(labelCounts[key])/numEntries
            shannonEnt -= prob*log(prob, 2)
        return shannonEnt

    def splitDataSet(self,dataset, axis, value):
        """
        按照给定特征划分数据集
        :param dataset: 待划分数据集
        :param axis: 待划分数据集特征索引
        :param value: 需要返回的特征的值
        :return:
        """
        retDataSet = []
        for featVec in dataset:
            if featVec[axis] == value:
                reduceFeatVec = featVec[:axis]
                reduceFeatVec.extend(featVec[axis+1:])
                retDataSet.append(reduceFeatVec)
        return retDataSet

    def chooseBestFeatureToSplit(self,dataset):
        """
        选择最好的数据集划分方式
        :param dataset: 数据集
        :return: 最好的划分特征
        """
        numFeatures = len(dataset[0]) - 1
        bestEntropy = self.clacShannonEnt(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 = self.splitDataSet(dataset, i, value)
                prob = len(subDataSet)/float(len(dataset))
                newEntropy += prob*self.clacShannonEnt(subDataSet)
            infoGain = bestEntropy-newEntropy
            if (infoGain>bestInfoGain):
                bestInfoGain = infoGain
                bestFeature = i
        return bestFeature

    def majorityCnt(self, classList):
        """
        多数投票法
        :param classList:分类名称的列表
        :return: 返回出现次数最多的类
        """
        classCount = {}
        for vote in classList:
            classCount[vote] = classCount.get(vote, 0) + 1
        sortedClassCount = sorted(classCount.iteritems(),
                                  key=operator.itemgetter(1), reverse=True)
        return sortedClassCount[0][0]

    def createTree(self, dataset, labels):
        """
        创建树的函数代码
        :param dataset: 数据集
        :param labels: 标签列表
        :return: 决策树
        """
        classList = [example[-1] for example in dataset]
        #print(classList)
        #递归终止条件
        if classList.count(classList[0]) == len(classList):
            return classList[0]
        if len(dataset[0])==1:
            return self.majorityCnt(classList)
        bestFeat = self.chooseBestFeatureToSplit(dataset)
        bestFeatLabel = labels[bestFeat]
        myTree = {bestFeatLabel:{}}
        del(labels[bestFeat])
        featValue = [example[bestFeat] for example in dataset]
        uniqueVals = set(featValue)
        for value in uniqueVals:
            subLabels = labels[:]
            myTree[bestFeatLabel][value] = self.createTree(self.splitDataSet(dataset, bestFeat, value)
                                                           , subLabels)
        return myTree

    def classify(self, inputTree, featLabels, testVec):
        global classLabel
        firstStr = list(inputTree.keys())[0]
        # print(featLabels)
        # print(firstStr)
        secondDict = inputTree[firstStr]
        featIndex = featLabels.index(firstStr)
        #print(featIndex)
        for key in secondDict.keys():
            print(key)
            if testVec[featIndex] == key:
                if type(secondDict[key]).__name__=='dict':
                    classLabel = self.classify(secondDict[key],featLabels,testVec)
                else:
                    classLabel = secondDict[key]
        return classLabel
if __name__ == "__main__":
    dataset = [[1,1,'yes'],
               [1,1,"yes"],
               [1,0,'no'],
               [0,1,'no'],
               [0,1,'no']]
    labels = ["no surfacing", "fileters"]
    #print(labels.index("no surfacing"))
    tree = Tree()
    myTree = tree.createTree(dataset, labels)
    print(myTree)
    a = tree.classify(myTree,["no surfacing", "fileters"], [1,0])
    print("leibie",a)

你可能感兴趣的:(机器学习,决策树,python,机器学习)