前言
决策树是机器学习中的一种常用算法。相关数学理论我也曾在数学建模专栏中数学建模学习笔记(二十五)决策树 介绍过,本篇博文不注重相关数学原理,主要注重使用sklearn实现分类树的效果。 参考课程见【2020机器学习全集】菜菜的sklearn完整版
决策树简介
决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。
sklearn中的决策树
- 模块sklearn.tree
树类型 | 库表示 |
---|---|
分类树 | tree.DecisionTreeClassifier |
回归树 | tree.DecisionTreeRegressor |
生成的决策树导出为DOT格式,画图专用 | tree.export_graphviz |
高随机版本的分类树 | tree.ExtraTreeClassifier |
高随机版本的回归树 | tree.ExtraTreeRegressor |
sklearn的基本建模流程
对应python代码
代码语言:javascript复制from sklearn import tree #导入需要的模块
clf = tree.DecisionTreeClassifier() #实例化
clf = clf.fit(X_train,y_train) #用训练集数据训练模型
result = clf.score(X_test,y_test) #导入测试集,从接口中调用需要的信息
分类树 DecisionTreeClassifier
重要参数
criterion 决定不纯度的计算方法
为了要将表格转化为一棵树,决策树需要找出最佳节点和最佳的分枝方法,对分类树来说,衡量这个“最佳”的指标叫做“不纯度”。通常来说,不纯度越低,决策树对训练集的拟合越好。 通俗理解:为了将一群混在一起的复杂样本分开,用不纯度来进行衡量,没分之前,也就是根节点,不纯度最高,之后越往下面不纯度越低,到叶子节点,就完全分离开,不纯度最低,得到的结果最“纯净”!
Criterion这个参数正是用来决定不纯度的计算方法的。sklearn提供了两种选择: 1)输入”entropy“,使用信息熵(Entropy) 2)输入”gini“,使用基尼系数(Gini Impurity)
不填写,默认的是gini。 sklearn实际计算的是基于信息熵的信息增益(Information Gain),即父节点的信息熵和子节点的信息熵之差。
选取规则: 通常就使用基尼系数 数据维度很大,噪音很大时使用基尼系数 维度低,数据比较清晰的时候,信息熵和基尼系数没区别 当决策树的拟合程度不够的时候,使用信息熵 两个都试试,不好就换另外一个
建立分类树步骤
1.导入需要的算法库和模块
代码语言:javascript复制from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import pandas as pd
import graphviz
2.查看数据 这里使用的是sklearn自带的wine数据集。
代码语言:javascript复制wine = load_wine()
print(wine.data.shape)
print(pd.concat([pd.DataFrame(wine.data), pd.DataFrame(wine.target)], axis=1))
print(wine.feature_names)
print(wine.target_names)
总共178条数据,3分类问题。
3.划分训练集和测试集
代码语言:javascript复制Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
print(Xtrain.shape)
print(Xtest.shape)
test_size=0.3表示测试集占样本数量的30%
划分之后,训练集为124条数据,测试集为54条数据。
4.模型建立
代码语言:javascript复制clf = tree.DecisionTreeClassifier(criterion="entropy")
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) # 返回预测的准确度
print(score)
这里选用信息熵entropy作为计算方法。 score代表准确度 由于决策树的建立包含随机变量,每次运行结果都不一样。 这里我运行几次大致结果准确率在90%以上。
5.决策树可视化
代码语言:javascript复制feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf
,feature_names= feature_name
,class_names=["类型一","类型二","类型三"]
,filled=True #控制颜色填充
,rounded=True #控制图片为圆角
)
graph = graphviz.Source(dot_data.replace('helvetica','"Microsoft YaHei"'), encoding='utf-8')
graph.view()
这里直接运行会报错,问题是虽然安装了graphviz库,但仍需安装graphviz插件才能显示图片。 插件下载地址https://graphviz.gitlab.io/download/ windows选择:
在安装时,勾选将graphviz添加到环境变量
replace(‘helvetica’,’“Microsoft YaHei”’), encoding='utf-8’目的是防止中文乱码,使用utf-8进行重新编码。
运行之后,会直接打开一张pdf图片。
这就是分类决策树,每一个分支节点上第一行代表分支的依据。 颜色代表不纯度,颜色越深代表代表不纯度越小,叶子节点不纯度为0。
6.特征重要性显示 上图的决策树分支是根据特征重要性(信息增益)来进行分支,通过下面的程序可以打印出各个特征的重要性。
代码语言:javascript复制print([*zip(feature_name,clf.feature_importances_)])
得到结果:
代码语言:javascript复制[('酒精', 0.0), ('苹果酸', 0.0), ('灰', 0.0), ('灰的碱性', 0.03448006546085971), ('镁', 0.0), ('总酚', 0.0), ('类黄酮', 0.4207777417026953), ('非黄烷类酚类', 0.0), ('花青素', 0.0), ('颜色强度', 0.1444829682905809), ('色调', 0.03408453152321241), ('od280/od315稀释葡萄酒', 0.0), ('脯氨酸', 0.3661746930226517)]
有些特征的重要性为0,说明这些指标在决策树中没有被利用。
随机参数 random_state & splitter
在上面的例子中,每次运行结果都会有些不同,原因在于使用sklearn自带的决策树时,它会默认“栽种”好几棵不同的决策树,从中返回出效果最好的那一棵。
random_state用来设置分枝中的随机模式的参数,默认None,输入任意整数,会一直长出同一棵树,让模型稳定下来。
splitter也是用来控制决策树中的随机选项的,有两种输入值:
- 输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看)
- 输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。
clf = tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30
,splitter="random"
)
设置随机参数可以让决策树稳定或者更随机,效果不确定,一切以最后的score为主。
剪枝策略 max_depth
max_depth 用来限制树的最大深度,超过设定深度的树枝全部剪掉 策树多生长一层,对样本量的需求会增加一倍。 实际使用时,建议从=3开始尝试,看看拟合的效果再决定是否增加设定深度。
剪枝策略 min_samples_leaf & min_samples_split
min_samples_leaf限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分 枝就不会发生,一般来说,建议从=5开始使用。
min_samples_split限定,一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则 分枝就不会发生。
代码语言:javascript复制clf = tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30
,splitter="random"
,max_depth=3
,min_samples_leaf=10
,min_samples_split=10
)
剪枝策略max_features & min_impurity_decrease
max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃。
min_impurity_decrease限制信息增益的大小,信息增益小于设定数值的分枝不会发生。
确认最优的剪枝参数
通过编程循环,控制其它量不变,一个量循环改变,画图显示,可以显示出这个量的最优值。
下面以max_depth为例:
代码语言:javascript复制import matplotlib.pyplot as plt
test = []
for i in range(10):
clf = tree.DecisionTreeClassifier(max_depth=i 1
,criterion="entropy"
,random_state=30
,splitter="random"
)
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest)
test.append(score)
plt.plot(range(1,11),test,color="red",label="max_depth")
plt.legend()
plt.show()
绘制结果如图所示
说明max_depth取4时,效果最好。
目标权重参数class_weight & min_weight_fraction_leaf
想象这种情况:在银行要判断“一个办了信用卡的人是否会违约”,就是 是vs否(1%:99%)的比例,在这种情况下,出现了样本不平衡,这个时候就需要调整其目标权重参数。
使用class_weight参数对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类,向捕获少数类的方向建模。该参数默认None,此模式表示自动给与数据集中的所有标签相同的权重。
有了权重之后,样本量就不再是单纯地记录数目,而是受输入的权重影响了,因此这时候剪枝,就需要搭配min_weight_fraction_leaf这个基于权重的剪枝参数来使用。
重要属性和接口
1.(上面提到过)feature_importances_ 能够查看各个特征对模型的重要性 注意后面的下划线_不能省略
2.apply 返回每个测试样本所在的叶子节点的索引
代码语言:javascript复制clf.apply(Xtest)
3.predict返回每个测试样本的分类/回归结果
代码语言:javascript复制clf.predict(Xtest)
其它内容补充
分类树天生不擅长环形数据,最擅长月亮型数据的是最近邻算法,RBF支持向量机和高斯过程;最擅长环形数据的是最近邻算法和高斯过程;最擅长对半分的数据的是朴素贝叶斯,神经网络和随机森林。
上面是分类树的结果,环形数据可以看到左侧出现一块白色,说明分类效果不好。