淘先锋技术网

首页 1 2 3 4 5 6 7


上一篇文章中,讲解了ID3决策树及C4.5决策树,但是特们仍有许多不足:比如模型是用较为复杂的熵来度量,使用了相对较为复杂的多叉树,只能处理分类不能处理回归等。对于这些问题, CART算法大部分做了改进。由于CART算法可以做回归,也可以做分类,我们分别加以介绍,先从CART分类树算法开始,重点比较和C4.5算法的不同点。接着介绍CART回归树算法,重点介绍和CART分类树的不同点。然后我们讨论CART树的建树算法和剪枝算法,最后总结决策树算法的优缺点。

一、CART树原理

CART 全称为 Classification And Regression Trees,即分类回归树。顾名思义,该算法既可以用于分类还可以用于回归。

克服了 ID3 算法只能处理离散型数据的缺点,CART 可以使用二元切分来处理连续型变量。二元切分法,即每次把数据集切分成两份,具体地处理方法是:如果特征值大于给定值就走左子树,否则就走右子树。对 CART 稍作修改就可以处理回归问题。先前我们使用香农熵来度量集合的无组织程度,如果选用其它方法来代替香农熵,就可以使用树构建算法来完成回归。

与ID3决策树及C4.5决策树一样,CART是决策树的一种,主要由特征选择,树的生成和剪枝三部分组成。它主要用来处理分类和回归问题,下面对分别对其进行介绍。

本部分将构建两种树,第一种是分类树;第二种是回归树

二、CART分类树

2.1 特征选择

CART分类树使用基尼指数最小化准则来选择最佳属性

ID3算法使用了信息增益来选择特征,信息增益大的优先选择。C4.5算法采用了信息增益比来选择特征,以减少信息增益容易选择特征值多的特征的问题。但是无论是ID3还是C4.5,都是基于信息论的熵模型的,这里面会涉及大量的对数运算。因此,我们希望有一种方法简化模型同时又不至于完全丢失熵模型的优点,这就是基尼系数CART分类树算法使用基尼系数来代替信息增益比,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好。这和信息增益(比)是相反的。

在分类问题中,假设有K个类别,第k个类别的概率为 p k p_k pk, 则基尼系数的表达式为:
G i n i ( p ) = ∑ k = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 Gini(p)=∑k=\sum_{k=1}^{K}p_k(1−p_k)=1-\sum_{k=1}^{K}p^2_k Gini(p)=k=k=1Kpk(1pk)=1k=1Kpk2

如果是二类分类问题,计算就更加简单了,如果属于第一个样本输出的概率是p,则基尼系数的表达式为:
G i n i ( p ) = 2 p ( 1 − p ) Gini(p)=2p(1−p) Gini(p)=2p(1p)

对于个给定的样本D,假设有K个类别, 第k个类别的数量为 C k C_k Ck,则样本D的基尼系数表达式为:
G i n i ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 Gini(D)=1-\sum_{k=1}^{K}(\frac{|C_k|}{|D|})^2 Gini(D)=1k=1K(DCk)2

特别的,对于样本D,如果根据特征A的某个值a,把D分成D1和D2两部分,则在特征A的条件下,D的基尼系数表达式为:
G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D,A)=\frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2) Gini(D,A)=DD1Gini(D1)+DD2Gini(D2)

其中:

  • Gini(D):表示集合D的不确定性。
  • Gini(A,D):表示经过A=a分割后的集合D的不确定性。

对比基尼系数表达式和熵模型的表达式,二次运算要比对数简单很多,尤其是二类分类的计算,更加简单。但是简单归简单,和熵模型的度量方式比,基尼系数对应的误差有多大呢?对于二类分类,基尼系数和熵之半的曲线如下:
在这里插入图片描述
 从上图可以看出,基尼系数和熵之半的曲线非常接近,仅仅在45度角附近误差稍大。因此,基尼系数可以做为熵模型的一个近似替代。而CART分类树算法就是使用的基尼系数来选择决策树的特征。同时,为了进一步简化,CART分类树算法每次仅仅对某个特征的值进行二分,而不是多分,这样CART分类树算法建立起来的是二叉树,而不是多叉树。这样一可以进一步简化基尼系数的计算,二可以建立一个更加优雅的二叉树模型。

2.2 建立流程

算法从根节点开始,用训练集递归的建立CART树。

  • 1)对于当前节点的数据集为D,如果样本个数小于阈值或者没有特征,则返回决策子树,当前节点停止递归。
  • 2)计算样本集D的基尼系数,如果基尼系数小于阈值,则返回决策树子树,当前节点停止递归。
  • 3)计算当前节点现有的各个特征的各个特征值对数据集D的基尼系数。
  • 4)在计算出来的各个特征的各个特征值对数据集D的基尼系数中,选择基尼系数最小的特征A和对应的特征值a。根据这个最优特征和最优特征值,把数据集划分成两部分D1和D2,同时建立当前节点的左右节点,做节点的数据集D为D1,右节点的数据集D为D2.
  • 5)对左右的子节点递归的调用1-4步,生成决策树。

对于生成的决策树做预测的时候,假如测试集里的样本A落到了某个叶子节点,而节点里有多个训练样本。则对于A的类别预测采用的是这个叶子节点里概率最大的类别。

2.3 连续特征和离散特征的处理

1)、连续特征的处理

对于CART分类树连续值的处理问题,其思想和C4.5是相同的,都是将连续的特征离散化。唯一的区别在于在选择划分点时的度量方式不同,C4.5使用的是信息增益比,则CART分类树使用的是基尼系数。

具体的思路如下,比如m个样本的连续特征A有m个,从小到大排列为 a 1 , a 2 , . . . , a m a_1,a_2,...,a_m a1,a2,...,am,则CART算法取相邻两样本值的平均数,一共取得m-1个划分点,其中第i个划分点 T i T_i Ti表示为: T i = a i + a ( i + 1 ) 2 Ti=\frac{a_i+a_{(i+1)}}{2} Ti=2ai+a(i+1)。对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。选择基尼系数最小的点作为该连续特征的二元离散分类点。比如取到的基尼系数最小的点为 a t a_t at,则小于 a t a_t at的值为类别1,大于 a t a_t at的值为类别2,这样我们就做到了连续特征的离散化。要注意的是,与ID3或者C4.5处理离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。

2)、离散特征处理

对于CART分类树离散值的处理问题,采用的思路是不停的二分离散特征。

在ID3或者C4.5,如果某个特征A被选取建立决策树节点,如果它有A1,A2,A3三种类别,我们会在决策树上一下建立一个三叉的节点。这样导致决策树是多叉树。但是CART分类树使用的方法不同,他采用的是不停的二分,还是这个例子,CART分类树会考虑把A分成{A1}和{A2,A3}, {A2}和{A1,A3}, {A3}和{A1,A2}三种情况,找到基尼系数最小的组合,比如{A2}和{A1,A3},然后建立二叉树节点,一个节点是A2对应的样本,另一个节点是{A1,A3}对应的节点。同时,由于这次没有把特征A的取值完全分开,后面我们还有机会在子节点继续选择到特征A来划分A1和A3。这和ID3或者C4.5不同,在ID3或者C4.5的一棵子树中,离散特征只会参与一次节点的建立。

三、CART回归树

CART回归树使用平方误差最小准则。

训练回归树其实和训练分类树没有本质不同,也是自上而下,不断分裂节点的过程。不同之处在于对节点分裂的分裂准则和分裂值的选择方法上。我下面将一步步推导回归树的构建算法。

设有训练数据集 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)}。我们知道决策树最终将数据元组划分到了多个叶子节点中,比如分类树的每个叶子就有一个类标号,作为元组的预测类。回归树的思路类似,我将数据空间划分成了mm个区域 { R 1 , R 2 , … , R m } \{R_1,R_2,…,R_m\} {R1,R2,,Rm},实际上就是将训练元组的分区。然后赋给每个区域有一个固定的代表值 C i C_i Ci(类似于分类树中的类标号),这样,回归树的模型可以表示成r如下公式的形式:
f ( X ) = ∑ i = 1 m C i I ( X ∈ R i ) f(X)=\sum_{i=1}^{m}C_iI(X \in R_i) f(X)=i=1mCiI(XRi)

其中 I ( X ∈ R i ) = 1 I(X∈R_i)=1 I(XRi)=1,如果 X ∈ R i X∈R_i XRi;否则, I ( X ∈ R i ) = 0 I(X∈R_i)=0 I(XRi)=0。这么看来,上式的含义是:先判断X属于哪个区域,然后返回这个区域的代表值。

这样,我们可以写出对于某个区域Ri回归模型的损失函数:
J ( C ) = ∑ X j ∈ R i ( f ( X j ) − g i ) 2 J(C)=\sum_{X_j \in R_i}(f(X_j)-g_i)^2 J(C)=XjRi(f(Xj)gi)2

其中, g i g_i gi为这个区域的代表值(这里用 g i g_i gi而不用 C i C_i Ci的原因是我要表达 g i g_i gi是分裂中间的某个区域的代表值,而 C i C_i Ci为最终完成分裂之后的叶子节点的代表值), g i g_i gi的计算方法一般是这个区域中的元组的y值的均值(取均值时,损失函数达到最优)。
g i = 1 N i ∑ X j ∈ R i y i g_i=\frac{1}{N_i}\sum_{X_j \in R_i}y_i gi=Ni1XjRiyi

其中, N i N_i Ni为这个区域内数据元组的总数。初始时,所有的数据元组都在一个区域内,我们按照 J ( C ) J(C) J(C)计算损失函数,如果计算得到的误差值太大,不能满足要求,则寻找最佳的分裂准则(属性)和分裂值将数据集一分为二。这里的关键在于怎样的分裂准则和分裂值对于回归树来说是最佳的。其实很明显,“最佳”指我按照这样的属性和属性值将数据集一分为二后,使得分裂得到的两个子区域的误差和最小。

综上所述,回归树的计算步骤如下:

  • 1)初始时,将所有训练元组放置于根节点中,计算损失函数得到误差值,倘若误差大于事先定好的参数,那么执行下面的2~4步;
  • 2)选择用于分裂区域的属性A和对应的属性值s,将当前节点的数据元组划分到以下两个区域 R 1 R_1 R1, R 2 R_2 R2中。:
    R 1 = { X ∣ x i ≤ s } ; R 1 = { X ∣ x i > s } R_1=\{X|x_i\leq s\};R_1=\{X|x_i>s\} R1={Xxis}R1={Xxi>s}

其中 x i x_i xi为元组 X X X中属性 A A A的值。而选择分裂属性 A A A以及属性值 s s s的依据是使得分裂后的子区域误差值的和最小。即:
m i n ∑ X j ∈ R 1 ( f ( X j ) − y 1 ) 2 + ∑ X j ∈ R 2 ( f ( X j ) − y 2 ) 2 min\sum_{X_j \in R_1}(f(X_j)-y_1)^2+\sum_{X_j \in R_2}(f(X_j)-y_2)^2 minXjR1(f(Xj)y1)2+XjR2(f(Xj)y2)2

这一步具体执行的时候,我们遍历所有的属性以及每个属性的所有可能的划分值,分别计算他们划分数据元组之后的两个子区域的代表值 y 1 , y 2 y_1,y_2 y1,y2,这样就可以计算 R 1 , R 2 R_1,R_2 R1,R2的误差和上式,最终选择误差和最小的属性 A A A和属性值 s s s,作为分裂依据。

四、CART树算法的剪枝

CART回归树和CART分类树的剪枝策略除了在度量损失的时候一个使用均方差,一个使用基尼系数,算法基本完全一样,这里我们一起来讲。

由于决策时算法很容易对训练集过拟合,而导致泛化能力差,为了解决这个问题,我们需要对CART树进行剪枝,即类似于线性回归的正则化,来增加决策树的泛化能力。但是,有很多的剪枝方法,我们应该这么选择呢?CART采用的办法是后剪枝法,即先生成决策树,然后产生所有可能的剪枝后的CART树,然后使用交叉验证来检验各种剪枝的效果,选择泛化能力最好的剪枝策略。

也就是说,CART树的剪枝算法可以概括为两步第一步是从原始决策树生成各种剪枝效果的决策树,第二步是用交叉验证来检验剪枝后的预测能力,选择泛化预测能力最好的剪枝后的数作为最终的CART树。

4.1 剪枝的损失函数度量

首先我们看看剪枝的损失函数度量,在剪枝的过程中,对于任意的一刻子树T,其损失函数为:
C α ( T t ) = C ( T t ) + α ∣ T t ∣ C_{\alpha}(T_t)=C(T_t)+\alpha|T_t| Cα(Tt)=C(Tt)+αTt

其中, α α α为正则化参数,这和线性回归的正则化一样。 C ( T t ) C(T_t) C(Tt)为训练数据的预测误差,分类树是用基尼系数度量,回归树是均方差度量。 ∣ T t ∣ |T_t| Tt是子树T的叶子节点的数量。

α = 0 α=0 α=0时,即没有正则化,原始的生成的CART树即为最优子树。当 α = ∞ α=∞ α=时,即正则化强度达到最大,此时由原始的生成的CART树的根节点组成的单节点树为最优子树。当然,这是两种极端情况。一般来说, α α α越大,则剪枝剪的越厉害,生成的最优子树相比原生决策树就越偏小。对于固定的 α α α,一定存在使损失函数 C α ( T ) C_α(T) Cα(T)最小的唯一子树。

4.2 剪枝的思路

对于位于节点t的任意一颗子树Tt,如果没有剪枝,它的损失是:
C α ( T t ) = C ( T t ) + α ∣ T t ∣ C_{\alpha}(T_t)=C(T_t)+\alpha|T_t| Cα(Tt)=C(Tt)+αTt

如果将其剪掉,仅仅保留根节点,则损失是:
C α ( T ) = C ( T ) + α C_{\alpha}(T)=C(T)+\alpha Cα(T)=C(T)+α

α = 0 α=0 α=0或者 α α α很小时, C α ( T t ) &lt; C α ( T ) C_α(T_t)&lt;C_α(T) Cα(Tt)<Cα(T) , 当 α α α增大到一定的程度时:
C α ( T t ) = C α ( T ) C_{\alpha}(T_t)=C_{\alpha}(T) Cα(Tt)=Cα(T)

当α继续增大时不等式反向,也就是说,如果满足下式:
α = C ( T ) − C ( T t ) ∣ T t ∣ − 1 \alpha=\frac{C(T)-C(T_t)}{|T_t|-1} α=Tt1C(T)C(Tt)

T t T_t Tt T T T有相同的损失函数,但是 T T T节点更少,因此可以对子树 T t T_t Tt进行剪枝,也就是将它的子节点全部剪掉,变为一个叶子节点T。

4.3 CART树的交叉验证

由上面可知:可以计算出每个子树是否剪枝的阈值α,如果我们把所有的节点是否剪枝的值α都计算出来,然后分别针对不同的α所对应的剪枝后的最优子树做交叉验证。这样就可以选择一个最好的α,有了这个α,我们就可以用对应的最优子树作为最终结果。

4.4 CART树的剪枝算法

  • 输入:CART树建立算法得到的原始决策树T。
  • 输出:最优决策子树Tα。

算法如下

  • 1)初始化 α m i n = ∞ α_{min}=∞ αmin=, 最优子树集合 ω = { T } ω=\{T\} ω={T}
  • 2)从叶子节点开始自下而上计算各内部节点t的训练误差损失函数 C α ( T t ) C_α(T_t) Cα(Tt)(回归树为均方差,分类树为基尼系数),叶子节点数 ∣ T t ∣ |T_t| Tt,以及正则化阈值 α = m i n { C ( T ) − C ( T t ) ∣ T t ∣ − 1 , α m i n } α=min\{\frac{C(T)−C(T_t)}{|T_t|−1},α_{min}\} α=min{Tt1C(T)C(Tt),αmin},更新 α m i n = α α_{min}=α αmin=α
  • 3)得到所有节点的 α α α值的集合 M M M
  • 4)从M中选择最大的值 α k α_k αk,自上而下的访问子树 t t t的内部节点,如果 C ( T ) − C ( T t ) ∣ T t ∣ − 1 \frac{C(T)−C(T_t)}{|T_t|−1} Tt1C(T)C(Tt)时,进行剪枝。并决定叶节点 t t t的值。如果是分类树,则是概率最高的类别,如果是回归树,则是所有样本输出的均值。这样得到αk对应的最优子树Tk;
  • 5)最优子树集合 ω = ω ∪ T k ω=ω∪T_k ω=ωTk M = M − α k M=M−{α_k} M=Mαk
  • 6)如果M不为空,则回到步骤4。否则就已经得到了所有的可选最优子树集合ω;
  • 7)采用交叉验证在ω选择最优子树 T α T_α Tα

五、决策树总结

决策树优点

  • 1)简单直观,生成的决策树很直观。
  • 2)基本不需要预处理,不需要提前归一化,处理缺失值。
  • 3)使用决策树预测的代价是O(log2m)。 m为样本数。
  • 4)既可以处理离散值也可以处理连续值。很多算法只是专注于离散值或者连续值。
  • 5)可以处理多维度输出的分类问题。
  • 6)相比于神经网络之类的黑盒分类模型,决策树在逻辑上可以得到很好的解释
  • 7)可以交叉验证的剪枝来选择模型,从而提高泛化能力。
  • 8) 对于异常点的容错能力好,健壮性高。

决策树缺点:

  • 1)决策树算法非常容易过拟合,导致泛化能力不强。可以通过设置节点最少样本数量和限制决策树深度来改进。
  • 2)决策树会因为样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习之类的方法解决。
  • 3)寻找最优的决策树是一个NP难的问题,我们一般是通过启发式方法,容易陷入局部最优。可以通过集成学习之类的方法来改善。
  • 4)有些比较复杂的关系,决策树很难学习,比如异或。这个就没有办法了,一般这种关系可以换神经网络分类方法来解决。
  • 5)如果某些特征的样本比例过大,生成决策树容易偏向于这些特征。这个可以通过调节样本权重来改善。

文章有很多直接复制了决策树算法原理(下)中的总结,因为该文章写的太全了。