机器学习——决策树理论及Python实现

一、理论介绍:

决策树(DTs)是一种用于分类和回归的非参数有监督学习方法。决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。
长这样:
在这里插入图片描述
**决策树最重要的是决策树的构造。构造决策树的关键步骤是分裂属性。**所谓分裂属性就是在某个节点处按照某一特征属性的不同划分构造不同的分支,其目标是让各个分裂子集尽可能地“纯”。尽可能“纯”就是尽量让一个分裂子集中待分类项属于同一类别。
分裂属性分为三种不同的情况:
1、属性是离散值且不要求生成二叉决策树。此时用属性的每一个划分作为一个分支。
2、属性是离散值且要求生成二叉决策树。此时使用属性划分的一个子集进行测试,按照“属于此子集”和“不属于此子集”分成两个分支。
3、属性是连续值。此时确定一个值作为分裂点split_point,按照>split_point和<=split_point生成两个分支。

决策树的属性分裂选择是”贪心“算法,也就是没有回溯的。

决策树的构建

决策树的构建方法, 即如何决定每步应该分裂的节点. 决策树的构建一般有2种方法: 1. 深度优先:深度优先采用递归的思想, 在每个节点都是优先有左边的子树建好, 再建右边子树
2. 广度优先:广度优先按照层次来构建树的.

决策树ID3.5算法

决策树ID3算法的核心思想是以信息增益度量属性选择,选择分裂后信息增益最大的属性进行分裂。
设D为用(输出)类别对训练元组进行的划分,则D的熵表示为:
在这里插入图片描述
如果将训练元组D按属性A进行划分,则A对D划分的期望信息为:
在这里插入图片描述
举例说明:
一、 确定数据
在这里插入图片描述

二、正例(好瓜)占8/17,反例(坏瓜)占9/17,则根结点的信息熵为:
在这里插入图片描述

三、计算当前属性集合{色泽,根蒂,敲声,纹理,脐带,触感}中每个属性的信息增益。

色泽有三个可能的取值:青绿、乌黑、浅白

D1{色泽=青绿}={1,4,6,10,13,17},正例3/6,反例3/6

D2{色泽=乌黑}={2,3,7,8,9,15},正例4/6,反例2/6

D3{色泽=浅白}={5,11,12,14,16},正例1/5,反例4/5

这三个分支结点的信息熵为:
在这里插入图片描述

四、由此我们可以计算出色泽属性的信息增益是(青绿,乌黑, 浅白三个分类类别分别乘自己对应的信息熵):
在这里插入图片描述

对于特征属性为连续值,可以如此使用ID3算法:先将D中元素按照特征属性排序,则每两个相邻元素的中间点可以看做潜在分裂点,从第一个潜在分裂点开始,分裂D并计算两个集合的gain,具有最大gain的点称为这个属性的最佳分裂点,其gain作为此属性的gain。

C4.5算法

ID3算法存在一个问题,就是偏向于多值属性,例如,如果存在唯一标识属性ID,则ID3会选择它作为分裂属性,这样虽然使得划分充分纯净,但这种划分对分类几乎毫无用处。
ID3的后继算法C4.5使用增益率(gain ratio)的信息增益扩充,试图克服这个偏倚。
C4.5算法定义了分裂信息,表示为:
在这里插入图片描述

定义信息增益率:
在这里插入图片描述

C4.5就是选择最大增益率的属性来分裂,其他类似ID3.5。
C4.5算法是从ID3算法演变而来,除了拥有ID3算法的功能外, C4.5克服了ID3在应用中的不足,主要体现在:

  1. 用信息增益比例/信息增益率来选择属性,克服了用信息增益选择属性时偏向于选择取值多的属性的不足
  2. 能够完成对连续属性的离散化处理
  3. 可以处理具有缺少属性值的训练样本
  4. 在树构造过程中或者构造完成之后,通过使用不同的修剪技术以避免树的过度拟合
  5. K折交叉验证
    C4.5的缺点
  6. C4.5算法采用的是分而治之的策略,搜索过程中不进行回溯,在构造树的内部结点的时候是局部最优的搜索方式,所以它所得到的最终结果尽管有很高的准确性,仍然可能达不到全局最优的结果
  7. C4.5算法用一边构造决策树一边进行评价的方法构造决策树,当决策树构造出来之后,很难再调整树的结构和内容,决策树性能的改善十分困难。
  8. C4.5算法也会产生过度拟合问题
  9. ID3算法和C4.5算法都是在建树时将训练集一次性装载入内存的。但当面对有着上百万条纪录的数据库时,就无法实际应用这些算法。
CART算法

CART使用的是Gini(基尼系数),后续的决策树算法都是利用了 Gini(基尼系数)作为度量标准。
基尼指数反映了从数据集中随机抽取两个样本,其类别标记不一致的概率。因此基尼指数越小,则数据集纯度越高。把具有最小基尼系数的属性选作分支属性。

决策树的优缺点以及适用场景
决策树的一些优点:

1.易于理解和解释。树可以被可视化。
2.几乎不需要数据准备。其他算法通常需要数据标准化,需要创建虚拟变量并删除缺失值。但是,请注意,此模块不支持缺失值。
3.使用树的成本(即预测数据)是用于训练树的数据点数的对数。
4.能够处理数值型和分类型数据。其他技术通常专门分析只有一种类型变量的数据集。
5.能够处理多输出问题。
6.使用白盒模型。如果给定的情况在模型中是可以观察到的,那么对条件的解释就很容易用布尔逻辑来解释。相反,在黑箱模型中(例如,在人工神经网络中),结果可能很难解释。
7.可以使用统计测试验证模型。这样就有可能对模型的可靠性作出解释。
8.即使它的假设在某种程度上被生成数据的真实模型所违背,它也表现得很好。

决策树的缺点包括:

1.决策树学习器可以创建过于复杂的树,不能很好地概括数据。这就是所谓的过拟合。为了避免这个问题,必须设置剪枝、设置叶节点所需的最小样本数或设置树的最大深度等机制。
2.决策树可能是不稳定的,因为数据中的小变化可能导致生成完全不同的树。通过集成决策树来缓解这个问题。
3.学习最优决策树的问题在最优性的几个方面都是NP-complete的,甚至对于简单的概念也是如此。因此,**实际的决策树学习算法是基于启发式算法,如贪婪算法,在每个节点上进行局部最优决策。这种算法不能保证返回全局最优决策树。**这可以通过训练多棵树再集成一个学习器来缓解,其中特征和样本被随机抽取并替换。
4.有些概念很难学习,因为决策树不能很容易地表达它们,例如异或、奇偶校验或多路复用器问题。
5.如果某些类占主导地位,则决策树学习者会创建有偏见的树。因此,建议在拟合决策树之前平衡数据集。

适用场景

对于决策树分类而言,它更擅长分月亮型和半分型的数据,对于环状分布的数据效果并不是太好。

最擅长月亮型数据的是最近邻算法,RBF支持向量机和高斯过程

最擅长环形数据的是最近邻算法和高斯过程

最擅长半分的是朴素贝叶斯、神经网络和随机森林

trick

停止条件

决策树的构建过程是一个递归的过程,所以需要确定停止条件,否则过程将不会结束。一种最直观的方式是当每个子节点只有一种类型的记录时停止,但是这样往往会使得树的节点过多,导致过拟合问题(Overfitting)。另一种可行的方法是当前节点中的记录数低于一个最小的阀值,那么就停止分割,将max(P(i))对应的分类作为当前叶节点的分类。
过度拟合优化方案
优化方案1:修剪枝叶
决策树过渡拟合往往是因为太过“茂盛”,也就是节点过多,所以需要裁剪(Prune Tree)枝叶。裁剪枝叶的策略对决策树正确率的影响很大。主要有两种裁剪策略。
前置裁剪 在构建决策树的过程时,提前停止。那么,会将切分节点的条件设置的很苛刻,导致决策树很短小。结果就是决策树无法达到最优。实践证明这中策略无法得到较好的结果。

后置裁剪 决策树构建好后,然后才开始裁剪。采用两种方法:1)用单一叶节点代替整个子树,叶节点的分类采用子树中最主要的分类;2)将一个字数完全替代另外一颗子树。后置裁剪有个问题就是计算效率,有些节点计算后就被裁剪了,导致有点浪费。
优化方案2:K-Fold Cross Validation

首先计算出整体的决策树T,叶节点个数记作N,设i属于[1,N]。对每个i,使用K-Fold Validataion方法计算决策树,并裁剪到i个节点,计算错误率,最后求出平均错误率。(意思是说对每一个可能的i,都做K次,然后取K次的平均错误率。)这样可以用具有最小错误率对应的i作为最终决策树的大小,对原始决策树进行裁剪,得到最优决策树。
优化方案3:Random Forest

**Random Forest是用训练数据随机的计算出许多决策树,形成了一个森林。然后用这个森林对未知数据进行预测,选取投票最多的分类。**实践证明,此算法的错误率得到了经一步的降低。这种方法背后的原理可以用“三个臭皮匠定一个诸葛亮”这句谚语来概括。一颗树预测正确的概率可能不高,但是集体预测正确的概率却很高。RF是非常常用的分类算法,效果一般都很好。

p.s.

1、如果属性用完了怎么办
在决策树构造过程中可能会出现这种情况:所有属性都作为分裂属性用光了,但有的子集还不是纯净集,即集合内的元素不属于同一类别。在这种情况下,由于没有更多信息可以使用了,一般对这些子集进行“多数表决”,即使用此子集中出现次数最多的类别作为此节点类别,然后将此节点作为叶子节点。
2.什么是过度拟合及原因
采用上面算法生成的决策树在事件中往往会导致过度拟合。也就是该决策树对训练数据可以得到很低的错误率,但是运用到测试数据上却得到非常高的错误率。过渡拟合的原因有以下几点:
•噪音数据:训练数据中存在噪音数据,决策树的某些节点有噪音数据作为分割标准,导致决策树无法代表真实数据。
•缺少代表性数据:训练数据没有包含所有具有代表性的数据,导致某一类数据无法很好的匹配,这一点可以通过观察混淆矩阵(Confusion Matrix)分析得出。
•多重比较(Mulitple Comparision):举个列子,股票分析师预测股票涨或跌。假设分析师都是靠随机猜测,也就是他们正确的概率是0.5。每一个人预测10次,那么预测正确的次数在8次或8次以上的概率只有5%左右,比较低。但是如果50个分析师,每个人预测10次,选择至少一个人得到8次或以上的人作为代表,那么概率为 0.9399,概率十分大,随着分析师人数的增加,概率无限接近1。但是,选出来的分析师其实是打酱油的,他对未来的预测不能做任何保证。上面这个例子就是多重比较。这一情况和决策树选取分割点类似,需要在每个变量的每一个值中选取一个作为分割的代表,所以选出一个噪音分割标准的概率是很大的。
3.为什么决策树不需要数据归一化?
一般来说, 机器学习都需要特征归一化, 目的是让特征之间的比较可以在同一个量纲上进行. 但是从数据构建过程来看, 不纯函数的计算和比较都是单特征的. 所有决策树不需要数据的归一>化. 但是有点需要注意, 从上文中对splitter的源码分析中可以看出, 决策树为了加速遍历没有真正遍历所有取值, 当特征的绝对值太小的时候会导致相邻值的间隔小于step, 因此尽量让特征值不要太小(大于0.01比较保险).

4.如何正确的选择不纯度函数?
在gini函数小节中我们比较了gini和entroy在两元下的图形. 可以看到基本上没有区别. 有研究表明不同的不纯度函数对决策树产生的影响在2%以内1.

事实上很少有实际案例说明选择不纯度函数有显著的作用. 所以对于分类决策树选择gini, 对于回归选择MSE, 这样的默认配置可以满足绝大多数的需求.
5.决策树哪些参数最重要?
决策树的重要参数都是防止过拟合的. 我认为2个参数一定要设置:

1、min_samples_leaf 这个sklearn的默认值是1. 经验上必须大于100, 如果一个节点都没有100个样本支持他的决策, 一般都被认为是过拟合.
2、max_path 这个参数控制树的规模. 决策树是一个非常直观的机器学习方法. 一般我们都会把它的决策树结构打印出来观察, 如果深度太深对于我们的理解是有难度的. max_path也是防止过拟合的有效手段.

二、实战

0.库
from sklearn import tree
import pandas as pd
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import graphviz
import matplotlib.pyplot as plt
1.类
在sklearn中,共有五个关于决策树的类:

    Tree.DecisionTreeClassfier 分类树

    Tree.DecisionTreeRegressor 回归树

    Tree.export_graphviz 将生成的决策树导出为dot格式,画图专用

    Tree.ExtraTreeClassifier 高随机版本的分类树

    Tree.ExtraTreeRegressor 高随机版本的回归树

1.Tree.DecisionTreeClassfier参数
1)Criterion
不纯度,两种方式:gini:基尼系数(默认);entropy:信息熵。
差别不大,但是信息熵会慢一些,且对不纯度会更加敏感,所以决策树会更加精细。对于高维数据和噪音过多的数据信息熵很容易过拟合,建议基尼系数,当然也不是绝对的。
2)random_state
在建树的过程中,是依靠优化节点来追求一棵优化的树,但最优的节点不一定保证最优的树。所以,在sklearn中,会建更多的树从中取最好的。在每次分枝时,不从全部特征中选取,而是随机从中选取一部分特征,从中选取不纯度相关指标达到最优的作为分枝用的节点。

可以设置任意整数,目的是让指标更优秀。
3)splitter
控制决策树的随机性。

输入值:best(选择重要性更高的特征)和 random(随机选择,可以防止过拟合)
4) 剪枝参数
max_depth:特征多,但数据少,限制树的深度可以防止过拟合,建议从3开始尝试

min_samples_leaf 和 min_samples_split:限制叶子节点,可以整数,也可以是浮点数(百分比),搭配max_depth使用,建议从5开始尝试,一般情况,对于类别不多的分类问题,最佳是1。

max_features:限制考虑的特征数,超过限制个数的特征会被舍弃(不太推荐,太暴力,可以选择PCA降维或者sklearn自带的特征选择)

min_impurity_decrease:限制信息增益的大小,信息增益小于设定值分枝就不会发生
如何找出最优秀的剪枝参数?
绘制超参数曲线

当然,剪枝后,效果不一定会更好。只是当数据集较大,特征较多时,树枝的无限生长会对内存消耗很大,所以需要提前剪枝处理
5)目标权重参数
Class_weight 和 Min_weight_fraction_leaf:完成样本标签平衡的参数。默认值为None,即相同的权重。
2、重要接口
fit(xtrain,ytrain) 训练模型

score(xtest,ytest) 得分

predict(xtest) 返回每个测试样本的分类或者回归的结果

apply(xtest) 返回每个测试样本所在的叶子的索引

predict_proba(xtest) 预测的分类结果属于每个标签的概率值
3、重要属性
feature_importances_ :特征重要性

#决策树实现
# 数据集:选择经典的红酒数据集,也是sklearn自带的数据集
from sklearn import tree
import pandas as pd
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import graphviz
import matplotlib.pyplot as plt
#载入数据
wine=load_wine()
# print(wine.data)
# print(wine.data.shape)#(178, 13)
# print(wine.target)#0 1 2
# print(wine.feature_names)
# print(wine.target_names)
df=pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1)#纵向拼接合并特征和标签
#划分训练集和测试集
xtrain,xtest,ytrain,ytest=train_test_split(wine.data,wine.target,test_size=0.3)#测试集比例30%
#创建决策树
#实例化
clf=tree.DecisionTreeClassifier(criterion='gini',random_state=42,max_depth=3,min_samples_leaf=10,min_samples_split=10)
# 训练
clf=clf.fit(xtrain,ytrain)
#为测试集打分
result=clf.score(xtest,ytest)
# print('测试集打分',result)
# print('训练集打分',clf.score(xtrain,ytrain))
# ypredit=clf.predict(xtest)#返回每个测试样本的分类或者回归的结果
# print('ypredit',ypredit)
# yt=clf.apply(xtest)           #返回每个测试样本所在的叶子的索引
# print('yt',yt)
# yp=clf.predict_proba(xtest)  #预测的分类结果属于每个标签的概率值
# print('yp',yp)
# #可视化
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot=tree.export_graphviz(clf,feature_names=feature_name,class_names=['琴酒','雪莉','贝尔摩德'],filled=True,rounded=True)#上色,圆角
graph=graphviz.Source(dot)
# graph.render("tree")
print("树:",graph)
#重要性
# importance =clf.feature_importances_
# print(importance)
# feature_importance=[(feature,importance.round(3)) for feature,importance in zip(feature_name,importance)]
# feature_importance=sorted(feature_importance,key=lambda x:x[1],reverse=True)
# [print('variable:{:20} importance: {}'.format(*pair)) for pair in feature_importance]
# # 绘制超参数曲线
#
# test=[]
# for i in range(100):
#     clf=tree.DecisionTreeClassifier(max_depth=2,criterion='gini',random_state=i,splitter='random')
#     clf.fit(xtrain,ytrain)
#     score=clf.score(xtest,ytest)
#     test.append(score)
# plt.plot(range(100),test)
# plt.xlabel('max_depth')
# plt.ylabel('score')
# plt.show()


  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值