决策树算法梳理

一. 信息论基础(熵 联合熵 条件熵 信息增益 基尼不纯度)

信息熵

信息量度量的是一个具体事件发生了所带来的信息,而熵则是在结果出来之前对可能产生的信息量的期望——考虑该随机变量的所有可能取值,即所有可能发生事件所带来的信息量的期望。即 H ( x ) = − s u m ( p ( x ) l o g 2 p ( x ) ) H(x)=-sum(p(x)log_2p(x)) H(x)=sum(p(x)log2p(x))
转换为:
H ( x ) = − ∑ i = 1 n p ( x i ) l o g p ( x i ) H(x)=-\sum^n_{i=1}p(x_i)logp(x_i) H(x)=i=1np(xi)logp(xi)
这个就是公式的推导。
还有另一个理解。信息熵还可以作为一个系统复杂程度的度量,如果系统越复杂,出现不同情况的种类越多,那么他的信息熵是比较大的。相反,系统越复杂,出现的情况少(极端情况下如果只有一种情况,那就是概率为1,那么对应的信息熵为0),此时的信息熵较少。

联合熵

联合熵就是度量一个联合分布的随机系统的不确定度,下面给出两个随机变量的联合熵的定义:
在这里插入图片描述

容易知道,联合熵的物理意义就是,观察一个多个随机变量的随机系统获得的信息量,为了进一步剖析联合熵,我们对其的进行数学推导如下:
在这里插入图片描述

对于式子 H(X,Y)=H(X)+H(Y|X) 所表达的物理含义是,对一个两个随机变量的随机系统,我们可以先观察一个随机变量获取信息量,观察完后,我们可以在拥有这个信息量的基础上观察第二个随机变量的信息量。其那么先观察哪一个随机变量对信息量的获取有影响吗?利用概率论的知识,我们可以轻易得出:
在这里插入图片描述

也就是说,先观察谁,对信息量都不会有影响,这是非常符合直觉的。

基于上述的讨论,我们不禁会问,如果有n个随机变量处于一个随机系统中,那么我们获取其联合熵也是无关观察先后吗?答案是肯定的。为了说明原因,我们给出熵的链式法则:
在这里插入图片描述

我们可以利用数学推导证明:
在这里插入图片描述

从链式法则,我们可以更进一步得到,如果随机变量 X_{1},X_{2},\cdots,X_{n} 是独立的,那么联合熵则可以表示为:
在这里插入图片描述

条件熵

定义为X给定条件下,Y的条件概率分布的熵对X的数学期望
设有随机变量(X,Y),其联合概率分布为
在这里插入图片描述

条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性。随机变量X给定的条件下随机变量Y的条件熵H(Y|X)
公式推导:
在这里插入图片描述

信息增益

信息增益恰好是:信息熵-条件熵。
换句话说,信息增益代表了在一个条件下,信息复杂度(不确定性)减少的程度。
这样我们可以根据信息增益来在决策树算法中选择哪一个特征,如果选择一个特征后,信息增益最大(信息不确定性减少的程度最大),那么我们就选取这个特征。

基尼不纯度

样本集合数据集的纯度除了可以用熵来衡量外,也可以用基尼值来度量。基尼值反映了从数据集中随机抽取两个样本,其类别标记不一致的概率,因此基尼值越小,则数据集纯度越高。公式如下:
在这里插入图片描述
对属性a进行划分,则属性a的基尼指数定义为:
在这里插入图片描述
因此,在选择划分属性时,在后续介绍的决策树算法中应该选择那个使得划分之后基尼指数最小的属性作为划分属性。

二.决策树的不同分类算法(ID3算法、C4.5、CART分类树)的原理及应用场景

ID3算法

ID3算法的核心是在决策树各个结点上应用信息增益准则选择特征,递归地构建决策树。具体方法是:从根节点开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子结点;再对子节点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一个决策树。ID3相当于用最大似然法进行概率模型的选择。

C4.5算法

C4.5算法与ID3算法相似,C4.5算法对ID3算法进行了改进。C4.5在生成的过程中,用信息增益比来选择特征。

CART算法

CART(classification and regression tree)是在给定输入随机变量X条件下输出随机变量Y的条件概率分布的学习方法。CART假设决策树是二叉树,内部结点特征的取值为“是”和“否”,左分支是取值为“是”的分支,有分支是取值为“否”的分支。这样的决策树等价于递归地二分每个特征,将输入空间即特征空间划分为有限个单元,并在这些单元上确定预测的概率分布,也就是输入给定的条件下输出的条件概率分布。
CART算法由以下两步组成:
(1)决策树生成:基于训练数据集生成决策树,生成的决策树要尽量大;
(2)决策树剪枝:用验证数据集对已生成的树进行剪枝并选择最优子树,这时用损失函数最小作为剪枝的标准。

适用情景

因为它能够生成清晰的基于特征选择不同预测结果的树状结构,数据分析师希望更好的理解手上的数据的时候往往可以使用决策树。
同时它也是相对容易被攻击的分类器。这里的攻击是指人为的改变一些特征,使得分类器判断错误。常见于垃圾邮件躲避检测中。因为决策树最终在底层判断是基于单个条件的,攻击者往往只需要改变很少的特征就可以逃过监测。
它的简单性,决策树更大的用处是作为一些更有用的算法的基石

三.回归树原理

决策树的生成就是递归地构建二叉决策树的过程,其中回归树是用平方误差最小化准则进行特征选择,生成二叉树。
假设X与Y分别为输入和输出变量,并且Y是连续变量,给定训练数据集 D = ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) D={(x_1,y_1),(x_2,y_2),...,(x_N,y_N)} D=(x1,y1),(x2,y2),...,(xN,yN)来考虑如何生成回归树。
一个回归树对应着输入空间(即特征空间)的一个划分以及在划分的单元上的输出值。假设已将输入空间划分为M个单元 R 1 , R 2 , . . . , R 3 R_1,R_2,...,R_3 R1,R2,...,R3,并且在每个单元 R m R_m Rm上有一个固定的输出值 c m c_m cm,于是回归树模型可表示为: f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) f(x)=\sum_{m=1}^M c_mI(x \in R_m) f(x)=m=1McmI(xRm)
当输入空间的划分确定时,可以用平方误差 ∑ x i ∈ R m ( y i − f ( x i ) ) 2 \sum_{x_i \in R_m} (y_i - f(x_i))^2 xiRm(yif(xi))2来表示回归树对于训练数据的预测误差,用平方误差最小的准则求解每个单元上的最优输出值。易知,单元 R m R_m Rm上的 c m c_m cm的最优值 c m ^ \hat{c_m} cm^ R m R_m Rm上的所有输入实例 x i x_i xi对应的输出 y i y_i yi的均值,即
c ^ m = a v e ( y i ∣ x i ∈ R m ) \hat{c}_m = ave(y_i | x_i \in R_m) c^m=ave(yixiRm)
问题是怎样对输入空间进行划分。这里采用启发式的方法,选择第j个变量 x ( j ) x^{(j)} x(j)和它取的值s,作为切分变量和切分点,并定义两个区域:
R 1 ( j , s ) = { x ∣ x ( j ) ≤ s } 和 R 2 ( j , s ) = { x ∣ x ( j ) > s } R_1(j,s) = \{x|x^{(j)} \leq s\} 和 R_2(j,s) = \{x|x^{(j)} > s\} R1(j,s)={xx(j)s}R2(j,s)={xx(j)>s}
然后寻找最优切分变量j和最优切分点s。具体地,求解
m i n j , s [ m i n c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + m i n c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] min_{j,s} [min_{c_1} \sum_{x_i \in R_1(j,s)}(y_i - c_1)^2 + min_{c_2} \sum_{x_i \in R_2(j,s)}(y_i - c_2)^2] minj,s[minc1xiR1(j,s)(yic1)2+minc2xiR2(j,s)(yic2)2]
对固定输入变量j可以找到最优切分点s
c ^ 1 = a v e ( y i ∣ x i ∈ R 1 ( j , s ) ) 和 c ^ 2 = a v e ( y i ∣ x i ∈ R 2 ( j , s ) ) \hat {c} _1 = ave(y_i|x_i \in R_1(j,s)) 和 \hat {c} _2 = ave(y_i|x_i \in R_2(j,s)) c^1=ave(yixiR1(j,s))c^2=ave(yixiR2(j,s))
遍历所有输入变量,找到最优的切分变量j,构成一个对(j,s)。依此将输入空间划分为两个区域。接着对每个区域重复上诉划分过程,直到满足停止条件为止。这样就生成一棵回归树。这样的回归树通常被称为最小二乘回归树。

四. 决策树防止过拟合手段

剪枝是决策树学习算法对付“过拟合”的主要手段。在决策树学习中,为了尽可能正确分类训练样本,结点划分过程将不断重复。有时会造成决策树分支过多,这是就可能因训练样本学得“太好”了,以致于训练样本自身的一些特点当作所有数据都具有的一般性质而导致过拟合。因此,可通过主动去掉一些分支来降低过拟合的风险。

预剪枝

预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶节点;

后剪枝

后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶节点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶节点。

如何判断决策树泛化性能是否提升呢?这就是根据之前的验证集或者交叉验证的方法

五. 模型评估

自助法(bootstrap)

训练集是对于原数据集的有放回抽样,如果原始数据集N,可以证明,大小为N的自助样本大约包含原数据63.2%的记录。当N充分大的时候, 1 − ( 1 − 1 / N ) ( N ) 1-(1-1/N)^{(N)} 111/N(N) 概率逼近 1- e ( − 1 ) e^{(-1)} e(1)=0.632。抽样 b 次,产生 b 个bootstrap样本,则,总准确率为(accs为包含所有样本计算的准确率):
在这里插入图片描述

准确度的区间估计

将分类问题看做二项分布,则有:
令 X 为模型正确分类,p 为准确率,X 服从均值 Np、方差 Np(1-p)的二项分布。acc=X/N为均值 p,方差 p(1-p)/N 的二项分布。acc 的置信区间
在这里插入图片描述

六. sklearn参数详解,Python绘制决策树

DecisionTreeClassifier 是能够在数据集上执行多分类的类,与其他分类器一样,DecisionTreeClassifier 采用输入两个数组:数组X,用 [n_samples, n_features] 的方式来存放训练样本。整数值数组Y,用 [n_samples] 来保存训练样本的类标签:

决策树通过使用 DecisionTreeRegressor 类也可以用来解决回归问题。如在分类设置中,拟合方法将数组X和数组y作为参数,只有在这种情况下,y数组预期才是浮点值:

下面是python绘制决策树的代码

import matplotlib.pyplot as plt
 
# pylint: disable=redefined-outer-name
 
# 定义文本框和箭头格式
decision_node = dict(boxstyle="sawtooth", fc="0.8")
leaf_node = dict(boxstyle="round4", fc="0.8")
arrow_args = dict(arrowstyle="<-")
 
def retrieve_tree(i):
    list_of_trees = [{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}},
                     {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}}
                    ]
    return list_of_trees[i]
 
def get_num_leafs(mytree):
    '''
    获取叶子节点数
    '''
    num_leafs = 0
    first_str = mytree.keys()[0]
    second_dict = mytree[first_str]
    
    for key in second_dict.keys():
        if type(second_dict[key]).__name__ == 'dict':
            num_leafs += get_num_leafs(second_dict[key])
        else:
            num_leafs += 1
            
    return num_leafs
 
def get_tree_depth(mytree):
    '''
    获取树的深度
    '''
    max_depth = 0
    first_str = mytree.keys()[0]
    second_dict = mytree[first_str]
    
    for key in second_dict.keys():
        # 如果子节点是字典类型,则该节点也是一个判断节点,需要递归调用
        # get_tree_depth()函数
        if type(second_dict[key]).__name__ == 'dict':
            this_depth = 1 + get_tree_depth(second_dict[key])
        else:
            this_depth = 1
            
        if this_depth > max_depth:
            max_depth = this_depth
            
    return max_depth
 
def plot_node(ax, node_txt, center_ptr, parent_ptr, node_type):
    '''
        绘制带箭头的注解
    '''
    ax.annotate(node_txt, xy=parent_ptr, xycoords='axes fraction',
                xytext=center_ptr, textcoords='axes fraction',
                va="center", ha="center", bbox=node_type, arrowprops=arrow_args)
 
def plot_mid_text(ax, center_ptr, parent_ptr, txt):
    '''
    在父子节点间填充文本信息
    '''
    x_mid = (parent_ptr[0] - center_ptr[0]) / 2.0 + center_ptr[0]
    y_mid = (parent_ptr[1] - center_ptr[1]) / 2.0 + center_ptr[1]
 
    ax.text(x_mid, y_mid, txt)
 
def plot_tree(ax, mytree, parent_ptr, node_txt):
    '''
    绘制决策树
    '''
    # 计算宽度
    num_leafs = get_num_leafs(mytree)
    
    first_str = mytree.keys()[0]
    center_ptr = (plot_tree.x_off + (1.0 + float(num_leafs)) / 2.0 / plot_tree.total_width, plot_tree.y_off)
    
    #绘制特征值,并计算父节点和子节点的中心位置,添加标签信息
    plot_mid_text(ax, center_ptr, parent_ptr, node_txt)
    plot_node(ax, first_str, center_ptr, parent_ptr, decision_node)
    
    second_dict = mytree[first_str]
    #采用的自顶向下的绘图,需要依次递减Y坐标
    plot_tree.y_off -= 1.0 / plot_tree.total_depth
    
    #遍历子节点,如果是叶子节点,则绘制叶子节点,否则,递归调用plot_tree()
    for key in second_dict.keys():
        if type(second_dict[key]).__name__ == "dict":
            plot_tree(ax, second_dict[key], center_ptr, str(key))
        else:
            plot_tree.x_off += 1.0 / plot_tree.total_width
            plot_mid_text(ax, (plot_tree.x_off, plot_tree.y_off), center_ptr, str(key))
            plot_node(ax, second_dict[key], (plot_tree.x_off, plot_tree.y_off), center_ptr, leaf_node)
    
    #在绘制完所有子节点之后,需要增加Y的偏移
    plot_tree.y_off += 1.0 / plot_tree.total_depth
 
def create_plot(in_tree):
    fig = plt.figure(1, facecolor="white")
    fig.clf()
    
    ax_props = dict(xticks=[], yticks=[])
    ax = plt.subplot(111, frameon=False, **ax_props)
    plot_tree.total_width = float(get_num_leafs(in_tree))
    plot_tree.total_depth = float(get_tree_depth(in_tree))
    plot_tree.x_off = -0.5 / plot_tree.total_width
    plot_tree.y_off = 1.0
    plot_tree(ax, in_tree, (0.5, 1.0), "")
#     plot_node(ax, "a decision node", (0.5, 0.1), (0.1, 0.5), decision_node)
#     plot_node(ax, "a leaf node", (0.8, 0.1), (0.3, 0.8), leaf_node)
    plt.show()
 
if __name__ == '__main__':
#     create_plot()
    mytree = retrieve_tree(1)
    mytree['no surfacing'][3] = "maybe"
    create_plot(mytree)

参考文献:
https://zhuanlan.zhihu.com/p/26486223
https://zhuanlan.zhihu.com/p/26551798
https://zhuanlan.zhihu.com/p/36385989
https://zhuanlan.zhihu.com/p/32053821
李航《统计学习方法》
《西瓜书》
https://blog.csdn.net/longgb123/article/details/52972604
https://blog.csdn.net/sinat_29957455/article/details/76553987
https://blog.csdn.net/jinguangliu/article/details/78556104

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值