决策树是一款非常有地位的分类算法。任意一场数据分析比赛,名列前茅的除了深度学习,剩下的机器学习算法中多半是选用XGBoost算法或者Lightbgm算法。然而这两个算法都是基于决策树分类算法而发明出来的。
决策树,也称“判定树” - 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算法,而它只支持二叉树,如下图所示。
当然,决策树也支持其他算法,如ID3、C4.5等等,他们既支持二叉树也支持多叉树。这些算法的具体作用,在后面的章节介绍。
通过以上分析可以观察出决策树的两个特征:规则(或判决条件,if 的内容)和嵌套(else层层递进)。所以,我们可以定义:决策树表示利用一组嵌套的规则对数据集进行分类的算法。(上面例子将数据集分成可以和不可以贷款的对象)
但由于机器学习不同于普通的编程,它是模型自动根据数据集来定义规则。如果以前有使用过模型进行实践的应该不难理解,只需要调用决策树算法包中的几句简单的函数便可以轻松得到结果,而无需自己一行行码。
所以,有人定义为:决策树算法是根据给定的数据集归纳出分类规则,并采用自顶向下的递归划分 (Recursive Partitoning) 的方式,并以树的形式展现出来。
节点是树的主体部分,它一般被分为两类:决策节点和叶子节点。
决策节点:通过条件判断而进行分支选择的节点。如:将某个样本中的属性值(特征值)与决策节点上的值进行比较,从而判断它的流向。
叶子节点:没有子节点的节点,表示最终的决策结果。下图中,叶子节点的值有两种,即可以贷款和不能贷款。
如图,决策树所有的内部节点(决策节点、非叶子结点)为矩形,叶子节点为椭圆形。
决策树的深度:定义为所有节点的最大层次数。
决策树具有一定的层次结构,根节点的层次数定为0,从下面开始每一层子节点层次数+1。根据上图来判断树的深度,则最大深度为3,说明要得到一个决策结果,最多经过3次判定。
如何像上面一个例子中展现的图一样实现一个的决策树?
完整决策树的实现需要经历两个阶段:构造和剪枝。
通过决策树的组成得知,决策树主要由决策节点和叶子节点构成。
于是产生了两个问题:
问题一:决策节点和叶子节点从何而来?
问题二:决策节点如何排布?
以前面银行借贷的决策树为例,对于问题一,那些if判断条件,以及判断结果是依据什么而来的?,对于问题二,为什么年薪的优先级最高,房产其次,最后是家庭情况,这样的排布有什么道理呢?
决策树主要由节点构成,而节点由决策条件和决策结果构成,而决策条件和结果的依据来源于样本的属性特征。由此可知,决策树的构造为:选择样本特征作为节点的过程。那么样本的特征为决策树节点的来源。
样本的属性特征
决策树的构造离不开样本的属性特征,只有知道研究的对象具备什么特性,才能构造出合理的决策树。这里列举了数据集的样本。
ID | 年薪 | 是否有房产 | 是否有子女 | 类别 |
---|---|---|---|---|
1 | 350000 | 无 | 无 | 可以借贷 |
2 | 100000 | 无 | 无 | 不能借贷 |
第二张决策树图中每一个if条件是根据第一张样本图中的每一列属性来进行构造的,样本中的每一列称作属性(Attribute) 或特征或特征维度。(多个属性被称作属性集或者特征维度集)
对于特征也有不同的分类。年收入的结果与其他三项有明显区别,它为数值型,可以比较大小,一般为整数或者实数,这种列属性则称为数值特征。而是否有房产和子女则不能比较大小,只有有和没有两种情况,这种列则称为类别特征。
决策节点如何排布?如何构造出最优属性划分?通过纯度这个指标,一般而言,我们希望每次判别节点能将样本进行最低错误率的划分,该节点正确率也就越高,同时也表明纯度也越高,该节点也就越适合优先排布。
纯度是衡量节点优劣的判断指标,纯度越大的决策树越优,结果的分歧越小。
纯度的计算需要信息熵或者基尼值,他们可以用来度量某样本的纯度。
每一层决策节点都可以将样本划分为两个子集,而每一次划分都不能保证完全将样本划分分类正确,因为总有一些决策树规则之外的样本,在进行划分的时候会出现错误,比如ID=5的样本,在有子女的情况下,应该是可以借贷的,但是类别是不能借贷。
这可能是样本的属性不完全,导致决策树规则生成不完整,从而使分类出现异常,但是,世界上不可找到最全的样本,也就不可能生成最完美的决策树模型,我们能做的就是在有限的条件下,最小化误差。
以“年薪>300000”的属性为判别节点进行划分,值为“是”时,类别全部为“可以借贷”,说明纯度很高,而值为“否”时,类别既有“可以借贷”也有“不能借贷”,表明纯度相对较低。
ID | 年薪>300000 | 是否有房产 | 是否有子女 | 类别 |
---|---|---|---|---|
1 | 是 | 无 | 无 | 可以借贷 |
2 | 否 | 无 | 无 | 不能借贷 |
3 | 否 | 有 | 无 | 可以借贷 |
4 | 是 | 无 | 有 | 可以借贷 |
5 | 否 | 无 | 有 | 不能借贷 |
纯度表示结果分歧大小,决策树的构造过程也可以叫做寻找纯净划分的过程。
信息熵源于热力学中的熵(Entropy),最早由德国物理学家克劳修斯提出,用来表述某个个体的混乱程度。
在信息论中,随机离散事件出现的概率存在着不确定性。为了衡量这种信息的不确定性,美国数学家信息学之父,香农引入了信息熵的概念。它代表了一个给定数据集中的不确定性或者随机性程度的度量。
比如:当一个数据集中的记录全部为同一类时,则没有不确定性,此时熵为0.因此我们可以把熵看成是信息的数学期望。
若要求出熵的大小,则先要计算信息:
设某个事物具有N种相互独立的可能结果,则信息的定义为:
l ( x i ) = − l o g 2 P ( x i ) l(x_i) = - log_2P(x_i) l(xi)=−log2P(xi)
之所以是以2为底的对数,可能和信息中的最小单位比特有关 (一个比特只有0或1,两种状态),其中 x i x_i xi 表示第 i i i 个分类, p ( x i ) p(x_i) p(xi) 表示第 i i i 个分类的概率函数, l o g 2 X log_2X log2X 中 X X X 为属于0-1之间时为负数,所以需要加上负号来回正。
并且每个概率之和为1:
∑ i = 1 n P ( x i ) = 1 \displaystyle \sum^{n}_{i = 1}P(x_i) = 1 i=1∑nP(xi)=1
于是,计算信息熵的数学公式可以表示为:
E n t r o p y ( t ) = Entropy(t)= Entropy(t)= 求和( 每种属性概率 * 每种属性的信息 )
= ∑ i = 1 n [ P ( x i ) × − l o g 2 P ( x i ) ] =\displaystyle \sum^{n}_{i = 1}{[P({x_i})} \times-{log_{2}{P({x_i}})]} =i=1∑n[P(xi)×−log2P(xi)]
= − ∑ i = 1 n P ( x i ) l o g 2 P ( x i ) =-\displaystyle \sum^{n}_{i = 1}{P({x_i})} {log_{2}{P({x_i}})} =−i=1∑nP(xi)log2P(xi)
x i = k t x_i=\frac{k}{t} xi=tk 且 P ( k t ) P(\frac{k}{t}) P(tk)表示以节点 t t t 为分类 k k k 的概率。
对于一个简单的二元分类,此时n=2,那么其整体熵为:
E n t r o p y ( t ) = − P ( x 1 ) l o g 2 P ( x 1 ) − P ( x 2 ) l o g 2 P ( x 2 ) Entropy(t) = -P(x_1)log_2P(x_1) - P(x_2)log_2P(x_2) Entropy(t)=−P(x1)log2P(x1)−P(x2)log2P(x2)
该公式表示一种度量帮我们反映出这个信息的不确定度。当不确定性越大时,它所包含的信息量也就也大,信息熵也就越高。
算法会根据所有样本-信息熵的变化 (信息增益) 来选择最佳分类。因而信息熵就是决策树方法中分支产生的衡量标准之一。
基尼值同样也是一种度量节点纯度的方法,与信息熵计算公式十分相似
G i n i ( D ) = 1 − ∑ i = 1 N [ P ( x i ) ] 2 Gini(D) = 1-\displaystyle \sum^{N}_{i=1}{[P(x_i)]^2} Gini(D)=1−i=1∑N[P(xi)]2
同样以银行借贷为例子,假设某个样本集中有12个客户,在某次决策树的构造过程中,对该样本集按照属性“是否能借贷”进行二元划分来计算纯度值,划分成D1和D2,其中D1有5个可以借贷的客户,1个不能借贷的客户;D2有一半可以借贷和不能借贷。
结果如下图,我们的目的是计算各个节点的信息熵和基尼值。
根据公式可得:
信息熵:
D: Entropy(D) = ( − 4 12 ) × l o g 2 4 12 + ( − 8 12 ) × l o g 2 8 12 = 0.53 + 0.39 = 0.92 (-\frac{4}{12}) \times log_2\frac{4}{12}+(-\frac{8}{12}) \times log_2\frac{8}{12} = 0.53 + 0.39 = 0.92 (−124)×log2124+(−128)×log2128=0.53+0.39=0.92
D1:Entropy(D1) = − 1 6 × l o g 2 1 6 + − 5 6 × l o g 2 5 6 = 0.43 + 0.22 = 0.65 -\frac{1}{6} \times log_2\frac{1}{6}+-\frac{5}{6} \times log_2\frac{5}{6} = 0.43 + 0.22 = 0.65 −61×log261+−65×log265=0.43+0.22=0.65
D2:Entropy(D2) = − 3 6 × l o g 2 3 6 + − 3 6 × l o g 2 3 6 = 0.5 + 0.5 = 1 -\frac{3}{6} \times log_2\frac{3}{6}+-\frac{3}{6} \times log_2\frac{3}{6} = 0.5 + 0.5 = 1 −63×log263+−63×log263=0.5+0.5=1
基尼值:
D:Gini(D) = 1 − [ ( 4 12 ) 2 + ( 8 12 ) 2 ] 1 - [ (\frac{4}{12})^2 +(\frac{8}{12})^2 ] 1−[(124)2+(128)2]= 0.44
D1:Gini(D1) = 1 − [ ( 1 6 ) 2 + ( 5 6 ) 2 ] 1 - [ (\frac{1}{6})^2 +(\frac{5}{6})^2 ] 1−[(61)2+(65)2] = 0.28
D2:Gini(D2) = 1 − [ ( 3 6 ) 2 + ( 3 6 ) 2 ] 1 - [ (\frac{3}{6})^2 +(\frac{3}{6})^2 ] 1−[(63)2+(63)2] = 0.5
无论是信息熵还是基尼值,值越大,纯度越低。当所有类型的样本均匀混合时,如D2,信息熵和基尼值最大,纯度最低。
这种纯度的度量是基于自身单个节点,而对决策树纯度的度量是包括一个父节点和几个子节点,节点之间有相互关联。
决策树的构建会基于前面纯度的度量,有三种不同决策树节点纯度的度量规则:信息增益、信息增益率和基尼指数。基于决策树的信息度量的不同方式,将决策树划分为三种最著名的算法,它们分别是:ID3、C4.5和CART。
ID - Iterative Dichotomiser 迭代二分器的简称
ID3算法通过比较样本划分前后的信息熵的增减来衡量节点的纯度,即该节点的提纯能力的好坏。
信息增益 = = = 父节点(划分前的样本)的信息熵 − - − 所有子节点(按照特征a划分后的子样本)的信息熵的概率和(归一化信息熵)
在计算的过程中:
G a i n ( D , a ) = E n t r o p y ( D ) − ∑ i = 1 k ( ∣ D i ∣ ∣ D ∣ ) E n t r o p y ( D i ) Gain(D, a) = Entropy(D)-\displaystyle \sum^{k}_{i = 1}{(\frac{|D_i|}{|D|})}{Entropy(D_i)} Gain(D,a)=Entropy(D)−i=1∑k(∣D∣∣Di∣)Entropy(Di)
通过计算和比较不同属性的信息增益,值越大的,则提纯的效果越好,则优先级越高。
题目内容同简单案例1,按照某方式划分后得到一个决策树,我们的目的是计算父节点D的信息增益
以及简单案例1中对各个节点信息熵的计算,可得:
E n t r o p y ( D ) = 0.92 Entropy(D) = 0.92 Entropy(D)=0.92
E n t r o p y ( D 1 ) = 0.65 Entropy(D_1) = 0.65 Entropy(D1)=0.65
E n t r o p y ( D 2 ) = 1 Entropy(D_2) = 1 Entropy(D2)=1
根据信息增益的公式,将父节点信息熵减去子节点信息熵的占比,可得:
G a i n ( D , a ) = E n t r o p y ( D ) − [ ( 6 12 ) × E n t r o p y ( D 1 ) + ( 6 12 ) × E n t r o p y ( D 2 ) ] Gain(D, a)=Entropy(D) - [ (\frac{6}{12})\times Entropy(D_1) + (\frac{6}{12})\times Entropy(D_2) ] Gain(D,a)=Entropy(D)−[(126)×Entropy(D1)+(126)×Entropy(D2)]
= 0.92 − [ ( 6 12 ) × 0.65 + ( 6 12 ) × 1 ] =0.92 - [ (\frac{6}{12})\times 0.65 + (\frac{6}{12})\times 1 ] =0.92−[(126)×0.65+(126)×1]
= 0.92 − 0.825 =0.92 - 0.825 =0.92−0.825
= 0.095 =0.095 =0.095
对父节点不同的划分,计算出的结果,信息增益越大,说明纯度的增加越大,优先级越高。
ID3算法有个特点:倾向于选择,值的种类较多的属性为高优先级节点。因为节点是按照信息增益的大小来进行划分与选择,节点属性值的种类越多,越容易将不同样本清晰地划分开来,子节点的信息熵越容易低,纯度越容易高,信息增益越容易大。
但是像属性"ID"这样每一行样本都具有不同种类,划分时,每一个ID为一个子节点,纯度最大,但却是无关属性,对后面的样本无法进行有效的预测。
像ID这样类似的、却不易观察的属性有一些,不过,这种缺陷发生的概率很小,大部分情况下都能生成效果较好的决策树。但是为了追求完美,改进版的ID3算法 - C4.5算法应运而生。
为了减少样本某个属性的值的种类较多所带来的不利影响,C4.5算法在信息增益的基础上求得信息增益率来选择最优属性划分。
公式:
G a i n _ r a t e ( A ) = G a i n ( D , a ) I V ( a ) Gain\_rate(A) = \frac{Gain(D, a)}{IV(a)} Gain_rate(A)=IV(a)Gain(D,a)
G a i n ( D , a ) = E n t r o p y ( D ) − ∑ i = 1 k ( ∣ D i ∣ ∣ D ∣ ) E n t r o p y ( D i ) {Gain(D, a)} = Entropy(D)-\displaystyle \sum^{k}_{i = 1}{(\frac{|D_i|}{|D|})}{Entropy(D_i)} Gain(D,a)=Entropy(D)−i=1∑k(∣D∣∣Di∣)Entropy(Di)
I V ( a ) = − ∑ i = 1 k ( ∣ D i ∣ ∣ D ∣ ) l o g 2 ( ∣ D i ∣ ∣ D ∣ ) {IV(a)} = -\displaystyle \sum^{k}_{i = 1}{(\frac{|D_i|}{|D|})}{log_{2}({\frac{|D_i|}{|D|})}} IV(a)=−i=1∑k(∣D∣∣Di∣)log2(∣D∣∣Di∣)
题目内容同简单案例1,我们的目的是计算父节点D的信息增益率
直接计算固有值IV
I V ( a ) = − ( 6 12 ) l o g 2 ( 6 12 ) − ( 6 12 ) l o g 2 ( 6 12 ) IV(a) = -(\frac{6}{12})log_{2} (\frac{6}{12})-(\frac{6}{12})log_{2} (\frac{6}{12}) IV(a)=−(126)log2(126)−(126)log2(126)
= 1 = 1 =1
于是:
G a i n _ r a t e ( A ) = G a i n ( D , a ) I V ( a ) Gain\_rate(A) = \frac{Gain(D, a)}{IV(a)} Gain_rate(A)=IV(a)Gain(D,a)
= 0.095 1 = \frac{0.095}{1} =10.095
= 0.095 = 0.095 =0.095
- 采用信息增益率
- 采用悲观剪枝
- 离散化处理连续属性
- 处理缺失值
Cart算法全称:Classification And Regression Tree - 分类回归树。
ID3与C4.5算法与信息熵有关,而基尼指数与基尼值有关。
基尼指数用来衡量决策树纯度的一种指标,而经济学中有基尼系数(Gini index、Gini Coefficient),是一个用来衡量一个国家或地区居民收入差距的常用指标。
正如CART的名字,它既可以做分类树也可以做回归树。如何区别两者呢?
假设给定下列数据集,用来构造决策树。
构造完成后的树,用来预测某个测试集的“是否能借贷”属性时,为分类树。分类树用来处理离散型数据,也就是数据种类有限的数据,它输出的是样本的类别。
用该树预测某个测试集的“年龄”属性时,为回归树。回归树用来处理连续型数据,也就是数据在某段区间内的取值,输出是一个数值。
ID | 年薪>300000 | 是否有房产 | 是否有子女 | 是否能借贷 | 年龄 |
---|---|---|---|---|---|
1 | 是 | 无 | 无 | 可以借贷 | 30 |
2 | 否 | 无 | 无 | 不能借贷 | 20 |
3 | 否 | 有 | 无 | 可以借贷 | 26 |
4 | 是 | 无 | 有 | 可以借贷 | 34 |
5 | 否 | 无 | 有 | 不能借贷 | 18 |
G i n i _ i n d e x ( D , A ) = ∑ i = 1 k ∣ D i ∣ ∣ D ∣ ( G i n i ( D i ) ) Gini\_index(D,A) = \displaystyle\sum^{k}_{i=1}{\frac{|Di|}{|D|}}({Gini(D_i)}) Gini_index(D,A)=i=1∑k∣D∣∣Di∣(Gini(Di))
假设某个样本集中有12个客户,在某次决策树的构造过程中,对该样本集按照属性“是否能借贷”进行二元划分来计算纯度值,划分成D1和D2,其中D1有6个可以借贷的客户,D2有一半可以借贷和不能借贷。
结果如下图,我们的目的是计算根节点D的基尼指数。
通过观察基尼指数的公式,首先计算子样本D1和D2的基尼值,即 G i n i ( D i ) Gini(D_i) Gini(Di)
再通过公式,可以直接计算,每个子样本各占总样本一半比例
Gini_index(D) = 6 12 × G i n i ( D 1 ) + 6 12 × G i n i ( D 2 ) \frac{6}{12} \times Gini(D_1) + \frac{6}{12} \times Gini(D_2) 126×Gini(D1)+126×Gini(D2)
= 6 12 × 0.28 + 6 12 × 0.5 = 0.39 \frac{6}{12} \times0.28 + \frac{6}{12} \times 0.5 = 0.39 126×0.28+126×0.5=0.39
一般情况下,为了尽最大可能正确地分类训练样本,所以决策树会将样本的所有属性当做标准(标答)来进行构造。
但训练集有时会出现“假性关联”问题。训练集的样本属性真的是标准吗?真的可以反映其他数据集或者测试样本的属性吗?当然不是,这项标准可能只适合该训练集本身而已,所以会导致用训练样本得出的结果过好而出现过拟合现象,过拟合的决策树使用其他数据集测试时会突然出现结果不好的现象。
此时,就需要去掉决策树的部分分支,即去掉样本的部分属性,去掉那些与样本整体分类实际上不存在关联的属性,以此来降低过拟合的风险,提高模型的泛化能力。
如何对决策树进行剪枝?同决策树算法一样,剪枝的算法也有很多款,但根据剪枝的触发时机的不同,基本分为两种:预剪枝和后剪枝
如何评估剪枝前后的性能呢?通过使用测试集对划分前后的精准度进行比较。
预剪枝:在决策树生成的过程中,每个决策节点原本是按照信息增益、信息增益率或者基尼指数等纯度指标,按照值越大优先级越高来排布节点。由于预剪枝操作,所以对每个节点在划分之前要对节点进行是否剪枝判断,如何判断?即:使用测试集按照该节点的划分规则得出结果。若验证集精度提升,则不进行裁剪,划分得以确定;若验证集精度不变或者下降,则进行裁剪,并将当前节点标记为叶子节点。
限定树的高度、限定节点的训练样本数、限定划分带来的纯度的提升的阈值
预剪枝使得决策树很多相关性不大的分支都没有展开,这不仅仅降低了过拟合的风险,还显著减少了决策树的训练时间开销和测试时间开销。
但另一方面,有些分支的当前划分虽不能提升泛化能力,甚至可能导致泛化能力暂时下降,但是在其基础上进行的后续划分却有可能提高性能。预剪枝基于“贪心”本质禁止这些分支展开,给预剪枝决策树带来了欠拟合的风险。
后剪枝:已经通过训练集生成一颗决策树,然后自底向上地对决策节点(非叶子结点)用测试集进行考察,若将该节点对应的子树替换为叶子节点能提升测试集的精确度,则将该子树替换成叶子节点,该决策树泛化能力提升。
降低错误剪枝 - Reduced-Error Pruning, REP
悲观错误剪枝 - Pesimistic-Error Pruning, PEP
代价-复杂度剪枝 - Cost-Complexity Pruning, CCP
C4.5采用PEP,CART采用CCP
后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情况下,后剪枝决策树的欠拟合风险很小,泛化能力往往优于预剪枝决策树。
但是后剪枝过程是在生成完决策树之后进行的,并且要自底向上地对树中的所有决策节点进行逐一考察,因此其训练时间开销比未剪枝的决策树和预剪枝决策树都要大得多。
决策树一般为分类树,用来处理具有离散属性的样本,但是现实项目中,会遇到很多连续属性,对它的处理方法有所不同。
前面案例中的属性都是通过离散属性来生成决策树,比如“是否贷款”,结果通常都是类别判定的有限集合,通常构造的的决策树为分类树。
然而,有些属性比如说前面案例样本中的“年龄”属性,值为:22、30和27等等,基本只要是实数就行,结果通常是实数型的无限集合,这样的属性为连续属性,构造的决策树为回归树。
原来划分离散属性时,如“是否贷款”,可以直接按照属性值来对节点进行划分,如:可以贷款与不能贷款。
然是对于连续属性,如“年龄”,则不可能也按照属性值来对节点进行划分的规则,如:1, … …, 100。没办法通过某些值进行划分,种类过多且数量密集,但是可以将这些值划分成几个不同类型的区间,于是连续属性离散化技术便应运而生。
连续属性离散化技术有很多,但是最简单的策略则是二分法(bi-partition),这也是C4.5决策树算法中采用的机制。
给定样本集D和连续属性a,假设a在D中出现了n个不同的取值,将这些值从小到大进行排序,记为 { a 1 , a 2 , . . . , a n } \{ a^1, a^2, ..., a^n \} {a1,a2,...,an}。
划分点选取公式为:
T a = { a i + a i + 1 2 ∣ 1 ⩽ i ⩽ n − 1 } T_a = \{ \frac{a^i + a^{i+1} }{2} | 1\leqslant i \leqslant n-1 \} Ta={2ai+ai+1∣1⩽i⩽n−1}
拥有n-1个划分节点后,需要选取最佳划分点,则又可以像离散属性那样考察这些划分点。比如计算信息增益:
G a i n ( D , a ) = max t ∈ T a G a i n ( D , a , t ) = max t ∈ T a E n t r o p y ( D ) − ∑ λ ∈ { − , + } ∣ D t λ ∣ ∣ D ∣ E n t r o p y ( D t λ ) \begin{aligned} Gain(D, a) &= \max_{t \in T_a} \ Gain(D, a, t) \\ &=\max_{t \in T_a} \ Entropy(D) - \displaystyle\sum_{\lambda \in \{ -, + \}}{\frac{|D^{\lambda}_t|}{|D|} \, Entropy(D^{\lambda}_t)} \end{aligned} Gain(D,a)=t∈Tamax Gain(D,a,t)=t∈Tamax Entropy(D)−λ∈{−,+}∑∣D∣∣Dtλ∣Entropy(Dtλ)
西瓜书中的一个案例改编而来,假设对于某种水果有一批样本,它们的密度和含糖量以及质量分别为:
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个差样本
E n t r o p y ( D ) = ( − 8 17 × l o g 2 8 17 ) + − 9 17 × l o g 2 9 17 ) Entropy(D) = (-\frac{8}{17} \times log_2\frac{8}{17}) + -\frac{9}{17} \times log_2\frac{9}{17}) Entropy(D)=(−178×log2178)+−179×log2179) = 0.998
划分后,分成两个子集,按照划分点来一个个计算:
由于篇幅过大,不一一计算,经过验证,第四个中位数为最佳划分点,具体计算如下
a 4 a_4 a4:第四个和第五个点中间划分开,统计结果如下:
左右子集 | good | bad |
---|---|---|
D − D^- D− | 0 | 4 |
D + D^+ D+ | 8 | 5 |
E n t r o p y ( D t − ) = ( − 0 4 × l o g 2 0 4 − 4 4 × l o g 2 4 4 ) = 0 Entropy(D_t^-) = (- \frac{0}{4} \times log_2\frac{0}{4} - \frac{4}{4} \times log_2 \frac{4}{4})=0 Entropy(Dt−)=(−40×log240−44×log244)=0
E n t r o p y ( D t + ) = ( − 8 13 × l o g 2 8 13 − 5 13 × l o g 2 5 13 ) = 0.43 + 0.53 = 0.96 Entropy(D_t^+) = (- \frac{8}{13} \times log_2\frac{8}{13} - \frac{5}{13} \times log_2 \frac{5}{13}) = 0.43 + 0.53=0.96 Entropy(Dt+)=(−138×log2138−135×log2135)=0.43+0.53=0.96
G a i n ( D , a ) = E n t r o p y ( D ) − ( 4 17 × E n t r o p y ( D t − ) + 13 17 × E n t r o p y ( D t + ) ) = 0.998 − ( 4 17 × 0 + 13 17 × 0.96 ) = 0.998 − 0.734 = 0.264 \begin{aligned} Gain(D, a) &= Entropy(D) -( \frac{4}{17} \times Entropy(D_t^-) + \frac{13}{17} \times Entropy(D_t^+)) \\ &= 0.998 - ( \frac{4}{17} \times 0 + \frac{13}{17} \times 0.96) \\ &=0.998-0.734 \\ &=0.264 \end{aligned} Gain(D,a)=Entropy(D)−(174×Entropy(Dt−)+1713×Entropy(Dt+))=0.998−(174×0+1713×0.96)=0.998−0.734=0.264
决策树对连续节点的划分则按照“密度 ⩽ \leqslant ⩽ 0.3815” 为决策节点,分为左侧4个元素的左子集,右侧13个元素的右子集。
然而不同于离散属性的是,连续属性可以继续将子集作为父节点,继续划分子集,比如:在13个元素的右子集中继续进行“密度 ⩽ \leqslant ⩽ 0.621”的划分。
在样本获得的过程中,难免会因为某些原因,比如隐私和成本问题,致使最后拿到的样本集出现某些属性数据的缺失。
当缺失的数据非常少时,一般直接舍弃掉那些缺失的数据;而当缺失的数据较多时,简单舍弃则是对样本的极大浪费,则按照一定的方法
对信息增益的计算公式进行修改:
G a i n ( D , a ) = ρ × G a i n ( D ~ , a ) = ρ × ( E n t r o p y ( D ~ ) − ∑ i = 1 k ∣ ω i ~ ∣ ∣ ω ~ ∣ E n t r o p y ( D i ~ ) ) \begin{aligned} Gain(D, a) &= \rho \times Gain(\tilde{D}, a)\\ &=\rho \times (Entropy(\tilde{D})-\displaystyle\sum_{i=1}^{k} \frac{|\tilde{\omega_i}|}{|\tilde{\omega}|}Entropy(\tilde{D_{i}}))\\ \end{aligned} Gain(D,a)=ρ×Gain(D~,a)=ρ×(Entropy(D~)−i=1∑k∣ω~∣∣ωi~∣Entropy(Di~))
其中:
E n t r o p y ( D ~ ) = − ∑ i = 1 k p i ~ l o g 2 p i ~ Entropy(\tilde{D})=-\displaystyle\sum_{i=1}^{k}\tilde{p_i} \ log_2\tilde{p_i} Entropy(D~)=−i=1∑kpi~ log2pi~
C4.5算法在ID3算法的基础上进行缺失值处理的改进,就使用了此方法。
下列是路人对某出行方式的便利程度的数据收集。如果简单的去除所有含缺失值的数据则只有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 |
通过对含有缺失值的样本的计算公式得知:
ρ = 14 17 \rho = \frac{14}{17} ρ=1714
E n t r o p y ( D ~ ) = − 6 14 × l o g 2 6 14 − 8 14 × l o g 2 8 14 = 0.985 Entropy(\tilde{D}) = -\frac{6}{14} \times log_2 \frac{6}{14}-\frac{8}{14} \times log_2 \frac{8}{14} = 0.985 Entropy(D~)=−146×log2146−148×log2148=0.985
飞机的信息熵:
E n t r o p y ( D 1 ~ ) = − 4 6 × l o g 2 4 6 − 2 6 × l o g 2 2 6 = 0.918 Entropy(\tilde{D_1}) = -\frac{4}{6} \times log_2 \frac{4}{6}-\frac{2}{6} \times log_2 \frac{2}{6} =0.918 Entropy(D1~)=−64×log264−62×log262=0.918
飞机所占 D ~ \tilde{D} D~比例: ∣ D 1 ~ ∣ ∣ D ~ ∣ = 6 14 \frac{|\tilde{D_1}|}{|\tilde{D}|}=\frac{6}{14} ∣D~∣∣D1~∣=146
高铁的信息熵:
E n t r o p y ( D 2 ~ ) = − 2 4 × l o g 2 2 4 − 2 4 × l o g 2 2 4 = 1 Entropy(\tilde{D_2}) = -\frac{2}{4} \times log_2 \frac{2}{4}-\frac{2}{4} \times log_2 \frac{2}{4} =1 Entropy(D2~)=−42×log242−42×log242=1
高铁所占 D ~ \tilde{D} D~比例: ∣ D 2 ~ ∣ ∣ D ~ ∣ = 4 14 \frac{|\tilde{D_2}|}{|\tilde{D}|}=\frac{4}{14} ∣D~∣∣D2~∣=144
汽车的信息熵:
E n t r o p y ( D 3 ~ ) = − 0 4 × l o g 2 0 4 − 4 4 × l o g 2 4 4 = 0 Entropy(\tilde{D_3}) = -\frac{0}{4} \times log_2 \frac{0}{4}-\frac{4}{4} \times log_2 \frac{4}{4} =0 Entropy(D3~)=−40×log240−44×log244=0
汽车所占 D ~ \tilde{D} D~比例: ∣ D 3 ~ ∣ ∣ D ~ ∣ = 4 14 \frac{|\tilde{D_3}|}{|\tilde{D}|}=\frac{4}{14} ∣D~∣∣D3~∣=144
带入完整公式:
G a i n ( D , a ) = ρ × G a i n ( D , a ~ ) = ρ × ( E n t r o p y ( D ) − ∣ D 1 ~ ∣ ∣ D ~ ∣ × E n t r o p y ( ( ~ D 1 ) ) − ∣ D 2 ~ ∣ ∣ D ~ ∣ × E n t r o p y ( ( ~ D 2 ) ) − ∣ D 3 ~ ∣ ∣ D ~ ∣ × E n t r o p y ( ( ~ D 3 ) ) ) = 14 17 × ( 0.985 − 6 14 × 0.918 − 4 14 × 1 − 4 14 × 0 ) = 14 17 × ( 0.985 − 0.679 ) = 14 17 × 0.306 = 0.252 \begin{aligned} Gain(D, a) &= \rho \times Gain(\tilde{D,a}) \\ &= \rho \times (Entropy(D)-\frac{|\tilde{D_1}|}{|\tilde{D}|} \times Entropy(\tilde(D_1))-\frac{|\tilde{D_2}|}{|\tilde{D}|} \times Entropy(\tilde(D_2))-\frac{|\tilde{D_3}|}{|\tilde{D}|} \times Entropy(\tilde(D_3))) \\ &= \frac{14}{17} \times(0.985-\frac{6}{14}\times 0.918 - \frac{4}{14} \times 1 - \frac{4}{14} \times 0) \\ &= \frac{14}{17}\times (0.985-0.679) \\ &=\frac{14}{17} \times 0.306 \\ &= 0.252 \end{aligned} Gain(D,a)=ρ×Gain(D,a~)=ρ×(Entropy(D)−∣D~∣∣D1~∣×Entropy((~D1))−∣D~∣∣D2~∣×Entropy((~D2))−∣D~∣∣D3~∣×Entropy((~D3)))=1714×(0.985−146×0.918−144×1−144×0)=1714×(0.985−0.679)=1714×0.306=0.252
前面研究的都是单变量决策树,即每个决策节点都只针对一个属性进行判别。
例如(参照西瓜书中的案例):
对于多变量决策树(,multivatiate decision tree),每一个决策节点,都是一个合适的线性分类器,即多个属性组合成的一组分类规则。
多变量决策树指的是一类树,同样也有很多不同的算法,它是在单变量决策树的基础上改进而来,关键是对决策节点的改变,比如前面的将决策节点改成线性分类器.除此之外,还有在决策树的每个叶子节点上嵌入神经网络而形成感知机树(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
决策树分类算法的优点:
决策树分类算法的缺点:
目前sklearn的决策树模型只实现了ID3和CART。
默认情况下,sklearn中决策树算法默认使用gini作为标准(criterion),也就是使用CART决策树,同时也是二叉树。而指定标准为entropy时,则是使用ID3决策树,实际与C4.5差别不大。
决策树可视化后更加直观易懂,但是需要使用训练好的决策树来进行可视化。可以使用 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
目标:使用自带的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
目标:使用自带的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
数据下载链接:download
Titanic数据集有两个:
train.csv - 包含特征信息和是否存活的标签
test.csv - 只包含特征信息
结果为判断测试集test.csv中的乘客是否存活,使用分类树。
准备阶段: 我们首先需要对训练集、测试集的数据进行探索,分析数据质量,并对数据进行清洗,然后通过特征选择对数据进行降维,方便后续分类运算;
分类阶段: 首先通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集。然后我们对决策树分类器的准确性进行分析,并对决策树模型进行可视化。
使用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进行设置外,不需要进行其他手动设定,默认就行,即不会限制决策树的最大深度,不限制叶子节点数,所有分类权重都相等等等。
构造完决策树(分类器)后,使用fit方法对分类器进行拟合训练,然后使用predict方法对测试集进行预测,得到预测的结果后,可以使用score方法对分类器的准确率进行评估。
按照项目的流程来完成整个项目
# 导入文件读取和分析包-pandas
import pandas as pd
# 读取训练集和测试集文件
test = pd.read_csv('./test.csv')
train = pd.read_csv('./train.csv')
数据探索对分类器没实质性的作用,但有助于对数据特性的了解,帮我我们做数据清洗和特征选择。
一些用于数据探索的函数:
函数 | 解释 |
---|---|
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()
# 查看字符串类型数据
train.describe(include=['O'])
# 查看训练集前几个样本
train.head(n=5)
# 查看训练集后几个样本
train.tail(n=5)
通过数据探索,发现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)
特征选择是分类器的关键,选择不同则得到的分类器也不同。但并不是所有属性都对分类结果有作用,所以需要选择对结果关联性大的属性特征。
通过数据探索可以发现:
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 列的特征矩阵。
现在我们使用 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')
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])
from sklearn.tree import export_graphviz
import graphviz
dot_data = export_graphviz(clf, out_file=None)
graph = graphviz.Source(dot_data)
graph
对于决策树的详细理解,可以参考下一篇文章:决策树结合可视化理解
2022/1/27