【学习笔记】决策树模型

什么是决策树模型?

  决策树模型就是通过树来做决策的模型。所有数据从根节点出发,每经过一个节点,就做一次分类判断,最终将所有数据落到叶节点中。例如以是否喜欢打游戏为标准进行分类。在这里构造一个简单的决策树。

  这个决策树以年龄和性别进行了一个粗略分类。注:决策树中的判断先后顺序不可以轻易替换,决策树对于这个顺序是比较敏感的。如果替换,会对模型效果有影响。决策树既可以做分类问题也可以做回归问题。

决策树的组成

根节点:第一个选择点

非叶子节点和分支:中间过程

叶子节点:最终的决策结果

决策树的训练

  决策树的训练就是对节点的选择,以及如何对节点切分。那么如何进行切分和选择呢?

  首先,我们要选择分类效果最强的特征作为最开始的节点,然后再逐渐选择分类效果较弱的特征作为之后的节点。通过一种衡量标准来计算最强的特征——信息熵。

  熵就是随机变量不确定性的度量(或混乱程度)信息熵的计算公式是

H(X) = -\sum p_i \times log p_i \quad (i = 1,2,\dots,n)

其中pi是各个元素在集合中的占比。例如集合A = [1,1,1,1,1,1,1,1,2,2],集合B=[1,2,3,4,5,6,7,8,9,10],显然集合A的熵值要更小。我们显然希望在分类之后所得到的各个类别信息熵要更小。当p = 0或p = 1时,熵值为0,当p = 0.5时,熵值最大。

  这里补充一个定义:信息增益——表示特征X使Y的不确定性减少的程度。以上的描述就说明信息增益是最大的。

H_{gain} = H_2 - H_1

决策树解决回归问题,只需要将衡量标准从信息熵转化为方差即可。方差在一定程度上可以展现数据的相似性。

决策树算法

ID3

利用信息增益来选取节点。ID3自身存在问题,因为它没有考虑分类前自身的熵。从而导致选择节点时由于自身熵过小导致信息增益上限小的问题。

C4.5

利用信息增益率。解决ID3问题,考虑自身熵。

CART

使用GINI系数当作衡量标准。GINI系数计算公式:

\displaystyle Gini(p) = \sum_{k = 1}^{K}p_k(1-p_k) = 1-\sum_{k=1}^{K}p_k^2

决策树剪枝策略

为什么要剪枝?

决策树模型过拟合风险很大,理论上可以完全分开数据。因为如果节点过多,是可以将每一个样本分在一篇叶子上的。可以在训练集取得较好的效果,但是在测试集的效果并不好。因此,在构建好决策树模型之后,要采取一个剪枝的策略,使分类标准更强而有力,也可以使树更加简洁,加速运行效率,同时提升模型的适用程度。

预剪枝策略

预剪枝策略就是边建立决策树边剪枝的策略。具体而言,可以限制深度,叶子节点数量,叶子节点样本数,信息增益量等。

后剪枝策略

建立完决策树再来剪枝。通过一定的衡量标准。

C_{\alpha}(T) = C(T)+\alpha|T_{leaf}|

其中C(T)是自身的一个GINI系数值。T_{leaf}是从某节点开始的叶子节点的个数。\alpha是常量,表示对于叶子节点的数量的重视程度。C_{\alpha}(T)的值越大,损失越多,以这个标准判断要不要剪枝。

代码实现

首先我们创建一个数据集,并指定各列的标签。

from matplotlib.font_manager import FontProperties
import mathplotlib.pyplot as plt
from math import log
import operator
import pickle

def createDataSet():
    dataSet = [[0,0,0,0,"no"],
                [0,0,0,1,"no"]
                [0,1,0,1,"yes"]
                [0,1,1,0,"yes"]
                [0,0,0,0,"no"]
                [1,0,0,0,"no"]
                [1,0,0,1,"no"]
                [1,1,1,1,"yes"]
                [1,0,1,2,"yes"]
                [1,0,1,2,"yes"]
                [2,0,1,2,"yes"]
                [1,2,0,1,"yes"]
                [2,1,0,2,"yes"]
                [2,0,0,0,"no"]]
    labels = ["F1-AGE","F2-WORD","F3-HOME","F4-LOAN"]
    return dataSet, labels
创建树模型
#创建树
#dataset是数据集,labels为现数据集中可选用的特征,表示是否以满足要求,featlabels表示顺序
def createTree(dataset,labels,featlabels):
    #取出数据集中的最后一列
    classlist = [example[-1] for example in dataset]

    #判断是否全部为同一个值,如果是,返回这一列的数据的值(yes或no),作为叶子节点
    if classlist.count(classlist[0]) == len(classlist):
        return classlist[0]

    #判断是否已经用完了所有分类标签,如果是,返回这一列中占比最大的值
    if len(dataset[0]) == 1:
        return majorityCnt(classlist)
    
    #计算熵值的函数,选出最强的特征,featlabels用来存储已经使用了的特征
    bestfeat = chooseBestFeatureToSplit(dataset)
    bestfeatlabel = labels[bestfeat]
    featlabels.append(bestfeatlabel)

    #创建树
    myTree = {bestfeatlabel:{}}

    #删除已经使用的特征
    del labels[bestfeat]

    #提取选出来的特征
    featvalues = [example[bestfeat] for example in dataset]
    uniquevals = set(featvalue)
    for value in uniquevals:
        sublabels = labels[:]

        #递归调用函数
        myTree[bestfeatlabel][value] = createTree(splitDataset(dataset,bestfeat,value),sublabels,value,featlabels)
    return myTree
熵值的计算
#计算最多的类别
def majorityCnt(classlist):
    classcount = {}
    for vote in classlist:
        if vote not in classcount.keys():classcount[vote] = 0
        classcount[vote] += 1
    sortedclasscount = sort(classcount.itesms(),key = operator.itemgetter(1),reverse = True)
    return sortedclasscount[0][0]

以上代码定义了majorityCnt函数,利用字典对象进行排序,选出该节点数量最多的元素。

def chooseBestFeatureToSplit(dataset):
    numfeatures = len(dataset[0]) - 1
    baseentropy = calcShannonEnt(dataset)
    baseinfogain = 0
    basefeature = -1
    for i in range(numfeatures):
        featlist = [example[i] for example in dataset]
        uniquevals = set(featlist)
        newentropy = 0
        for val in uniquevals:
            subdataset = splitdataset(dataset,i,val)
            prob = len(subdataset)/float(len(dataset))
            newentropy += prob * calcShannonEnt(subdataset)
        infogain = baseEntropy - newentropy
        if (infogain > baseentropy):
            bestinfogain = infogain
            bestfeature = i
    return bestfeature



def splitdataset(datset,axis,val):
    retdataset = []
    for featvec in dataset:
        if featvec[axis] == val:
                reducedfeatvec = featvec[:axis]
                reducedfeatvec.extend(featvec[axis+1])
                retdataset.append(reducedfeatvec)
    return retdataset

#计算熵值
def calcShannonEnt(dataset):
    numexamples = len(dataset)
    labelcounts = {}
    for featvec in dataset:
        currentlabel = featvec[-1]
        ifcurrentlabel not in lavelcounts.keys():
            labelcounts[currentlabel] = 0
        labelcounts[currentlabel] += 1
        
    shannonEnt = 0
    for key in labelcounts:
        prop = float(labelcounts[key])/numexamples
        shannonEnt -= prop*log(prop,2)
    return shannonEnt

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值