上一篇文章中,讲解了ID3决策树及C4.5决策树,但是特们仍有许多不足:比如模型是用较为复杂的熵来度量,使用了相对较为复杂的多叉树,只能处理分类不能处理回归等。对于这些问题, CART算法大部分做了改进。由于CART算法可以做回归,也可以做分类,我们分别加以介绍,先从CART分类树算法开始,重点比较和C4.5算法的不同点。接着介绍CART回归树算法,重点介绍和CART分类树的不同点。然后我们讨论CART树的建树算法和剪枝算法,最后总结决策树算法的优缺点。
CART 全称为 Classification And Regression Trees,即分类回归树。顾名思义,该算法既可以用于分类还可以用于回归。
克服了 ID3 算法只能处理离散型数据的缺点,CART 可以使用二元切分来处理连续型变量。二元切分法,即每次把数据集切分成两份,具体地处理方法是:如果特征值大于给定值就走左子树,否则就走右子树。对 CART 稍作修改就可以处理回归问题。先前我们使用香农熵来度量集合的无组织程度,如果选用其它方法来代替香农熵,就可以使用树构建算法来完成回归。
与ID3决策树及C4.5决策树一样,CART是决策树的一种,主要由特征选择,树的生成和剪枝三部分组成。它主要用来处理分类和回归问题,下面对分别对其进行介绍。
本部分将构建两种树,第一种是分类树;第二种是回归树。
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=1∑Kpk(1−pk)=1−k=1∑Kpk2
如果是二类分类问题,计算就更加简单了,如果属于第一个样本输出的概率是p,则基尼系数的表达式为:
G i n i ( p ) = 2 p ( 1 − p ) Gini(p)=2p(1−p) Gini(p)=2p(1−p)
对于个给定的样本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)=1−k=1∑K(∣D∣∣Ck∣)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)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
其中:
对比基尼系数表达式和熵模型的表达式,二次运算要比对数简单很多,尤其是二类分类的计算,更加简单。但是简单归简单,和熵模型的度量方式比,基尼系数对应的误差有多大呢?对于二类分类,基尼系数和熵之半的曲线如下:
从上图可以看出,基尼系数和熵之半的曲线非常接近,仅仅在45度角附近误差稍大。因此,基尼系数可以做为熵模型的一个近似替代。而CART分类树算法就是使用的基尼系数来选择决策树的特征。同时,为了进一步简化,CART分类树算法每次仅仅对某个特征的值进行二分,而不是多分,这样CART分类树算法建立起来的是二叉树,而不是多叉树。这样一可以进一步简化基尼系数的计算,二可以建立一个更加优雅的二叉树模型。
算法从根节点开始,用训练集递归的建立CART树。
对于生成的决策树做预测的时候,假如测试集里的样本A落到了某个叶子节点,而节点里有多个训练样本。则对于A的类别预测采用的是这个叶子节点里概率最大的类别。
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回归树使用平方误差最小准则。
训练回归树其实和训练分类树没有本质不同,也是自上而下,不断分裂节点的过程。不同之处在于对节点分裂的分裂准则和分裂值的选择方法上。我下面将一步步推导回归树的构建算法。
设有训练数据集 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=1∑mCiI(X∈Ri)
其中 I ( X ∈ R i ) = 1 I(X∈R_i)=1 I(X∈Ri)=1,如果 X ∈ R i X∈R_i X∈Ri;否则, I ( X ∈ R i ) = 0 I(X∈R_i)=0 I(X∈Ri)=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)=Xj∈Ri∑(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=Ni1Xj∈Ri∑yi
其中, N i N_i Ni为这个区域内数据元组的总数。初始时,所有的数据元组都在一个区域内,我们按照 J ( C ) J(C) J(C)计算损失函数,如果计算得到的误差值太大,不能满足要求,则寻找最佳的分裂准则(属性)和分裂值将数据集一分为二。这里的关键在于怎样的分裂准则和分裂值对于回归树来说是最佳的。其实很明显,“最佳”指我按照这样的属性和属性值将数据集一分为二后,使得分裂得到的两个子区域的误差和最小。
综上所述,回归树的计算步骤如下:
其中 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 minXj∈R1∑(f(Xj)−y1)2+Xj∈R2∑(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树。
首先我们看看剪枝的损失函数度量,在剪枝的过程中,对于任意的一刻子树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)最小的唯一子树。
对于位于节点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 ) < C α ( T ) C_α(T_t)<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} α=∣Tt∣−1C(T)−C(Tt)
T t T_t Tt 和 T T T有相同的损失函数,但是 T T T节点更少,因此可以对子树 T t T_t Tt进行剪枝,也就是将它的子节点全部剪掉,变为一个叶子节点T。
由上面可知:可以计算出每个子树是否剪枝的阈值α,如果我们把所有的节点是否剪枝的值α都计算出来,然后分别针对不同的α所对应的剪枝后的最优子树做交叉验证。这样就可以选择一个最好的α,有了这个α,我们就可以用对应的最优子树作为最终结果。
算法如下:
决策树优点:
决策树缺点:
文章有很多直接复制了决策树算法原理(下)中的总结,因为该文章写的太全了。