决策树

  • 一、决策树的理解
  • 1.1 字面含义
  • 1.2 定义
  • 二、决策树的组成
  • 三、决策树的实现流程
  • 3.1 构造
  • 3.1.1 节点的来源
  • 3.1.2 决策节点的排布
  • (1)纯度及其度量
  • (1.1)纯度 - Purity
  • (1.2)信息熵 - Information Entropy
  • (1.2.1)来源
  • (1.2.2)公式
  • (1.3)基尼值
  • (1.4)简单案例1
  • (2)决策树纯度的度量
  • (2.1)信息增益(information gain) - ID3算法
  • (2.1.1)信息增益的计算
  • (2.1.2)简单案例2
  • (2.1.3)ID3的缺陷
  • (2.2)信息增益率(gain ratio) - C4.5算法
  • (2.2.1)信息增益率的计算
  • (2.2.2)简单案例3
  • (2.2.3)特点
  • (2.3)基尼指数(gini index) - CART算法
  • (2.3.1)基尼系数
  • (2.3.2)分类与回归
  • (2.3.3)基尼指数的计算
  • (2.3.4)简单案例4
  • 3.2 剪枝 - pruning
  • 3.2.1 假性关联与过拟合现象
  • 3.2.2 剪枝处理
  • (1)预剪枝 - Pre-pruning
  • (1.1)概念
  • (1.2)方法
  • (1.3)优点与缺点
  • (2)后剪枝 - Post-pruning
  • (1.1)概念
  • (1.2)方法
  • (1.3)优点与缺点
  • 四、连续值与缺失值
  • 4.1、连续值处理
  • 4.1.1、离散属性与连续属性
  • 4.1.2、二分法 - 连续属性离散化
  • (1)二分法的划分
  • (2)简单案例5
  • 4.2、缺失值处理
  • (1)处理方法
  • (2)简单案例6
  • 五、多变量决策树
  • 六、应用场景
  • 七、总结
  • 八、案例
  • 0、决策树可视化操作
  • 1、决策树之分类树 - 鸢尾花数据集分类
  • 2、决策树之回归树 - 波士顿房价数据集回归
  • 3、泰坦尼克号生存者预测
  • 3.1、项目分析
  • (1)样本的分析
  • (2)项目流程
  • (3)决策树的函数使用
  • 3.2、项目实践
  • (1)数据获取
  • (2)数据探索
  • (3)数据清洗
  • (4)特征选择
  • (5)决策树模型
  • (6)模型测试&评估
  • (7)可视化决策树
  • 修改时间


一、决策树的理解

决策树是一款非常有地位的分类算法。任意一场数据分析比赛,名列前茅的除了深度学习,剩下的机器学习算法中多半是选用XGBoost算法或者Lightbgm算法。然而这两个算法都是基于决策树分类算法而发明出来的。

1.1 字面含义

决策树,也称“判定树” - Decision Tree。我们将名字分为“决策”和“树”来理解。

决策:
“决策”是指“判断”,这种“判断”就像编程语言中的if-else判断结构一样:首先if + 判断条件,判断是否为真,为真则符合条件而结束判断;否则继续下一个条件判断,else后面再次介入一个if-else判断结构。

例如,举一个银行根据客户的收入、房产和家庭情况来判断是否给客户发放贷款的例子:

if salary_year > 300000:
	return "可以贷款"
else
	if estate:
		return "可以贷款"
	else
		if son_or_daug == True:
			return "可以贷款"
		else
			return "不能贷款"
				... ...



树:

“树”是指数据结构中的树形结构,那么决策树则表示一类采用树形结构的算法,如二叉树和多叉树等等。在scikit-learn中,决策树算法默认使用CART算法,而它只支持二叉树,如下图所示。

Easy Rules决策树_决策树


当然,决策树也支持其他算法,如ID3、C4.5等等,他们既支持二叉树也支持多叉树。这些算法的具体作用,在后面的章节介绍。



1.2 定义

通过以上分析可以观察出决策树的两个特征:规则(或判决条件,if 的内容)和嵌套(else层层递进)。所以,我们可以定义:决策树表示利用一组嵌套的规则对数据集进行分类的算法。(上面例子将数据集分成可以和不可以贷款的对象)

但由于机器学习不同于普通的编程,它是模型自动根据数据集来定义规则。如果以前有使用过模型进行实践的应该不难理解,只需要调用决策树算法包中的几句简单的函数便可以轻松得到结果,而无需自己一行行码。

所以,有人定义为:决策树算法是根据给定的数据集归纳出分类规则,并采用自顶向下的递归划分 (Recursive Partitoning) 的方式,并以树的形式展现出来。



二、决策树的组成

节点是树的主体部分,它一般被分为两类决策节点叶子节点

决策节点:通过条件判断而进行分支选择的节点。如:将某个样本中的属性值(特征值)与决策节点上的值进行比较,从而判断它的流向。

叶子节点:没有子节点的节点,表示最终的决策结果。下图中,叶子节点的值有两种,即可以贷款和不能贷款。

Easy Rules决策树_Easy Rules决策树_02

如图,决策树所有的内部节点(决策节点、非叶子结点)为矩形,叶子节点为椭圆形。

决策树的深度:定义为所有节点的最大层次数。
决策树具有一定的层次结构,根节点的层次数定为0,从下面开始每一层子节点层次数+1。根据上图来判断树的深度,则最大深度为3,说明要得到一个决策结果,最多经过3次判定。





三、决策树的实现流程

如何像上面一个例子中展现的图一样实现一个的决策树?

完整决策树的实现需要经历两个阶段:构造剪枝



3.1 构造

通过决策树的组成得知,决策树主要由决策节点叶子节点构成。

于是产生了两个问题:
问题一:决策节点和叶子节点从何而来?
问题二:决策节点如何排布?

以前面银行借贷的决策树为例,对于问题一,那些if判断条件,以及判断结果是依据什么而来的?,对于问题二,为什么年薪的优先级最高,房产其次,最后是家庭情况,这样的排布有什么道理呢?

3.1.1 节点的来源

决策树主要由节点构成,而节点由决策条件和决策结果构成,而决策条件和结果的依据来源于样本的属性特征。由此可知,决策树的构造为:选择样本特征作为节点的过程。那么样本的特征为决策树节点的来源。

样本的属性特征
决策树的构造离不开样本的属性特征,只有知道研究的对象具备什么特性,才能构造出合理的决策树。这里列举了数据集的样本。

ID

年薪

是否有房产

是否有子女

类别

1

350000



可以借贷

2

100000



不能借贷

Easy Rules决策树_剪枝_03

第二张决策树图中每一个if条件是根据第一张样本图中的每一列属性来进行构造的,样本中的每一列称作属性(Attribute) 或特征或特征维度。(多个属性被称作属性集或者特征维度集)

对于特征也有不同的分类。年收入的结果与其他三项有明显区别,它为数值型,可以比较大小,一般为整数或者实数,这种列属性则称为数值特征。而是否有房产和子女则不能比较大小,只有有和没有两种情况,这种列则称为类别特征

3.1.2 决策节点的排布



决策节点如何排布?如何构造出最优属性划分?通过纯度这个指标,一般而言,我们希望每次判别节点能将样本进行最低错误率的划分,该节点正确率也就越高,同时也表明纯度也越高,该节点也就越适合优先排布。

(1)纯度及其度量

纯度是衡量节点优劣的判断指标,纯度越大的决策树越优,结果的分歧越小。

纯度的计算需要信息熵或者基尼值,他们可以用来度量某样本的纯度

(1.1)纯度 - Purity

每一层决策节点都可以将样本划分为两个子集,而每一次划分都不能保证完全将样本划分分类正确,因为总有一些决策树规则之外的样本,在进行划分的时候会出现错误,比如ID=5的样本,在有子女的情况下,应该是可以借贷的,但是类别是不能借贷。

这可能是样本的属性不完全,导致决策树规则生成不完整,从而使分类出现异常,但是,世界上不可找到最全的样本,也就不可能生成最完美的决策树模型,我们能做的就是在有限的条件下,最小化误差。

以“年薪>300000”的属性为判别节点进行划分,值为“是”时,类别全部为“可以借贷”,说明纯度很高,而值为“否”时,类别既有“可以借贷”也有“不能借贷”,表明纯度相对较低。

ID

年薪>300000

是否有房产

是否有子女

类别

1




可以借贷

2




不能借贷

3




可以借贷

4




可以借贷

5




不能借贷

纯度表示结果分歧大小,决策树的构造过程也可以叫做寻找纯净划分的过程。

(1.2)信息熵 - Information Entropy



(1.2.1)来源

信息熵源于热力学中的熵(Entropy),最早由德国物理学家克劳修斯提出,用来表述某个个体的混乱程度。

在信息论中,随机离散事件出现的概率存在着不确定性。为了衡量这种信息的不确定性,美国数学家信息学之父,香农引入了信息熵的概念。它代表了一个给定数据集中的不确定性或者随机性程度的度量。

比如:当一个数据集中的记录全部为同一类时,则没有不确定性,此时熵为0.因此我们可以把熵看成是信息的数学期望。

(1.2.2)公式

若要求出熵的大小,则先要计算信息:

设某个事物具有N种相互独立的可能结果,则信息的定义为:

Easy Rules决策树_python_04

之所以是以2为底的对数,可能和信息中的最小单位比特有关 (一个比特只有0或1,两种状态),其中 Easy Rules决策树_Easy Rules决策树_05 表示第 Easy Rules决策树_python_06 个分类,Easy Rules决策树_决策树_07 表示第 Easy Rules决策树_python_06 个分类的概率函数,Easy Rules决策树_Easy Rules决策树_09Easy Rules决策树_剪枝_10

并且每个概率之和为1:

Easy Rules决策树_Easy Rules决策树_11

于是,计算信息熵的数学公式可以表示为:

Easy Rules决策树_信息熵_12 求和( 每种属性概率 * 每种属性的信息 )
       Easy Rules决策树_python_13
       Easy Rules决策树_Easy Rules决策树_14

Easy Rules决策树_剪枝_15Easy Rules决策树_决策树_16表示以节点 Easy Rules决策树_python_17 为分类 Easy Rules决策树_Easy Rules决策树_18

对于一个简单的二元分类,此时n=2,那么其整体熵为:

Easy Rules决策树_信息熵_19

该公式表示一种度量帮我们反映出这个信息的不确定度。当不确定性越大时,它所包含的信息量也就也大,信息熵也就越高。

算法会根据所有样本-信息熵的变化 (信息增益) 来选择最佳分类。因而信息熵就是决策树方法中分支产生的衡量标准之一。

(1.3)基尼值

基尼值同样也是一种度量节点纯度的方法,与信息熵计算公式十分相似

Easy Rules决策树_决策树_20

  • Easy Rules决策树_Easy Rules决策树_21
  • Easy Rules决策树_信息熵_22 作用类似于信息熵中的 Easy Rules决策树_剪枝_23,但省去了对数运算,更加方便计算。
(1.4)简单案例1

同样以银行借贷为例子,假设某个样本集中有12个客户,在某次决策树的构造过程中,对该样本集按照属性“是否能借贷”进行二元划分来计算纯度值,划分成D1和D2,其中D1有5个可以借贷的客户,1个不能借贷的客户;D2有一半可以借贷和不能借贷。

结果如下图,我们的目的是计算各个节点的信息熵和基尼值

Easy Rules决策树_Easy Rules决策树_24

根据公式可得:
信息熵:
D: Entropy(D) = Easy Rules决策树_python_25

D1:Entropy(D1) = Easy Rules决策树_决策树_26

D2:Entropy(D2) = Easy Rules决策树_Easy Rules决策树_27

基尼值:
D:Gini(D) = Easy Rules决策树_python_28= 0.44

D1:Gini(D1) = Easy Rules决策树_Easy Rules决策树_29

D2:Gini(D2) = Easy Rules决策树_剪枝_30

无论是信息熵还是基尼值,值越大,纯度越低。当所有类型的样本均匀混合时,如D2,信息熵和基尼值最大,纯度最低。

这种纯度的度量是基于自身单个节点,而对决策树纯度的度量是包括一个父节点和几个子节点,节点之间有相互关联。




(2)决策树纯度的度量

决策树的构建会基于前面纯度的度量,有三种不同决策树节点纯度的度量规则:信息增益、信息增益率和基尼指数。基于决策树的信息度量的不同方式,将决策树划分为三种最著名的算法,它们分别是:ID3C4.5CART

(2.1)信息增益(information gain) - ID3算法

ID - Iterative Dichotomiser 迭代二分器的简称

ID3算法通过比较样本划分前后的信息熵增减来衡量节点的纯度,即该节点的提纯能力的好坏。

(2.1.1)信息增益的计算

信息增益 Easy Rules决策树_决策树_31 父节点(划分前的样本)的信息熵 Easy Rules决策树_剪枝_32

在计算的过程中:

Easy Rules决策树_剪枝_33

  • Easy Rules决策树_Easy Rules决策树_34 代表子样本Easy Rules决策树_决策树_35信息熵
  • Easy Rules决策树_决策树_36 代表样本 Easy Rules决策树_Easy Rules决策树_21 选择属性 Easy Rules决策树_python_38 划分子样本时的信息增益
  • Easy Rules决策树_Easy Rules决策树_21
  • Easy Rules决策树_决策树_35
  • 符号 Easy Rules决策树_剪枝_41 不是求绝对值的意思,而是求样本 Easy Rules决策树_信息熵_42 的个数。如 Easy Rules决策树_python_43 是求样本 Easy Rules决策树_Easy Rules决策树_21 的个数, Easy Rules决策树_剪枝_45 求第 Easy Rules决策树_Easy Rules决策树_46
  • Easy Rules决策树_剪枝_47 表示按照属性 Easy Rules决策树_python_38 划分后共有 Easy Rules决策树_剪枝_47 个子样本。如划分后产生5个子样本,则此时 Easy Rules决策树_python_50
  • Easy Rules决策树_python_51 表示第 Easy Rules决策树_Easy Rules决策树_46

通过计算和比较不同属性的信息增益,值越大的,则提纯的效果越好,则优先级越高。

(2.1.2)简单案例2

题目内容同简单案例1,按照某方式划分后得到一个决策树,我们的目的是计算父节点D的信息增益

Easy Rules决策树_Easy Rules决策树_24


以及简单案例1中对各个节点信息熵的计算,可得:

Easy Rules决策树_剪枝_54

Easy Rules决策树_决策树_55

Easy Rules决策树_Easy Rules决策树_56

根据信息增益的公式,将父节点信息熵减去子节点信息熵的占比,可得:
Easy Rules决策树_剪枝_57

Easy Rules决策树_信息熵_58

Easy Rules决策树_python_59

Easy Rules决策树_Easy Rules决策树_60

对父节点不同的划分,计算出的结果,信息增益越大,说明纯度的增加越大,优先级越高。

(2.1.3)ID3的缺陷

ID3算法有个特点:倾向于选择,值的种类较多的属性为高优先级节点。因为节点是按照信息增益的大小来进行划分与选择,节点属性值的种类越多,越容易将不同样本清晰地划分开来,子节点的信息熵越容易低,纯度越容易高,信息增益越容易大。

但是像属性"ID"这样每一行样本都具有不同种类,划分时,每一个ID为一个子节点,纯度最大,但却是无关属性,对后面的样本无法进行有效的预测。

像ID这样类似的、却不易观察的属性有一些,不过,这种缺陷发生的概率很小,大部分情况下都能生成效果较好的决策树。但是为了追求完美,改进版的ID3算法 - C4.5算法应运而生。

(2.2)信息增益率(gain ratio) - C4.5算法

为了减少样本某个属性的值的种类较多所带来的不利影响,C4.5算法在信息增益的基础上求得信息增益率来选择最优属性划分。

(2.2.1)信息增益率的计算

公式:

Easy Rules决策树_Easy Rules决策树_61

  • Easy Rules决策树_python_62

Easy Rules决策树_Easy Rules决策树_63

  • Easy Rules决策树_python_64 - 固有值(intrinsic value),有的地方也叫属性熵
    属性a的划分越多,k越大,则IV(a)的值通常会越大

Easy Rules决策树_Easy Rules决策树_65

  • Easy Rules决策树_剪枝_45为子集的个数
(2.2.2)简单案例3

题目内容同简单案例1,我们的目的是计算父节点D的信息增益率

Easy Rules决策树_Easy Rules决策树_24


直接计算固有值IV

Easy Rules决策树_信息熵_68

Easy Rules决策树_决策树_69

于是:
Easy Rules决策树_信息熵_70

Easy Rules决策树_决策树_71

Easy Rules决策树_Easy Rules决策树_72



(2.2.3)特点
  • 采用信息增益率
  • 采用悲观剪枝
  • 离散化处理连续属性
  • 处理缺失值




(2.3)基尼指数(gini index) - CART算法

Cart算法全称:Classification And Regression Tree - 分类回归树。

ID3与C4.5算法与信息熵有关,而基尼指数与基尼值有关。



(2.3.1)基尼系数

基尼指数用来衡量决策树纯度的一种指标,而经济学中有基尼系数(Gini index、Gini Coefficient),是一个用来衡量一个国家或地区居民收入差距的常用指标。



(2.3.2)分类与回归

正如CART的名字,它既可以做分类树也可以做回归树。如何区别两者呢?

假设给定下列数据集,用来构造决策树。

构造完成后的树,用来预测某个测试集的“是否能借贷”属性时,为分类树。分类树用来处理离散型数据,也就是数据种类有限的数据,它输出的是样本的类别

用该树预测某个测试集的“年龄”属性时,为回归树。回归树用来处理连续型数据,也就是数据在某段区间内的取值,输出是一个数值

ID

年薪>300000

是否有房产

是否有子女

是否能借贷

年龄

1




可以借贷

30

2




不能借贷

20

3




可以借贷

26

4




可以借贷

34

5




不能借贷

18


(2.3.3)基尼指数的计算

Easy Rules决策树_决策树_73

  • Easy Rules决策树_Easy Rules决策树_74 表示根节点Easy Rules决策树_Easy Rules决策树_21的子节点Easy Rules决策树_决策树_35基尼值
  • Easy Rules决策树_信息熵_77 表示子节点Easy Rules决策树_决策树_35的样本数占根节点Easy Rules决策树_Easy Rules决策树_21总样本数的比例
(2.3.4)简单案例4

假设某个样本集中有12个客户,在某次决策树的构造过程中,对该样本集按照属性“是否能借贷”进行二元划分来计算纯度值,划分成D1和D2,其中D1有6个可以借贷的客户,D2有一半可以借贷和不能借贷。

结果如下图,我们的目的是计算根节点D的基尼指数

Easy Rules决策树_Easy Rules决策树_24


通过观察基尼指数的公式,首先计算子样本D1和D2的基尼值,即 Easy Rules决策树_Easy Rules决策树_81

再通过公式,可以直接计算,每个子样本各占总样本一半比例

Gini_index(D) = Easy Rules决策树_剪枝_82

Easy Rules决策树_决策树_83



3.2 剪枝 - pruning

3.2.1 假性关联与过拟合现象

一般情况下,为了尽最大可能正确地分类训练样本,所以决策树会将样本的所有属性当做标准(标答)来进行构造。

但训练集有时会出现“假性关联”问题。训练集的样本属性真的是标准吗?真的可以反映其他数据集或者测试样本的属性吗?当然不是,这项标准可能只适合该训练集本身而已,所以会导致用训练样本得出的结果过好而出现过拟合现象,过拟合的决策树使用其他数据集测试时会突然出现结果不好的现象。

此时,就需要去掉决策树的部分分支,即去掉样本的部分属性,去掉那些与样本整体分类实际上不存在关联的属性,以此来降低过拟合的风险,提高模型的泛化能力。

3.2.2 剪枝处理

如何对决策树进行剪枝?同决策树算法一样,剪枝的算法也有很多款,但根据剪枝的触发时机的不同,基本分为两种:预剪枝后剪枝

如何评估剪枝前后的性能呢?通过使用测试集对划分前后的精准度进行比较。

(1)预剪枝 - Pre-pruning
(1.1)概念

预剪枝:在决策树生成的过程中,每个决策节点原本是按照信息增益信息增益率或者基尼指数等纯度指标,按照值越大优先级越高来排布节点。由于预剪枝操作,所以对每个节点在划分之前要对节点进行是否剪枝判断,如何判断?即:使用测试集按照该节点的划分规则得出结果。若验证集精度提升,则不进行裁剪,划分得以确定;若验证集精度不变或者下降,则进行裁剪,并将当前节点标记为叶子节点。

(1.2)方法

限定树的高度、限定节点的训练样本数、限定划分带来的纯度的提升的阈值

(1.3)优点与缺点

预剪枝使得决策树很多相关性不大的分支都没有展开,这不仅仅降低了过拟合的风险,还显著减少了决策树的训练时间开销和测试时间开销。

但另一方面,有些分支的当前划分虽不能提升泛化能力,甚至可能导致泛化能力暂时下降,但是在其基础上进行的后续划分却有可能提高性能。预剪枝基于“贪心”本质禁止这些分支展开,给预剪枝决策树带来了欠拟合的风险。

(2)后剪枝 - Post-pruning
(1.1)概念

后剪枝已经通过训练集生成一颗决策树,然后自底向上地对决策节点(非叶子结点)用测试集进行考察,若将该节点对应的子树替换为叶子节点能提升测试集的精确度,则将该子树替换成叶子节点,该决策树泛化能力提升。

(1.2)方法

降低错误剪枝 - Reduced-Error Pruning, REP
悲观错误剪枝 - Pesimistic-Error Pruning, PEP
代价-复杂度剪枝 - Cost-Complexity Pruning, CCP

C4.5采用PEP,CART采用CCP

(1.3)优点与缺点

后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情况下,后剪枝决策树的欠拟合风险很小,泛化能力往往优于预剪枝决策树。

但是后剪枝过程是在生成完决策树之后进行的,并且要自底向上地对树中的所有决策节点进行逐一考察,因此其训练时间开销比未剪枝的决策树和预剪枝决策树都要大得多。





四、连续值与缺失值



4.1、连续值处理

决策树一般为分类树,用来处理具有离散属性的样本,但是现实项目中,会遇到很多连续属性,对它的处理方法有所不同。

4.1.1、离散属性与连续属性

前面案例中的属性都是通过离散属性来生成决策树,比如“是否贷款”,结果通常都是类别判定的有限集合,通常构造的的决策树为分类树

然而,有些属性比如说前面案例样本中的“年龄”属性,值为:22、30和27等等,基本只要是实数就行,结果通常是实数型的无限集合,这样的属性为连续属性,构造的决策树为回归树

4.1.2、二分法 - 连续属性离散化

原来划分离散属性时,如“是否贷款”,可以直接按照属性值来对节点进行划分,如:可以贷款与不能贷款。

然是对于连续属性,如“年龄”,则不可能也按照属性值来对节点进行划分的规则,如:1, … …, 100。没办法通过某些值进行划分,种类过多且数量密集,但是可以将这些值划分成几个不同类型的区间,于是连续属性离散化技术便应运而生。

连续属性离散化技术有很多,但是最简单的策略则是二分法(bi-partition),这也是C4.5决策树算法中采用的机制。

(1)二分法的划分

给定样本集D和连续属性a,假设a在D中出现了n个不同的取值,将这些值从小到大进行排序,记为Easy Rules决策树_信息熵_84

划分点选取公式为:

Easy Rules决策树_python_85

  • Easy Rules决策树_决策树_86
  • Easy Rules决策树_剪枝_87 。n个值取划分点,除去第一个点Easy Rules决策树_Easy Rules决策树_88的左侧和最后一个点Easy Rules决策树_剪枝_89的右侧不能选择,中间一共有n-1个划分点。

拥有n-1个划分节点后,需要选取最佳划分点,则又可以像离散属性那样考察这些划分点。比如计算信息增益

Easy Rules决策树_python_90

  • Gain(D, a, t)表示样本集D基于划分点t二分后的信息增益,前面的max表示选择使结果最大化的划分点
  • t 为划分点,Easy Rules决策树_信息熵_91Easy Rules决策树_Easy Rules决策树_92取+和-代表划分点的右侧集合和左侧集合
  • Easy Rules决策树_剪枝_93,表示划分点 t 属于上一个公式中的取值之一
(2)简单案例5

西瓜书中的一个案例改编而来,假设对于某种水果有一批样本,它们的密度和含糖量以及质量分别为:

ID

density

qualify

1

0.697

good

2

0.774

good

3

0.634

good

4

0.608

good

5

0.556

good

6

0.403

good

7

0.481

good

8

0.437

good

9

0.666

bad

10

0.243

bad

11

0.245

bad

12

0.343

bad

13

0.639

bad

14

0.657

bad

15

0.360

bad

16

0.593

bad

17

0.719

bad

目的:求密度的信息增益
密度:
对密度数据从小到大排序( + 与 - 表示好与不好):
0.243-、0.245-、0.343-、0.360-、0.403-、0.437+、0.481+、0.556+、0.593-、0.608+、0.634+、0.639-、0.657-、0.666-、0.697+、0.719-、0.774-
计算中位数:
0.244、0.294、0.3515、0.3815、0.420、0.459、0.5185、0.5745、0.6005、0.621、0.6365、0.648、0.6615、0.6815、0.708、0.7465
计算信息增益:
划分前,8个好样本,9个差样本
Easy Rules决策树_剪枝_94

划分后,分成两个子集,按照划分点来一个个计算:

由于篇幅过大,不一一计算,经过验证,第四个中位数为最佳划分点,具体计算如下

Easy Rules决策树_Easy Rules决策树_95:第四个和第五个点中间划分开,统计结果如下:

左右子集

good

bad

Easy Rules决策树_剪枝_96

0

4

Easy Rules决策树_python_97

8

5

Easy Rules决策树_Easy Rules决策树_98

Easy Rules决策树_剪枝_99

Easy Rules决策树_信息熵_100

决策树对连续节点的划分则按照“密度Easy Rules决策树_Easy Rules决策树_101 0.3815” 为决策节点,分为左侧4个元素的左子集,右侧13个元素的右子集。

然而不同于离散属性的是,连续属性可以继续将子集作为父节点,继续划分子集,比如:在13个元素的右子集中继续进行“密度Easy Rules决策树_Easy Rules决策树_101 0.621”的划分。

4.2、缺失值处理

在样本获得的过程中,难免会因为某些原因,比如隐私和成本问题,致使最后拿到的样本集出现某些属性数据的缺失。

当缺失的数据非常少时,一般直接舍弃掉那些缺失的数据;而当缺失的数据较多时,简单舍弃则是对样本的极大浪费,则按照一定的方法

(1)处理方法

对信息增益的计算公式进行修改:

Easy Rules决策树_信息熵_103
其中:
Easy Rules决策树_决策树_104

  • Easy Rules决策树_Easy Rules决策树_21表示整个样本,Easy Rules决策树_信息熵_106表示不包含缺失值的样本
  • Easy Rules决策树_剪枝_107 表示完整度,为Easy Rules决策树_python_108
  • Easy Rules决策树_剪枝_47表示划分的个数,Easy Rules决策树_Easy Rules决策树_46Easy Rules决策树_Easy Rules决策树_111Easy Rules决策树_剪枝_47
  • Easy Rules决策树_信息熵_113 类似于以前公式中的 Easy Rules决策树_python_51,当默认为1时,两者相同;但是在每个样本的权值有差异的情况下,按照权值来计算比例。
  • Easy Rules决策树_python_115 为对Easy Rules决策树_剪枝_116子集内不同分类结果的频率,即不同分类结果的数量占该子集元素总数的比值。

C4.5算法在ID3算法的基础上进行缺失值处理的改进,就使用了此方法。

(2)简单案例6

下列是路人对某出行方式的便利程度的数据收集。如果简单的去除所有含缺失值的数据则只有4、6、8、9、12、15可以使用,损失了一大半数据。

目标:将出行按照飞机、高铁和汽车划分成三类,求出行的信息增益。(各样例的权值均为1)

ID

出行

消费

时间

方便

1

-


早晨


2

飞机


-


3

飞机

-

早晨


4

高铁


晚上


5

-

中等

晚上


6

高铁

中等

早晨


7

飞机

-

下午


8

飞机


下午


9

飞机


下午


10

高铁


-


11

汽车

-

下午


12

汽车


下午


13

-


下午


14

汽车


-


15

飞机


晚上


16

汽车

-

下午


17

高铁


-


通过观察得知:

type

good

bad

sum

飞机

4

2

6

高铁

2

2

4

汽车

0

4

4

sum

6

8

14

通过对含有缺失值的样本的计算公式得知:
Easy Rules决策树_python_117
Easy Rules决策树_决策树_118

飞机的信息熵:
Easy Rules决策树_Easy Rules决策树_119
飞机所占Easy Rules决策树_决策树_120比例:Easy Rules决策树_剪枝_121

高铁的信息熵:
Easy Rules决策树_信息熵_122
高铁所占Easy Rules决策树_决策树_120比例:Easy Rules决策树_python_124

汽车的信息熵:
Easy Rules决策树_信息熵_125
汽车所占Easy Rules决策树_决策树_120比例:Easy Rules决策树_Easy Rules决策树_127

带入完整公式:
Easy Rules决策树_剪枝_128

五、多变量决策树

前面研究的都是单变量决策树,即每个决策节点都只针对一个属性进行判别。

例如(参照西瓜书中的案例):

Easy Rules决策树_python_129


对于多变量决策树(,multivatiate decision tree),每一个决策节点,都是一个合适的线性分类器,即多个属性组合成的一组分类规则。

Easy Rules决策树_决策树_130


多变量决策树指的是一类树,同样也有很多不同的算法,它是在单变量决策树的基础上改进而来,关键是对决策节点的改变,比如前面的将决策节点改成线性分类器.除此之外,还有在决策树的每个叶子节点上嵌入神经网络而形成感知机树(Perception tree)



六、应用场景

决策树算法的应用:
适用于需要“决策”的领域,如商业决策、管理决策等等,应用领域非常广。

实例:
Kinect是微软公司在2010年6月发布的XBOX 360体感交互外设,这是一种较为新颖的人机交互显示技术,使用者在Kinect镜头前所做的动作将实时呈现在游戏的画面中。为了实现这一效果,Kinect正是利用决策树分类算法,对镜头捕获的玩家行为图像进行分类,从而实现了效果良好的人及行为识别功能。

七、总结

决策树的含义: 类似于if-then-else的树形判断(二叉树则没有then)

决策树构成: 决策节点 + 叶子节点

决策树流程:
1.构造:
决策树算法:基于信息熵:ID3、C4.5;基于基尼值:基尼指数

2.剪枝:

类别

优点

缺点

预剪枝

去除无关分支降低过拟合;减少训练和测试时间开销

基于贪心算法,可能去除无关分支下的有关分支而导致欠拟合

后剪枝

保留更多分支,减少欠拟合风险

需要先生成决策树,训练开销较大

连续值处理: 连续值划分成区间值,每个区间值为一个类别,从而又形成离散值。

缺失值处理: 按照缺失数据占整个数据的比例来更改算法。

多变量决策树: 决策节点的判断标准由单个属性变成多个属性。

决策树算法的训练流程:

Train DecisionTree(D)      							//D为本节点的训练样本集
if(样本集无法再划分 | 达到最大树深度 | D的样本数小于指定阈值)
	leafNode = CalcLeafValue(D);					//无法再分裂,设置为叶子节点,计算其值
	return leafNode;								//返回创建的叶子节点
else
	(split, D1, D2) = FindBestSplit(D)				//寻找最佳分裂split,将训练集D分为D1额D2
	node = CreateTreeNode();						//创建当前节点
	node -> split=split;							//设置节点的分裂规则
	FinSurrogatesplit(D);							//寻找替代分裂,加入到节点的分裂规则列表
	node -> leftChild = TrainDecisionTree(D1);		//递归训练左子树
	node -> rightChild = TrainDecisionTree(D2);		//递归训练右子树
	return node;									//返回训练的树节点
end if

决策树分类算法的优点

  • 分类逻辑清晰易懂
  • 采用树形结构进行分类,便于可视化,在需要演示和讲解的场景下可直观展现决策树整个分类过程

决策树分类算法的缺点

  • 最大的问题就是容易过拟合,减少和解决这个问题是该算法和改进的类似算法的热门研究方向。目前认为最有效解决这个问题的方法是剪枝操作。
  • 特征维度之间若存在关联,则可能对预测结果有影响。ID3算法、C4.5算法和CART算法都是用选择了统计学指标作为特征维度,这些指标都有一个默认的假设,认为特征维度之间都是彼此独立的。


八、案例

目前sklearn的决策树模型只实现了ID3和CART。

默认情况下,sklearn中决策树算法默认使用gini作为标准(criterion),也就是使用CART决策树,同时也是二叉树。而指定标准为entropy时,则是使用ID3决策树,实际与C4.5差别不大。

0、决策树可视化操作

决策树可视化后更加直观易懂,但是需要使用训练好的决策树来进行可视化。可以使用 Graphviz 可视化工具帮我们把决策树呈现出来。

下载地址:http://www.graphviz.org/download/

需要先在电脑上安装graphviz添加到环境变量 PATH 中,再在python环境中pip install graphviz安装插件

from sklearn.tree import export_graphviz
import graphviz
# clf为决策树参数名字
dot_data = export_graphviz(clf, out_file=None)
graph = graphviz.Source(dot_data)
# 同级目录生成PDF文件
graph.render('iris_clf')
# 直接打印输出决策树
graph

Easy Rules决策树_信息熵_131


1、决策树之分类树 - 鸢尾花数据集分类

目标:使用自带的iris数据集,构造一棵分类树

导包:

# 导入决策树分类算法
from sklearn.tree import DecisionTreeClassifier
# 导入分离训练集和测试集的方法
from sklearn.model_selection import train_test_split
# 导入评估分类结果准确率的方法
from sklearn.metrics import accuracy_score
# 导入数据集
from sklearn.datasets import load_iris

数据探索与准备:

# 读取数据集
iris = load_iris()
# 获取特征和标签
features = iris.data
labels = iris.target

# 划分总数据集为测试集和训练集,训练集占比33%
train_features, test_features, train_lables, test_labels = train_test_split(features, labels, test_size=0.33, random_state=1)

构造决策树 - CRAT分类树:

# 创建决策树对象
clf = DecisionTreeClassifier(criterion='gini')
# 训练决策树
clf = clf.fit(train_features, train_labels)

应用决策树 - 预测结果:

# 预测测试集的标签
test_labels_predict = clf.predict(test_features)
# 将预测后的结果与实际结果进行对比
score = accuracy_score(test_labels, test_labels_predict)
# 打印输出
print("CART分类树的准确率 %.4lf" % score)

# 结果
CART分类树的准确率为: 0.9600



2、决策树之回归树 - 波士顿房价数据集回归

目标:使用自带的Boston数据集,构造一棵回归树

导包:

# 导入回归树
from sklearn.tree import DecisionTreeRegressor
# 导入训练集和测试集划分方法
from sklearn.mode_selection import train_test_split
# 导入评估回归结果准确率的方法
# 绝对值偏差,二乘偏差
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.metrics import r2_score

# 导入数据集
from sklearn.datasets import load_boston

数据探索与准备:

# 导入数据集
boston = load_boston()
# 选取特征属性和标签
features = boston.data
prices = boston.target

# 划分数据集合测试集
train_features, test_features, train_prices, test_prices = train_test_split(features, prices, test_size=0.33)

构造决策树 - CART回归树:

# 创建决策树对象
dtr= DecisionTreeRegressor()
# 训练决策树
dtr= dtr.fit(train_features, train_prices)

应用决策树 - 预测结果

# 预测结果
test_prices_predict = dtr.predict(test_features)

# 结果评分
print("回归树二乘偏差均值:", mean_squared_error(test_prices, test_prices_predict))
print("回归树绝对值偏差均值:", mean_absolute_error(test_prices, test_prices_predict)

# 结果
回归树二乘偏差均值: 32.30946107784431
回归树绝对值偏差均值: 3.525748502994012



3、泰坦尼克号生存者预测

 

3.1、项目分析

(1)样本的分析

Titanic数据集有两个:
train.csv - 包含特征信息和是否存活的标签
test.csv - 只包含特征信息

结果为判断测试集test.csv中的乘客是否存活,使用分类树

样本的信息为:

Easy Rules决策树_剪枝_132


train.csv

Easy Rules决策树_剪枝_133


test.csv

Easy Rules决策树_决策树_134


(2)项目流程

Easy Rules决策树_信息熵_135


准备阶段: 我们首先需要对训练集、测试集的数据进行探索,分析数据质量,并对数据进行清洗,然后通过特征选择对数据进行降维,方便后续分类运算;

分类阶段: 首先通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集。然后我们对决策树分类器的准确性进行分析,并对决策树模型进行可视化。

(3)决策树的函数使用

使用ID3分类树,它的函数及参数为:

DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=None,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

各参数如下,一般除了criterion进行设置外,不需要进行其他手动设定,默认就行,即不会限制决策树的最大深度,不限制叶子节点数,所有分类权重都相等等等。

Easy Rules决策树_剪枝_136


构造完决策树(分类器)后,使用fit方法对分类器进行拟合训练,然后使用predict方法对测试集进行预测,得到预测的结果后,可以使用score方法对分类器的准确率进行评估。下面是对应的函数方法:

Easy Rules决策树_Easy Rules决策树_137


3.2、项目实践

按照项目的流程来完成整个项目

(1)数据获取
# 导入文件读取和分析包-pandas
import pandas as pd
# 读取训练集和测试集文件
test = pd.read_csv('./test.csv')
train = pd.read_csv('./train.csv')



(2)数据探索

数据探索对分类器没实质性的作用,但有助于对数据特性的了解,帮我我们做数据清洗和特征选择。

一些用于数据探索的函数:

函数

解释

info()

数据表的基本情况:行数、列数、每列的数据类型、数据完整度等

describe()

数据表的统计情况:总数、平均值、标准差、最小值、最大值、上四分位值、下四分位值等等

describe(include=[“O”])

查看字符串(非数字)类型数据的整体情况

head(n=5)

查看最前n行数据

tail(n=5)

查看最后n行数据

# 展示列属性名
display(test.columns, train.columns)

# 结果
Index(['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch',
       'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
# 查看数据整体信息
display(train.info(), test.info())

# 结果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
None
None
# 查看数据的统计情况
train.describe()

Easy Rules决策树_决策树_138

# 查看字符串类型数据
train.describe(include=['O'])

Easy Rules决策树_决策树_139

# 查看训练集前几个样本
train.head(n=5)

Easy Rules决策树_Easy Rules决策树_140

# 查看训练集后几个样本
train.tail(n=5)

Easy Rules决策树_信息熵_141


(3)数据清洗

通过数据探索,发现Age和Fare字段有所缺失。
补齐空值:
Age是年龄字段,是数值型,可以通过平均值补齐。
Fare是票价字段,是数值型,可以通过平均值补齐。

# 补齐年龄
train['Age'].fillna(train['Age'].mean(), inplace=True)
test['Age'].fillna(test['Age'].mean(), inplace=True)

# 补齐标价
test['Fare'].fillna(test['Fare'].mean(), inplace=True)

Cabin表示船舱,缺失值过多,达到77%和78%,无法补齐。

Embarked为登陆港口,含有少量缺失值,使用最多值来填充

# S港口最多,占到了73%,所以用S来填充
train['Embarked'].value_counts()

# 结果
S    644
C    168
Q     77
Name: Embarked, dtype: int64
train['Embarked'].fillna('S', inplace=True)



(4)特征选择

特征选择是分类器的关键,选择不同则得到的分类器也不同。但并不是所有属性都对分类结果有作用,所以需要选择对结果关联性大的属性特征。

通过数据探索可以发现:
PassengerId 为乘客编号,对分类没有作用,可以放弃;
Name 为乘客姓名,对分类没有作用,可以放弃;
Cabin 字段缺失值太多,可以放弃;Ticket 字段为船票号码,杂乱无章且无规律,可以放弃。
其余的字段包括:Pclass、Sex、Age、SibSp、Parch 和 Fare,这些属性分别表示了乘客的船票等级、性别、年龄、亲戚数量以及船票价格,可能会和乘客的生存预测分类有关系。具体是什么关系,我们可以交给分类器来处理。

因此我们先将 Pclass、Sex、Age 等这些其余的字段作特征,放到特征向量 features 里。

# 特征选择
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train[features]
train_labels = train['Survived']
test_features = test[features]

某些非数字型特征,如Sex,有male和female两种字符串;Embarked有S、C 和Q 三种字符串,我们使用Sklearn特征选择中的 DictVectorizer 类,用它将可以处理符号化的对象,将符号转成数字 0/1 进行表示。将Sex转变成Sex=male和Sex=female,使用0和1进行表示。

具体方法如下:

# 将符号转成 0 / 1 进行表示
from sklearn.feature_extraction import DictVectorizer
dvec = DictVectorizer(sparse=False)
train_features = dvec.fit_transform(train_features.to_dict(orient='record'))

fit_transform 函数可以将特征向量转化为特征值矩阵,转化后的

dvec.feature_names_

# 结果
['Age',
 'Embarked=C',
 'Embarked=Q',
 'Embarked=S',
 'Fare',
 'Parch',
 'Pclass',
 'Sex=female',
 'Sex=male',
 'SibSp']

原本是一列的 Embarked,变成了“Embarked=C”“Embarked=Q”“Embarked=S”三列。Sex 列变成了“Sex=female”“Sex=male”两列。

train_features 特征矩阵就成了包括 10 个特征值(列),以及 891 个样本(行),即 891 行,10 列的特征矩阵。

(5)决策树模型

现在我们使用 ID3 算法,即在创建 DecisionTreeClassifier 时,设置 criterion=‘entropy’,然后使用 fit 进行训练,将特征值矩阵和分类标识结果作为参数传入,得到决策树分类器。

# 导入分类树
from sklearn.tree import DecisionTreeClassifier
# 构造ID3决策树
clf = DecisionTreeClassifier(criterion='entropy')
# 训练决策树
clf.fit(train_features, train_labels)

# 结果
DecisionTreeClassifier(ccp_alpha=0.0, class_weight=None, criterion='entropy',
                       max_depth=None, max_features=None, max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, presort='deprecated',
                       random_state=None, splitter='best')



(6)模型测试&评估
test_features = dvec.transform(test_features.to_dict(orient='record'))
# 决策树预测
pred_labels = clf.predict(test_features)

由于测试集文件test.csv中缺少标签,即缺少真实结果,所以无法比较结果的准确率。只能使用训练集中数据进行模型评估,可以使用决策树自带的 score 函数计算下得到的结果:

# 得到决策树准确率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score 的准确率为:%.4lf'% acc_decision_tree)

# 结果

score准确率为 0.9820

因为我们没有测试集的实际结果,因此无法用测试集的预测结果与实际结果做对比。如果我们使用 score 函数对训练集的准确率进行统计,正确率会接近于 100%(如上结果为 98.2%),无法对分类器的在实际环境下做准确率的评估。

# K折交叉验证
import numpy as np
from sklearn.model_selection import cross_val_score
print(u'cross_val_score准确率为 %.4lf'% np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))

这里可以使用 K 折交叉验证的方式,交叉验证是一种常用的验证分类准确率的方法,原理是拿出大部分样本进行训练,少量的用于分类器的验证。

K 折交叉验证,就是做 K 次交叉验证,每次选取 K 分之一的数据作为验证,其余作为训练。轮流 K 次,取平均值。

K 折交叉验证的原理是这样的:
1.将数据集平均分割成 K 个等份;
2.使用 1 份数据作为测试数据,其余作为训练数据;
3.计算测试准确率;
4.使用不同的测试集,重复 2、3 步骤。

在 sklearn 的 model_selection 模型选择中提供了 cross_val_score 函数。cross_val_score 函数中的参数 cv 代表对原始数据划分成多少份,也就是我们的 K 值,一般建议 K 值取 10,因此我们可以设置 CV=10,我们可以对比下 score 和 cross_val_score 两种函数的正确率的评估结果:

cross_val_score(clf, train_features, train_labels, cv=10)

# 结果
array([0.67777778, 0.7752809 , 0.69662921, 0.80898876, 0.84269663,
       0.71910112, 0.80898876, 0.71910112, 0.83146067, 0.80898876])



(7)可视化决策树
from sklearn.tree import export_graphviz
import graphviz
dot_data = export_graphviz(clf, out_file=None)
graph = graphviz.Source(dot_data)
graph

Easy Rules决策树_信息熵_142