决策树及决策树生成与剪枝

(转自个人博客)

决策树 (Decision tree) 是一种基本的分类与回归方法。它是一个树形结构,对于指定特征空间上的数据点来说,总能顺着决策树的根节点一步步分配到子节点最终到达叶节点,而叶节点表示了该数据点所属的分类。在每一次分配到子节点的过程中可以看作是对数据点中特有的特征属性值进行的 if-then 判断。

决策树可以认为是 if-then 规则的集合,也可以认为时定义在特征空间与类空间上的条件概率分布。其主要优点是模型具有可读性,分类速度快。如何得到该决策树叫做决策树学习,决策树学习时,利用训练数据,根据损失函数最小化的原则建立决策树模型。预测试,对新的数据,利用决策树模型进行分类。

接下来按照周志华老师的《机器学习》第 4 章[1] 来梳理一下对决策树的学习:

1. 决策树学习

决策树学习的目的是为了生成一颗泛化能力强,即处理未见示例能力强的决策树,其基本流程遵循简单而直观的 分而治之 (Divide and Conquer) 策略,如下图所示:

Decision_tree_Learning

决策树的生成是一个自根结点一直到叶结点的递归生成过程。

在递归生成的伪代码表述中,可以看到,有三个地方导致递归返回:

  1. (行 3) 当前结点包含的样本全部属于同一个类别,无需划分;
  2. (行 6) 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分。在这种情况下,把当前结点标记为叶结点,并且将其类别设定为该结点所含样本最多的类别;
  3. (行 12) 当前结点包含的样本集和为空,不能划分,把当前结点标记为叶结点,但是将其类别设定为其父结点所含样本最多的类别,周志华老师的《机器学习》中在该条件下执行了 return,但是按照我的理解由于这里处于 for 循环中,虽然属性中的一个取值样本集合为空,但是其它取值情况下还有有可能有样本集合的,如果这里执行了 return,那么就跳过了其它取值判断的可能。

另外,其中第 14 行 A \ { a ∗ } A \backslash \lbrace a_\ast \rbrace A\{a} 表示从 A A A 中去除 a ∗ a_\ast a 属性。

2. 最优划分属性的选择

从递归生成伪代码图示中可以看出,(行 8)选择最优划分属性 a ∗ a_\ast a 是最关键的一步。如何选择决定了决策树的效率与准确率。一般而言我们希望选择一个属性 a ∗ a_\ast a 之后,其分支节点所包含的样本尽可能属于同一类别,即结点的 纯度 (Purity) 越来越高。

根据最优属性 a ∗ a_\ast a 选择方法的不同,决策树大致分为了 ID3 [Quinlan, 1986]、C4.5 [Quinlan, 1993]、CART [Breiman et al., 1984]。

接下来分别介绍三种方法,在之前,先给出周志华老师《机器学习》中表 4.1 4.1 4.1 中的西瓜数据如下:

编号色泽根蒂敲声纹理脐部触感好瓜
1青绿蜷缩浊响清晰凹陷硬滑
2乌黑蜷缩沉闷清晰凹陷硬滑
3乌黑蜷缩浊响清晰凹陷硬滑
4青绿蜷缩沉闷清晰凹陷硬滑
5浅白蜷缩浊响清晰凹陷硬滑
6青绿稍蜷浊响清晰稍凹软粘
7乌黑稍蜷浊响稍糊稍凹软粘
8乌黑稍蜷浊响清晰稍凹硬滑
9乌黑稍蜷沉闷稍糊稍凹硬滑
10青绿硬挺清脆清晰平坦软粘
11浅白硬挺清脆模糊平坦硬滑
12浅白蜷缩浊响模糊平坦软粘
13青绿稍蜷浊响稍糊凹陷硬滑
14浅白稍蜷沉闷稍糊凹陷硬滑
15乌黑稍蜷浊响清晰稍凹软粘
16浅白蜷缩浊响模糊平坦硬滑
17青绿蜷缩沉闷稍糊稍凹硬滑

2.1 信息增益 - ID3

2.1.1 什么是信息增益

按照周志华老师《机器学习》中第 4.2.1 4.2.1 4.2.1 小节中所述,假定离散属性 a a a V V V 个可能的取值 { a 1 , a 2 , . . . , a V } \lbrace a_1,a_2,...,a_V \rbrace {a1,a2,...,aV},若使用 a a a 来对样本集 D D D 进行划分,会产生 V V V 个分支结点,其中第 v v v 个分支结点包含了 D D D 中所有在属性 a a a 上取值为 a v a_v av 的样本,记为 D v D_v Dv,其对应的信息熵为:

H ( D v ) = − ∑ k ∈ Y p ( k ∣ v ) log ⁡ p ( k ∣ v ) = − ∑ k ∈ Y ∣ D v k ∣ ∣ D v ∣ log ⁡ ∣ D v k ∣ ∣ D v ∣ H(D_v)=-\sum_{k \in \mathcal{Y}}p(k\mid v)\log p(k \mid v)=-\sum_{k \in \mathcal{Y}}\frac{|D_{vk}|}{|D_v|}\log \frac{|D_{vk}|}{|D_v|} H(Dv)=kYp(kv)logp(kv)=kYDvDvklogDvDvk

其中 D v k D_{vk} Dvk 表示 D v D_v Dv 中分类为 k k k 的样本。再考虑到不同的分支 v v v 结点所包含的样本数不同,给分支结点赋予权重 ∣ D v ∣ / ∣ D ∣ |D_v|/|D| Dv∣/∣D,于是可以计算出属性 a a a 对样本集 D D D 进行划分所获得的 信息增益 (Information gain)

Gain ( D , a ) = H ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ H ( ∣ D v ∣ ) = H ( D ) − H ( D , a ) = I ( D , a ) \textrm{Gain}(D,a)=H(D)-\sum_{v=1}^V \frac{|D_v|}{|D|}H(|D_v|)=H(D)-H(D,a)=I(D,a) Gain(D,a)=H(D)v=1VDDvH(Dv)=H(D)H(D,a)=I(D,a)

其实,信息增益就是训练数据集 D D D 中的类别与某一属性之间(或者两个属性之间的,因为最终决策树的目的是要进行分类,所以在决策树的每一个分支判断时都用类别与对应的属性进行信息增益比较)的互信息。我们知道,互信息表示了两事件发生所代表的信息之间的重复部分,当两事件信息重复的部分越大,那么采用一种事件为标准来划分另一种事件所能确定的部分也就越大,也就是说采用属性 a a a 来进行划分所获得的“纯度提升”越大。考虑极端情况,当属性 a a a 与类别完全没有关系时,其互信息为 0 0 0,这时候采用 a a a 属性作为划分标准对于数据集的类别确认完全没有帮助;当属性 a a a 的各属性分别与数据集的类别一一对应时,也就是其概率分布完全相同,那么根据互信息的计算公式,其互信息等于各自的熵,也就是说,如果我们采用 a a a 进行数据的划分,那么数据可以完全干净的划分出来,也就不再含有额外的不可知信息了。

当决策树中选择最优划分属性(行 8)按照信息增益最大来进行时,决策树属于 ID3 决策树

2.1.2 ID3 树中最优划分属性计算举例

根据上节给出的西瓜数据集,我们学习一个对瓜好坏判断的决策树。在这个例子中分类个数为 2 2 2 (是好瓜、不是好瓜),即 Y = 2 \mathcal{Y}=2 Y=2。在决策树学习开始时,根结点包含 D D D 中所有样例,正例占 p 1 = 8 17 p_1=\frac{8}{17} p1=178,反例占 p 2 = 9 17 p_2=\frac{9}{17} p2=179。根结点的信息熵(下面我们都以比特为单位计算)为:

H ( D ) = − ∑ k = 1 2 p ( k ) log ⁡ 2 p ( k ) = − ( 8 17 log ⁡ 2 8 17 + 9 17 log ⁡ 2 9 17 ) = 0.998 H(D)=-\sum_{k=1}^2p(k)\log_2 p(k)=-\left(\frac{8}{17}\log_2\frac{8}{17}+\frac{9}{17}\log_2\frac{9}{17}\right)=0.998 H(D)=k=12p(k)log2p(k)=(178log2178+179log2179)=0.998

然后我们要计算出与当前属性集合 {色泽、根蒂、敲声、纹理、脐部、触感}中每个属性的信息增益,也就是对应的互信息。以属性“色泽”为例,它有 3 3 3 个可能取值:{青绿、乌黑、浅白}。以该属性对数据集进行划分,可以得到 3 3 3 个子集,分别为: D 1 D_1 D1(色泽=青绿)、 D 2 D_2 D2(色泽=乌黑)、 D 3 D_3 D3(色泽=浅白)。

对子集 D 1 D_1 D1 来说,包含了编号为 { 1 , 4 , 6 , 10 , 13 , 17 } \lbrace1,4,6,10,13,17\rbrace {1,4,6,10,13,17} 6 6 6 个样例,其中正例为 { 1 , 4 , 6 } \lbrace1,4,6\rbrace {1,4,6},占 p 1 = 3 6 p_1=\frac{3}{6} p1=63 ;反例为 { 10 , 13 , 17 } \lbrace10,13,17\rbrace {10,13,17},占 p 1 = 3 6 p_1=\frac{3}{6} p1=63。计算其熵为:

H ( D 1 ) = − ( 3 6 log ⁡ 2 3 6 + 3 6 log ⁡ 2 3 6 ) = 1.000 H(D_1)=-\left(\frac{3}{6}\log_2\frac{3}{6}+\frac{3}{6}\log_2\frac{3}{6}\right)=1.000 H(D1)=(63log263+63log263)=1.000

依次可以计算另外两个子集的信息熵为:

H ( D 2 ) = 0.918 H ( D 3 ) = 0.722 \begin{aligned} H(D_2)&=0.918 \\ H(D_3)&=0.722 \end{aligned} H(D2)H(D3)=0.918=0.722

最终可以计算数据集 D D D 的类别信息在属性“色泽”熵的信息增益(也可以理解为类别与“色泽”属性之间的互信息)为:

Gain ( D , 色泽 ) = H ( D ) − ∑ v = 1 3 p ( v ) H ( D v ) = 0.998 − ( 6 17 × 1.000 + 6 17 × 0.918 + 5 17 × 0.722 ) = 0.109 \begin{aligned} \textrm{Gain}(D,色泽) &=H(D)-\sum_{v=1}^3p(v)H(D_v) \\ &=0.998-\left(\frac{6}{17}\times1.000+\frac{6}{17}\times0.918+\frac{5}{17}\times0.722\right) \\ &=0.109 \end{aligned} Gain(D,色泽)=H(D)v=13p(v)H(Dv)=0.998(176×1.000+176×0.918+175×0.722)=0.109

重复上述的计算步骤,我们可以计算出其他属性的信息增益:

Gain ( D , 根蒂 ) = 0.143 ; Gain ( D , 敲声 ) = 0.141 ; Gain ( D , 纹理 ) = 0.381 ; Gain ( D , 脐部 ) = 0.289 ; Gain ( D , 触感 ) = 0.006. \begin{aligned} \textrm{Gain}(D,根蒂) &=0.143; \\ \textrm{Gain}(D,敲声) &=0.141; \\ \textrm{Gain}(D,纹理) &=0.381; \\ \textrm{Gain}(D,脐部) &=0.289; \\ \textrm{Gain}(D,触感) &=0.006. \\ \end{aligned} Gain(D,根蒂)Gain(D,敲声)Gain(D,纹理)Gain(D,脐部)Gain(D,触感)=0.143;=0.141;=0.381;=0.289;=0.006.

经过比较,发现采用“纹理”进行划分得到的信息增益最大,于是它被选为划分属性。下图给出了根据“纹理”属性划分之后的数据子集:

Root_node_decision

对每一个数据子集按照上边的步骤继续划分下去就能得到最终的决策树(需要注意的是每次样例子集中的属性不包含父结点中划分所依赖的属性),如下图所示:

Decision-tree

2.2 信息增益率 - C4.5

采用信息增益来进行划分属性的决策有一个潜在的问题,当某一个属性的取值种类非常多时,对应每一个属性取值的样本子集,其分类的信息熵可能会变得很小。为了说明,采用一种极端情况,假设我们对上一节中要分类的西瓜数据进行决策树生成时,把“编号”也当作一种可以作为划分依据的属性。则在这种情况下,每一个编号属性对应一个实例,且其分类是确定的,那么对于每一个“编号”属性取值来说,其分类信息熵为 0 0 0,计算“编号”分类所带来的信息增益为:

Gain ( D , a ) = H ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ H ( ∣ D v ∣ ) = − 8 17 log ⁡ 2 8 17 − 8 17 log ⁡ 2 8 17 − ∑ v = 1 17 1 17 × 0 = 0.9975 \begin{aligned} \textrm{Gain}(D,a)&=H(D)-\sum_{v=1}^V \frac{|D_v|}{|D|}H(|D_v|) \\ &=-\frac{8}{17}\log_2\frac{8}{17}-\frac{8}{17}\log_2\frac{8}{17}-\sum_{v=1}^{17}\frac{1}{17}\times0 \\ &=0.9975 \end{aligned} Gain(D,a)=H(D)v=1VDDvH(Dv)=178log2178178log2178v=117171×0=0.9975

最后计算出来的信息增益很大。但是显然,用“编号”属性来作为结点的划分是没有意义的。思考其中的问题在于,对数函数并不是线性的,信息量的减少速度大于类别数量的增加速度。信息增益准则对取值数目较多的属性有所偏好,为了减小这种偏好,C4.5 决策树 采用 信息增益率 (gain ratio) 来选择最优划分属性。其定义如下:

Gain ratio ( D , a ) = G a i n ( D , a ) I V ( a ) {\textrm{Gain}}_\textrm{ratio}(D,a)=\frac{\rm{Gain}(D,a)}{\rm{IV}(a)} Gainratio(D,a)=IV(a)Gain(D,a)

其中,

IV ( a ) = − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ log ⁡ ∣ D v ∣ ∣ D ∣ = H ( a ) \textrm{IV}(a)=-\sum_{v=1}^V \frac{|D_v|}{|D|}\log \frac{|D_v|}{|D|}=H(a) IV(a)=v=1VDDvlogDDv=H(a)

到这里,我们就可以发现,信息增益率是用属性分类的信息熵对由属性分类引起的互信息熵进行了归一。属性的种类越多其信息熵通常也会越大。对西瓜数据,有: H ( 触感 ) = 0.874   ( V = 2 ) H(触感)=0.874\ (V=2) H(触感)=0.874 (V=2) H ( 色泽 ) = 1.580   ( V = 3 ) H(色泽)=1.580\ (V=3) H(色泽)=1.580 (V=3) H ( 编号 ) = 4.088   ( V = 17 ) H(编号)=4.088\ (V=17) H(编号)=4.088 (V=17)

最后一点需要注意的是,增益率准则虽然减少了对取值数目较多的属性依赖,但是增加了对取值数目较少的属性偏好。因此, C4.5 并没有直接选择增益率最大的候选划分属性,而是使用了一个启发式:先从候选划分属性中找出 信息增益 高于 平均水平 的属性,再从中选择 增益率 最高的。

2.3 基尼指数 - CART

最后介绍一种选择划分属性的依据是使用 基尼指数 (Gini index)。数据集合 D D D 的纯度可用基尼指数来度量:

Gini ( D ) = ∑ k = 1 ∣ Y ∣ ∑ k ′ ≠ k p k p k ′ = 1 − ∑ k = 1 Y p k 2 \textrm{Gini}(D) = \sum_{k=1}^{|\mathcal{Y}|}\sum_{k^\prime\neq k}p_kp_{k^\prime}=1-\sum_{k=1}^{\mathcal{Y}}p_k^2 Gini(D)=k=1Yk=kpkpk=1k=1Ypk2

直观来看, Gini ( D ) \textrm{Gini}(D) Gini(D) 反映了从数据集 D D D 中随机抽取两个样本,其类别标记不一致的概率。因此, Gini ( D ) \textrm{Gini}(D) Gini(D) 越小,则数据集 D D D 的纯度越高。

对特定属性 a a a 的基尼指数定义如下:

Gain index ( D , a ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ Gini ( D v ) {\textrm{Gain}}_\textrm{index}(D,a) = \sum_{v=1}^{V}\frac{|D_v|}{|D|}\textrm{Gini}(D_v) Gainindex(D,a)=v=1VDDvGini(Dv)

我们在候选属性集合 A A A 中,选择那个使得划分后基尼指数最小的属性作为最优划分属性,即:

a ∗ = arg ⁡ min ⁡ a ∈ A Gain index ( D , a ) a_\ast=\arg \min \limits_{a \in A}{{\textrm{Gain}}_\textrm{index}(D,a)} a=argaAminGainindex(D,a)

采用基尼指数作为划分属性的判据的决策树是一种 CART 决策树。

3. 决策树剪枝

3.1 决策树的损失函数

刚开始我提到,决策树可以看作是一系列 if-then 规则的集合。这个规则集合有一个重要的性质:互斥并且完备。意思就是说,拿来任意一个实例,顺着规则的起点(根结点)出发,最终都有且只有一条路径到达某一个具体的叶结点(具体的分类),并且不会出现实例无法分类的情况。

如果不考虑泛化能力,在训练集上生成的所有不同规则集合对应的决策树中,挑选出最优的决策树,可以根据所有叶结点中的预测误差来衡量,即模型与训练数据的拟合程度。设树 T T T 的叶结点个数为 ∣ T ∣ |T| T t t t 是树 T T T 的一个叶结点,该叶结点有 N t N_t Nt 个样本点,其中 k k k 类的样本点有 N t k N_{tk} Ntk 个, k = 1 , 2 , . . . , K k=1,2,...,K k=1,2,...,K K K K 为样本空间中的所属分类数量。叶结点 t t t 上的经验熵 H t ( T ) H_t(T) Ht(T)

H t ( T ) = − ∑ k N t k N t log ⁡ N t k N t H_t(T)=-\sum_k\frac{N_{tk}}{Nt}\log\frac{N_{tk}}{N_t} Ht(T)=kNtNtklogNtNtk

代表了该叶结点的分类还有多少信息量不知道(混乱程度),可以这么考虑一个理想的极端情况,当该叶结点中只有一个分类 k n k_n kn 时,那么 N t k n = N t N_{tk_n}=N_t Ntkn=Nt,其它的 N k 1 , . . . , N k n − 1 , N k n + 1 , . . . , N k K N_{k_1},...,N_{k_{n-1}},N_{k_{n+1}},...,N_{k_K} Nk1,...,Nkn1,Nkn+1,...,NkK 全部为 0 0 0,最终 H t ( T ) = 0 H_t(T)=0 Ht(T)=0,这个结论与分类已经完全的结果是相吻合的。那么我们可以说,经验熵 H t ( T ) H_t(T) Ht(T) 就代表了连接该叶结点的整个路径对数据分类的彻底性。

考虑到所有的叶结点每个叶结点中的样例个数不同,我们采用

C ( T ) = ∑ t = 1 ∣ T ∣ N t H t ( T ) = − ∑ t = 1 ∣ T ∣ ∑ k = 1 K N t k log ⁡ N t k N t C(T)=\sum_{t=1}^{|T|}N_tH_t(T)=-\sum_{t=1}^{|T|}\sum_{k=1}^K N_{tk}\log\frac{N_{tk}}{N_t} C(T)=t=1TNtHt(T)=t=1Tk=1KNtklogNtNtk

来衡量模型对训练数据的整体测量误差。

但是如果仅仅用 C ( T ) C(T) C(T) 来作为优化目标函数,就会导致模型走向过拟合的结果。因为我们可以尽可能的对每一个分支划分到最细结来使得每一个叶结点的 H t ( T ) = 0 H_t(T)=0 Ht(T)=0,最终使得 C ( T ) = 0 C(T)=0 C(T)=0 最小。

为了避免过拟合,我们需要给优化目标函数增加一个正则项,正则项应该包含模型的复杂度信息。对于决策树来说,其叶结点的数量 ∣ T ∣ |T| T 越多就越复杂,我们用添加正则项的

C α ( T ) = C ( T ) + α ∣ T ∣ C_\alpha(T)=C(T)+\alpha|T| Cα(T)=C(T)+αT

来作为优化的目标函数,也就是树的损失函数。参数 α \alpha α 控制了两者之间的影响程度。较大的 α \alpha α 促使选择较简单的模型(树),较小的 α \alpha α 促使选择较复杂的模型(树)。

决策树的生成过程并不是一个准确的求解树的损失函数的最优化方法。三种决策树学习方法都是一种启发式的求解步骤,在每一步扩大树的规模的时候都是找当前步能使分类结果提升最明显的选择。

如果以文章最开始标示的三个递归学习返回条件进行树的学习,那么最后学习的树是一个以 C ( T ) C(T) C(T) 为损失函数的最优化过程。最后学习到的决策树对训练数据集能达到令人满意的结果,但是对于未知的测试集来说却未必有很好的分类能力。即数据集的泛化能力不能保证。

为了提高决策树的泛化能力,需要对树进行 剪枝 (Pruning),把过于细分的叶结点(通常是数据量过少导致噪声数据的影响增加)去掉而回退到其父结点或更高的结点,使其父结点或更高的结点变为叶结点。

3.2 如何进行决策树剪枝

决策树的剪枝基本策略有 预剪枝 (Pre-Pruning)后剪枝 (Post-Pruning) [Quinlan, 1933]。根据周志华老师《机器学习》一书中所描述是先对数据集划分成训练集和验证集,训练集用来决定树生成过程中每个结点划分所选择的属性;验证集在预剪枝中用于决定该结点是否有必要依据该属性进行展开,在后剪枝中用于判断该结点是否需要进行剪枝。

3.2.1 预剪枝

仿照第 1 小节中的决策树生成流程图,加入预剪枝后的决策树生成流程图如下,

Decision_tree_Learning_add_Pruning

其中红色部分是我参考周志华老师《机器学习》第 4.3.1 小节中给出的实例总结的算法流程。其中的核心思想就是,在每一次实际对结点进行进一步划分之前,先采用验证集的数据来验证如果划分是否能提高划分的准确性。如果不能,就把结点标记为叶结点并退出进一步划分;如果可以就继续递归生成节点。

3.2.2 后剪枝

对于后剪枝,周志华老师《机器学习》中述说如下:

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

对于后剪枝,书中采用了具体的西瓜分类决策树作为剪枝操作讲解,跟着流程走下来感觉挺顺畅,无非就是对想要进行剪枝的结点进行验证集数据的准确性比较,如果剪枝能带来准确性的提高,那么就剪枝,否则保留。然后去判断其它需要考虑剪枝的结点。

在进行剪枝判断的操作中,只对底端结点进行判断。一步一步收缩至每一个底端结点对验证集数据都有更好的分类准确率为止。

更具体地,李航老师《统计学习方法》中第 5.5.2 小节具体介绍了 CART 剪枝算法的步骤流程。刚开始看这部分内容的时候,不容易看懂,后来结合网上的解释回过头来发现其实很简单。下边是参考书中以及网上的解释总结的一套算法流程:

Decision_tree_Post-Pruning

针对流程图中的第 8 步计算,按照书中的描述逻辑,对 T 0 T_0 T0 中的任意内部结点 t t t,以 t t t 为单结点树(结点即为叶,没有分支)的损失函数是

C α ( t ) = C ( t ) + α C_\alpha(t)=C(t)+\alpha Cα(t)=C(t)+α

t t t 为根结点的子树 T t 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

α = 0 \alpha=0 α=0或充分小时,有不等式

C α ( T t ) < C α ( t ) C_\alpha(T_t)<C_\alpha(t) Cα(Tt)<Cα(t)

α \alpha α 增大到某一个值时,有

C α ( T t ) = C α ( t ) C_\alpha(T_t)=C_\alpha(t) Cα(Tt)=Cα(t)

α \alpha α 再继续增大时,不等式反向,所以只要 α = 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 t T_t Tt 更可取,对 T t T_t Tt 进行剪枝。

这个位置容易让人疑惑,逻辑上让人不是很容易想明白,需要多看几遍。其实它的逻辑是这样的:

剪枝的目的不是为了最小化损失函数,剪枝的目的是为了达到一个更好的泛化能力。而对于决策树来说,叶结点的数量越多,反应了决策树对训练数据的细节反应的越多,继而弱化了泛化能力。要想提高泛化能力就需要进行剪枝,而在 3.1 节中给出的损失函数中, α \alpha α 值衡量了损失函数中叶结点数量的权重, α \alpha α 值越大,在最小化损失函数时,需要更多的考虑叶结点数量的影响。 α \alpha α 可以看作一个系数,不同的 α \alpha α 对应于不同的损失函数。而对于所有的这些损失函数来说,在训练集上进行决策树生成时候的步骤都一样,差别只是在判断某些结点是否进行展开的区别。

这个有点类似于对一个函数的泰勒级数展开,而 α \alpha α 值控制着展开的次数项,越小的值展开的次数项越多(往回收缩的高次项越少)。因为决策树的结点个数是有限的,对应到 α \alpha α 的值来说也是有限的。上述求 α \alpha α 过程就是得到具体收缩每一个分支对应的值。

CART 剪枝算法的前半部分递归寻找 α \alpha α 的过程就相当于对一个已经展开到极值的泰勒展开式依次进行收缩,并且找到其对应的系数。最终哪一级的展开泛化能力更好,还是要靠验证集数据来进行验证。展开的太多对训练集符合度好而对验证集不好,展开的太小了偏差又变大了,对训练集和验证集数据符合都不好。所以算法中直到最后才用到了验证集数据,最开始的剪枝判断递归过程都是基于训练数据进行的。

这里有一个疑惑就是,按照这样的逻辑来获得的 α list \alpha_\textrm{list} αlist 真的满足递增顺序吗?

借鉴周志华老师《机器学习》中的图 4.5 来对 CART 剪枝算法进行梳理如下。

Decision_tree_Pruning

该图展示的是表 4.2 中的西瓜数据集对应的决策树。

编号色泽根蒂敲声纹理脐部触感好瓜
1青绿蜷缩浊响清晰凹陷硬滑
2乌黑蜷缩沉闷清晰凹陷硬滑
3乌黑蜷缩浊响清晰凹陷硬滑
6青绿稍蜷浊响清晰稍凹软粘
7乌黑稍蜷浊响稍糊稍凹软粘
10青绿硬挺清脆清晰平坦软粘
14浅白稍蜷沉闷稍糊凹陷硬滑
15乌黑稍蜷浊响清晰稍凹软粘
16浅白蜷缩浊响模糊平坦硬滑
17青绿蜷缩沉闷稍糊稍凹硬滑
4青绿蜷缩沉闷清晰凹陷硬滑
5浅白蜷缩浊响清晰凹陷硬滑
8乌黑稍蜷浊响清晰稍凹硬滑
9乌黑稍蜷沉闷稍糊稍凹硬滑
11浅白硬挺清脆模糊平坦硬滑
12浅白蜷缩浊响模糊平坦软粘
13青绿稍蜷浊响稍糊凹陷硬滑

下面按照给出的算法流程来对该决策树进行剪枝操作。我们采用 3.1 小节中关于 C ( T ) C(T) C(T) C α ( T ) C_\alpha(T) Cα(T) 的定义。

1. 第一层递归: T 0 = { 1 , 2 , 3 , 4 , 5 } T_0=\lbrace 1,2,3,4,5\rbrace T0={1,2,3,4,5},其在训练集数据上的执行情况见下图:
Decision_tree_Execute
对其每一个内部结点计算其剪枝后的整体树的损失函数减少程度 g ( t ) g(t) g(t)
以结点 5 5 5 为例,一例好一例坏,
C ( 5 ) = − ∑ t = 1 1 ∑ k ∈ ( 好、坏 ) N t k log ⁡ 2 N t k N t = − 1 × log ⁡ 2 ( 1 / 2 ) − 1 × log ⁡ 2 ( 1 / 2 ) ) = 2 ; C ( T 5 ) = − ∑ t = 1 3 ∑ k ∈ ( 好、坏 ) N t k log ⁡ 2 N t k N t = − ( 1 × log ⁡ 2 ( 1 / 1 ) + 0 × log ⁡ 2 ( 0 / 1 ) ) − ( 0 × log ⁡ 2 ( 0 / 1 ) + 1 × log ⁡ 2 ( 1 / 1 ) ) − 0 = 0 ; ∣ T 5 ∣ = 3 ; g ( 5 ) = C ( 5 ) − C ( T 5 ) ∣ T 5 ∣ − 1 = 2 − 0 3 − 1 = 1. \begin{aligned} C(5)&=-\sum_{t=1}^1\sum_{k \in (好、坏)}N_{tk}\log_2\frac{N_{tk}}{N_t} \\ &=-1\times\log_2(1/2)-1\times\log_2(1/2)) \\ &=2; \\ C(T_5)&=-\sum_{t=1}^3\sum_{k \in (好、坏)}N_{tk}\log_2\frac{N_{tk}}{N_t} \\ &=-\left(1\times\log_2(1/1)+0\times\log_2(0/1)\right)-\left(0\times\log_2(0/1)+1\times\log_2(1/1)\right)-0 \\ &=0; \\ |T_5|&=3; \\ g(5)&=\frac{C(5)-C(T_5)}{|T_5|-1}=\frac{2-0}{3-1}=1. \end{aligned} C(5)C(T5)T5g(5)=t=11k(好、坏)Ntklog2NtNtk=1×log2(1/2)1×log2(1/2))=2;=t=13k(好、坏)Ntklog2NtNtk=(1×log2(1/1)+0×log2(0/1))(0×log2(0/1)+1×log2(1/1))0=0;=3;=T51C(5)C(T5)=3120=1.
同理,可以计算出:
g ( 4 ) = C ( 4 ) − C ( T 4 ) ∣ T 4 ∣ − 1 = 2.7549 − 0 5 − 1 = 0.6887 g ( 3 ) = C ( 3 ) − C ( T 3 ) ∣ T 3 ∣ − 1 = 4 − 0 7 − 1 = 0.5714 g ( 2 ) = C ( 2 ) − C ( T 2 ) ∣ T 2 ∣ − 1 = 3.2451 − 0 3 − 1 = 1.6226 g ( 1 ) = C ( 1 ) − C ( T 1 ) ∣ T 1 ∣ − 1 = 10 − 0 11 − 1 = 1 \begin{aligned} g(4)&=\frac{C(4)-C(T_4)}{|T_4|-1}=\frac{2.7549-0}{5-1}=0.6887 \\ g(3)&=\frac{C(3)-C(T_3)}{|T_3|-1}=\frac{4-0}{7-1}=0.5714 \\ g(2)&=\frac{C(2)-C(T_2)}{|T_2|-1}=\frac{3.2451-0}{3-1}=1.6226 \\ g(1)&=\frac{C(1)-C(T_1)}{|T_1|-1}=\frac{10-0}{11-1}=1 \end{aligned} g(4)g(3)g(2)g(1)=T41C(4)C(T4)=512.75490=0.6887=T31C(3)C(T3)=7140=0.5714=T21C(2)C(T2)=313.24510=1.6226=T11C(1)C(T1)=111100=1
比较之后发现,最小的是 g ( 3 ) g(3) g(3),对应的结点是 t ( 3 ) t(3) t(3),对其进行剪枝后得到新的树 T 1 T_1 T1 在训练集数据上的执行情况见下图:
Decision_tree_Learning_Pruned_1

2. 第二层递归: 以树 T 1 = { 1 , 2 } T_1=\lbrace 1,2\rbrace T1={1,2} 为新的输入,重复第一层递归中的计算步骤:
g ( 2 ) = C ( 2 ) − C ( T 2 ) ∣ T 2 ∣ − 1 = 3.2451 − 0 3 − 1 = 1.6226 g ( 1 ) = C ( 1 ) − C ( T 1 ) ∣ T 1 ∣ − 1 = 10 − 0 5 − 1 = 2.5 \begin{aligned} g(2)&=\frac{C(2)-C(T_2)}{|T_2|-1}=\frac{3.2451-0}{3-1}=1.6226 \\ g(1)&=\frac{C(1)-C(T_1)}{|T_1|-1}=\frac{10-0}{5-1}=2.5 \end{aligned} g(2)g(1)=T21C(2)C(T2)=313.24510=1.6226=T11C(1)C(T1)=51100=2.5
比较之后发现,最小的是 g ( 2 ) g(2) g(2),对应的结点是 t ( 2 ) t(2) t(2),对其进行剪枝后得到新的树 T 2 T_2 T2 在训练集数据上的执行情见下图:
Decision_tree_Learning_Pruned_2

3. 第三层递归: 以树 T 2 = { 1 } T_2=\lbrace 1\rbrace T2={1} 为新的输入,重复第一层递归中的计算步骤:
g ( 1 ) = C ( 1 ) − C ( T 1 ) ∣ T 1 ∣ − 1 = 10 − 0 3 − 1 = 5 g(1)=\frac{C(1)-C(T_1)}{|T_1|-1}=\frac{10-0}{3-1}=5 g(1)=T11C(1)C(T1)=31100=5
现在只有一个 g ( 1 ) g(1) g(1),所以其也是最小的一个。对应的结点是 t ( 1 ) t(1) t(1),对其进行剪枝后得到新的树 T 3 T_3 T3 在训练集数据上的执行情见下图:
Decision_tree_Learning_Pruned_3
到此为止, T 3 T_3 T3 已经是一个根结点单独构成的树,触发了递归退出条件。最后得到的 T list = ( T 0 , T 1 , T 2 , T 3 ) T_\textrm{list}=(T_0,T_1,T_2,T_3) Tlist=(T0,T1,T2,T3) α list = ( 0 , 0.5714 , 1.6226 , 5 ) \alpha_\textrm{list}=(0,0.5714,1.6226,5) αlist=(0,0.5714,1.6226,5),从结果看 α list \alpha_\textrm{list} αlist 中的值确实是从小到大排列,但为什么是这样的内层逻辑还需要深入思考一下。

4. 验证集数据交叉验证:
T list T_\textrm{list} Tlist 中的每一个决策树来预测验证集上的数据得到准确率(正确分类样本数量除以整个样本数量)列表
P list = ( ( 4 , 9 , 11 , 12 ) 正确 , ( 4 , 8 , 9 , 11 , 12 ) 正确 , ( 4 , 5 , 8 , 9 , 11 , 12 ) 正确 , ( 4 , 5 , 8 ) 正确 ) = ( 4 7 , 5 7 , 6 7 , 3 7 ) \begin{aligned} P_\textrm{list}&=\left((4,9,11,12)正确,(4,8,9,11,12)正确,(4,5,8,9,11,12)正确,(4,5,8)正确\right) \\ &=(\frac{4}{7},\frac{5}{7},\frac{6}{7},\frac{3}{7}) \end{aligned} Plist=((4,9,11,12)正确,(4,8,9,11,12)正确,(4,5,8,9,11,12)正确,(4,5,8)正确)=(74,75,76,73)
所以最终选择 T 2 T_2 T2 作为最优子树:
Decision_tree_Best

3.3.3 两种剪枝策略对比

  • 后剪枝决策树通常比预剪枝决策树保留了更多的分支;
  • 后剪枝决策树的欠拟合风险很小,泛化性能往往优于预剪枝决策树;
  • 后剪枝决策树训练时间开销比未剪枝决策树和预剪枝决策树都要大的多。

更多的剪枝方法参考文后链接 7。

参考

1: 《机器学习》Page 73
2:《统计学习方法》 Page 72
2: 机器学习相关知识整理系列之一:决策树算法原理及剪枝(ID3,C4.5,CART)
3: 决策树剪枝算法原理
4: 决策树之剪枝原理与CART算法
5: 决策树剪枝(cart剪枝)的原理介绍
6: CART树剪枝的操作的理解
7: 决策树剪枝算法

  • 82
    点赞
  • 438
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
### 回答1: 剪枝决策树算法中一个重要的步骤,它的目的是防止过拟合。CART(Classification and Regression Trees)分类决策树剪枝主要有两种方法:预剪枝和后剪枝。 预剪枝是在构建决策树的过程中,提前停止某些分支的生长,以防止过拟合。常见的预剪枝策略有限制树的最大深度、限制叶子节点的最小样例数、限制信息增益的最小值等。预剪枝策略可以有效地降低决策树的复杂度,但它也会使得决策树的精度降低。 后剪枝是在构建完整个决策树之后,再对决策树进行简化。常见的后剪枝方法有:REP(Reduced Error Pruning)、PEP(Pessimistic Error Pruning)等。后剪枝策略可以通过删除一些叶子节点来降低决策树的复杂度,同时还能保证决策树的精度。 下面是一个使用后剪枝的 CART分类决策树剪枝的代码及详解: ```python def prune(tree, testData): ''' 后剪枝函数 :param tree: 待剪枝的树 :param testData: 剪枝所需的测试数据集 :return: 剪枝后的树 ''' # 如果测试数据集为空,则直接返回该树的叶子节点的均值 if len(testData) == 0: return getMean(tree) # 如果当前节点是一个子树,则对该子树进行剪枝 if (isinstance(tree, dict)): # 对训练数据进行划分 leftSet, rightSet = binSplitDataSet(testData, tree['spInd'], tree['spVal']) # 对左子树进行剪枝 if (isinstance(tree['left'], dict)): tree['left'] = prune(tree['left'], leftSet) # 对右子树进行剪枝 if (isinstance(tree['right'], dict)): tree['right'] = prune(tree['right'], rightSet) # 如果当前节点的两个子节点都是叶子节点,则考虑合并这两个叶子节点 if not isinstance(tree['left'], dict) and not isinstance(tree['right'], dict): # 计算合并前的误差 errorNoMerge = sum(np.power(leftSet[:, -1] - tree['left'], 2)) + \ sum(np.power(rightSet[:, -1] - tree['right'], 2)) # 计算合并后的误差 treeMean = (tree['left'] + tree['right']) / 2.0 errorMerge = sum(np.power(testData[:, -1] - treeMean, 2)) # 如果合并后的误差小于合并前的误差,则进行合并 if errorMerge < errorNoMerge: return treeMean return tree ``` 该函数的输入参数为待剪枝的树以及用于剪枝的测试数据集。函数的主要流程如下: 1. 如果测试数据集为空,则直接返回该树的叶子节点的均值; 2. 如果当前节点是一个子树,则对该子树进行剪枝,分别对左右子树进行剪枝; 3. 如果当前节点的两个子节点都是叶子节点,则考虑合并这两个叶子节点; 4. 如果合并后的误差小于合并前的误差,则进行合并; 5. 最后返回剪枝后的树。 剪枝过程中最重要的是如何判断是否进行剪枝,并且如何进行剪枝。在上面的代码中,我们通过计算合并前和合并后的误差,来判断是否进行剪枝。如果合并后的误差小于合并前的误差,则进行剪枝。 需要注意的是,在剪枝过程中,我们需要对整个决策树进行遍历,因此该过程非常耗时。为了提高剪枝的效率,我们可以先对整个决策树进行建立,然后再对其进行剪枝。这样可以大大减少计算量,同时也可以避免在建立决策树的过程中出现剪枝误差。 ### 回答2: 决策树剪枝是为了解决决策树过拟合的问题,减小模型复杂度,提高泛化能力。CART算法(Classification and Regression Tree)是一种常用的决策树算法。 CART算法在进行剪枝时,采用了后剪枝的方法。具体代码如下: 1. 数据准备:首先需要准备训练数据和测试数据。将数据集按照一定的比例划分成训练集和测试集,通常训练集占总数据集的70-80%。 2. 构建决策树:利用训练数据构建初始的决策树。对于CART算法来说,树的每个非叶子节点会有两个分支,根据Gini指数或信息增益来选择最优的划分属性。 3. 后剪枝:对构建好的决策树进行后剪枝操作。后剪枝的步骤如下: (1)利用测试集评估从根节点到每个叶子节点的分类准确率,保存在错误率数组中。 (2)递归地从树的底层开始,自底向上地计算每个节点的代价函数。代价函数定义为:路径上节点的错误率加上一个参数乘以路径的复杂度。 (3)计算每个非叶子节点的剪枝前与剪枝后的代价函数之差,选取差值最小的节点作为剪枝节点。 (4)使用剪枝节点的父节点的多数投票法更新剪枝节点,将其变为叶子节点。 (5)重复步骤2-4,直到无法再剪枝为止。 4. 模型评估:使用剪枝后的决策树对测试集进行预测,并计算预测准确率。根据准确率来评估模型的性能和泛化能力。 决策树剪枝的代码实现比较复杂,需要涉及到模型的构建、剪枝、以及模型的评估等环节。以上是对决策树剪枝代码及详解的简要概述,具体实现过程还需要根据具体的编程语言和库进行相应的代码编写和调试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值