看这一章时有一种直观的感受:决策树是一种基于计算机算法的方法,而不太像是基于统计学习理论的方法。用决策树做分类的过程,就是很符合人类思考方式地一个一个属性判断过去,只是判断用哪一种属性更好时借用了一个概率的指标,而整个算法流程(至少是基础的决策树)没有建立在某种概率或者统计模型之上。
用决策树作划分时,每一个结点都是对一个属性做二分,在坐标轴中表示出来,就是一条平行于坐标轴的直线,可以用如下一张图(两种属性、二分类)来说明用决策树做分类的过程:
先看“颜色”这一属性,画一条线:
对于颜色为乌黑的西瓜,再看“根蒂”这一属性,画一条线
这样就完成了划分,实际上,上图对应了这样一棵决策树:
注意,划分的结果并不纯,这是因为并非所有分类任务都可以用平行于坐标轴的直线划分出来。《机器学习》书中4.5节介绍了多变量决策树,也就是在每一个非叶结点采用属性的线性组合满足的条件作为划分的依据,这样,划分线就变成了“线性分类器”。
建立决策树是一个递归的过程。简单来说,就是要用栈作为数据结构,在建树的函数中调用它自己,将关键信息存在栈中,到了递归出口,把栈中存的所有东西全部pop出来。决策树的思想说起来简单,可是要想实现它(建树、用树做预测)可一点都不简单,c语言中二叉树相关算法是基于链表的,python是一种解释语言,貌似无法实现链表。在python中,可以用字典来建立一棵决策树作为展示,但(好像)没法用这棵树做预测……sklearn
中的决策树是用cpython写的,比较复杂。
即使没法直接看决策树的代码,也可以大致勾勒一下,最重要的部分其一是递归出口,其二是划分过程。对于递归出口的设定,书中做了这样的描述,对于对应属性为 A A A 的结点:
对于划分过程,在每一个非叶结点需要选择一个属性,将这一结点对应的样本按照在这一属性上的取值划分成不同的分支,每个分支接受对应的样本后,成为一个新的结点,要么选择属性再做划分,要么进入递归出口。
1947年,Shannon提出了“信息熵”(information entropy)这一概念,用来表示排除了冗余后的平均信息量或者样本集合的纯度。假定集合 D D D 中第 k k k 类样本占的比例是 p k p_k pk,共 N N N 类样本,那么集合 D D D 的信息熵定义为
E n t ( D ) = − ∑ k = 1 N p k log 2 p k {\rm Ent}(D) = -\sum_{k=1}^{N} p_k \log_2 p_k Ent(D)=−k=1∑Npklog2pk
为什么 E n t ( D ) {\rm Ent}(D) Ent(D) 能反映样本集合的纯度呢?可以用极端情况来理解:
可以证明2, E n t ( D ) {\rm Ent}(D) Ent(D) 是关于类别数 N N N 的单调上升函数,且取值范围就是 ( 0 , log 2 N ) (0,\log_2 N) (0,log2N),因此,“信息熵”能够反映样本的“纯度”,也就是随机试验不确定性的大小,这个值越小,做随机试验(随便选一个样本,看它属于哪一类)的不确定性就越小,有利于分类的完成。
当我们用决策树做分类任务时,总希望经过一次划分后,每一个分支的样本变得更纯、不确定性更低,如果只有一类就最好了。因此,可以用这一属性划分得到的每一个子结点信息熵的加权平均值减去父节点的信息熵作为划分时选择属性的依据,这个指标称作信息增益(information gain),信息增益越大,说明划分后的纯度越高。假定离散属性 A A A 有 V V V 个不同的取值,划分后得到的子结点分别对应样本集合 D 1 , . . . , D V D^1,...,D^V D1,...,DV,那么用信息增益做划分的表达是:
A ∗ = a r g m a x A E n t ( D ) − ∑ i = 1 V ∣ D i ∣ ∣ D ∣ E n t ( D i ) ≡ G a i n ( D , A ) A^* = {\rm \underset{A}{arg \; max}} \; {\rm Ent}(D) - \sum_{i=1}^{V} \frac{|D^i|}{|D|} {\rm Ent}(D^i) \equiv {\rm Gain}(D,A) A∗=AargmaxEnt(D)−i=1∑V∣D∣∣Di∣Ent(Di)≡Gain(D,A)
信息增益准则对于可取值数目较多的属性有所偏好。还是考虑极端情况,当集合中每一个样本都自成一类时,划分后必然得到许多分支,且每个分支仅包含一个样本,此时每一个结点的纯度都达到了最大,信息增益显然最大。但是这种划分未必能达到理想的分类效果(泛化能力差)。为了缓解这一问题,可以用增益率作为划分准则。首先定义属性 A A A 的固有值 I V ( A ) {\rm IV}(A) IV(A):
I V ( A ) = ∑ i = 1 V ∣ D i ∣ D log 2 ∣ D i ∣ D {\rm IV}(A) = \sum_{i=1}^{V} \frac{|D^i|}{D} \log_2 \frac{|D^i|}{D} IV(A)=i=1∑VD∣Di∣log2D∣Di∣
这个值随着属性取值数目增大而增大,再用信息增益除以 I V ( A ) {\rm IV}(A) IV(A) 就得到了增益率。这个指标平抑了属性取值数目对于划分准则的影响。
增益率准则对可取值数目较少的属性有所偏好。为此,在原始的算法中,会先找出信息增益高于平均水平的属性,再找出增益率最高的属性。
还可用基尼指数来作为样本划分的依据。基尼指数反映了从数据集 D D D 中随机抽取两个样本,其类别不一致的概率:
G i n i ( D ) = ∑ k ≠ j p k p j = 1 − ∑ i = 1 N p i 2 {\rm Gini}(D) = \sum_{k \neq j} p_kp_j = 1-\sum_{i=1}^{N} p_i^2 Gini(D)=k=j∑pkpj=1−i=1∑Npi2
因此,基尼指数越小,类别不一致的概率越小,样本纯度越高。在划分时,选择最小化划分后各结点基尼指数加权值的那个属性:
A ∗ = a r g m i n A ∑ i = 1 N ∣ D i ∣ D G i n i ( D i ) A^* = {\rm \underset{A}{arg \; min}} \; \sum_{i=1}^{N} \frac{|D^i|}{D} {\rm Gini}(D^i) A∗=Aargmini=1∑ND∣Di∣Gini(Di)
注意,这里的剪枝和数据结构中的剪枝含义不同。在数据结构中,使用递归回溯法时往往采用剪枝处理,在每一层递归时就判断一下是否满足(或已经不可能满足)递归出口条件,如果已经不可能满足,就不用往下继续了,从而减少计算时间与空间占用,减少爆栈的可能性。而决策树的剪枝是一种对抗过拟合的手段。为了防止分支划分过多而产生过拟合,主动去掉一些分支。剪枝办法有两种:
就是在每次选出最优属性,将要做划分时,将已经建成的树和划分后的树用测试集测试一下,如果这次划分没能提升测试精度,就不做这次划分了。
可以看出,预剪枝的策略是贪心的,即每一步都选取当前的最优解,但未必是整个算法的全局最优解。目前的划分可能没法使得测试精度提升,但是这次划分之后的划分可能使得测试精度提升,如果做了预剪枝,则失去了之后划分的机会,有可能陷入局部最优解,产生欠拟合。
就是在整棵树建完后,按照自下而上的顺序分别考察每个结点,看看将这个结点之后的分支去掉(并将这一节点的类别标记为其中占比最多的类)会不会提高测试精度,如果会,就剪去这一节点之后的分支,并将这一节点的类别标记为其中占比最多的类;如果一样,根据奥卡姆剃刀原则,也做剪枝处理;如果反而降低了,就不剪枝。
相比于预剪枝,后剪枝不会陷入局部最优解中,泛化性能往往更好,但是计算开销最大。
在《机器学习》第三章,用到了离散属性数值化的办法。这里由于决策树的每次划分是基于有限、离散的属性的,如果遇到连续属性,就没法用有限的取值来计算信息增益等指标了。因此,需要将连续值离散化。书中介绍了二分法的办法,简单来说,就是:
从而选出最好的划分点作为这一属性的划分,再和其它属性作比较,选出最优属性。
若当前划分结点时联连续属性,则该属性还可以作为子结点的划分属性。如果是离散属性,则划分后每个结点中所有的样本在这个属性上取值是相同的,就没法作为后代结点的划分属性了。
第一种办法:放弃不完整样本,但是会造成信息浪费。
第二种办法,就是书上介绍的处理办法,仅用无缺失样本计算增益。用 D ~ \tilde{D} D~ 表示属性 A A A 中没有缺失值的样本子集,假定其有 V V V 个取值, N N N 种类别,用 D ~ v \tilde{D}^v D~v 表示在属性 A A A 上取值为 a v a^v av 的样本,用 D ~ k \tilde{D}_k D~k 表示类别为 k k k 的样本,为每个样本赋予权重 w i w_{i} wi,再定义一系列比值:
ρ = ∑ i ∈ D ~ w i ∑ i ∈ D w i p ~ k = ∑ i ∈ D k ~ w i ∑ i ∈ D ~ w i r ~ v = ∑ i ∈ D ~ v w i ∑ i ∈ D ~ w i \begin{align} \rho &= \frac{\sum_{i \in \tilde{D}} w_i}{\sum_{i \in D} w_{i}}\\[2mm] \tilde{p}_{k} &= \frac{\sum_{i \in \tilde{D_k}} w_i}{\sum_{i \in \tilde{D}} w_{i}}\\[2mm] \tilde{r}_{v} &= \frac{\sum_{i \in \tilde{D}^v} w_i}{\sum_{i \in \tilde{D}} w_{i}}\\[2mm] \end{align} ρp~kr~v=∑i∈Dwi∑i∈D~wi=∑i∈D~wi∑i∈Dk~wi=∑i∈D~wi∑i∈D~vwi
其中,
由此,可将信息增益推广为(用无缺失样本计算的增益 × \times × 无缺失样本占比)
ρ × [ E n t ( D ~ ) − ∑ v = 1 V r ~ v E n t ( D ~ v ) ] \rho \times \Big[{\rm Ent}(\tilde{D}) - \sum_{v=1}^{V} \tilde{r}^v {\rm Ent}(\tilde{D}^v) \Big] ρ×[Ent(D~)−v=1∑Vr~vEnt(D~v)]
若某个样本在属性 A A A 上取值已知,则做正常划分即可。若取值未知,则将这一样本同时划入所有子结点,并将其在第 v v v 个子结点中的权值调整为 r ~ v ⋅ w i \tilde{r}^v \cdot w_i r~v⋅wi。
为什么这样的处理是合理的呢?
P ( 占比贡献 ) = P ( 占比贡献 ∣ 在这一结点的概率 ) ⋅ P ( 在这一结点的概率 ) = r ~ v ⋅ w i \begin{aligned} P(占比贡献) &= P(占比贡献|在这一结点的概率) \cdot P(在这一结点的概率)\\ &=\tilde{r}^v \cdot w_i \end{aligned} P(占比贡献)=P(占比贡献∣在这一结点的概率)⋅P(在这一结点的概率)=r~v⋅wi
全文参考《机器学习》(周志华)第四章 ↩︎
参见《概率论基础》(李贤平)第四章第三节 ↩︎