决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。决策树算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法。
个人感受:一组数据样本,具备许多特征,每个数据具备自己的标签,从众多特征中,逐层利用特征进行分类,直到每一层所有的样本都得到唯一标签(即无法再分类下去)。这种实现效果形如一棵树。
决策树结构:
根节点:没有进边,有出边。包含最初的,针对特征的提问。
中间节点:既有进边也有出边,进边只有一条,出边可以有很多条。都是针对特征的提问。
叶子节点:有进边,没有出边,每个叶子节点都是一个类别标签。
决策树的核心研究问题:
1)如何从数据表中找出最佳节点和最佳分枝?
2)如何让决策树停止生长,防止过拟合?
sklearn (scikit-learn) 基于python语言的机器学习工具包,运行环境:jupyter lab(其他python开发环境也可以)
通过sklearn 直观感受分类决策树
from sklearn import tree from sklearn.datasets import load_wine #导入红酒数据集 from sklearn.model_selection import train_test_split # import pandas as pd import graphviz wine=load_wine() wine.data #显示红酒数据集的数据部分 pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1) #将数据集的数据部分和标签部分转化为表格形式 wine.feature_names wine.target_names Xtrain,Xtest,Ytrain,Ytest=train_test_split(wine.data,wine.target,test_size=0.3) #随机选取数据作为训练集和测试集,测试集占30% clf=tree.DecisionTreeClassifier(criterion="entropy") #创建分类决策树 clf=clf.fit(Xtrain,Ytrain) #开始训练 score=clf.score(Xtest,Ytest) #对测试集进行评分 feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸'] import graphviz dot_data = tree.export_graphviz(clf, out_file=None ,feature_names = feature_name ,class_names=["琴酒","雪莉","贝尔摩德"] ,filled=True ,rounded=True ) graph = graphviz.Source(dot_data) graph
运行结果:
说明数据集具备13个特征,图中是13个特征的名字
通过13个特征将数据集分成了三个类别
在测试集上的分数,得分越高说明准确性越好
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸'] import graphviz dot_data = tree.export_graphviz(clf, out_file=None ,feature_names = feature_name ,class_names=["琴酒","雪莉","贝尔摩德"] ,filled=True ,rounded=True ) #graph=graphviz.Source graph = graphviz.Source(dot_data) graph
可以看到通过对数据集的13个特征进行判定,将所有数据分成三类
不过也发现在其中并不是所有的特征都起到了作用,而且,有些特征的重要程度明显会高于其他特征,如何判断分析各个特征在整个结构中的重要性?
[*zip(feature_name,clf.feature_importances_)]
得到所有特征的重要性,其中有许多特征在这个决策树中是没有贡献的。
重要参数说明 criterion:为了要将表格转化为一棵树,决策树需要找出最佳节点和最佳的分枝方法,对分类树来说,衡量这个“最佳”的指标叫做“不纯度”。通常来说,不纯度越低,决策树对训练集的拟合越好。现在使用的决策树算法在分枝方法上的核心大多是围绕在对某个不纯度相关指标的最优化上。不纯度基于节点来计算,树中的每个节点都会有一个不纯度,并且子节点的不纯度一定是低于父节点的,也就是 说,在同一棵决策树上,叶子节点的不纯度一定是最低的。
Criterion这个参数正是用来决定不纯度的计算方法的,
两种方式衡量不纯度
1、参数选择 ”entropy“,使用信息熵(Entropy)
2、参数选择 ”gini“,使用基尼系数(Gini Impurity)
其中t代表给定的节点,i代表标签的任意分类, p(i|t)代表标签分类i在节点t上所占的比例。注意,当使用信息熵 时,sklearn实际计算的是基于信息熵的信息增益(Information Gain),即父节点的信息熵和子节点的信息熵之差。
比起基尼系数,信息熵对不纯度更加敏感,对不纯度的惩罚最强。但是在实际使用中,信息熵和基尼系数的效果基本相同。信息熵的计算比基尼系数缓慢一些,因为基尼系数的计算不涉及对数。另外,因为信息熵对不纯度更加敏 感,所以信息熵作为指标时,决策树的生长会更加“精细”,因此对于高维数据或者噪音很多的数据,信息熵很容易 过拟合,基尼系数在这种情况下效果往往比较好。当模型拟合程度不足的时候,即当模型在训练集和测试集上都表现不太好的时候,使用信息熵
参数设置调优:
按照最开始的程序,每次运行得到的score是不相同的,除了随机选取部分数据作为训练集的影响,更主要的是因为无论决策树模型如何进化,在分枝上的本质都还是追求某个不纯度相关的指标的优化,而正如我 们提到的,不纯度是基于节点来计算的,也就是说,决策树在建树时,是靠优化节点来追求一棵优化的树,但最优 的节点能够保证最优的树吗?集成算法被用来解决这个问题:sklearn表示,既然一棵树不能保证最优,那就建更多的不同的树,然后从中取最好的。怎样从一组数据集中建不同的树?在每次分枝时,不从使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样,每次生成的树也就不同了。
参数random_state & splitte
random_state用来设置分枝中的随机模式的参数,默认None,在高维度(特征多)时随机性会表现更明显,在低维度的数据随机性几乎不会显现。输入任意整数,会一直长出同一棵树,让模型稳定下来。
splitter也是用来控制决策树中的随机选项的,有两种输入值,输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看),输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。
clf = tree.DecisionTreeClassifier(criterion="entropy" ,random_state=30 ,splitter="random" ) clf = clf.fit(Xtrain, Ytrain) score = clf.score(Xtest, Ytest) score
可以看到通过设置这两个参数,可以将一棵树的得分稳定在一个较高的分数
剪枝参数:
max_depth
限制树的最大深度,超过设定深度的树枝全部剪掉
在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所 以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从3开始尝试,看看拟合的效果再决定是否增加设定深度。
min_samples_leaf & min_samples_split
min_samples_leaf 限定一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生。一般搭配max_depth使用,在回归树中有神奇的效果,可以让模型变得更加平滑。这个参数的数量设置得太小会引起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从5开始使用。如果叶节点中含有的样本量变化很 大,建议输入浮点数作为样本量的百分比来使用。同时,这个参数可以保证每个叶子的最小尺寸,可以在回归问题 中避免低方差,过拟合的叶子节点出现。
min_samples_split 限定一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则分枝就不会发生。
clf = tree.DecisionTreeClassifier(criterion="entropy" ,random_state=30 ,splitter="random" ,max_depth=3 # ,min_samples_leaf=10 # ,min_samples_split=25 ) clf = clf.fit(Xtrain, Ytrain) dot_data = tree.export_graphviz(clf,out_file=None ,feature_names= feature_name ,class_names=["琴酒","雪莉","贝尔摩德"] ,filled=True ,rounded=True ) graph = graphviz.Source(dot_data) graph
程序里将min_samples_leaf & min_samples_split注释的原因是加上这两个参数后,得分反而降低了,所以这些参数不是加了一定就有增强效果的,主要还是依赖于数据集,一切以增大得分为目的。可以看到将数的深度限制为3后得分又有了较大的提升。
max_features & min_impurity_decrease
max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃。和max_depth异曲同工。用来限制高维度数据的过拟合的剪枝参数,但其方法比较暴力,是直接限制可以使用的特征数量 而强行使决策树停下的参数,在不知道决策树中的各个特征的重要性的情况下,强行设定这个参数可能会导致模型学习不足。
min_impurity_decrease限制信息增益的大小,信息增益小于设定数值的分枝不会发生。
如何确定最佳的剪枝参数
利用一个简单的迭代,确定最好的max_depth值。
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,绘制不同参数下的score得分曲线图,可以直观的看到max_depth的最佳取值。
常用属性接口
属性是在模型训练之后,能够调用查看的模型的各种性质。对决策树来说,最重要的是feature_importances_,能 够查看各个特征对模型的重要性。在前面的代码中已经展示了它的作用。
sklearn中许多算法的接口都是相似的,比如说我们之前已经用到的fit和score,几乎对每个算法都可以使用。除了 这两个接口之外,决策树最常用的接口还有apply和predict。apply中输入测试集返回每个测试样本所在的叶子节点的索引,predict输入测试集返回每个测试样本的标签。