决策树是一种机器学习算法,它可以实现分类和回归任务,甚至是多输出任务。它也是随机森林的重要组成部分,而随机森林则是现今最为强大的机器学习算法之一,因此了解决策树的原理是非常重要的。
说明: 为了说明方便,本篇博客插入了许多图片,部分图片由《机器学习实战》的作者通过jupyter notebook绘制,上传于GitHub供大家学习。
决策树分类
决策树训练和可视化
为了更加直观的了解决策树的工作原理,我们可以对决策树的训练结果进行可视化。首先我们可以使用决策树来制作一个鸢尾花分类器:
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
iris = load_iris()
X = iris.data[:,2:] #读取花瓣的长和宽
y = iris.target
tree_clf = DecisionTreeClassifier(max_depth=2) #深度为2
tree_clf.fit(X,y)
训练好了决策树之后,我们可以使用export_graphviz()方法输出一个图形定义文件来查看训练好的决策树的结构:
from sklearn.tree import export_graphviz
export_graphviz(
tree_clf, #训练好的决策树
out_file="iris_tree.dot", #输出的文件路径
feature_names=iris.feature_names[2:], #提取特征的名字
class_names=iris.target_names, #提取标签的名字
rounded=True, #让决策树的框图圆润
filled=True
)
对于输出的dot文件我们需要下载graphviz来打开,并将其输出为png等常见的文件格式。下载地址(下载msi)。下载安装完成之后,在目录中的bin文件夹中打开gvedit.exe,使用其打开dot文件,即可查看图片,点击setting按钮对其进行输出。
可视化结果如下:
分类过程
预测过程根据上面的图可以清楚的看出来,当有一个示例输入时,先判断花瓣长度是否小于等于2.45cm,若是则进入左边的结点(深度1),属于setosa鸢尾花,若不是则进入右边的结点继续进行判断:花瓣的宽度是否小于等于1.75cm,若是则属于versicolor否则属于virginica。
每个结点中的samples表示它使用的训练实例的数量,value则表示该节点中训练集中每个类别中的数目,而gini则代表基尼不纯度,用来表示不纯度,如果应用的所有训练实例都属于一个类别,则gini=0,结点是“纯”的。基尼不纯度的计算公式为:
G
i
=
1
−
∑
k
=
1
n
p
i
,
k
2
G_i=1-\sum^n_{k=1}p_{i,k}^2
Gi=1−k=1∑npi,k2
其中
p
i
,
k
p_{i,k}
pi,k是第i个节点上,类别为k的训练实例占比。
还有一种计算不纯度的方式,成为信息熵,其计算公式为:
H
i
=
−
∑
k
=
1
p
i
,
k
≠
0
n
p
i
,
k
l
o
g
(
p
i
,
k
)
H_i=-\sum_{k=1\ p_{i,k}\neq0}^np_{i,k}log(p_{i,k})
Hi=−k=1 pi,k=0∑npi,klog(pi,k)
其中
p
i
,
k
p_{i,k}
pi,k如之前的定义。可以将超参数criterion设置为"entropy"来选择信息熵作为不纯度的测量方式,两者无非常明显的区别。基尼不纯度的计算速度略快,所以作为默认选择。根据书中提到的:基尼不纯度倾向于从树枝中分裂出最常见的类别,而信息熵则倾向于生产更平衡的树。
对于sklearn,其训练决策树时使用的是CART算法,该算法仅生成二叉树。
下面这张图就是决策树的决策边界。加粗直线表示根节点(深度0)的决策边界:x=2.45cm这条线。由于左侧区域是纯的,所以不可再分,对于右侧,则在y=1.75cm处再次分裂(虚线),此时达到了最大深度2,因此不再分裂。如果最大深度设置为3,则会再次产生一条分界线(点线)。
估计类别的概率
估算类别的方法很简单,还是回到最开始的决策树,比如说现在有一个实例,根据决策树的训练结果,走到了右下角(紫色),那么他就有
1
1
+
45
=
2.17
%
\frac{1}{1+45}=2.17\%
1+451=2.17%的概率属于versicolor,有
45
1
+
45
=
97.83
%
\frac{45}{1+45}=97.83\%
1+4545=97.83%的概率属于virginica。
CART训练算法
CART算法首先根据单个特征k和阈值来分裂,特征k和阈值的选取由CART分类的成本函数决定,CART分类的成本函数为:
J
(
k
,
t
k
)
=
m
l
e
f
t
m
G
l
e
f
t
+
m
r
i
g
h
t
m
G
r
i
g
h
t
其
中
{
G
l
e
f
t
/
r
i
g
h
t
衡
量
左
/
右
子
集
的
不
纯
度
m
l
e
f
t
/
r
i
g
h
t
是
左
/
右
子
集
的
实
例
数
量
J(k,t_k)=\frac{m_{left}}{m}G_{left}+\frac{m_{right}}{m}G_right\newline其中\begin{cases}G_{left/right}衡量左/右子集的不纯度\\m_{left/right}是左/右子集的实例数量\end{cases}
J(k,tk)=mmleftGleft+mmrightGright其中{Gleft/right衡量左/右子集的不纯度mleft/right是左/右子集的实例数量
将训练集一分为二之后,继续使用相同的方式对每个子集中的实例进行分类,直到达到最大深度或者不纯度为0或者再也不能找到能够降低不纯度的分裂(当然也可以使用一些超参数进行正则化)。
正则化超参数
决策树非常容易过拟合,因为在不进行正则化的条件下,很可能会生成许多参数导致过拟合。为避免过拟合,需要设置一些超参数进行正则化,常见的超参数如下:
- max_depth:默认值为None,即无限深度,所以如果不设定的话则必然会出现过拟合的现象。
- max_featrue:分裂结点时进行评估的特征的最大数量
- max_leaf_nodes:最大叶结点数量
- min_samples_leafs:叶子结点必须拥有的最小样本数量
- min_weight_fraction_leaf:叶子结点中每一类实例最低的加权实例总数占比
我们可以增大min_*或者减小max_*超参数的值来对模型进行正则化。
下面是书中给出的没有进行约束和进行约束后决策树的分类情况(数据集由make_moom生成):
回归
决策树也可以执行回归任务,就像之前说的,为了了解其工作原理我们可以使用可视化技术。首先我们需要对模型进行训练,训练集是带有噪声的二次数据集,我们设置最大深度为2:
import numpy as np
from sklearn.tree import DecisionTreeRegressor
# 创建数据集
np.random.seed(42)
m = 200
X = np.random.rand(m, 1)
y = 4 * (X - 0.5) ** 2
y = y + np.random.randn(m, 1) / 10
# 训练
tree_reg = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg.fit(X, y)
# 可视化
export_graphviz(
tree_reg1,
out_file="regression_tree.dot",
feature_names=["x1"],
rounded=True,
filled=True
)
可视化结果如下:
其原理与决策树分类非常类似,通过计算成本函数将数据分段(分块),计算每一段中数据的平均值作为预测结果。上图中value为预测结果,mse则为方差。其成本函数为:
J
(
k
,
t
k
)
=
m
l
e
f
t
m
M
S
E
l
e
f
t
+
m
r
i
g
h
t
m
M
S
E
r
i
g
h
t
J(k,t_k)=\frac{m_{left}}{m}MSE_{left}+\frac{m_{right}}{m}MSE_{right}
J(k,tk)=mmleftMSEleft+mmrightMSEright
其
中
{
M
S
E
n
o
d
e
=
∑
i
∈
n
o
d
e
(
y
^
n
o
d
e
−
y
(
i
)
)
2
y
^
n
o
d
e
=
1
m
n
o
d
e
∑
i
∈
n
o
d
e
y
(
i
)
其中\begin{cases}MSE_{node}=\sum_{i\in node}(\hat y_{node}-y^{(i)})^2\\ \hat y_{node}=\frac{1}{m_{node}}\sum_{i \in node}y^{(i)} \end{cases}
其中{MSEnode=∑i∈node(y^node−y(i))2y^node=mnode1∑i∈nodey(i)
根据上面的成本函数,即可完成分块,之后取每一个块中数据的平均值作为预测结果。下面是不同深度的决策树的回归表现:
当然我们可以使用min_samples_leaf来进行正则化,下面左边这张图是没用任何约束的条件下训练模型得到的结果,可以看出其发生了过拟合,而右图则是将min_samples_leaf设置为10后的结果,看起来预测结果更加的靠谱:
不稳定性
决策树对训练集的微小变化非常敏感。决策树的所有边界都是垂直或者水平的,这就是决策树的局限性,比如下面这张图显示的是一个线性可分的数据集,在旋转之前决策树可以轻松的分裂来对其进行分裂,但是当数据集旋转了45°之后,边界就产生了不必要的卷曲,这可能会使得其在实际工作中表现较差。
再比如说,如果移除之前鸢尾花数据集中花瓣最长的Versicolor鸢尾花,则决策边界将会发生巨大的变化: