机器学习模型2 决策树-基于Python sklearn的实现

1、模型原理

(一)原理

1、原理:引入信息熵(不确定程度)的概念,通过计算各属性下的信息增益程度(信息增益越大,则意味着使用该属性来进行划分所获得的“纯度提升”越大),增益程度最大的,则将其选择为一个结点,然后再进行下一步信息增益计算,选择下一个属性结点,直至无法划分为止,则最后的结点成为叶结点。

2、三种属性划分准则:

(1)以“信息增益”为准则,代表算法为ID3,但信息增益准则对可取值数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,改进为使用“增益率”。

(2)以“增益率”为准则,代表算法为C4.5,但信息率准则对可能取值数据较少的属性有所偏好,因此,C4.5算法并不是直接选择增益率最大的候选划分属性,而是使用了一张启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。

(3)以“基尼指数”为准则,代表算法为CART(classification and regression tree,分类与回归树)。全是二叉树。

(4)各划分准则,对决策树的尺寸有较大影响,但对泛化能力影响有限,有研究对信息增益和基尼指数进行了对比分析,发现仅在2%的情况下会有所不同。

(5)但剪枝方法和程度对决策树泛化性能的影响相当显著。

(6)“增量学习”,即在接收到新样本后可对己学得的模型进行调整,而不用完全重新学习.主要机制是通过调整分支路径上的划分属性次序来对树进行部分重构,代表性算法有ID4、ID5R、ITI等。增量学习可有效地降低每次接收到新样本后的训练时间开销,但多步增量学习后的模型会与基于全部数据训练而得的模型有较大差别。

3、连续值和缺失值处理

(1)连续值处理,常使用二分法,对连续属性a中的两个属性样本数据ai和ai+1,取其中位点(平均数)作为划分点,因此对连续属性a,它如果有n个取值,那它就有n-1个候选划分点集合,需要对这些候选划分点分别计算信息增益,找到最佳划分点。

还有一点需要注意,连续属性与离散属性一个不同点是,若当前节点划分属性为连续属性,该属性还可以作为其后代结点的划分属性,例如在父结点上使用了“密度≤0.381”,不会禁止在子节点上使用“密度≤0.294”。(主要是连续属性还可以再细分,但离散属性,比如“颜色=青绿”,已经不能再细分为“很青绿”、“一般青绿”之类的,不然就直接作为一个属性存在了。)

(2)缺失值处理方面

在根节点选择划分属性时,计算信息增益时,对某属性的缺失值直接忽略进行计算,如“纹理”属性,总共16个样本,只有14个样本在“纹理”属性上有数据,那就只对这14个样本算“增益”,然后再乘以14/16的权重。

再进行子节点的属性划分时,就通过设置权重,将缺失样本分别进入各属性分类中。如“纹理”中的2个缺失值,按照“8/14个清晰”,“4/14个稍糊”,“2/14个模糊”分别参加计算,决定这一层哪个属性信息增益最大。

突然想到一个问题,那连续属性的缺失值如何处理。

4、剪枝

两大类思路:

一种是西瓜书上的,首先需要划分训练集和验证集,分为预剪枝和后剪枝:

预剪枝,先用训练集计算一个划分属性,然后用验证集计算划分前和划分后的精度变化,如果划分后精度变低,则剪枝。

后剪枝,先用训练集训练整个决策树,然后从下往上,用验证集逐个计算结点的精确度变化,决定剪枝与否。

两者优缺点:预剪枝,使得很多分支都没有展开,不仅降低了过拟合(对训练数据分类很准,但对未知的测试数据分类却不那么准确)的风险,还显著减少了决策树的训练时间开销和测试时间开销。但另一方面,有些分支的当前划分虽不能提升泛化性能,甚至能导致泛化性能暂时下降,但在其基础上进行的后续划分却有可能导致性能显著提高,预剪枝基于贪心本质禁止这些分支展开,给预剪枝决策树带来了欠拟合的风险。

后剪枝,通常比预剪枝决策树保留了更多的分支,一般情况下,后剪枝欠拟合的风险很小,泛化性能往往优于预剪枝,但后剪枝在生产完全决策树之后进行的,并且要自底向上的对树中的所有非叶节点进行逐一考察,因此其训练时间开销要远远大于未剪枝和预剪枝决策树。

另一类是李航《统计学习方法》上的,通过“训练数据”来先训练出来整个决策树,然后从叶结点往上一个个比较决策树整体的损失函数(等于整体信息熵+α*叶结点个数,其中α自己设定),如果剪枝后损失函数变小,则剪枝,直至不能继续剪枝为止。

5、多变量决策树

(1)决策树所形成的分类边界有一个明显的特点:轴平行,即它的分类边界由若干个与坐标轴平行的分段组成。但在学习任务的真实分类边界比较复杂时,必须使用很多段划分才能获得较好的近似,此时的决策树会相当复杂,由于要进行大量的属性测试,预测时间开销会很大.

1.png

(2)多变量决策树

若能使用斜的划分边界,如下图中红色线段所示,则决策树模型将大为简化"多变量决策树" (multivariate decision tree) 就是能实现这样的"斜划分"甚至更复杂划分的决策树.以实现斜划分的多变量决策树为例,在此类决策树中,非叶结点不再是仅对某个属性,而是对属性的线性组合进行测试。

2.png
3.png

6、CART算法

分为回归树和分类树两种,回归树用平方误差最小化准则,分类树用基尼指数最小化准则。

CART的剪枝方法,是李航《统计学习方法》上的,通过“训练数据”来先训练出来整个决策树,然后从叶结点往上一个个比较决策树整体的损失函数(等于整体信息熵+α*叶结点个数,其中α自己设定),如果剪枝后损失函数变小,则剪枝,直至不能继续剪枝为止。

在这里又进行了推广,α设置的大小不同,导致你剪枝的取舍不同(α越大,对叶结点个数越厌恶,越偏向于取少的叶结点。当α足够大时,就只有根节点,当α足够小时,则保留未剪枝过的决策树)。

通过算法,对应每个子决策树(即,通过剪枝,拥有不同叶结点的决策树)都有一个参数α,这时,利用独立的验证数据集,测试各个子决策树的平方误差和基尼指数,找到其中最小的,就是最优的决策树,同时最优的参数α也确定了。

ID3、C4.5的缺点:

(1)ID3算法虽然提出了新思路,但是还是有很多值得改进的地方。  
  a)ID3没有考虑连续特征,比如长度,密度都是连续值,无法在ID3运用。这大大限制了ID3的用途。
  b)ID3采用信息增益大的特征优先建立决策树的节点。很快就被人发现,在相同条件下,取值比较多的特征比取值少的特征信息增益大。比如一个变量有2个值,各为1/2,另一个变量为3个值,各为1/3,其实他们都是完全不确定的变量,但是取3个值的比取2个值的信息增益大。
  c)ID3算法对于缺失值的情况没有做考虑
  d) 没有考虑过拟合的问题

(2) C4.5虽然改进或者改善了ID3算法的几个主要的问题,仍然有优化的空间。
  a)由于决策树算法非常容易过拟合,因此对于生成的决策树必须要进行剪枝。剪枝的算法有非常多,C4.5的剪枝方法有优化的空间。思路主要是两种,一种是预剪枝,即在生成决策树的时候就决定是否剪枝。另一个是后剪枝,即先生成决策树,再通过交叉验证来剪枝。后面在下篇讲CART树的时候我们会专门讲决策树的减枝思路,主要采用的是后剪枝加上交叉验证选择最合适的决策树。
  b)C4.5生成的是多叉树,即一个父节点可以有多个节点。很多时候,在计算机中二叉树模型会比多叉树运算效率高。如果采用二叉树,可以提高效率。
  c)C4.5只能用于分类,如果能将决策树用于回归的话可以扩大它的使用范围。
  d)C4.5由于使用了熵模型,里面有大量的耗时的对数运算,如果是连续值还有大量的排序运算。如果能够加以模型简化可以减少运算强度但又不牺牲太多准确性的话,那就更好了。

2、对于不含冲突数据(即特征向量完全相同但标记不同)的训练集,必存在与训练集一致(即训练误差为0)的决策树。只要规则划分的够细,肯定能分出来。

2、Python3中sklearn-tree的代码实现

决策树分类和决策树回归的参数设置

  • sklearn.tree.DecisionTreeClassifier (criterion=’gini’*, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
  • DecisionTreeRegressor(criterion=’mse’, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, presort=False)

criterion,表示划分属性时选用的准则,可选,默认设置是“gini”或“mse”
— 在决策树分类时,可选择“gini”,表示采用基尼系数,即CART算法;选择“entropy”,表示采用信息增益。
— 在决策树回归时,可选择“mse”,表示采用均方差;选择“mae”,表示采用与均值之差的绝对值的和。推荐使用默认的"mse"。一般来说"mse"比"mae"更加精确。除非你想比较二个参数的效果的不同之处。

splitter,表示特征划分点选择策略,可选,默认设置是“best”
— 可以使用"best"或者"random"。前者在特征的所有划分点中找出最优的划分点。后者是随机的在部分划分点中找局部最优的划分点。默认的"best"适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推荐"random" 。

max_depth,表示决策树的最大深度,可选,默认设置为None
— 可以选择是int值或None,None时表示不限制决策树的深度,一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,即输入一个int值,具体的取值取决于数据的分布。常用的可以取值10-100之间。

min_samples_split,表示结点再划分所需最小样本数,可选,默认设置为2
— 可以选择int或者float,这个值限制了结点继续划分的条件,如果某结点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2;如果样本量不大,不需要管这个值;如果样本量数量级非常大,则推荐增大这个值。我之前的一个项目例子,有大概10万样本,建立决策树时,我选择了min_samples_split=10。可以作为参考。

min_samples_leaf,表示叶结点所需的最少样本数,可选,默认值为1
— 这个值限制了叶结点最少样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。之前的10万样本项目使用min_samples_leaf的值为5,仅供参考。

min_weight_fraction_leaf,表示叶结点中所需的样本的权重和的最小值,默认为0.
— 可为float型,这个值限制了叶结点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。

max_features,表示在进行划分时,可以考虑的最大的特征数,可选,默认为None
— 可以选择int、float、auto、sqrt、log2、None。默认是"None",意味着划分时考虑所有的特征数;如果是"log2"意味着划分时最多考虑log2N个特征;如果是"sqrt"或者"auto"意味着划分时最多考虑N的二次方根个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。
一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。

random_state,表示随机数的生成方法,可选,默认为None。
— 可选int, RandomState instance或者None。如果是int,random_state是随机数发生器使用的种子; 如果RandomState实例,random_state是随机数生成器; 如果为None,则随机数生成器是np.random使用的RandomState实例。

max_leaf_nodes,表示最大叶结点数,可选,默认为None。
— 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。

min_impurity_decrease,表示结点减少的最小不纯度,也是一种阀值,可选,默认为0.0。
— 可选float。意思,如果加权的不纯度减少量(基尼系数,信息增益,均方差,绝对差)超过阀值,则该结点将会被继续分割 。

min_impurity_split,表示结点划分的最小不纯度,也是一种阀值,默认为None。
— 可选float。这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。即为叶结点 。

class_weight,表示结点划分的最小不纯度,也是一种阀值,默认为None。
— 可选dict, list of dicts, “balanced” or None;指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重,或者用“balanced”,如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果你的样本类别分布没有明显的偏倚,则可以不管这个参数,选择默认的"None"。
— 不适用于决策树回归。

presort,表示结点划分的最小不纯度,也是一种阀值,默认为False。
— 可选Ture或False,一般来说,如果样本量少或者限制了一个深度很小的决策树,设置为true可以让划分点选择更加快,决策树建立的更加快。如果样本量太大的话,反而没有什么好处。问题是样本量少的时候,我速度本来就不慢。所以这个值一般懒得理它就可以了。

参考sklearn官方文档说明,决策树分类 sklearn.tree.DecisionTreeClassifier
和决策树回归 sklearn.tree.DecisionTreeRegressor

一个简单例子

import numpy as np
from sklearn.datasets import load_iris #用sklearn中自带的iris数据
iris = load_iris()
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(iris.data,iris.target)
x_new=[5,3,1,0.1]   #自己随便写的一个样本数据
x_new = np.array(x_new).reshape(1, -1)
clf.predict (x_new)

决策树可视化展示

1、决策树可视化环境搭建
scikit-learn中决策树的可视化一般需要安装graphviz。主要包括graphviz的安装和python的graphviz插件的安装。
  第一步是安装graphviz。下载地址在:http://www.graphviz.org/。如果你是linux,可以用apt-get或者yum的方法安装。如果是windows,就在官网下载msi文件安装。无论是linux还是windows,装完后都要设置环境变量,将graphviz的bin目录加到PATH,比如我是windows,将C:/Program Files (x86)/Graphviz2.38/bin/加入了PATH
(备注:环境设置步骤-电脑-属性-高级系统设置-环境变量-系统变量-Path-将graphviz的bin目录加到Path,记得加分号;)
  第二步是安装python插件graphviz: pip install graphviz
  第三步是安装python插件pydotplus: pip install pydotplus
  这样环境就搭好了,有时候python会很笨,仍然找不到graphviz,这时,可以在代码里面加入这一行:
  os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'
  注意后面的路径是你自己的graphviz的bin目录。
2、决策树可视化的三种方法
首先载入类库:

from sklearn.datasets import load_iris 
from sklearn import tree 
import sys 
import os       
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/
#最后这行就是设置路径

接着载入sciki-learn的自带数据,有决策树拟合,得到模型:

iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)

现在可以将模型存入dot文件iris.dot。

with open("iris.dot", 'w') as f:
    f = tree.export_graphviz(clf, out_file=f)

这时候我们有3种可视化方法,第一种是用graphviz的dot命令生成决策树的可视化文件,敲完这个命令后当前目录就可以看到决策树的可视化文件iris.pdf.打开可以看到决策树的模型图。

#注意,这个命令在命令行执行
dot -Tpdf iris.dot -o iris.pdf

第二种方法是用pydotplus生成iris.pdf。这样就不用再命令行去专门生成pdf文件了。

import pydotplus 
dot_data = tree.export_graphviz(clf, out_file=None) 
graph = pydotplus.graph_from_dot_data(dot_data) 
graph.write_pdf("iris.pdf") 

第三种办法是个人比较推荐的做法,因为这样可以直接把图产生在ipython的notebook。代码如下:

from IPython.display import Image  
dot_data = tree.export_graphviz(clf, out_file=None, 
                         feature_names=iris.feature_names,  
                         class_names=iris.target_names,  
                         filled=True, rounded=True,  
                         special_characters=True)  
graph = pydotplus.graph_from_dot_data(dot_data)  
Image(graph.create_png()) 

在ipython的notebook生成的图如下:


clf.png

参考:这篇 scikit-learn决策树算法类库使用小结的第三部分就是可视化,还有[简单Python决策树可视化实例]
它采用的方法是安装第三方库graphviz,并且安装Python的graphviz插件,联合作用来实现,需要实际操作试验一下效果。
一篇单独讲graphviz的帖子,Graphviz绘图的安装与使用

参考文献

一个系列文章,写的很好,重点参考
第一篇:决策树算法原理(上),介绍了ID3和C4.5算法原理,及各自的优缺点
第二篇: 决策树算法原理(下),介绍了CART算法,优缺点和适用场景。
第三篇: scikit-learn决策树算法类库使用小结,介绍了sklearn中决策树的两个算法,决策树分类DecisionTreeClassifier和决策数回归DecisionTreeRegressor,其中各个参数代表的意义和调参时候需要注意的事项。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,423评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,339评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,241评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,503评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,824评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,262评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,615评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,337评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,989评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,300评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,829评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,193评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,753评论 3 230
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,970评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,708评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,295评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,207评论 2 258

推荐阅读更多精彩内容

  • sklearn、XGBoost、LightGBM的文档阅读小记 文章导航 目录 1.sklearn集成方法 1.1...
    nightwish夜愿阅读 12,440评论 1 49
  • 积跬步以致千里,积怠惰以致深渊 注:本篇文章在整理时主要参考了 周志华 的《机器学习》。 主要内容 决策树是机器学...
    指尖上的魔术师阅读 1,316评论 0 5
  • 本文是sklearn官网文档中集成模型一文的翻译,加入了自己的理解,翻译中难免有误,仅作参考。 集成学习的目标是结...
    迅速傅里叶变换阅读 7,740评论 0 7
  • 缘来在想你说:爱一个人,一个很重要的前提是给予她最诚的“责任”的同时给予最真的“信任”。 lilinx123456...
    天边月白阅读 307评论 0 1
  • 学习向日葵,做一个积极吸收正能量的人。人生多数时候都是自寻烦恼,或者吸收的负能量太多。要学习向日葵,哪里有阳光就朝...
    海蓉sarah阅读 365评论 0 0