现实生活中,我们会进行各种各样的选择。不论是挑选商品,还是挑选任何东西,都是通过以往经验所得。如果我们把挑选东西背后的逻辑整理成一个结构图,你会发现它实际上就是一个树状图,就像公司人员结构组成那样,这就是我们本篇博客要讲解的内容——决策树。
决策树是一种机器学习的方法。决策树的生成算法有ID3, C4.5和CART等。决策树是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果。
1、主题
本篇博客讲述决策树算法,以及通过算法实现分类与回归任务。
先来了解一下树的概念:
树:是由节点和边两种元素组成的结构。理解树,就需要理解几个关键词:根节点、父节点、子节点和叶子节点。
父节点和子节点是相对的,说白了子节点由父节点根据某一规则分裂而来,然后子节点作为新的父亲节点继续分裂,直至不能分裂为止。而根节点是没有父节点的节点,即初始分裂节点,叶子节点是没有子节点的节点,如下图所示:
决策树利用如上图所示的树结构进行决策,每一个非叶子节点是一个判断条件,每一个叶子节点是结论。从跟节点开始,经过多次判断得出结论。
2、目标
- 熟知决策树算法的原理,训练与预测方式。
- 分类树与回归树的训练准则及各自的度量标准。
- 三种常用的决策树算法。
- 使用决策树实现分类与回归任务。
3、信息熵
讲解决策树之前,我们需要知道信息熵的概念。
3.1 概念
信息熵
,在1948年由香农提出。用来描述系统信息量的不确定度。
不确定性越大,则信息熵越大,反之,信息熵越小。
例如,4只猎豹参与赛跑,每只猎豹的能力都是旗鼓相当,平分秋色。我们很难确定哪只猎豹会获得胜利,因此,这种情况下,不确定性很大,信息熵就大。但是,假设让1只猎豹与3只蜗牛进行赛跑,则猎豹取胜便是毋容置疑的,因此,这种情况下,不确定性很小,信息熵就小。
练习:
以下哪个选项的信息熵最大? (
C
)
A 明天会下雨。
B 足球是圆的。
C 下一期双色球的一等奖的中奖号码是红球:1,2,3,4,5,6,蓝球:7。
D 下周二某支股票会上涨。
3.2 计算方式
假设随机变量具有M个值,分别为: V1,V2 ,… ,Vm 。并且各个值出现的概率如下:
则变量的信息期望值(信息熵)为:
信息熵公式:
例如:
一个班级男生了60人,女生有40人。那么男生的概率是0.6,女生的概率是0.4。
信息熵 H(X)= - (0.6log2(0.6) + 0.4log2(0.4)) 。
3.3 概率分布与信息熵
对于信息熵,我们可以用概率分布来衡量,对于随机变量X,其分布越均衡,则不确定性越多,信息熵越大。其分布越不均衡,则不确定性越小,信息熵越小。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["font.size"] = 12
# 假设随机变量X可以取两个值,一个值的概率为p,则另外一个值的概率为1 - p。
p = np.linspace(0.01, 0.99, 100)
# 计算在不同概率分布下的信息熵。
h = -p * np.log2(p) - (1 - p) * np.log2(1 - p)
plt.plot(p, h)
plt.xlabel("p1取值")
plt.ylabel("信息熵")
plt.title("概率-信息熵对比")
plt.show()
结果:
从以上图中我们可以看出当p1取值越来越大时,信息熵就越大,分布就越来越均衡,也就是不确定性越大。p1取值为0.5时,信息熵最大,p1和p2概率都是0.5,这时候分布均等,不确定性也是最大的。
如果我们把概率分布转换到数据集上。信息熵体现的就是数据的不纯度,即样本类别的均衡程度。
样本类别越均衡,不纯度越高,信息熵越大。反之,样本类别越不均衡,不纯度越低,信息熵越小。
我们可以进行如下的表示:
信息熵 =》 概率(比例)均衡度 =》 不纯度
练习:
不通过计算,以下哪个数据集的信息熵最大? (
C
)
A 箱子中有9个红球,1个白球。
B 班级中有30个男生,25个女生。
C饮料店成交30单,其中10单苹果味,10单哈密瓜味,10单葡萄味。
D 路口经过55辆车,其中50辆本地车,5辆外地车。
4、决策树训练与预测
4.1 决策树概念
决策树
是一种树形结构,通过特征的不同来将样本数据划分到不同的分支(子树)中,最终,每个样本一定会划分到一个叶子节点中。 我们可以将每个特征视为一个问题(提问),特征值的不同,就视为样本给出的不同答案,然后,我们就可以根据一系列问题(特征),将样本划分到不同的叶子节点中。决策树是一种非参数监督学习方法,可以用于分类与回归
。
树形结构适合一对多的情形。
例如:家族族谱,公司组织机构等都是树形结构。
4.2 训练决策树
例如,给定如下的数据集:
我们就可以将三个特征作为三个问题,依次来“询问”数据集中的每个样本,经过每个样本依次“作答”之后,就可以将样本划分到不同的分支中,这样,决策树就训练完成。其实,决策树的训练,就是根据训练集去构建一颗决策树。结果如下图所示。
4.3 预测原理
当在训练集上构建决策树后,我们就可以对未知样本进行预测。
预测的过程为:根据未知样本的特征,逐步进行分支选择(回答问题),直到叶子节点为止。
那么,我们就可以使用该叶子节点中的已知样本来预测该未知样本。
可是,我们预测的依据是什么呢?
我们可以想象,假设对与两个人进行性格测试,两个人回答一些相同的选择题,如果两个人的选项完全一致,则说明两个人的性格存在很大的相似性。
同样,决策树是根据特征值来划分样本的(这类似于回答问题)。
如果样本经过层层划分之后,分到了同一个叶子节点中,则表明这些样本应该也是非常
相似的。
因此,我们就可以使用在同一个叶子节点中的已知样本,去预测未知样本的标签了。
预测的方式为:
- 对于分类树,使用叶子节点中,类别数量最多的类别,作为未知样本的类别。
- 对于回归树,使用叶子节点中,所有样本的均值,作为未知样本的类别。
例如,样本11预测的类别为:能。
5、分类决策树
5.1 训练的疑问
从刚才的介绍可知,决策树可以看做由若干个节点构成,其中,每个节点包含一定数量的样本(根节点包含所有样本数据)。
决策树的训练过程就是根据特征值来分割样本数据。
然而,从刚才训练决策树的方式上,我们需要解决如下的问题:
- 我们是否可以先根据“拥有房产”或“婚姻情况”特征进行划分?当选择特征划分样本时,顺序是否是任意的?
- 对于连续变量类型的特征,年收入为什么选择以97.5来进行划分,而不是其他值?
5.2 信息增益
信息增益在决策树算法中是用来选择特征的指标,信息增益越大,则这个特征的选择性越好。
信息增益(IG-Information gain)定义如下:
信息增益就是父节点的不纯度减去所有子节点不纯度(加权)。
我们要优先选择信息增益大的节点当做父节点。
练习:
假设你是总经理助理,目前有10份文件,其中4个与M公司有关,6个与N公司有关。我们需要对文件 进行整理,目前X与Y两个盒子,我们会选择(
A
)。
A 4个与M公司相关的文件放入X,6个与N公司相关的文件放入Y。
B 3个与M公司相关的文件放入X,另外1个与6个与N公司相关的文件放入Y。
C 1个与M公司相关的文件放入X,另外3个与6个与N公司相关的文件放入Y。
D 所有文件放入X或Y中,另外一个盒子空闲。
就像我们整理文件一样,在选择特征分裂样本时,我们应该让子节点的不纯度尽可能的低,这样就可以更快的完成训练(更少的分割次数),同时,在预测未知样本时,也会具有更高的准确度。
因此,在我们选择特征进行划分时,特征的顺序不是任意的,而是应该选择在分割样本集后,能够使得所有子节点不纯度最低(加权)的特征。由于父节点的不纯度是不变的,因此,能够让所有子节点不纯度最小的特征,实际上也就是能够所得信息增益最大的特征。 而这,也正是训练分类决策树时,选择特征顺序的依据。
5.3 训练规则
训练分类决策树的具体规则如下:
- 将每一个特征看成是一种分裂可能。特征可以分为离散型与连续性。
- 离散型特征,每一个类别可以划分为一个子节点(多叉树),或者属于类别A与不属于类别A(二叉树)。
- 对于连续性特征,可以划分为大于等于A与小于A。
- 从根节点开始,选择可获得最大信息增益的特征进行分裂(实现信息增益最大化)。
- 对子节点继续选择能够获得最大信息增益的特征进行分裂,直到满足如下条件之一,停止分裂。
- 所有叶子节点中的样本属于同一个类别。
- 树达到指定的最大深度(max_depth),每次分裂视为一层。
- 叶子节点包含的样本数量小于指定的最小分裂样本数量(min_samples_split)。
- 如果分裂后,叶子节点包含的样本数量小于指定的叶子最小样本数量 (min_samples_leaf)。
5.4 分类决策树示例
以不纯度衡量使用信息熵为例,父节点的信息熵为:
如果以特征“拥有房产”作为分裂特征,则:
因此,特征“拥有房产”的信息增益为:
对于收入来说,为连续型变量,我们这里将收入的取值进行排序,然后选择“否”与“是”的分界点(75与85之间,95与100之间),取平均值进行分割:
从上面的结果中可知,相比于收入=80来说,收入=97.5可以获得更大的信息增益。
这也就是为什么我们一开始选择年收入(信息增益大的)作为父节点,作为分裂条件。
6、不纯度度量标准
不纯度可以采用如下方式度量:
- 信息熵(Entropy)
- 基尼系数(Gini Index)
- 错误率(classification error)
6.1 信息熵
- m:节点中含有样本的类别数量。
- p(i | D):节点中,属于类别的样本占节点中样本总数的比例(概率)。
6.2 基尼系数
6.3 错误率
无论哪种度量标准,都有一个特性:
如果样本以相同的比例分布于不同的类别时,度量值最大,不纯度最高。如果所有的样本都属于同一个类别,则度量值为0,不纯度最低。
# 定义函数,返回基尼系数。
def gini(p):
return 1 - p ** 2 - (1 - p) ** 2
# 定义函数,返回信息熵。
def entropy(p):
return -p * np.log2(p) - (1 - p) * np.log2(1 - p)
# 定义函数,返回错误率。
def error(p):
return 1 - np.max([p, 1 - p], axis=0)
# 定义概率的取值范围。
p = np.linspace(0.01, 0.99, 200)
# 计算信息熵。
en = entropy(p)
# 计算缩放的信息熵。
en2 = en * 0.5
# 计算错误率。
err = error(p)
# 计算基尼系数。
g = gini(p)
fig = plt.figure()
for i, lab, ls, c, in zip([en, en2, g, err], ["信息熵", "信息熵(缩放)", "基尼系数", "错误率"],
["-", ":", "--", "-."], ["r", "g", "b", "y"]):
plt.plot(p, i, label=lab, linestyle=ls, lw=2, color=c)
plt.legend(loc="right", bbox_to_anchor=(1.55, 0.8))
plt.axhline(y=0.5, linewidth=1, color='k', linestyle="--")
plt.axhline(y=1.0, linewidth=1, color='k', linestyle="--")
plt.ylim([0, 1.1])
plt.xlabel("概率1取值")
plt.ylabel("纯度系数")
plt.show()
结果:
可以看到信息熵、信息熵(缩放)、基尼系数、错误率他们都会随着不纯度的增大而增大,随着不纯度的减小而减小。
7、决策树算法
决策树主要包含以下三种算法:
- ID3
- C4.5
- CART(Classification And Regression Tree)
7.1 ID3
ID3(Iterative Dichotomiser3-迭代二分法)算法
是非常经典的决策树算法,该算法描述如下:
- 使用多叉树结构。
- 使用信息熵作为不纯度度量标准,选择信息增益最大的特征分割数据。
ID3算法简单,训练较快。但该算法具有一些局限,如下:
- 不支持连续特征。
- 不支持缺失值。
- 仅支持分类,不支持回归。
- 在选择特征时,会倾向于选择类别多的特征。
7.2 C4.5
C4.5算法
是在ID3算法上改进而来,该算法描述如下:
- 使用多叉树结构。
- 仅支持分类,不支持回归。
不过,C4.5在ID3算法上,进行了一些优化,包括:
- 支持对缺失值的处理。
- 支持将连续值进行离散化处理。
- 使用信息熵作为不纯度度量标准,但选择信息增益率(而不是信息增益)最大的特征分裂节点。
信息增益率的定义方式为:
- IH(f) :根据特征 f 的不同类别值比例(概率),计算得到的信息熵。
之所以从信息增益改为信息增益率,是因为在ID3算法中,倾向于选择类别多的特征,因此,经过这样的调整,在C4.5中就可以得到缓解。因为类别多的特征在计算信息熵时,往往会比类别少的特征信息熵 IH(f) 大。这样,就可以在分母上进行一定的惩罚。
def entropy(p):
return np.sum(-p * np.log2(p))
# 计算在不同类别数量时,信息熵的对比。每个元素代表一个类别所占的比例。
a1 = np.array([0.4, 0.6])
a2 = np.array([0.3, 0.3, 0.2, 0.2])
a3 = np.array([0.1] * 10)
print(entropy(a1))
print(entropy(a2))
print(entropy(a3))
结果:
从结果中可以看出,类别比较多的话信息熵也会比较大。
7.3 CART
CART(Classification And Regression Tree),分类与回归树。
该算法描述如下:
- 使用二叉树结构。
- 支持连续值与缺失值处理。
- 既支持分类,也支持回归。
- 使用基尼系数作为不纯度度量标准,选择基尼增益最大的特征分裂节点。(分类)
- 使用MSE或MAE最小的特征分类节点。(回归)
8、回归决策树
当使用回归决策树时,与分类决策树会有所不同。
回归任务的标签( 值)是连续的,故之前以分类为基础的不纯度度量标准(信息熵,基尼系数与错误率)都不适用于回归树,因此,在回归树中,自然也就没有信息增益,信息增益率或基尼增益等概念了。可以说,分类决策树选择特征的方式,完全不适用于回归决策树。
对于回归决策树,会使用叶子节点的均值来预测未知样本。
同时,回归决策树使用MSE或MAE作为评估指标,用来选择特征。也就是说,回归决策树在选择特征上,每次选择能够使得MSE或MAE最小的特征,用来分裂节点。
9、程序(代码)实现
在scikit-learn中,使用优化的CART算法来实现决策树。
9.1 分类
scikit-learn中,提供DecisionTreeClassifier类,用来实现决策树分类
。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
X, y = load_iris(return_X_y=True)
# 为了后续的可视化方便,这里选择两个特征。
X = X[:, :2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,
random_state=0)
# criterion:不纯度度量标准,默认为gini。
# gini:基尼系数 entropy:信息熵
# splitter:选择分裂节点的方式。默认为best。
# best:在最好的位置分裂节点。 random:在随机的位置分裂节点。
# max_depth:树的最大深度,默认为None(不限制深度)。
# min_samples_split:分裂节点的最小样本数,默认为2。
# min_samples_leaf:分裂节点后,叶子节点最少的样本数量,默认为1。
# max_features:分裂节点时,考虑的最大特征数量,默认为None(考虑所有特征)。
# random_state:随机种子。
tree = DecisionTreeClassifier()
tree.fit(X_train, y_train)
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))
决策树参数:
criterion
:不纯度度量标准,默认为gini。
gini
:基尼系数 entropy:信息熵
splitter
:选择分裂节点的方式。默认为best。
best
:在最好的位置分裂节点。 random
:在随机的位置分裂节点。
max_depth
:树的最大深度,默认为None(不限制深度)。
min_samples_split
:分裂节点的最小样本数,默认为2。
min_samples_leaf
:分裂节点后,叶子节点最少的样本数量,默认为1。
max_features
:分裂节点时,考虑的最大特征数量,默认为None(考虑所有特征)。
random_state
:随机种子。
结果:
结果中,训练集高于测试集,原因就在于过拟合。
我们发现,模型存在严重的过拟合倾向,原因在于,如果没有指定树的深度,则默认会训练一颗完全生长的决策树(不限深度),这会容易导致模型复杂化,从而过分依赖于训练集数据的特性,造成过拟合。
我们可以从不同深度树的决策边界,来证实这一点。
from matplotlib.colors import ListedColormap
def plot_decision_boundary(model, X, y):
color = ["r", "g", "b"]
marker = ["o", "v", "x"]
class_label = np.unique(y)
cmap = ListedColormap(color[: len(class_label)])
x1_min, x2_min = np.min(X, axis=0)
x1_max, x2_max = np.max(X, axis=0)
x1 = np.arange(x1_min - 1, x1_max + 1, 0.02)
x2 = np.arange(x2_min - 1, x2_max + 1, 0.02)
X1, X2 = np.meshgrid(x1, x2)
Z = model.predict(np.c_[X1.ravel(), X2.ravel()])
Z = Z.reshape(X1.shape)
plt.contourf(X1, X2, Z, cmap=cmap, alpha=0.5)
for i, class_ in enumerate(class_label):
plt.scatter(x=X[y == class_, 0], y=X[y == class_, 1],c=cmap.colors[i], label=class_, marker=marker[i])
plt.legend()
plt.figure(figsize=(15, 10))
for index, depth in enumerate([1, 4, 7, 12], start=1):
plt.subplot(2, 2, index)
plt.title(f"最大深度:{depth}")
tree = DecisionTreeClassifier(random_state=0, max_depth=depth)
tree.fit(X_train, y_train)
plot_decision_boundary(tree, X_test, y_test)
结果:
最大深度为1时,只能分成两个类别,因为只能建立一个二叉树,没有办法变出更多的分类。
最大深度为4的时候,三个类别,可以有4个问题,比深度为1时要复杂。
当最大深度越来越大时,决策边界越来越复杂,模型也就越复杂。
对于决策树来说,最大深度对模型有着较重要的影响,如果最大深度很小,意味着仅进行少数的切分,容易欠拟合,但是,如果最大深度很大,则意味着可能进行较多次切分,容易过拟合。
# 定义列表,用来存储在不同深度下,模型的分值。
train_score = []
test_score = []
for depth in range(1, 13):
tree = DecisionTreeClassifier(random_state=0, max_depth=depth)
tree.fit(X_train, y_train)
train_score.append(tree.score(X_train, y_train))
test_score.append(tree.score(X_test, y_test))
plt.plot(train_score, marker="o", c="red", label="训练集")
plt.plot(test_score, marker="o", c="green", label="测试集")
plt.legend()
结果:
从运行结果中,我们可知,随着最大深度的增加,训练集的表现越来越好,但是测试集的表现,是先增加后减少,这说明,在树深度较小时,模型是欠拟合的,因此,增加树深度,能够提升预测效果,但随着深度的增加,模型越来越依赖于训练集,这反而降低预测效果,造成过拟合。
9.2 回归
scikit-learn中,提供DecisionTreeRegressor类,用来实现决策树回归。
from sklearn.datasets import load_boston
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,
random_state=0)
# 回归决策树的参数,可以参考分类决策树的参数。
tree = DecisionTreeRegressor(max_depth=3)
tree.fit(X_train, y_train)
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))
结果:
10、总结
我们学习了以下内容:
-
决策树的三种算法。
分别是:ID3、C4.5、CART -
分类树节点分裂准则。
①将每一个特征看成是一种分裂可能。特征可以分为离散型与连续性。
②从根节点开始,选择可获得最大信息增益的特征进行分裂(实现信息增益最大化)。
③对子节点继续选择能够获得最大信息增益的特征进行分裂,直到满足如下条件之一,停止分裂。 -
回归树节点分裂准则。
对于回归决策树,会使用叶子节点的均值来预测未知样本。
同时,回归决策树使用MSE或MAE作为评估指标,用来选择特征。也就是说,回归决策树在选择特征上,每次选择能够使得MSE或MAE最小的特征,用来分裂节点。 -
深度对决策树的影响。
对于决策树来说,最大深度对模型有着较重要的影响,如果最大深度很小,意味着仅进行少数的切分,容易欠拟合,但是,如果最大深度很大,则意味着可能进行较多次切分,容易过拟合。
颜值高的关注/点赞 鼓励下呗!
学习链接:
https://www.cnblogs.com/yonghao/p/5061873.html
https://www.cnblogs.com/molieren/articles/10664954.html