文章目录
前言
本文主要介绍一个常见的分类算法——决策树。决策树虽然简单,但是它的结果非常直观,容易理解和解释,并且它是很多集成模型的基学习器,在机器学习中具有重要的地位。
一、算法推导
李航老师的《统计学习方法》中提到,统计学习方法都是由模型
、策略
和算法
构成的,因此本文在算法推导也主要从这三部分进行展开讨论。
1.模型
决策树模型并不像之前介绍的模型那样存在一个显式的表达式,它的最终模型呈现一个树形结构,本质上是使用特征对实例进行分类的过程。整个模型可以分为三步:选择最优划分特征
、生成决策树
和剪枝
。
最终决策树模型如图所示:
图片来源于周志华老师的《机器学习》
2.策略
上面提到决策树模型没有显式表达式,那么它是否有显式的损失函数呢?其实是有的,它的损失函数如下所示:
C
α
(
T
)
=
∑
t
=
1
∣
T
∣
N
t
H
t
(
T
)
+
α
∣
T
∣
C_{\alpha}(T)=\sum_{t=1}^{|T|} N_{t} H_{t}(T)+\alpha|T|
Cα(T)=t=1∑∣T∣NtHt(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,
H
t
(
T
)
H_{t}(T)
Ht(T)为叶子节点t上的经验熵,
α
>
=
0
\alpha >=0
α>=0为正则化参数。
经验熵为:
H
t
(
T
)
=
−
∑
k
N
t
k
N
t
log
N
t
k
N
t
H_{t}(T)=-\sum_{k} \frac{N_{t k}}{N_{t}} \log \frac{N_{t k}}{N_{t}}
Ht(T)=−k∑NtNtklogNtNtk怎么理解这个损失函数呢?或者说为什么这个函数就能够用来衡量决策树的好坏?
首先看正则项
α
∣
T
∣
\alpha |T|
α∣T∣,这个比较容易理解,它主要是用来控制决策树的复杂度,一般来说叶子节点数越多,模型越复杂,模型也就越不好。
比较费解的是
∑
t
=
1
∣
T
∣
N
t
H
t
(
T
)
\sum_{t=1}^{|T|} N_{t} H_{t}(T)
∑t=1∣T∣NtHt(T),直观上理解就是T个节点的损失相加,然后
N
t
N_t
Nt代表每个节点的权重,问题是
H
t
H_t
Ht为什么就能代表每个节点的损失呢?
其实我们在讨论决策树的时候经常会讨论到“纯度”这个词,纯度越高的树就是越好的(因为能够完全分类,这个不理解可以参考后面选择最优特征这一部分)。回到
H
t
(
T
)
H_{t}(T)
Ht(T),如果叶子节点t纯度非常高,只有一种类别的样本i,那么此时
N
i
k
=
N
t
N_{ik}=N_t
Nik=Nt,而
N
j
k
=
0
,
j
≠
i
N_{jk}=0, j≠i
Njk=0,j=i,代入到
H
t
(
T
)
H_{t}(T)
Ht(T)中就会得到
H
t
(
T
)
=
−
[
1
∗
l
o
g
1
+
0
∗
l
o
g
0
+
.
.
.
.
+
0
∗
l
o
g
9
]
=
0
H_{t}(T)=-[1*log1 + 0*log0 + ....+ 0*log9]=0
Ht(T)=−[1∗log1+0∗log0+....+0∗log9]=0,也就是说此时损失为0。
决策树的损失函数主要是用于决策树最后的剪枝部分,对于决策树的生成部分,我们在算法部分重点讲解。
3.算法
虽然上一小节介绍了损失函数,但是我们发现它好像跟决策树的生成过程联系不大,因为决策树的生成主要是取决于特征的选择,所以这一部分我们重点介绍决策树中是如果选择最优划分特征的。
我们希望决策树的分支结点所包含的样本尽可能属于同一类别,即结点的“纯度”越来越高
。
所以我们每次应该选择那个使得决策树纯度提升最大的特征。既然如此,我们首先要对纯度进行定义,一般使用信息熵
来衡量纯度。
Ent
(
D
)
=
−
∑
k
=
1
∣
Y
∣
p
k
log
2
p
k
\operatorname{Ent}(D)=-\sum_{k=1}^{|\mathcal{Y}|} p_{k} \log _{2} p_{k}
Ent(D)=−k=1∑∣Y∣pklog2pk其中
p
k
p_k
pk表示第k类样本所占的比例,
E
n
t
(
D
)
Ent(D)
Ent(D)值越小,则D的纯度越高(具体解释可参考上一小节关于
H
t
(
T
)
H_{t}(T)
Ht(T)的介绍)。
3.1 ID3(信息增益最大)
一开始的时候,整个样本属于一个节点,此时熵为
E
n
t
(
D
)
Ent(D)
Ent(D),如果选择属性
a
a
a作为划分属性,假设划分后会产生
V
V
V个分支,那么此时熵为这
V
V
V个分支的加权和。即划分后的熵为:
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
Ent
(
D
v
)
\sum_{v=1}^{V} \frac{\left|D^{v}\right|}{|D|} \operatorname{Ent}\left(D^{v}\right)
v=1∑V∣D∣∣Dv∣Ent(Dv)那么整个划分过程所带来的信息增益就等于
Gain
(
D
,
a
)
=
Ent
(
D
)
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
Ent
(
D
v
)
\operatorname{Gain}(D, a)=\operatorname{Ent}(D)-\sum_{v=1}^{V} \frac{\left|D^{v}\right|}{|D|} \operatorname{Ent}\left(D^{v}\right)
Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)信息增益越大,则意味着使用属性a来进行划分所获得的“纯度提升”越大。
所以我们要做的是遍历所有的特征,计算每个特征划分后所带来的信息增益,然后选择信息增益最大那个特征作为我们的最后划分特征,来对节点进行一轮划分。
但是ID3有个缺陷,就是对可取数目较多的属性有所偏好。比如说,把样本的索引也作为一个划分属性,那么它将产生N(样本数量)叶子节点,每个叶子节点只包含一个样本,这些节点的纯度已达到最大,然而这样的决策树不具有泛化能力,无法对新样本进行预测。
3.2 C4.5 (信息增益率最大)
为了避免ID3算法偏好可取值数目较多的属性的缺陷,C4.5使用增益率来选择最优划分属性。增益率定义为:
G
a
i
n
_
r
a
t
i
o
(
D
,
a
)
=
Gain
(
D
,
a
)
IV
(
a
)
Gain\_ratio (D, a)=\frac{\operatorname{Gain}(D, a)}{\operatorname{IV}(a)}
Gain_ratio(D,a)=IV(a)Gain(D,a)
其中
I
V
(
a
)
=
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
log
2
∣
D
v
∣
∣
D
∣
\mathrm{IV}(a)=-\sum_{v=1}^{V} \frac{\left|D^{v}\right|}{|D|} \log _{2} \frac{\left|D^{v}\right|}{|D|}
IV(a)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣其中IV(a)称为属性的固有值,属性a的可能取值数目越多,则IV(a)的值通常越大。但是C4.5又有个缺陷,增益率准则对可取值数目较少的属性有所偏好
因此C4.5算法并不是直接选择增益率最大的候选划分属性,而是使用一个启发式算法:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的属性。
3.3 CRAT(基尼系数最小)
事实上,最常用的筛选指标应该是基尼系数,它也是CRAT(Classification and Regression Tree)中所采用的指标:
Gini
(
D
)
=
∑
k
=
1
∣
Y
∣
∑
k
′
≠
k
p
k
p
k
′
=
1
−
∑
k
=
1
∣
Y
∣
p
k
2
\begin{aligned} \operatorname{Gini}(D) &=\sum_{k=1}^{|\mathcal{Y}|} \sum_{k^{\prime} \neq k} p_{k} p_{k^{\prime}} \\ &=1-\sum_{k=1}^{|\mathcal{Y}|} p_{k}^{2} \end{aligned}
Gini(D)=k=1∑∣Y∣k′=k∑pkpk′=1−k=1∑∣Y∣pk2
p
k
p_{k}
pk为D中第k类样本所占比例,直观上来看,基尼系数表示从数据集D中随机抽取两个样本,其类别标记不一致的概率。基尼系数越小,纯度越高。
属性a的基尼指数定义为:
Gini index
(
D
,
a
)
=
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
Gini
(
D
v
)
\text { Gini index }(D, a)=\sum_{v=1}^{V} \frac{\left|D^{v}\right|}{|D|} \operatorname{Gini}\left(D^{v}\right)
Gini index (D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv) 我们要做的就是选择一个划分属性,使得划分后,样本的基尼系数最小
a
∗
=
arg
min
a
∈
A
Gini
−
index
(
D
,
a
)
a_{*}=\underset{a \in A}{\arg \min } \operatorname{Gini}_{-} \operatorname{index}(D, a)
a∗=a∈AargminGini−index(D,a)
3.4 剪枝
如果决策树每个叶子节点只包含一个样本,这时候纯度肯定是最高的,但是这时候决策树过于复杂,容易过拟合,所以我们需要对决策树进行剪枝。
剪枝又可以分为预剪枝和后剪枝。
预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能(比如验证集上的准确率)的提升,则停止划分并将当前结点标记为叶节点。
后剪枝是指先从训练集生成一颗完整的决策树,然后自底向上地对非叶节点进行考察,若将该结点对应地子树替换成叶节点能够带来决策树泛化性能提升,则将该子树替换成叶子结点,即剪枝。
二、应用场景
(1)非常适合离散特征,对于离散特征,其他很多模型都需要对其进行编码,得到稀疏的编码向量再进行模型拟合,决策树直接对离散特征进行分析。
(2)能够生成清晰的基于特征(feature)选择不同预测结果的树状结构,数据分析师希望更好的理解手上的数据的时候往往可以使用决策树。
(3)作为很多集成学习模型的基学习器
三、代码实现
这里主要使用sklearn这个库来实现机器学习算法。
1.导入相关库
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score
import pandas as pd
import numpy as np
2.读取样例数据
data = load_breast_cancer() # 获取样例数据,这里的数据乳腺癌检测,y=0代表没有乳腺癌,y=1代表有
X = pd.DataFrame(data.data,columns=data.feature_names)
y = data.target
3.划分训练集和测试集
X_train, X_test, y_train, y_test =train_test_split(X,y,test_size=0.3,random_state=0)
4.建立模型
model = DecisionTreeClassifier().fit(X_train,y_train)
y_pred = model.predict(X_test)
## 5.评估模型
由于是分类问题,所以我们可以直接使用准确率来评估模型
```python
print('ACC:%.3f'%(accuracy_score(y_test,y_pred)))
ACC:0.924
其实上面用的方法是留出法,我们也可以使用交叉验证法来计算模型误差。这样就把划分训练集和测试集、建立模型以及评估模型这几步合并在一起。
acc = np.mean(cross_val_score(DecisionTreeClassifier(),X,y,cv=10,scoring='accuracy'))
print('ACC:%.3f'%(acc))
ACC:0.905
可以看到两者比较接近。
四、优缺点
1.优点
(1)易于理解和解释
(2)非参数型
(3)模型可以通过树的形式进行可视化展示
(4)可以直接处理非数值型数据,不需要进行哑变量的转化,甚至可以直接处理含缺失值的数据
2.缺点
(1)容易过拟合
(2)对于有大量数值型输入和输出的问题,决策树未必是一个好的选择
(3)模型不够稳健,某一个节点的小小变化可能导致整个树会有很大的不同(即模型容易受到攻击)