本文主要内容来自视频 '【2020机器学习全集】菜菜的sklearn完整版,价值4999元的最全机器学习sklearn全集,赶紧收藏_哔哩哔哩_bilibili' 以及视频课件“https://pan.baidu.com/s/1Xl4o0PMA5ysUILeCKvm_2w,提取码:a967”。
本文是一个学习笔记,不是一篇帮助新人入门的文章,其内容主要针对本人的薄弱环节,没有面面俱到,不一定适用所有人。
本文主要介绍了决策树的一些重要概念,比如节点不纯度、误差率、信息熵、基尼系数、信息增益、分支度以及增益率的数学计算原理;总结了使用sklearn工具包实现决策树的一些重要参数、接口及属性;还列举了两个sklearn实现回归以及分类树的例子;最后也是最重要的,对回归树的实现过程做了十分详细地介绍,并给出了代码对比。如果你想了解上述内容,本文将会适合你。
决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。
(1)节点不纯度
决策树的每个叶子节点中都会包含一组数据,在这组数据中,如果有某一类标签占有较大的比例,就说叶子节点纯”,分枝分得好。某一类标签占的比例越大,叶子就越纯,不纯度就越低,分枝就越好;如果没有哪一类标签的比例很大,各类标签都相对平均,则说叶子节点"不纯",分枝不好,不纯度高。
(2)误差率(error)
衡量节点不纯度的指标之一。计算公式如下:
(3)信息熵(entropy)
ID3采用的节点不纯度衡量指标。计算公式如下:
(4)基尼系数(gini impurity)
CART采用的节点不纯度的衡量指标。计算公式如下:
(5)信息增益(information gain)
父节点信息熵和子节点信息熵之差。计算公式如下:
特别地,有可能一个父节点之下会有多个子节点,此时的信息增益应该是父节点的信息熵 - 所有子节点信息熵的加权平均,其中的权重由单个叶子节点上所占的样本量与父节点上的总样本量的比值确定:
(5)分支度(information value)
一个对信息增益计算的修正方法。分支度简言之就是一个控制分类水平的惩罚项,计算方法如下:
(6)增益率(gain ratio)
C4.5采用的节点不纯度衡量指标,该指标通过修正原有增益率的计算公式得到,其计算方法为:
增益率的本质是同时考虑了信息增益以及分支度,分支倾向于选择信息增益大而分支度小的分类方法(也就是纯度提升很快,但也会避免把类别分特别细)。
tree.DecisionTreeClassifier | 分类树 |
tree.DecisionTreeRegressor | 回归树 |
tree.export_graphviz | 将生成的决策树导出为DOT格式,画图专用 |
tree.ExtraTreeClassifier | 高随机版本的分类树 |
tree.ExtraTreeRegressor | 高随机版本的回归树 |
(0)criterion
criterion这个参数是用来决定不纯度的计算方法的,分为基尼系数(geni)与信息(entropy)。信息熵对不纯度更加敏感,维度大时使用信息熵容易过拟合,因此,对于高维度数据,使用基尼系数;低维度是两者都可以;欠拟合时使用信息熵。默认使用的是基尼系数。使用方法如下:
clf = tree.DecisionTreeClassifier(criterion="entropy")
(1)random_state
对于高维度的数据。每次执行决策树的结果很可能不一样,设置random_state可以使得结果重现。使用方法为:
clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30)
(2)splitter
splitter决定分支的随机状态。best:随机,但是会优先选择重要特征进行分类;random:比best更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合,是防止过拟合手段之一。使用方法为:
# 默认就是best,可以不写
clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30,splitter="best" )
(3)减枝操作
max_depth | 设置最大深度,最好从三开始 |
min_samples_leaf | 限定一个节点分支后的每个子节点必须要包含至少min_samples_leaf个训练样本,一般搭配max_depth使用,使得模型更加平滑,太小过拟合,太大欠拟合,5开始,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生 |
min_samples_split | 限定一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝 |
min_impurity_decrease | 限制信息增益的大小,信息增益小于设定数值的分枝不会发生 |
max_features | 设定允许使用的特征个数 |
使用方法如下:
clf = tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30
,splitter="random"
,max_depth=3 # 设置最大深度为3,只有三层子节点,减少计算量
,min_samples_leaf=10 # 限制样本数目,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生
,min_samples_split=20 # 限定是否产生分支
,min_impurity_decrease=0.001 # 父子节点信息增益为0.001以下不再产生新的分支
,max_features=4 # 只允许使用4个特征
)
(0)fit
训练接口,实现决策树训练。使用方法:
clf = clf.fit(Xtrain, Ytrain)
(1)score
测试接口,计算训练准确度。使用方法:
score = clf.score(Xtest, Ytest) # 返回预测的准确度accuracy
(2)apply
返回每个测试样本所在的叶子节点的索引。使用方法如下:
clf.apply(Xtest)
(3)predict
返回每个测试样本的分类/回归结果。使用方法如下:
clf.predict(Xtest)
feature_importances_属性用于查看各个特征的重要性,使用方法如下:
clf.feature_importances_
导入有关的包(这里只给出与决策树有关的包,其他可能会用的包,比如pandas、matplotlib等不写出,根据实际需要import):
from sklearn import tree # 导入决策树
from sklearn.model_selection import train_test_split # 用于划分测试集和训练集
import graphviz # 用于绘制树状图
from sklearn.datasets import load_wine # 导入sklearn中的数据(实际应用中根据需要选择自己的数据)
加载数据:
wine = load_wine() # 导入数据
划分训练集和测试集:
# 划分数据为测试集与训练集,0.3表示0.3的数据为测试集,其余为训练集,4个变量的顺序一定不能乱
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data, wine.target, test_size=0.3)
实例化:
clf = tree.DecisionTreeClassifier(criterion="entropy") # 实例化
训练接口:
clf = clf.fit(Xtrain, Ytrain) # 开始训练
查看预测准确度:
score = clf.score(Xtest, Ytest) # 返回预测的准确度accuracy
绘制分类树:
# 设定特征名称,会根据特征名称进行分类,否则就是数字
feature_name = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '总酚', '类黄酮',
'非黄烷类酚类', '花青素', '颜色强度', '色调', 'od280/od315稀释葡萄酒', '脯氨酸']
# 绘制分类树
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True, rounded=True
) # filled表示填充颜色,rounded加圆角,颜色越深纯度越高
graph = graphviz.Source(dot_data)
graph
分类树的原理浅显易懂,不言自明。回归树的原理却不那么显然,下面专门花一个章节讨论一下Regression Tree的实现过程。
步骤1:对每一个变量j,选择不同的切分点s进行切分,每一个变量j及其对应的每一个切分方式,都对应一个数值对(j,s),按照这个(j,s)划分区域并按照以下公式计算输出值:
步骤2:计算出每一个特征变量j对应的最优的一种切分方式,计算公式如下:
重复执行步骤2操作直到选择出所有特征变量最优的切分方式。
步骤3:按照以下公式选择出最优的特征变量及其对应的切分方式:
步骤4:对获取的两个子区域中的每一个执行步骤1到步骤3过程;
步骤5:重复步骤1到步骤4,直到满足结束条件;
步骤6:将所有空间区域划分为M个区域,生成决策树,输出结果。
算法步骤属实有点晦涩难懂,下面结合一个简单实例手动实现一个回归树计算过程。
x | 1 | 2 | 3 | 4 |
y | 1 | 2 | 3 | 4 |
步骤1:对每一个特征变量j,选择不同的划分方式对其进行划分,这里以1.5, 2.5, 3.5作为划分点。若s=1.5,这R1={1},R2={2,3,4},因此c1=1/1*(1)=1;c2=1/3*(2+3+4)=3;s=2.5,这R1={1,2},R2={3,4},因此c1=1/2*(1+2)=1.5;c2=1/2*(3+4)=3.5;s=3.5,这R1={1,2,3},R2={4},因此c1=1/3*(1+2+3)=2;c2=1/1*(4)=4。
s | 1.5 | 2.5 | 3.5 |
c1 | 1 | 1.5 | 2 |
c2 | 3 | 3.5 | 4 |
步骤2:计算每一个特征变量最优的一种切分方式:
s | 1.5 | 2.5 | 3.5 |
Loss | 2 | 1 | 2 |
根据loss计算结果,对于特征变量x,其最优的划分应该是x=2.5。
步骤3:选择最优的特征变量,本例就一个变量x,故不用选择,这步直接跳过。
步骤4:按照前三步得到的划分方式,得到两个新的子区间R1={1,2};R2={3,4},对这两个区间执行步骤1到步骤3的所有过程。
对于R1,可以得到以下表格:
s | 1.5 |
c1 | 1 |
c2 | 2 |
对于R2,可以得到以下表格:
s | 3.5 |
c1 | 3 |
c2 | 4 |
此时已经达到结束条件,总共划分出4个区间:
以上就是回归树的整个计算过程。
下面以代码为证:
from sklearn.tree import DecisionTreeRegressor
from sklearn import tree
import numpy as np
import graphviz
import matplotlib.pyplot as plt
X = np.array([1, 2, 3, 4])[:, np.newaxis]
x = np.linspace(1, 4, 10)[:, np.newaxis]
Y = np.array([1, 2, 3, 4])[:, np.newaxis]
regr = DecisionTreeRegressor(max_depth=2)
regr.fit(X, Y)
y = regr.predict(x)
plt.figure()
plt.scatter(X, Y, s=20, edgecolor="black", c="darkorange")
plt.plot(x, y, color="yellowgreen", linewidth=2)
plt.show()
dot_data = tree.export_graphviz(regr, filled=True, rounded=True)
graph = graphviz.Source(dot_data)
graph
执行上述代码后的结果为:
树状图为:
这与我们的上述推导过程完全一致。
回归树几乎所有的参数,属性及接口都和分类树一模一样。下面只提出一些专用于回归树的参数
回归树衡量分枝质量的指标,支持的标准有三种:
mse | mean squared error,父节点和叶子节点之间的均方误差的差额被用来作为特征选择的标准,这种方法通过使用叶子节点的均值来最小化L2损失 |
mae |
mean absolute error,这种指标使用叶节点的中值来最小化L1损失 |
friedman_mse |
这种指标使用弗里德曼针对潜在分枝中的问题改进后的均方误差 |
mse采用一下公式计算:
其中,fi是模型回归出的数值,yi是样本点i实际的数值标签。MSE的本质其实是样本真实数据与回归结果的差异。在回归树中,MSE不只是分枝质量衡量指标,也是最常用的衡量回归树回归质量的指标。
需要注意的是回归树的score返回的不是mse,而是。的计算公式如下:
其中,u是残差平方和,v是总平方和,其计算公式为:
其中,fi是模型回归出的数值,yi是样本点i实际的数值标签,是真实数据标签的平均数。由的表达式可以看出。当模型的残差平方和远远大于模型的总平方和,模型非常糟糕,R平方就会为负。
需要注意的还有一点,虽然均方误差永远为正,但是sklearn当中使用均方误差作为评判标准时,却是计算”负均方误差“(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss),因此在sklearn当中,都以负数表示。真正的均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。
cross_val_score()表示交叉验证,交叉验证是用来观察模型稳定性的一种方法,将数据划分为n份,依次使用其中一份作为测试集,其他n-1份作为训练集,多次计算模型的精确性来评估模型的平均准确程度。因为训练集和测试集的划分会干扰模型的结果,采用交叉验证n次的结果求出的平均值,是对模型效果的一个更好的度量。使用方法为:
regressor = DecisionTreeRegressor(random_state=0) # 实例化
cross_val_score(regressor,
boston.data,
boston.target,
cv=10,
scoring="neg_mean_squared_error") # 划分为10份交叉验证,采用neg_mean_squared_error计算误差
导入必要的包:
import numpy as np
from sklearn.tree import DecisionTreeRegressor
生成训练数据并加噪声:
rng = np.random.RandomState(1) # 随机数种子
X = np.sort(5 * rng.rand(500,1), axis=0) # 生成0~5之间随机的x的取值
y = np.sin(X).ravel() # 生成正弦曲线,打平称为1维
y[::50] += 0.5*(0.5 - rng.rand(500//50)) #在正弦曲线上加噪声,每五十个加噪声
生成测试数据:
X_test = np.arange(0.0, 5.0, 0.001)[:, np.newaxis] # 升维
实例化回归树:
regr_2 = DecisionTreeRegressor(max_depth=10) # 深度为10
训练模型:
regr_2.fit(X, y)
获取预测值:
y_2 = regr_2.predict(X_test)