引言

决策树算法的思想其实早就在我们的生活中被广泛运用了,只是没有将其抽象出来。举个简单的例子:相亲时,可能首先检测相亲对方是否有房。如果有,则考虑进一步接触。如果没有房,则观察其是否有上进心,如果没有,直接Say Goodbye。如果有,则可以列入候选名单。那么,怎么准确的选择这个标准就是这个算法的关键了。1975年,罗斯昆(J. Ross Quinlan)在悉尼大学提出的一种分类预测算法ID3,来度量决策树的决策选择过程,其核心算法是信息熵。
下面我们就先来看看决策树算法是怎么做决策的。

算法介绍

决策树的定义:决策树在分类问题中,表示基于特征对实例进行分类的过程,可以认为是if-then的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。

我先根据一个例子来理解这个算法,再使用该算法来测试一个实际用例,关于辛福感指数,数据来源于天池大赛,写完后代码会上传github上。

举例说明

银行希望能够通过一个人的信息(包括职业、年龄、收入、学历)去判断他是否有贷款的意向,从而更有针对性地完成工作。下表是银行现在能够掌握的信息,我们的目标是通过对下面的数据进行分析建立一个预测用户贷款一下的模型。

职业 年龄 收入 学历 是否贷款
自由职业 28 5000 高中
工人 36 5500 高中
工人 42 2800 初中
白领 45 3300 小学
白领 25 10000 本科
白领 32 8000 硕士
白领 28 13000 博士
自由职业 21 4000 本科
自由职业 22 3200 小学
工人 33 3000 高中
工人 48 4200 小学

决策树用树结构实现判断流程,每次选择一个属性进行判断,如果不能得出结论,继续选择其他属性进行判断,直到能够“肯定地”判断出用户的类型或者是上述属性都已经使用完毕。如下图
决策树算法(1) 算法原理
输入用户信息,输出是否贷款
Input:{职业,年龄,收入,学历}={自由职业,21,4500,高中}
Output:{是否贷款}={未贷款}
根据决策树很容易的实现了分类,现在问题来了,怎么构建这一棵决策树呢?

构建决策树

决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得各个子数据集有一个最好的分类的过程。这一过程对应着对特征空间的划分,也对应着决策树的构建。

  1. 开始:构建根节点,将所有训练数据都放在根节点,选择一个最优特征,按着这一特征将训练数据集分割成子集,使得各个子集有一个在当前条件下最好的分类。

  2. 如果这些子集已经能够被基本正确分类,那么构建叶节点,并将这些子集分到所对应的叶节点去。

  3. 如果还有子集不能够被正确的分类,那么就对这些子集选择新的最优特征,继续对其进行分割,构建相应的节点,如果递归进行,直至所有训练数据子集被基本正确的分类,或者没有合适的特征为止。

  4. 每个子集都被分到叶节点上,即都有了明确的类,这样就生成了一颗决策树。

从上述步骤可以看出,决策生成过程中有两个重要的问题:

  1. 数据如何分割
  2. 如何构建叶节点

问题一:数据分割

在选择了最优特征的情况下,如何对数据分裂呢?

分裂属性的数据类型分为离散型和连续型两种类型,对于离散型数据,按照属性值进行分裂,每个属性值代表一个分裂节点;对于连续型属性,一般性的做法是对数据按照该属性进行排序,再将数据分成若干个区间,如该表,将年龄分成[20,30]、[30,40]、[40,50],一个区间对应一个节点,若数据的属性值落入某一个区间则该数据就属于其对应的节点。

例:
按照职业分类:离散型

取值 贷款 不贷款
白领 3 1
自由职业 1 2
工人 1 3

决策树算法(1) 算法原理
白领按照年龄分类:连续型

取值 贷款 不贷款
>=35 1 0
<35 2 1

决策树算法(1) 算法原理
两个例子描述了分类属性的选择

问题二:如何构建叶节点

现在我们知道了分裂属性是如何进行分割的,那么我们怎样选择分裂属性呢?叶节点如何选择呢?

决策树选择贪心思想进行分裂,即选择可以得到最优分裂结果的属性进行分裂。那么怎样才是最优的分裂结果?最理想的情况当然是能找到一个属性刚好能够将不同的类别分开,但是大多数情况下很难一步到位,我们希望分裂后孩子节点的数据尽量"纯",以下图为例
决策树算法(1) 算法原理
可以看出,属性2分裂后,孩子节点更纯。属性1分裂后每个节点的两类数量还是相同,跟节点的分类结果相比完全没有提高,按照属性2分裂后每个节点各类的数量的相差较大,可以很大概率的认为第一个孩子节点输出的是类1,第二个孩子节点输出的是类2。

总结:选择分裂属性就是要找出能够使所有孩子节点数据最纯的属性,决策树使用信息增益或者信息增益率作为选择属性的的依据。

信息增益

用信息增益表示分裂前后的数据复杂度和分裂节点的数据复杂度的变化值,计算公式表示为:
Info_Gain=Gaini=1nGainiInfo\_Gain = Gain - \sum_{i=1}^nGain_i
注: 其中Gain表示节点的复杂度,Gain越高,说明复杂度越高。信息增益说白了就是分裂前的数据复杂度减去孩子节点的数据复杂度的和,信息增益越大,分裂后的复杂度减小的越多,分类的效果越明显,越适合用来分类。

节点的复杂度可以用熵来度量

熵度量了事物的不确定性,越不确定的事物,它的熵就越大;即数据的混乱程度越高,熵越大。

  • 具体的,随机变量X的熵的表达式如下:
    H(X)=i=1npilogpiH(X) = -\sum_{i=1}^np_ilogp_i

注: 其中n代表X的n种不同的离散取值。而pip_i代表了X取值为i的概率,log为以2为底或者以e为底的对数。

例:X有两个两个取值,而这两个取值各位1/2时X的熵最大,此时X具有最大的不确定性,值为H(X)=(12log12+12log12)=log2H(X)=-(\frac{1}{2}log\frac{1}{2}+\frac{1}{2}log\frac{1}{2}) = log2。如果一个概率大于1/2,另一个一个概率小于1/2,则不确定性减少,对应的熵也会减少。比如一个概率为1/3,概率为2/3,则对应的熵为H(X)=(13log13+23log23)=log323log2&lt;log2H(X)=-(\frac{1}{3}log\frac{1}{3}+\frac{2}{3}log\frac{2}{3}) = log3-\frac{2}{3}log2&lt;log2

  • 熟悉了一个变量的熵,很容易推广到多个变量的联合熵,这里给出两个变量X和Y的联合熵的表达式:
    H(X,Y)=i=1np(xi,yi)logp(xi,yi)H(X,Y) = -\sum_{i=1}^np(x_i,y_i)logp(x_i,y_i)

  • 有了联合熵,又可以得到条件熵的表达式H(X|Y),条件熵又类似于条件概率,它度量了我们的X在知道Y以后剩下的不确定性。表达式如下:
    H(XY)=i=1np(xi,yi)logp(xiyi)=j=1np(yj)H(Xyj)H(X|Y) = -\sum_{i=1}^np(x_i,y_i)logp(x_i|y_i)= -\sum_{j=1}^np(y_j)H(X|y_j)

我们上面提到了 H(X) 确定了X的不确定性,条件熵 H(X|Y) 定义了我们在知道了Y以后X的不确定性,那么 H(X)-H(X|Y) 呢?从定义我们可以推断出,它度量了X在知道Y以后不确定性减少程度,这个度量我们在信息论中称之为互信息,记为I(X,Y),在ID3算法中也叫做信息增益

下面我用一个图很容易的表示出上面概念的关系。左边的椭圆代表H(X),右边的椭圆代表H(Y),中间重合的部分就是我们的互信息或者说信息增益I(X,Y),左边的椭圆去掉重合部分就是H(X|Y),右边的椭圆去掉重合部分就是H(Y|X),两个椭圆的并就是H(X,Y)。
决策树算法(1) 算法原理

决策树ID3算法的思路

ID3算法就是用信息增益的大小来判断当前节点应该用什么特征来构建决策树,用计算出的信息增益的最大的特征来建立决策树的当前节点。

我举个信息增益的计算例子来理解一下,数据就使用问题二中属性2分类中的数据。这里我们总共有我60个样本D,输出为0或1。其中有33(28+5)个输出为1,27(25+2)个输出为0。样本中有个特征A,取值为A1和A2。在取值为A1的样本输出中,有28个输出为1,2个输出为0;在取值为A2的样本输出中,有5个输出为1,25个输出为0。

样本D的熵为:H(D)=(3360log23360+2760log22760)=0.993H(D) = -(\frac{33}{60}log_2\frac{33}{60}+\frac{27}{60}log_2\frac{27}{60})=0.993
样本D在特征下的条件熵为H(DA)=12H(D1)+12H(D2)=12(2830log22830+230log2230)12(530log2530+2530log22530)=0.468H(D|A)=\frac{1}{2}H(D1)+\frac{1}{2}H(D2) = -\frac{1}{2}(\frac{28}{30}log_2\frac{28}{30}+\frac{2}{30}log_2\frac{2}{30})-\frac{1}{2}(\frac{5}{30}log_2\frac{5}{30}+\frac{25}{30}log_2\frac{25}{30}) =0.468
对应的信息增益为:I(D,A)=H(D)H(DA)=0.525I(D,A) = H(D) - H(D|A) = 0.525

下面我们来看看具体的算法过程是怎样的。
输入的是m个样本,样本输出集合为D,每个样本有n个离散特征,特征集合即为A,输出为决策树T。

算法的过程为:

  1. 初始化信息增益的阈值ϵ\epsilon
  2. 判断样本是否为同一类输出DiD_i,如果是则返回单节点树T。标记类别为DiD_i
  3. 判断特征是否为空,如果是则返回单节点树T,标记类别为样本中输出类别D实例数最多的类别。
  4. 计算A中各个特征(一共n个)对输出D的信息增益,选择信息增益最大的特征AgA_g
  5. 如果AgA_g的信息增益小于阈值ϵ\epsilon,则返回单节点树T,标记类别为样本中输出类比D实例数最多的类别。
  6. 否则,按特征AgA_g的不同取值AgiA_{gi}将对应的样本输出D分为不同的类别DiD_i。每个类别产生一个子节点,对应的特征为A。返回增加了节点的树T
  7. 对于所有的子节点,令D=Di,A=A{Ag}D = D_i,A = A - \{A_g\},递归调用2~6步,得到子树TiT_i并返回。

分析数据:
上面已经求得,特征A4(学历)的信息增益最大,所以选择A4作为根节点的特征,它将训练集D划分为2个子集D1(A4=小学)、D2(A4=初中)、D2(A4=高中)、(A4=本科)、(A4=硕士)、(A4=博士)。由于D1-D6只有同一类的样本点,所以它们成为一个叶节点。

重新开始新的特征选择,对D1则要从特征A1(职业),A2(年龄),A3(收入)中选择新的特征,计算各个特征新的信息增益:
g(D1,A1)=H(D1)H(D1A1)=0.270g(D1,A2)=H(D1)H(D1A2)=0.241g(D1,A3)=H(D1)H(D1A3)=0.225g(D1,A1) = H(D1)-H(D1|A1)=0.270\\ g(D1,A2) = H(D1)-H(D1|A2)=0.241\\ g(D1,A3) = H(D1 )-H(D1|A3)=0.225
根据计算,选择信息增益最大的A1作为节点的特征,由于其有3个可能取值,所以引出3个子节点:

  1. 对应"自由职业",包含一个样本,类别标记为"否"
  2. 对应"工人",包含一个样本,类别标记为"否"
  3. 对应"白领",包含一个样本,类别标记为"是"

D2-D6重复上述过程。
这样就生成一个决策树的分支,生成的部分决策树如下图所示:
决策树算法(1) 算法原理

决策树ID3算法的不足

ID3算法虽然提出了新思路,但是还是有很多值得改进的地方。

  1. ID3没有考虑连续特征,比如工资、年龄都是连续值,无法在ID3运用。这大大限制了ID3的用途。
  2. ID3采用信息增益大的特征优先建立决策树的节点。但是,在相同条件下,取值比较多的特征比取值比较少的特征信息增益大。比如一个变量有2个值,各为1/2,信息增益为12log122=log2-\frac12log\frac12*2=log2;另一个变量为3个值,各为1/3,信息增益为13log133=log3&gt;log2-\frac13log\frac13*3=log3&gt;log2。其实他们都是完全不确定的变量,但是取3个值的比取两个值的信息增益大。如何校正这个问题呢?
  3. ID3算法对缺失值的情况没有做考虑
  4. 没有考虑过拟合的问题

ID3算法的作者基于上述不足,对ID3算法做了改进,这就是C4.5算法。

决策树C4.5算法的改进

ID3主要有四个主要的不足,昆兰在C4.5算法中改进了上述四个问题。

a)对于第一个问题,不能处理连续特征,C4.5的思路是将连续的特征离散化。
比如m个样本的连续特征A有m个,从小到大排列为a1a2...ama_1、a_2、...、a_m,则C4.5取相邻两样本的值的平均数,一共取得m-1个划分点,其中第i个划分点TiT_i表示为ai+ai+12\frac{a_i+a_{i+1}}{2}。对于这m-1个点,分别计算以该点作为二元分类点时的信息增益。选择信息增益最大的点作为该连续特征的二元离散分类点。比如取到的增益最大的点为ata_t,则小于ata_t的值为类别1,大于ata_t的值为类别2,这样我们就做到了连续特征的离散化。要注意的是,与离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。

b)对于第二个问题,信息增益作为标准容易偏向取值较多的特征的问题,我们引入一个信息增益比的变量IR(X,Y)I_R(X,Y),它是信息增益和特征熵的比值。
表达式如下:
IR(D,A)=I(D,A)HA(D)I_R(D,A)=\frac{I(D,A)}{H_A(D)}
注: 其中D为样本特征输出的集合,A为样本特征,对于特征熵H_A(D),表达式如下:
HA(D)=i=1nDiDlog2DiDH_A(D)=-\sum_{i=1}^n\frac{|D_i|}{|D|}log_2\frac{|D_i|}{|D|}
注: 其中n为特征A的类别数,DiD_i为特征A的第i个取值对应的样本数,D为样本个数。

特征数越多的特征对应的特征熵越大,它作为分母,可以校正信息增益偏向于取值较多的特征的问题。

c)对于第三个问题,缺失值的处理。主要需要解决两个方面,一是在样本某些特征缺失的情况下选择划分的属性;二是选定了划分属性,对于该属性上缺失特征的样本处理。

  • 第一个子问题:对于某一个有缺失特征值的特征A
    C4.5算法的思路:将数据分为两部分,对每个样本设置一个权重(初始可以都为1),然后划分数据,一部分是有特征A的数据D1,另一部分是没有特征A的数据D2。然后对于没有缺失特征A的数据集D1来和对应的A特征的各个特征值一起计算加权重后的信息增益比,最后乘上一个系数,这个系数是无特征A缺失的样本加权后所占加权总样本的比例。

  • 第二个子问题:选定了划分属性,对于该属性上缺失特征的样本处理
    可以将缺失特征的样本同时划分入子节点,不过将该样本的权重按各个子节点样本的数量比例的来分配。比如缺失特征A的样本a之前的权重为1,特征A有3个值A1、A2、A3。三个特征值对应的无缺失A的样本个数为2、3、4,则a同时划分如A1、A2、A3,对应的权重调节为2/9、3/9、4/9。

d)对于第四个问题:过拟合的问题
C4.5引入了正则化系数进行初步的剪纸。

决策树C4.5算法的不足与思考

C4.5算法虽然改进或改善了ID3算法的几个主要的问题,但是仍然有优化的空间

a)由于决策树非常的容易过拟合,因此对于生成的决策树必须要进行剪纸。
剪枝的算法有许多,C4.5的剪枝方法有优化的空间。主要有两种思路:一种是预剪枝,即在生成决策树的时候就决定是否剪枝;另一个是后剪枝,即先生成决策树,再通过交叉验证来剪枝。具体在CART树中讲解决策树的剪枝思路。

b)C4.5生成的是多叉树,即一个父节点可以有多个节点
很多时候,在计算机中运用二叉树的模型会比多叉树运算效率高。如果采用二叉树,可以提高效率。

c)C4.5只能用于分类,如果能将决策树用于回归的话可以扩大它的使用范围

d)C4.5由于使用了熵模型,里面有大量的耗时的对数运算,如果是连续值还有大量的排序运算。 如果能够加以模型简化可以减少运算强度但又不牺牲太多准确性的话那就更好了

CART分类树算法的最优特征选择方法

在ID算法中,我们使用了信息增益来选择特征,信息增益大的优先选择。在C4.5算法中,采用了信息增益比来选择特征,以减少信息增益容易选择特征值多的特征的问题。但是无论是ID3还是C4.5都是基于信息论的熵模型的,这里面会涉及大量的对数运算。能不能化简模型同时也不至于完全丢失熵模型的优点呢?

有!CART树采用基尼系数来代替信息增益比,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好,这和信息增益(比)是相反的。

具体的,在分类问题中,假设有K个类别,第k个类别的概率为pkp_k,则基尼系数的表达式为:
Gini(p)=k=1Kpk(1pk)=1k=1Kpk2Gini(p) = \sum_{k=1}^Kp_k(1-p_k)=1-\sum_{k=1}^Kp_k^2
如果是二类分类问题,计算就更简单了,如果第一个样本的输出概率为p,则基尼系数的表达式为:
Gini(p)=2p(1p)Gini(p) = 2p(1-p)
对于给定的样本D,假设有K个类别,第k个类别的数量为CkC_k,则样本D的基尼系数的表达式为:
Gini(D)=1k=1K(CkD)2Gini(D)=1-\sum_{k=1}^K(\frac{|C_k|}{|D|})^2
特别的,对于样本D,如果根据特征A的某个值a,把D分成D1和D2两部分,则在特征A的条件下,D的基尼系数的表达式为:
Gini(D)=D1DGini(D1)+D2DGini(D2)Gini(D) = \frac{|D1|}{|D|}Gini(D1)+\frac{|D2|}{|D|}Gini(D2)

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

CART分类树算法对于连续特征和离散特征处理的改进

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

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

对于CART分类树离散值的处理问题,采用的思路是不停的二分离散特征。如下图所示
决策树算法(1) 算法原理
回忆下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的一棵子树中,离散特征只会参与一次节点的建立。

算法的过程:
Input: 训练集D,基尼系数的阈值,样本个数阈值。
Output: 决策树T。

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

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

CART回归树建立算法

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

首先,我们要明白,什么是回归树,什么是分类树。两者的区别在于样本输出,如果样本输出是离散值,那么这是一颗分类树。如果果样本输出是连续值,那么那么这是一颗回归树。

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

  • 连续值的处理方法不同
  • 决策树建立后预测的方式不同

对于连续值的处理,我们知道CART分类树采用的是用基尼系数的大小来度量特征的各个划分点的优劣情况。这比较适合分类模型,但是对于回归模型,我们使用了常见的和方差的度量方式,CART回归树的度量目标是,对于任意划分特征A,对应的任意划分点s两边划分成的数据集D1和D2,求出使D1和D2各自集合的均方差最小,同时D1和D2的均方差之和最小所对应的特征和特征值划分点。表达式为:
minA,s[minc1xiD1(A,s)(yic1)2+minc2xiD2(A,s)(yic2)2]\underbrace{min}_{A,s}\Bigg[\underbrace{min}_{c_1}\sum\limits_{x_i \in D_1(A,s)}(y_i - c_1)^2 + \underbrace{min}_{c_2}\sum\limits_{x_i \in D_2(A,s)}(y_i - c_2)^2\Bigg]
其中,c1c_1为D1数据集的样本输出均值,c2c_2为D2数据集的样本输出均值。

对于决策树建立后做预测的方式,上面讲到了CART分类树采用叶子节点里概率最大的类别作为当前节点的预测类别。而回归树输出不是类别,它采用的是用最终叶子的均值或者中位数来预测输出结果。

CART树算法的剪枝

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

由于决策时算法很容易对训练集过拟合,而导致泛化能力差,为了解决这个问题,我们需要对CART树进行剪枝,即类似于线性回归的正则化,来增加决策树的泛化能力。

剪枝就是当α确定时,选择损失函数最小的模型,即损失函数最小的子树。

  • 当α值确定时,子树越大,往往与训练数据的拟合越好,但是模型的复杂度越高;
  • 子树越小,模型的复杂度就越低,但是往往与训练数据的拟合不好
  • 损失函数正好表示了对两者的平衡。

但是,有很多的剪枝方法,我们应该这么选择呢?CART采用的办法是后剪枝法,即先生成决策树,然后产生所有可能的剪枝后的CART树,然后使用交叉验证来检验各种剪枝的效果,选择泛化能力最好的剪枝策略。

也就是说,CART树的剪枝算法可以概括为两步

  1. 从原始决策树生成各种剪枝效果的决策树
  2. 用交叉验证来检验剪枝后的预测能力,选择泛化预测能力最好的剪枝后的数作为最终的CART树。

首先我们看看剪枝的损失函数度量,损失函数认为对于每个分类终点(叶子节点)的不确定性程度就是分类的损失因子,而叶子节点的个数是模型的复杂程度,作为惩罚项,损失函数的第一项是样本的训练误差,第二项是模型的复杂度。如果一棵子树的损失函数值越大,说明这棵子树越差,因此我们希望让每一棵子树的损失函数值尽可能得小,损失函数最小化就是用正则化的极大似然估计进行模型选择的过程。在剪枝的过程中,对于任意的一课子树T,其损失函数为:
Cα(Tt)=C(Tt)+αTtC_{\alpha}(T_t) = C(T_t) + \alpha |T_t|

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

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

看过剪枝的损失函数度量后,我们再来看看剪枝的思路,对于位于节点t的任意一颗子树TtT_t,如果没有剪枝,它的损失是:
Cα(Tt)=C(Tt)+αTtC_{\alpha}(T_t) = C(T_t) + \alpha |T_t|

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

当α=0或者α很小时,Cα(Tt)&lt;Cα(T)C_α(T_t)&lt;C_α(T) ;当α增大到一定的程度时,Cα(Tt)=Cα(T)C_α(T_t)=C_α(T);当α继续增大时不等式反向,也就是说,如果满足下式:
α=C(T)C(Tt)Tt1\alpha = \frac{C(T)-C(T_t)}{|T_t|-1}

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

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

CART树的剪枝算法:
Input: CART树建立算法得到的原始决策树T,参数α
Output: 最优决策子树TαT_α

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

决策树算法小结

决策树算法的优点

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

决策树算法的缺点

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

好了,这一章到这里就完结了。下一章是本章内容的代码实现,通过代码实现加深理解

本文大部分参考该大牛的文章 刘建平Pinard,我整理一遍加深理解

相关文章: