Decision Tree
在分类模型中,决策树算法绝对是最常用的模型之一,当然目前基本没有人会用单棵决策树去做分类或者回归,都是用的集成的树模型。决策树的思想其实并不复杂,人类在判断一件事物是好是坏,是优是劣的时候,通常是这样做的,比如判断一个男人容不容易找到女朋友:先考虑年龄,太大的找不到,年龄合适的再看工资,太少的找不到,太多的没问题,中等水平的再看有没有房,没房的找不到,有房的再看……那么决策树就是在做这样一种判断,它借助树模型,把每个特征看作是一个节点,对每个特征进行划分,构建起一棵树,如图所示:
那么当我们拿到一条新数据的时候,预测的过程就是我们上面语言描述的过程。继续这个例子,假设现在有一位人类高质量男性,他30岁、年薪30k,在上海有一套汤臣一品,那么我们把这位人类高质量男性输入我们的决策树,模拟的过程如下:
- 年龄30岁,进入左子树
- 年新30k,进入左子树
- 有房产,进入左子树
- 已经是叶子节点了,返回结果:“能找到女朋友”
决策树算法通用形式伪代码如下:
那么问题来了,我怎么知道以什么顺序去评估每个节点呢?或者说,每当我要递归建树的时候,该怎么去选取我当前的根节点呢?也就是伪代码中“从A中选择最优划分属性 a ∗ a_* a∗”。答案是不唯一的,但不管是那种度量方法,我们的原则是不变的:按照该特征划分后,数据集的纯度能够越来越高。当下有三种非常经典的决策树模型:ID3、C4.5、CART。下面我们分别来详细介绍
ID3
在介绍ID3之前,我们先来介绍一个信息论当中非常重要的概念:熵
Entropy
信息熵是度量样本集合纯度的一种指标,假设样本集
D
D
D中第
k
k
k个类别的样本占比重为
p
k
p_k
pk,那么样本集
D
D
D的信息熵(香农熵)定义为:
E
n
t
(
D
)
=
−
∑
i
=
1
k
p
k
l
o
g
2
p
k
Ent(D) \ = \ -\sum_{i=1}^kp_klog_2{p_k}
Ent(D) = −i=1∑kpklog2pk
E
n
t
(
D
)
Ent(D)
Ent(D)越小代表样本集纯度越高。
Information Gain
假设特征
a
a
a有
V
V
V个不同的取值
{
a
1
,
a
2
,
…
,
a
V
}
\{a^1, a^2,\dots,a^V\}
{a1,a2,…,aV},如果使用特征
a
a
a对当前样本进行划分,那么就会产生
V
V
V个子结点,定义
D
v
D_v
Dv表示所有在特征
a
a
a取值为
a
v
a^v
av的样本集合,那么我们定义按照特征
a
a
a进行划分的信息增益为:
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=1}^V{\frac{|D_v|}{|D|}Ent(D_v)}
Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)
之前我们说
E
n
t
Ent
Ent越小,样本集纯度越高,那么来观察信息增益的式子,如果信息增益越大,就说明后面的那一项小,也就是
E
n
t
Ent
Ent小,那么样本集纯度就得到了提升。因此,ID3算法的划分属性依据就是信息增益,每一次选取剩余特征中信息增益最大的特征最为 当前的节点。
a
∗
=
a
r
g
m
a
x
G
a
i
n
(
D
,
a
)
a^*=argmax \ \ \ Gain(D,a)
a∗=argmax Gain(D,a)
西瓜书上的这个例子非常的具体,看一遍就能完全理解这个过程
Algorithm
输入训练集 D D D、特征集 A A A、阈值 ϵ \epsilon ϵ,输出决策树 T T T,ID3算法的大体流程如下:
- 如果当前 D D D中所有样本都属于同一类别,那么 T T T为点单点树,并以这个类别作为该节点的类别,返回 T T T。
- 如果 A = ϕ A=\phi A=ϕ,则 T T T为单节点树,返回 D D D中样本数最多的类别 C C C作为该节点的类别,返回 T T T。
- 否则,计算 A A A中所有特征对样本集 D D D的信息增益,选取信息增益最大的特征 A g A_g Ag。
- 如果 A g A_g Ag对 D D D的信息增益小于阈值 ϵ \epsilon ϵ,那么 T T T为单节点树,返回 D D D中样本数最多的类别 C C C作为该节点的类别,返回 T T T。
- 否则,对于 A g A_g Ag每一种可能的取值 a i a_i ai,按照 A g = a i A_g=a_i Ag=ai将 D D D划分为若干非空集合 D i D_i Di,将 D i D_i Di中样本数最多的类别作为当前节点的类别,构建子节点,由该结点和其子结点构成树 T T T,返回 T T T。
- 对于第5步中得到的每个子结点 i i i,以 D i D_i Di为训练集, A − { A g } A-\{A_g\} A−{Ag}为特征集,递归地调用第1~5步,得到子树 T i T_i Ti,返回 T i T_i Ti。
C4.5
下面我们来考虑一下信息增益的缺点:信息增益会偏向特征取值多的特征。举个最极端的例子,每个样本有都有一个编号,假如我们把编号也当做是一个特征,那么很显然,按照编号划分后的样本集纯度是最高的。但这样的模型显然过拟合现象非常严重。为了改进这一缺点,有人提出了C4.5算法。
Gain Ratio
C4.5采用的度量最优划分特征的依据是“信息增益率”,公式为:
G
a
i
n
_
R
a
t
i
o
(
D
,
a
)
=
G
a
i
n
(
D
,
a
)
I
V
(
a
)
I
V
(
a
)
=
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
l
o
g
2
∣
D
v
∣
∣
D
∣
Gain\_Ratio(D,a)=\frac{Gain(D,a)}{IV(a)} \\ IV(a)=-\sum_{v=1}^V{\frac{|D_v|}{|D|}log_2{\frac{|D_v|}{|D|}}}
Gain_Ratio(D,a)=IV(a)Gain(D,a)IV(a)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣
其中,
I
V
(
a
)
IV(a)
IV(a)可以看作是特征
a
a
a的某种属性值,是一个固定的数。
Algorithm
输入训练集 D D D、特征集 A A A、阈值 ϵ \epsilon ϵ,输出决策树 T T T,ID3算法的大体流程如下:
- 如果当前 D D D中所有样本都属于同一类别,那么 T T T为单结点树,并以这个类别作为该节点的类别,返回 T T T。
- 如果 A = ϕ A=\phi A=ϕ,则 T T T为单节点树,返回 D D D中样本数最多的类别 C C C作为该节点的类别,返回 T T T。
- 否则,计算 A A A中所有特征对样本集 D D D的信息增益率,选取信息增益最大的特征 A g A_g Ag。
- 如果 A g A_g Ag对 D D D的信息增益率小于阈值 ϵ \epsilon ϵ,那么 T T T为单节点树,返回 D D D中样本数最多的类别 C C C作为该节点的类别,返回 T T T。
- 否则,对于 A g A_g Ag每一种可能的取值 a i a_i ai,按照 A g = a i A_g=a_i Ag=ai将 D D D划分为若干非空集合 D i D_i Di,将 D i D_i Di中样本数最多的类别作为当前节点的类别,构建子节点,由该结点和其子结点构成树 T T T,返回 T T T。
- 对于第5步中得到的每个子结点 i i i,以 D i D_i Di为训练集, A − { A g } A-\{A_g\} A−{Ag}为特征集,递归地调用第1~5步,得到子树 T i T_i Ti,返回 T i T_i Ti。
CART
CART是Classification and Regression Tree的缩写,这个名字直接点明了CART可以做分类和回归任务,也就是说CART可以处理离散和连续数据(其实上面两个决策树算法也可以处理连续数据,采用的是二分法)。CART决策树是一棵严格的二叉树,对于每个结点,CART会根据 G i n i Gini Gini指数来选取最优的划分特征以及该最优特征下最优的划分值。因此,每个结点的取值只有“是”或“否”(对于数字来说也可以是大于或小于),这也就是CART树是一棵二叉树的原因。
Classification
Gini Index
先来介绍一下基尼指数,基尼指数也是度量样本集纯度的一种指标,设数据集
D
D
D中共有
K
K
K种类别的数据,每种类别占比为
p
k
p_k
pk,则数据集
D
D
D的计算公式为:
G
i
n
i
(
D
)
=
1
−
∑
k
=
1
K
p
k
2
Gini(D) \ = \ 1 - \sum_{k=1}^Kp^2_k
Gini(D) = 1−k=1∑Kpk2
同样的,Gini指数越小,数据集的纯度越高。选择当前特征为
A
A
A,按照
A
A
A的某个值
a
a
a将
D
D
D划分为两个子集
D
1
D_1
D1和
D
2
D_2
D2,那么当前划分的基尼指数就是:
G
i
n
i
_
i
n
d
e
x
(
D
,
a
)
=
∣
D
1
∣
∣
D
∣
G
i
n
i
(
D
1
)
+
∣
D
2
∣
∣
D
∣
G
i
n
i
(
D
2
)
Gini\_index(D,a)={\frac{|D_1|}{|D|}}Gini(D_1) \ + \ {\frac{|D_2|}{|D|}}Gini(D_2)
Gini_index(D,a)=∣D∣∣D1∣Gini(D1) + ∣D∣∣D2∣Gini(D2)
这次,我们选择能使得Gini指数最小的特征和特征值最为最优划分特征和最优划分值。
Algorithm
构建CART决策树的算法过程如下:
- 设当前节点的数据集为 D D D,计算现有特征对 D D D的Gini指数。具体来说,对于每一个特征 A A A,根据 A A A的每一种取值 a a a将 D D D划分为两个子集 D 1 D_1 D1和 D 2 D_2 D2,计算在给定特征 A A A和特征取值 a a a的Gini指数。
- 在所有可能的特征 A A A和特征 A A A取值 a a a中,选取能够使得Gini指数最小的特征和特征值作为当前的最优划分特征和最优划分特征值。依据最优划分特征和最优划分特征值构建两个子结点,将数据集分配到两个子结点中,具体来说,满足 A = a 或 A ≥ a A=a或A\ge a A=a或A≥a的样本进入左子结点,不满足的进入右子结点。
- 递归调用上述步骤,直至达到递归终止条件,如样本集数量已经小于阈值等
- 生成CART决策树
Regression
Min Square Error
之前提到CART可以用来做回归,下面来简单介绍一下。之前在分类任务当中,我们选取结点、划分数据的依据是基尼指数,基尼指数和信息熵一样,都是用来衡量一个集合的不纯度的。但在回归任务中,我们不需要衡量数据集的不纯度(很显然回归任务中的不纯度是非常大的,而且基本不会变),我们只需要衡量回归的误差。那么回归问题最经典常用的误差函数就是MSE,因此,将Gini Index替换为MSE用于特征、特征值选取,并用均值替换多数类别标记,我们就得到了一棵CART回归树。
具体来讲,每次得到两个子集合时,计算
m
i
n
j
,
s
[
m
i
n
c
1
∑
x
i
∈
R
1
(
j
,
s
)
(
y
i
−
c
1
)
2
+
m
i
n
c
2
∑
x
i
∈
R
2
(
j
,
s
)
(
y
i
−
c
2
)
2
]
其中,
j
表示特征,
s
表示特征划分值,
R
1
、
R
2
是划分之后的两个子集合,
c
1
、
c
2
是两个子集合数据的均值
具体来讲,每次得到两个子集合时,计算 \ \ \ min_{j,s}[min_{c_1}\sum_{x_i\in{R_1(j,s)}}(y_i \ - \ c_1)^2 \ + \ min_{c_2}\sum_{x_i\in{R_2(j,s)}}(y_i \ - \ c_2)^2]\\ 其中,j表示特征,s表示特征划分值,R_1、R_2是划分之后的两个子集合,c_1、c_2是两个子集合数据的均值
具体来讲,每次得到两个子集合时,计算 minj,s[minc1xi∈R1(j,s)∑(yi − c1)2 + minc2xi∈R2(j,s)∑(yi − c2)2]其中,j表示特征,s表示特征划分值,R1、R2是划分之后的两个子集合,c1、c2是两个子集合数据的均值
Algorithm
Pruning
其实介绍完三种决策树以后,我们不难发现决策树其实是一个比较容易过拟合的模型,因为随着树的深度变大,结点数变多,模型的泛化能力是会随之减弱的。因此,我们可以对决策树进行剪枝操作来降低模型的复杂度以达到抑制模型过拟合的效果。剪枝的方法有两种:预剪枝和后剪枝
Pre-Pruning
预剪枝的主要思想就是在选定特征进行划分之前,先使用测试集进行正确率的测试,以判断是否有必要按照当前这个结点进行展开。简言之,只要按照当前属性进行划分能够提高模型的准确率,那就划分;否则,就把当前结点标记为叶结点,并以样本数最多的类别作为该叶结点的类别标记。
Post Pruning
后剪枝相对于预剪枝来说复杂了很多。后剪枝是在决策树构建完成后,再自下而上地对所有非叶结点进行剪枝,最终得到一个子树序列 { T 0 , T 1 , … , T n } \{T_0, T_1, \dots, T_n\} {T0,T1,…,Tn},然后采取交叉验证的方法选出序列中最优的子树作为我们最终的模型。下面,我们介绍几个量
- T t T_t Tt表示以结点 t t t为根节点的子树
- ∣ T t ∣ |T_t| ∣Tt∣表示子树 T t T_t Tt中的结点数量(可以认为是子树 T t T_t Tt的复杂程度)
- C ( T ) C(T) C(T)表示子树 T T T对其所包含的训练数据的预测误差,如信息熵、基尼指数
- C ( t ) C(t) C(t)表示把 t t t当作单结点树对其所包含的训练数据的预测误差,如信息熵、基尼指数(是3的特例情况)
于是,我们定义
C
α
(
T
)
C_{\alpha}(T)
Cα(T)表示子树
T
T
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
)
=
C
(
t
)
+
α
C_{\alpha}(t) = C(t)+\alpha
Cα(t)=C(t)+α
我们肯定希望的是损失函数能够减小,但同时又不希望树模型的复杂度升高,因此,取折中的办法就是当
C
α
(
T
t
)
=
C
α
(
t
)
,
即
α
=
C
(
t
)
−
C
(
T
t
)
∣
T
t
∣
−
1
C_{\alpha}(T_t) = C_{\alpha}(t),即 \\\alpha \ = \ \frac{C(t) \ - \ C(T_t)}{|T_t| \ - \ 1}
Cα(Tt)=Cα(t),即α = ∣Tt∣ − 1C(t) − C(Tt)
我们就进行剪枝。于是,对于初始的决策树
T
0
T_0
T0,我们找到能够使得
α
\alpha
α最小的结点进行剪枝得到子树
T
1
T_1
T1,然后递归此过程得到
T
2
,
T
3
…
T
n
T_2, T_3 \dots T_n
T2,T3…Tn。
Multi Variable Decision Tree
上述介绍的决策树模型都是单变量的决策树,什么叫单变量?其实就是在每个结点处,我们只考虑了一种特征的取值,因此如果将分类过程可视化,我们的决策边界都是平行于坐标轴的,如图所示:
也正是因为我们每次只能画平行线,导致模型的复杂度会比较大,影响了泛化能力。显然,如果我们能够使用斜线,以上图为例,我们可以用两条斜线就实现四条平行线的任务。如果我们把每个特征看作是一个变量 x x x,那么单变量相当于是每次用常数函数,多变量相当于是一个所有变量的线性组合,如下图所示:
因此,多变量决策树就是把每个结点从只考虑单一特征变为考虑多个特征。
斜线,以上图为例,我们可以用两条斜线就实现四条平行线的任务。如果我们把每个特征看作是一个变量
x
x
x,那么单变量相当于是每次用常数函数,多变量相当于是一个所有变量的线性组合。
因此,多变量决策树就是把每个结点从只考虑单一特征变为考虑多个特征。