创建时间:2024-03-02
最后编辑时间:2024-03-02
作者:Geeker_LStar
你好呀~这里是 Geeker_LStar 的人工智能学习专栏,很高兴遇见你~
我是 Geeker_LStar,一名初三学生,热爱计算机和数学,我们一起加油~!
⭐(●’◡’●) ⭐ 那就让我们开始吧!
emmm,本来打算接着之前的内容把线性回归算法的实现写一下的,但是开学了在学校大部分时间都只能看理论 errrr,so 花了两天啃完了决策树算法,这篇启动!
(决策树不难,至少数学原理不难,不像 SVM(((bushi)
文章目录
- 简介 & 工作流程
- 数学原理
- ID3 决策树
- C4.5 决策树
- CART 决策树
- 剪枝操作
- 预剪枝
- 后剪枝
- 连续值与缺失值处理
- 连续值
- 缺失值
简介 & 工作流程
决策树,一个并不难的经典算法,属于监督学习。它仿照人类在判断一个东西的类别时的(一种)思维路径——先判断这个东西是否有 A 特征,若有,再判断是否有 B 特征,以此类推,直到这些特征足够判断这个东西的类别。比如西瓜书上那个经典的例子,我们要把一堆西瓜分成好瓜和坏瓜,此时我们的思维是有路径的,我们可能会先看这个西瓜的色泽如何,如果色泽是青绿色的,再敲一敲它听一下声音;如果色泽不是青绿色的,又进行其它操作…以此类推,直到我们能通过这些特征判断出这个瓜是好的还是坏的。
well,先简单复习一下树这种数据结构——它有一个根节点、若干内部节点和若干叶节点,每一个叶节点都对应一个分类结果(不同叶节点对应的分类结果可以重复,相当于不同的判断路径)。决策树要干的事就是寻找一些合理的从根节点到叶节点的路径,完成分类(当然,也可以回归,不过决策树更多还是用在分类上)。
那么,决策树什么时候会到达叶节点呢? 通常有以下三种情况:
(1)当前节点包含的样本全部属于同一类别,即这条分类路径已经很成功了,不用继续分类了。此时会把该节点设为叶节点,叶节点的类别就是这些样本的类别。
(2)所有的属性(如上面 “西瓜问题” 中提到的色泽、敲声)都已经用于划分了,没有其它属性可以再继续划分这个节点所包含的样本了;或者,此时把该节点设为叶节点,类别为这些样本中出现次数最多的类别(即后验概率最大的类别)。
(3)用某个属性划分后,没有符合要求的样本,即当前节点包含的样本集合为空。此时把该节点设为叶节点,类别为它的父节点包含的样本中出现次数最多的类别(即先验概率最大的类别)。
知道了以上三种情况,决策树的伪代码就好写了,其实是个递归:
# 训练集:
D = ((x_1, y_1), (x_2, y_2), ..., (x_n, y_n)),其中 $x_i = (x_{i1}, x_{i2}, ... , x_{id})
# 属性集
A = {a_1, a_2, ..., a_n}函数 TreeGenerate (D, A) 如下:
# 生成父节点
生成结点 node;
# 情况 1
if D 中样本全属于同一类别 C then将 node 标记为 C 类叶节点 ; return
end if
# 情况 2
if A = ∅ or D 中样本在 A 上取值相同 then将 node 标记为叶节点,其类别标记为 D 中样本数最多的类 ; return
end if
# 否则(递归)
从 A 中选择最优划分属性 a* # 选择的具体方法在下文
for a* 中的每一个值 do为 node 生成一个分支;把相应的样本放入合适的分支中,用 D_v 表示在 a* 中取值为 a*_v 的样本子集 ;# 情况 3if D_v 为空 then将分支节点标记为叶节点,其类别标记为 D 中样本最多的类 ; returnelse# 递归产生新的子树以 TreeGenerate(D_v, A \ a*) 为分支节点end if
end for
嗯~~!这样应该很清晰啦!至于那个 “最优划分属性” 到底怎么选,往下看!
ok,讲完基本流程,我们把本篇要用的数据集例子放在这~(yes 西瓜书,嘿嘿):
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 好瓜 |
---|---|---|---|---|---|---|---|
1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 是 |
3 | 乌黑 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 是 |
5 | 浅白 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
6 | 青绿 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 是 |
7 | 乌黑 | 稍蜷 | 浊响 | 稍糊 | 稍凹 | 软粘 | 是 |
8 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 硬滑 | 是 |
9 | 乌黑 | 稍蜷 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
10 | 青绿 | 硬挺 | 清脆 | 清晰 | 平坦 | 软粘 | 否 |
11 | 浅白 | 硬挺 | 清脆 | 模糊 | 平坦 | 硬滑 | 否 |
12 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 软粘 | 否 |
13 | 青绿 | 稍蜷 | 浊响 | 稍糊 | 凹陷 | 硬滑 | 否 |
14 | 浅白 | 稍蜷 | 沉闷 | 稍糊 | 凹陷 | 硬滑 | 否 |
15 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 否 |
16 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 硬滑 | 否 |
17 | 青绿 | 蜷缩 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
数学原理
决策树算法有三种主要的实现方式——ID3 决策树(基于信息增益)、C4.5 决策树(基于增益率)、CART 决策树(基于基尼指数),每种实现方式的数学原理都不一样,来看看~
ID3 决策树
ID3 决策树基于信息增益。它是最早提出的决策树算法,最简单,不支持剪枝操作,不提供连续值和缺失值处理。不过,它的基本思想被后来的两个决策树算法沿用,so 先从它开始吧!
要想理解信息增益,我们得先来看看信息熵(information entropy)是什么。
“熵” 这个概念你肯定不陌生,在物理/化学中,它表示一个系统的混乱程度,熵越大,说明整个体系越无序;熵越小,说明整个体系越有序。
在决策树中也一样,假设我们有一堆样本 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)),其中 x i = ( x i 1 , x i 2 , . . . , x i d ) x_i = (x_{i1}, x_{i2}, ... , x_{id}) xi=(xi1,xi2,...,xid), y i y_i yi 表示该样本的分类。“熵” 表示这个数据集的混乱程度,直观理解就是这个数据集中的数据属于不同分类的程度。这 n 个数据属于 n 个分类肯定比这 n 个数据属于 2 个分类的混乱程度高。
ok,说的简单,那我们怎么精确地度量一个数据集的混乱程度呢?信息熵的数学公式如下:
E n t ( D ) = − ∑ k = 1 ∣ Y ∣ p k log 2 p k Ent(D)=-\sum_{k=1}^{|Y|}p_k \log_2p_k Ent(D)=−k=1∑∣Y∣pklog2pk其中, ∣ Y ∣ |Y| ∣Y∣ 表示标记空间(或输出空间),简单理解就是不同的分类结果,引用西瓜书上的例子,“对一堆西瓜分类” 的标记空间就是 “好瓜、坏瓜”。 p k p_k pk 表示样本被分到第 k 类的概率,也就是第 k 类样本在数据集 D 中的占比。大多数情况下, p k < 1 p_k<1 pk<1,所以 log 2 p k \log_2 p_k log2pk 是个负数,so 我们需要在式子最前面加一个负号把结果变成正数。
不难发现, E n t ( D ) Ent(D) Ent(D) 的值越小,说明这组样本的 “混乱程度” 越低,即更多的样本都是同一类,数据集的 “纯度” 更高。
准确来说,当 p 1 = p 2 = . . . = p ∣ Y ∣ = 1 ∣ Y ∣ p_1 = p_2 = ... = p_{|Y|} = \frac{1}{|Y|} p1=p2=...=p∣Y∣=∣Y∣1 的时候,即每个类别的样本在数据集中占比相同时, E n t ( D ) Ent(D) Ent(D) 有最大值 log 2 ∣ Y ∣ \log_2|Y| log2∣Y∣,对应数据集纯度最低。而当 p k ( k = 1 , 2 , . . . , ∣ Y ∣ ) p_k (k = 1, 2, ..., |Y|) pk(k=1,2,...,∣Y∣) 中有一个为 1,其它为 0,即数据集中全是某一类样本的时候, E n t ( D ) Ent(D) Ent(D) 有最小值 0,对应数据集的纯度最高。
至于以上两种情况的证明,不在此展开,简单来说就是凸优化问题,让它的拉格朗日函数的一阶偏导等于 0 就 ok。
举个例子,用最开始的数据集算一下信息熵:共 17 个数据,正例(好瓜)8 个,反例 9 个,则信息熵为:
E n t ( D ) = − ∑ k = 1 ∣ Y ∣ p k log 2 p k = − ( 8 17 log 2 8 17 + 9 17 log 2 9 17 ) = 0.998 Ent(D)=-\sum_{k=1}^{|Y|}p_k \log_2p_k = -(\frac{8}{17} \log_2\frac{8}{17}+\frac{9}{17} \log_2\frac{9}{17})=0.998 Ent(D)=−k=1∑∣Y∣pklog2pk=−(178log2178+179log2179)=0.998
好~现在我们知道信息熵是什么了,根据前面说的决策树的工作流程,我们现在得找一个最优划分属性。即,用这个属性划分原数据集之后,分支数据集的纯度要尽可能高,也就是分支数据集的信息熵要尽可能小。
这事好办,原始数据集的信息熵是一个定值,我们想让划分后(每一个)分支数据集的信息熵(都)尽可能小,不妨转换个思路,让 “原始信息熵减去分支信息熵的和” 的结果最大。给这个结果一个名字——信息增益。简言之,信息增益越大,这次划分对数据集的 “纯度提升” 就越大。
换言之,最优划分属性,就是信息增益最大的属性。
先把信息增益的数学表达式放在这里,后面会用一个例子来过一遍完整的计算。
G a i n ( D , a ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) Gain(D,a) =Ent(D)-\sum^{V}_{v=1}\frac{|D^v|}{|D|}Ent(D^v) Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)其实很好理解,a 是我们选择的划分属性,V 是这个划分属性一共有几种可能的情况,减号前面的式子就是原始数据集的信息熵,后面就是把划分后的每个分支数据集的信息熵加起来,两个相减,就是信息增益。
这里说一下为什么有 ∣ D v ∣ ∣ D ∣ \frac{|D^v|}{|D|} ∣D∣∣Dv∣ 这一项。可以把它简单理解为权重,毕竟样本数越多的分支数据集对信息增益的影响(理应)越大,如果不加这一项,那不管分支数据集有 1 个数据还是有 100 个数据,对信息增益的影响程度都一样,这可就不合适了。
前面说过,所谓最优划分属性就是让信息增益最大的属性,用最开始的那个西瓜数据集,我们可以选择的特征有 { 色泽、根蒂、敲声、纹理、脐部、触感 } \{色泽、根蒂、敲声、纹理、脐部、触感\} {色泽、根蒂、敲声、纹理、脐部、触感},现在来算一下这些属性的信息增益,以第一项 “色泽” 为例。
“色泽” 属性有三个可能的取值,划分成三个分支数据集就是: D 1 ( 色泽 = 青绿 ) , D 2 ( 色泽 = 乌黑 ) , D 3 ( 色泽 = 浅白 ) D^1(色泽=青绿), \ D^2(色泽=乌黑), \ D^3(色泽=浅白) D1(色泽=青绿), D2(色泽=乌黑), D3(色泽=浅白).
其中 D 1 D^1 D1 包含 6 个样例,属于正例(好瓜)的有 3 个,属于反例(坏瓜)的也有 3 个,信息熵为 E n t ( D ) = − ∑ k = 1 ∣ Y ∣ p k log 2 p k = − ( 3 6 log 2 3 6 + 3 6 log 2 3 6 ) = 1 Ent(D)=-\sum_{k=1}^{|Y|}p_k \log_2p_k = -(\frac{3}{6} \log_2\frac{3}{6}+\frac{3}{6} \log_2\frac{3}{6})=1 Ent(D)=−∑k=1∣Y∣pklog2pk=−(63log263+63log263)=1。
同理, D 2 D^2 D2 和 D 3 D^3 D3 的信息熵分别为 0.918 和 0.722。
再用信息增益的公式:
( D , 色泽 ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) = E n t ( D ) − ∑ v = 1 3 ∣ D v ∣ ∣ D ∣ E n t ( D v ) = 0.998 − ( 6 17 × 1.000 + 6 17 × 0.918 + 5 17 × 0.722 ) = 0.109 \begin{align*} & (D,色泽) =Ent(D)-\sum^{V}_{v=1}\frac{|D^v|}{|D|}Ent(D^v) \\ & =Ent(D)-\sum^{3}_{v=1}\frac{|D^v|}{|D|}Ent(D^v) \\ & =0.998-(\frac{6}{17}×1.000+\frac{6}{17}×0.918+\frac{5}{17}×0.722) \\ &= 0.109 \end{align*} (D,色泽)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)=Ent(D)−v=1∑3∣D∣∣Dv∣Ent(Dv)=0.998−(176×1.000+176×0.918+175×0.722)=0.109计算出,使用属性 ”色泽“ 进行划分,信息增益是 0.109。
类似地,对所有属性进行计算后,发现属性 “纹理” 的信息增益最大,于是我们用它作为划分属性。
ok!这样第一轮划分就完成了,按照这个套路,继续对每个分支数据集进行划分即可~
注意,对于划分好的三个分支数据集,进一步划分它们的时候,每个分支数据集之间是彼此独立的,也就是说,可以用不同的划分属性去划分它们三个。
这样一步步划分的结果如下图(from 🍉书):
emmm 没错,你应该也感觉到了,这玩意明显过拟合了,不过现在先不管这个,后面会讲怎么解决。
代码会在下一篇文章里集中给出,这一篇先把原理讲完~~
C4.5 决策树
嗯,其实有了 ID3 决策树的各种铺垫,C4.5 和 CART 理解起来会超级简单,因为基本思想是一样的,只不过参考指标的计算方式变了。
怎么说,C4.5 是对 ID3 的改进。其中两点是 C4.5 支持剪枝、支持连续值和缺失值,不过这不是这部分的重点。
先举个例子,我如果在上面的划分属性中加一个 “编号”(共 17 个可取值),那这事可就好办了,直接用编号进行划分,划分之后每个分支数据集里就 1 个数据,信息增益高达 0.998,也就是说划分之后信息熵之和直接变成 0 了。这貌似很强,可是这样的决策树没有任何泛化能力。
这是个极端的例子,但是它反映了 ID 3 算法的不足之处——可取值数量多了,每一个取值下的数据就少了,纯度自然就提高了,那划分后的信息熵之和必然就减小了,信息增益肯定会提高。
也就是说,ID3 算法在选择最优划分属性的时候,会倾向于选择 “可取值数量多” 的属性。这会导致什么?yesyes,如果一个属性的可取值数量过多(比如上面举例的 “编号”),很容易就导致过拟合。。。so 我们得避免这种情况。
一个简单的思路是,既然信息增益会在属性可取值数量多的时候偏大,那就让信息增益除以另一个值,并且让这个值也在属性可取值多的时候偏大即可。类似于归一化。
这个 “除以的值” 的数学表达式如下:
I V ( a ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ log 2 ∣ D v ∣ ∣ D ∣ IV(a) = \sum^V_{v=1}\frac{|D^v|}{|D|}\log_2\frac{|D^v|}{|D|} IV(a)=v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣其中,a 即划分属性,这个值被称作属性 a 的固有值。
你可能会感觉,诶这个式子的形式怎么如此熟悉…好像,就是信息熵那个式子的形式?
yesyes,恭喜你,感觉的很对,这个式子其实就是在计算信息熵呀!它计算的是某个划分属性自身的信息熵。代换一下,V 是这个属性的可取值数量,类似于信息熵中的标记空间; ∣ D v ∣ ∣ D ∣ \frac{|D^v|}{|D|} ∣D∣∣Dv∣ 是样本在 a 属性上的值等于该属性某个可取值的概率,类似于信息熵中的 p k p_k pk。所以其实,这个公式可以理解为属性本身的信息熵,即不确定性。
如果一个属性的可取值越多,这个属性自身的 “混乱度”(不确定性)就越高,它的固有值(信息熵)也会相应的大,所以用固有值作为分母,还挺不错!
这么一来,C4.5 算法选择最优划分属性的公式如下,我们把它称作增益率。
G a i n _ r a d i o ( D , a ) = G a i n ( D , a ) I V ( a ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) ∑ v = 1 V ∣ D v ∣ ∣ D ∣ log 2 ∣ D v ∣ ∣ D ∣ \begin{align*} & Gain\_radio(D,a) =\frac{Gain(D,a)}{IV(a)} \\ &=\frac{Ent(D)-\sum^{V}_{v=1}\frac{|D^v|}{|D|}Ent(D^v)}{\sum^V_{v=1}\frac{|D^v|}{|D|}\log_2\frac{|D^v|}{|D|}} \end{align*} Gain_radio(D,a)=IV(a)Gain(D,a)=∑v=1V∣D∣∣Dv∣log2∣D∣∣Dv∣Ent(D)−∑v=1V∣D∣∣Dv∣Ent(Dv)
emmm,不过 C4.5 其实也有 bug。因为从数学角度来看,信息增益和增益率都不是线性变化的(有 log 的参与),所以不论是信息增益还是增益率,其二阶导数必定不为 0(或一阶导必定不为常数),这说明它们的变化率(一阶导)不是均匀变化的,即因变量会在自变量值较大的时候变化较快或在自变量值较小的时候变化较快,这就决定了它们必然会偏向样本较多的属性或样本较少的属性(看二阶导是正还是负了)。
C4.5 和 ID3 相反,它会偏向可取值数量较少的属性。所以 C4.5 算法并不是直接选择增益率最大的属性作为划分属性,而是用了一个折中的方法:先在所有属性中选出信息增益较大的一半,再从这一半属性中选择增益率最高的属性。 这是一种相对平衡的方法。
一样,代码会在下一篇给出~
CART 决策树
CART,英文全称 classification and regression tree,是三种决策树中性能最强的。从名字就能看出,它既可以用于分类,也可以用于回归。sklearn 中默认的决策树算法就是 CART。
CART 决策树使用基尼指数作为选择属性的指标。这个指标比前两个好理解一些。
直观来看,我在一堆样本中随机抽取两个,抽取 10 次,如果这 10 次中有 8 次我抽到的两个样本都是同类,那我可以大胆推测,这个数据集还蛮 “整齐” 的,纯度挺高的。但是如果我这 10 次每次抽到的两个样本都不同类,那我肯定觉得,这数据集也太混乱了。
没错,基尼指数的基本逻辑就是上面那段,它衡量的是:从一个数据集中随机抽取两个样本,它们的类别标记不一致的概率。 基尼指数越小,说明数据集的纯度越高。
式子很简单,“两个样本类别不一致的概率” 等于 1 减去 “两个样本类别一致的概率”:
G i n i ( D ) = 1 − ∑ k = 1 ∣ Y ∣ p k 2 Gini(D) = 1-\sum^{|Y|}_{k=1}p_k^2 Gini(D)=1−k=1∑∣Y∣pk2这就是原始数据集 D 的基尼指数。
再来计算每个属性的基尼指数,形式和之前信息熵那里保持统一:
G i n i _ i n d e x ( D , a ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ G i n i ( D v ) Gini\_index(D,a) = \sum^{V}_{v=1}\frac{|D^v|}{|D|}Gini(D^v) Gini_index(D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv)我们选择基尼指数最小的属性作为最优划分属性。
但是嘛…这种方法说来简单,在实际中却没办法应用,因为 CART 是一棵二叉树,它不像 ID3 那样可以同时生成多个分支。
不过关于 CART 的详细做法,我打算放到下一篇(二分类算法推广到多分类的一些方法)中再说,这里先说这么多~
OK!!以上就是三种决策树算法的数学原理~!其实并不难。
放一个各项对比图:
接下来我们开始解决过拟合问题…
剪枝操作
well,任何一个机器学习算法都不可避免地会遇到过拟合问题,决策树也一样。
对于决策树,过拟合的主要原因是它的分支过多,就比如我有 n 个样本,我如果用 “编号” 进行分类,那每个分支就一个样本,这样肯定最后的信息熵最低,看似是 “最优” 的划分方式,可是这个决策树完全不具有泛化能力。
所以,为了减少过拟合,我们需要对决策树进行 “剪枝”——如果用某一个条件进行划分不能使决策树的性能提升(甚至反而下降),那么就不必用这个条件进行划分了,这个 “树枝” 就可以被剪掉了,这就是剪枝操作的基本思想。
注意,“性能是否提升” 是需要验证集进行验证的,所以我们把最开始的数据集分成两部分,一部分作为训练集,另一部分作为验证集。(emm 的确这个数据集很小,但是作为示例够用了)
训练集:
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 好瓜 |
---|---|---|---|---|---|---|---|
1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 是 |
3 | 乌黑 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
6 | 青绿 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 是 |
7 | 乌黑 | 稍蜷 | 浊响 | 稍糊 | 稍凹 | 软粘 | 是 |
10 | 青绿 | 硬挺 | 清脆 | 清晰 | 平坦 | 软粘 | 否 |
14 | 浅白 | 稍蜷 | 沉闷 | 稍糊 | 凹陷 | 硬滑 | 否 |
15 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 软粘 | 否 |
16 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 硬滑 | 否 |
17 | 青绿 | 蜷缩 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
验证集:
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 好瓜 |
---|---|---|---|---|---|---|---|
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 是 |
8 | 乌黑 | 稍蜷 | 浊响 | 清晰 | 稍凹 | 硬滑 | 是 |
9 | 乌黑 | 稍蜷 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
11 | 浅白 | 硬挺 | 清脆 | 模糊 | 平坦 | 硬滑 | 否 |
12 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 软粘 | 否 |
13 | 青绿 | 稍蜷 | 浊响 | 稍糊 | 凹陷 | 硬滑 | 否 |
先来看一下如果不进行剪枝,基于训练集最终生成的决策树是什么样的:
常用的剪枝操作有两种——预剪枝和后剪枝,其实现过程和优劣势略有不同。
我们用上面的训练集来训练决策树,选择信息增益准则。
预剪枝
顾名思义,预剪枝就是在生成一棵完整的决策树之前就把没必要的树枝剪掉,也就是一些树枝根本就不会被生成。
根据信息增益准则,(如果要划分的话)对于训练集而言最优划分属性是 “脐部”。
但是,是否有必要进行这个划分呢?请记住决策树的剪枝准则(在剪枝部分最开始也有说过):如果用该属性进行划分能够提高决策树在验证集上的准确率,就划分,否则就不划分(即 “剪枝”)。
在基本流程中我们提到,如果在当前节点处不进行划分,那么当前节点将被标记为叶子节点,类别为在当前节点的所有样本中出现最多的类别。
也就是说,如果在根节点这里不用 “脐部” 属性进行划分,那直接把根节点标记为叶子节点,类别为在训练集中出现次数最多的类别,也就是 “好瓜”。换言之,这棵决策树会把验证集中所有数据都分类为好瓜。
well,不进行划分的情况下,验证集 7 个样本全部被判定为好瓜,但实际上好瓜只有 3 个,准确率为 42.9%。
再来看看进行划分的情况。我们先只用 “脐部” 属性划分一次,然后不再进行第二次划分,把节点类别标记为被分到节点那一类的数据中出现最多的类别。
okkk,大概就是这样,划分后在验证集上的准确率达到了 71.4%,大于不划分的情况(42.9%),于是,我们选择用 “脐部” 进行划分。
第一次划分完成,然后呢?还要继续划分吗?
没错,重复上述套路就 ok 了,④ 节点已经没有必要继续划分了(类别一致),对于 ②③ 节点,通过计算发现,划分后决策树在验证集上的准确率都下降了,于是不再划分,最终的决策树如上图所示(这种只有一层的决策树也被称为 “决策树桩”)。
对比一下预剪枝决策树和未剪枝决策树:
可以看出,预剪枝决策树的很多分支都没有展开。这在一方面减少了过拟合风险,降低了决策树训练的各种开销;但是从另一方面看,这同时增加了欠拟合的风险(嗯至少我觉得预剪枝决策树明显是欠拟合的),因为有些划分可能并不能直接提高决策树的准确率(甚至会导致准确率下降),但在其基础上的划分却能让决策树的准确率提升。但是预剪枝相当于直接否定了这种可能,造成欠拟合的风险。
后剪枝
很明显,后剪枝和预剪枝相对,它会在一棵完整的决策树生成完毕后,自底向上地计算哪些树枝是没必要的,再把它们去掉。
简单来说,后剪枝的过程就是:从一个叶子节点出发,看看如果把这个叶子节点的父节点作为叶子节点(相当于剪掉叶子节点这一层的分支),决策树的准确率是否提高,如果提高了,就剪掉这一层分支,否则就不剪。
特别地,如果是否剪枝对决策树的准确率没有影响,那可剪可不剪。
因为流程已经在预剪枝那里讲过一遍了,这里不用再展开说,放一张最终决策树的图即可。
不难发现,后剪枝比预剪枝保留了更多分支,因此它的欠拟合风险很小,泛化性能往往优于预剪枝和未剪枝决策树。但同时,后剪枝需要先训练一棵完整的决策树,再自底向上对每一个分支进行考察,这导致后剪枝的各种开销在属性数量较多且数据量较大时都远大于预剪枝和未剪枝。
连续值与缺失值处理
well,在现实情况中,我们很难收集到非常完整的(指没有属性缺失的)数据集,也很难保证一个数据集中所有的属性都是连续的 or 都是离散的。如果遇到这两种情况,原始的决策树算法就不那么好用了,我们需要做一些特殊处理。
连续值
显然,面对连续值,我们肯定不可能把每一个值单独分为一类。这就需要用到连续属性离散化技术了,在决策树算法中,最常用的是二分法,简单来说就是选出一个临界值,大于这个临界值的连续属性被分为一类,小于这个临界值的被分为另一类。
比如现在我们有一个数据集 D 和一个连续属性 a,a 在 D 上有 n 个不同的取值,把这些取值从小到大排序,即 { a 1 , a 2 , . . . , a n } \{a^1, a^2, ..., a^n\} {a1,a2,...,an},那么肯定存在一个临界值 t ( a 1 < t < a n ) t \ ( a^1 < t < a^n) t (a1<t<an) 能够通过属性 a 把数据集 D 划分为两部分,即 “在属性 a 上的取值大于 t 的部分” 和 “在属性 a 上的取值不大于 t 的部分”,记为 D t + D^+_t Dt+ 和 D t − D^-_t Dt−。
怎么找这个临界值 t 呢?不难发现,对于相邻的属性取值 a i a^i ai 和 a i + 1 a^{i+1} ai+1 来说,这个 t 取区间 [ a i , a i + 1 ) [a^i, a^{i+1}) [ai,ai+1) 里任意一个值的划分效果都是一样的,so 我们不妨就取中间值作为一个可能的临界值。
即,二分法的核心就是:对于每两个相邻的属性取值,都取中间值作为一个可能的临界值,这些中间值组成一个 “可能临界值” 的集合,再通过一些标准选出最合适的临界值。
由这些中间值组成的集合可表示为:
T a = { a i + a i + 1 2 , 1 ≤ i ≤ n − 1 } T_a = \{\frac{a^i+a^{i+1}}{2},1≤i≤n-1\} Ta={2ai+ai+1,1≤i≤n−1}那么,临界值怎么选呢?这就和选取最优划分属性一样了。因为选临界值的目的也是分类,那就用三种评价标准中的一个,比如信息增益,选出 “可能临界值” 集合中信息增益最大的那个值,作为最终的临界(划分)值 t。
不过,和离散值不同的是,连续值可以反复作为最优划分属性,只不过每次选择的临界值可能不一样。换言之,比如一棵决策树已经用 “属性 <= 7.25” 划分过一次了,但也可以在此基础上再用 “属性 >= 2.18” 继续划分。
连续值的处理比较简单,这里就不再举例子了。
缺失值
well,缺失值是很常见的,通常指样本在某个或某几个属性上的值有空缺。
如果有缺失值的样本很少,总样本量又比较大,那直接丢掉这些有缺失值的样本也可以,但是通常情况下事情不会这么简单,所以在遇到缺失值的时候,我们需要一些应对方案。
我们面临的问题主要有两个:
(1)如何在某些属性有缺失值的情况下选择最优划分属性?
(2)选好最优划分属性后,如果某样本在这个属性上的值缺失,怎么划分这个样本?
先来解决第一个问题。很显然,我们只能通过在该属性上没有缺失值的样本来判断这个属性是否能作为最优划分属性(依然采用信息增益作为标准)。
还是一样,给定数据集 D D D 和属性 a a a,作以下规定:
- D D D 中的数据共有 ∣ Y ∣ |Y| ∣Y∣ 个分类
- D ~ \tilde{D} D~ 表示数据集 D D D 中在属性 a a a 上没有缺失值的样本子集
- 属性 a a a 有 V V V 个可取值 { a 1 , a 2 , . . . , a v } \{a^1, a^2, ... , a^v\} {a1,a2,...,av}
- D v ~ \tilde{D^v} Dv~ 表示 D ~ \tilde{D} D~ 中在属性 a a a 上取值为 a v a^v av 的样本子集
- D k ~ \tilde{D_k} Dk~ 表示 D ~ \tilde{D} D~ 中属于第 k , ( k = 1 , 2 , . . . , ∣ Y ∣ ) k, (k=1, 2, ..., |Y|) k,(k=1,2,...,∣Y∣) 类的样本子集
- D D D 中的每个样本的初始权重 w x = 1 w_x=1 wx=1
ok,有了这些,我们来计算一些式子:
Ⅰ. 对于属性 a a a,在属性 a a a 上无缺失值的样本占总样本的比例:
ρ = ∑ x ∈ D ~ w x ∑ x ∈ D w x ρ = \frac{\sum_{x\in\tilde{D} \ w_x}}{\sum_{x\in D \ w_x}} ρ=∑x∈D wx∑x∈D~ wx
Ⅱ. 在属性 a a a 上无缺失值的样本中,属于第 k k k 类样本所占比例:
p k = ∑ x ∈ D k ~ w x ∑ x ∈ D ~ w x , k = 1 , 2 , . . . , ∣ Y ∣ p_k = \frac{\sum_{x\in\tilde{D_k} w_x}}{\sum_{x\in\tilde{D} \ w_x}}, k=1, 2, ..., |Y| pk=∑x∈D~ wx∑x∈Dk~wx,k=1,2,...,∣Y∣
Ⅲ. 在属性 a a a 上无缺失值的样本中,在属性 a a a 上取值为 a v a^v av 的样本所占比例:
r v ~ = ∑ x ∈ D v ~ w x ∑ x ∈ D ~ w x \tilde{r_v} = \frac{\sum_{x\in\tilde{D^v} \ w_x}}{\sum_{x\in \tilde{D} \ w_x}} rv~=∑x∈D~ wx∑x∈Dv~ wx
计算这些是为了把信息增益的计算式推广到有缺失值的场景。
再回顾一下信息增益的原始表达式:
G a i n ( D , a ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) Gain(D,a) =Ent(D)-\sum^{V}_{v=1}\frac{|D^v|}{|D|}Ent(D^v) Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)
把新计算出的数值代换进去,新的信息增益算式为:
G a i n ( D , a ) = ρ × G a i n ( D ~ , a ) = ρ × ( E n t ( D ~ ) − ∑ v = 1 V r v ~ E n t ( D v ~ ) ) Gain(D, a) = ρ × Gain(\tilde{D}, a) \\ = ρ × (Ent(\tilde{D})-\sum^{V}_{v=1}\tilde{r_v}Ent(\tilde{D^v})) Gain(D,a)=ρ×Gain(D~,a)=ρ×(Ent(D~)−v=1∑Vrv~Ent(Dv~))其中: E n t ( D ~ ) = − ∑ k = 1 ∣ Y ∣ p k log 2 p k Ent(\tilde{D}) = -\sum_{k=1}^{|Y|}p_k\log_2p_k Ent(D~)=−k=1∑∣Y∣pklog2pk
其实就相当于把 D ~ \tilde D D~ 当成了新的数据集,所有的计算方式都和原算式一样。至于为什么要乘 ρ ρ ρ,可以这么理解: ρ ρ ρ 类似置信度或权重,因为对于一个属性,肯定是缺失的值越少,参考价值越高, × ρ ×ρ ×ρ 相当于同时兼顾了纯度提升和可信度(参考价值)。
ok 终于把公式讲完了(对这是这一篇最后一个公式)。
emmmm,我们好像还有个问题(2)是吧,问题(2)比(1)好解决——之前不是说,给每一个样本在最初都赋予了 1 的权重嘛,这个权重这时候就有用了:我们不知道这个有缺失值的样本到底怎么分,那就同时把它分到所有分支中,它在新分支中的权重就等于这个分支的先验概率(也就是被分到这个分支的样本在总样本中的占比)。当然,其它有明确分类的样本的权重不变,还是 1。
C4.5 算法对缺失值的处理方式就如上两个问题的答案所说。
举个例子,我们现在有这样一个数据集:
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 好瓜 |
---|---|---|---|---|---|---|---|
1 | – | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | – | 是 |
3 | 乌黑 | 蜷缩 | – | 清晰 | 凹陷 | 硬滑 | 是 |
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 是 |
5 | – | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 是 |
6 | 青绿 | 稍蜷 | 浊响 | 清晰 | – | 软粘 | 是 |
7 | 乌黑 | 稍蜷 | 浊响 | 稍糊 | 稍凹 | 软粘 | 是 |
8 | 乌黑 | 稍蜷 | 浊响 | – | 稍凹 | 硬滑 | 是 |
9 | 乌黑 | 稍蜷 | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
10 | 青绿 | 硬挺 | 清脆 | – | 平坦 | 软粘 | 否 |
11 | 浅白 | 硬挺 | 清脆 | 模糊 | 平坦 | – | 否 |
12 | 浅白 | 蜷缩 | – | 模糊 | 平坦 | 软粘 | 否 |
13 | – | 稍蜷 | 浊响 | 稍糊 | 凹陷 | 硬滑 | 否 |
14 | 浅白 | 稍蜷 | 沉闷 | 稍糊 | 凹陷 | 硬滑 | 否 |
15 | 乌黑 | 稍蜷 | 浊响 | 清晰 | – | 软粘 | 否 |
16 | 浅白 | 蜷缩 | 浊响 | 模糊 | 平坦 | 硬滑 | 否 |
17 | 青绿 | – | 沉闷 | 稍糊 | 稍凹 | 硬滑 | 否 |
以属性 “色泽” 为例,17 个样本中共有 14 个样本在该属性上没有缺失值,则这 14 个样本的信息熵为: − ( 6 14 log 2 6 14 + 8 14 log 2 8 14 ) = 0.985 -(\frac{6}{14} \log_2\frac{6}{14}+\frac{8}{14} \log_2\frac{8}{14})=0.985 −(146log2146+148log2148)=0.985
同样,我们也可以计算出三个分支数据集的信息熵分别为 1.000、0.918、0。
ok,样本子集 D ~ \tilde{D} D~ 上属性 “色泽” 的信息增益就是:
0.998 − ( 4 14 × 1.000 + 6 14 × 0.918 + 4 14 × 0.000 ) = 0.306 0.998-(\frac{4}{14}×1.000+\frac{6}{14}×0.918+\frac{4}{14}×0.000) = 0.306 0.998−(144×1.000+146×0.918+144×0.000)=0.306
那么样本集 D D D 上的信息增益就是 14 17 × 0.306 = 0.252 \frac{14}{17}×0.306 = 0.252 1714×0.306=0.252.
类似的,我们计算出属性 “纹理” 的信息增益最大,为 0.424,于是我们选择它作为最优划分属性。
重复上述过程,直到决策树构建完成。
最终结果如下:
ok!!!以上就是决策树算法的原理,学完这些大概用了几节自习课吧,写这篇又写了好几个小时,代码之后发~!
好嘞,这篇就到这里!感谢~!
这篇文章详解决策树算法,写了好几个小时,希望对你有所帮助!⭐
欢迎三连!!一起加油!🎇
——Geeker_LStar