本文用通俗易懂的方式来讲解分类树中的回归树,并以“一维回归的图像绘制”和“泰坦尼克号幸存者预测”两个例子来说明该算法原理。
以下是本文大纲:
1 DecisionTreeRegressor1.1重要参数,属性及接口 criterion1.2 交叉验证 2实例:一维回归的图像绘制3实例:泰坦尼克号幸存者的预测4决策树的优缺点5附录5.1分类树参数列表5.2分类树属性列表5.3分类树接口列表
1 DecisionTreeRegressor 首先,我们来看看在sklearn中,这个库长什么样子吧:
class sklearn.tree.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 )
如果你读过本博客的前两篇关于决策树的文章,你会发现
几乎所有参数,属性及接口都和分类树一模一样。需要注意的是,在回归树种,没有标签分布是否均衡的问题,因此没有 class_weight 这样的参数。 下面我们介绍一下重要的参数和属性以及接口有哪些 1.1 重要参数,属性及接口 criterion 回归树衡量分枝质量的指标,支持的标准有三种:1 )输入 "mse" 使用均方误差 mean squared error(MSE) ,父节点和叶子节点之间的均方误差的差额将被用来作为特征选择的标准,这种方法通过使用叶子节点的均值来最小化 L2 损失。 2 )输入 “friedman_mse” 使用费尔德曼均方误差,这种指标使用弗里德曼针对潜在分枝中的问题改进后的均方误差。 3 )输入 "mae" 使用绝对平均误差 MAE ( mean absolute error ),这种指标使用叶节点的中值来最小化 L1 损失属性中最重要的依然是 feature_importances_ ,接口依然是 apply, fifit, predict, score 最核心。其中我们要看一下MAE是什么:
文章图片
其中 N 是样本数量, i 是每一个数据样本, fifi 是模型回归出的数值, yi 是样本点 i 实际的数值标签。 所以 MSE 的本质, 其实是样本真实数据与回归结果的差异。 在回归树中, MSE 不只是我们的分枝质量衡量指标,也是我们最常用的衡量回归树回归质量的指标 , 当我们在使用交叉验证,或者其他方式获取回归树的结果时,我们往往选择均方误差作 为我们的评估(在分类树中这个指标是score 代表的预测准确率)。 在回归中,我们追求的是, MSE 越小越好。然而, 回归树的接口 score 返回的是 R 平方,并不是 MSE 。 R 平方被定义如下:
文章图片
其中 u 是残差平方和( MSE * N ), v 是总平方和, N 是样本数量, i 是每一个数据样本, fifi 是模型回归出的数值, yi 是样本点i 实际的数值标签。 y 帽是真实数值标签的平均数。 R 平方可以为正为负(如果模型的残差平方和远远大于 模型的总平方和,模型非常糟糕,R 平方就会为负),而均方误差永远为正。值得一提的是,虽然均方误差永远为正,但是 sklearn 当中使用均方误差作为评判标准时,却是计算 ” 负均方误差 “ ( neg_mean_squared_error ) 。 这是因为 sklearn 在计算模型评估指标的时候,会考虑指标本身的性质,均 方误差本身是一种误差,所以被sklearn 划分为模型的一种损失 (loss) , 因此在 sklearn 当中,都以负数表示。真正的 均方误差MSE 的数值,其实就是 neg_mean_squared_error 去掉负号的数字。 下面我们来介绍另一个重要的知识点: 1.2 交叉验证 上篇文章我们写过,我们每次运行程序,得到的结果都不一样,我们通过建立多棵树,选择最优的那一棵来当我们的结果。 那么我们每次划分的训练集和测试集都不一样,每次得到的结果也不一样,我们如何来解决这个问题呢? 答案是交叉验证! 交叉验证是用来观察模型的稳定性的一种方法,我们将数据划分为 n 份,依次使用其中一份作为测试集,其他 n-1 份 作为训练集, 多次计算模型的精确性来评估模型的平均准确程度。训练集和测试集的划分会干扰模型的结果,因此 用交叉验证n 次的结果求出的平均值,是对模型效果的一个更好的度量。
文章图片
解释一下:我们将一个数据集划分成n份,每次取其中一份为测试机,剩下的为训练集,这样每一次划分都计算出一个精确性Ei,最后我们取平均值为最后的模型度量结果。 如果我们自己用代码来实现这个过程可能会很麻烦,但是,sklearn早已为我们封装好了,我们直接用就可以。 下面我们来介绍一下如何来使用sklearn来进行交叉验证: 首先我们导入相关的模块:
from sklearn.datasets import load_boston #我们导入波士顿的放假数据
from sklearn.model_selection import cross_val_score#导入交叉验证模块
from sklearn.tree import DecisionTreeRegressor#导入回归树模块
接着我们来看看数据长什么样:
boston = load_boston()
boston.data
文章图片
boston.target
文章图片
然后我们进行交叉验证:
regressor = DecisionTreeRegressor(random_state=0)#实例化
cross_val_score(regressor, boston.data, boston.target, cv=10,scoring = "neg_mean_squared_error") #交叉验证cross_val_score的用法,这里将数据集分为十个部分。
文章图片
解释一下:我们可以得到每次交叉验证得到的分数。 第一个参数是实例化后的模型。第二个参数是完整的数据集。第三个参数是完整数据集的标签。第四个参数表示交叉验证的次数,一般是5.第五个参数是指明交叉验证的分数指标。 2 实例:一维回归的图像绘制 接下来我们到二维平面上来观察决策树是怎样拟合一条曲线的。 我们用回归树来拟合正弦曲线,并添加一些噪声来 观察回归树的表现。首先导入使用的包:
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
然后我们开始造一个数据集:
rng = np.random.RandomState(1) #随机数种子
X = np.sort(5 * rng.rand(80,1), axis=0) #生成0~5之间随机的x取值,数据为80行1列(二维的),因为训练时候不允许有一维特征。sort是排序。
y = np.sin(X).ravel()#生成正弦曲线。ravel函数是将数据降成一维的。
y[::5] += 3 * (0.5 - rng.rand(16))#在正弦曲线上加噪声,每5个步长取一个数 ,共16个数,结果噪声就在3*(-0.5,0.5)=(-1.5,1.5)之间。
这四行代码已经做了比较详细的注释,若不清楚,请百度一下每个函数的作用,做做实验就会懂的。
然后我们将造好的数据画出来看看:
plt.figure()
plt.scatter(X,y,s=20,edgecolor="black",c="darkorange",label="data")
文章图片
解释一下:这些噪音就相当于我们平常做实验的时候所遇到的一些不好的数据
这里我们建立两个模型,目的是为了看不同拟合效果(max_depth不一样)能得到什么结果。
regr_1 = DecisionTreeRegressor(max_depth=2)
regr_2 = DecisionTreeRegressor(max_depth=5)
regr_1.fit(X, y)
regr_2.fit(X, y)
好了,现在模型已经训练好,现在我们得找一个数据来训练一下,看看模型的效果:
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]#这里是生成0~5,步长为0.01的数列,后面[]的功能是增维
y_1 = regr_1.predict(X_test)#得到模型1的结果
y_2 = regr_2.predict(X_test)#得到模型2的结果
我们将数据图、模型1的结果、模型2的结果画在一张图上:
plt.figure()
plt.scatter(X, y, s=20, edgecolor="black",c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",label="max_depth=2", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
文章图片
解释一下:我们可以很清晰的看到,当max_depth为2的时候,效果还算好,当max_depth为5的时候,模型会对数据有更强的拟合,但是结果却不如人意,结果画出来的图很夸张有没有。那就说明受噪声影响比较大。
3 实例:泰坦尼克号幸存者的预测 泰坦尼克号的沉没是世界上最严重的海难事故之一,今天我们通过分类树模型来预测一下哪些人可能成为幸存者。数据集来着 https://www.kaggle.com/c/titanic ,数据集会随着代码一起提供给大家,大家可以在下载页面拿到。 首先我们导入所需要的库:
import pandas as pd
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
然后我们导入数据集,并看看数据集长什么样吧:
data = https://www.it610.com/article/pd.read_csv(r"D:\学习笔记\毛维杨\data.csv",index_col= 0)
data
文章图片
可以看到这是一个891行11列的数据集。
接着,我们看看数据信息。
data.info()
文章图片
以上把数据集的详细信息都展示了出来。
有一个问题,要是这个数据集有100亿条呢,我们还要全部拿出来看看吗?
当然不用!
data.head(10)#默认为5,这里我们来看看前10行
文章图片
上面这个函数就能取出数据集中的前n行展示出来,默认为5.
拿到数据集了,首先当然是处理一下,取其精华去其糟粕!
data.drop(["Cabin","Name","Ticket"],inplace=True,axis=1) #删除缺失值过多的列,和观察判断来说和预测的y没有关系的列
data["Age"] = data["Age"].fillna(data["Age"].mean())#处理缺失值,对缺失值较多的列进行填补,有一些特征只确实一两个值,可以采取直接删除记录的方法
data = https://www.it610.com/article/data.dropna()#将有缺失值的行删掉#将分类变量转换为数值型变量
#astype能够将一个pandas对象转换为某种类型,和apply(int(x))不同,astype可以将文本类转换为数字,用这个方式可以很便捷地将二分类特征转换为0~1
data["Sex"] = (data["Sex"]== "male").astype("int") #将二分类变量转换为数值型变量0和1#将三分类变量转换为数值型变量
labels = data["Embarked"].unique().tolist()#将Embarked属性的三个标签取出来(是一个数组),再用tolist函数将数组转化为列表。
data["Embarked"] = data["Embarked"].apply(lambda x: labels.index(x))#属性标签列表的索引替换掉文本
#查看处理后的数据集
data.head()
(1)我们要做的是预测幸运者,那么有些属性和我们要得到的结果根本没啥关系,比如说票号,姓名,几等仓,这些和是否在灾难中能幸存关系不大,可以不要,第一行代码就是做这个事情的。inplace的意思是说删除完这些列后,覆盖原来没删除的表,默认为false。axis=1是说对列进行删除,为0的时候是对行进行删除。
【机器学习|机器学习-决策树之回归树python实战(预测泰坦尼克号幸存情况)(三)】(2)处理缺失值,有些特征它缺少一些数据,但不是缺失严重,且比较重要,我们可以进行填补,比如说本数据集的age属性,缺失200+个,但是在灾难中老人和孩子一般优先上救生艇,所以它对我们的实验很重要,我们不能直接删除,要进行填补。第二行就是用均值对age属性进行填补。当然填补还有很多方法。
(3)将有缺失值的行进行删除,第三行就是实现了这个功能
(4)文本数据我们不好处理,所以我们要将属性中的文本型数据转化为数值型数据,以上代码的后几行实现了这个功能,已经写了详细的注释。
我们来看看处理后的数据:
文章图片
是不是更“漂亮”了!
下面开始建模:
X = data.iloc[:,data.columns != "Survived"] #取出所有的行,其中去掉属性“Survived”
y = data.iloc[:,data.columns == "Survived"] #取出所有的行,仅包含属性“Survived”
from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3) #划分训练集和测试集
for i in [Xtrain, Xtest, Ytrain, Ytest]:#修正训练集和测试集的索引
i.index = range(i.shape[0])#查看分好的训练集和测试集
Xtrain.head()
文章图片
分完训练集和测试集后,我们的训练集和测试集的索引会变乱,所以我们要对其进行修正。
下面我们来进行建模:
tr = []
te = []
for i in range(10):
clf = DecisionTreeClassifier(random_state=25,max_depth=i+1,criterion="entropy")
clf = clf.fit(Xtrain, Ytrain)
score_tr = clf.score(Xtrain,Ytrain)
score_te = cross_val_score(clf,X,y,cv=10).mean()
tr.append(score_tr)
te.append(score_te)
print(max(te))
plt.plot(range(1,11),tr,color="red",label="train")
plt.plot(range(1,11),te,color="blue",label="test")
plt.xticks(range(1,11))
plt.legend()
plt.show()
#这里为什么使用“entropy”?因为我们注意到,在最大深度=3的时候,模型拟合不足,在训练集和测试集上的表现接近,但却都不是非常理想,只能够达到83%左右,所以我们要使用entropy。
文章图片
本文首先以max_depth为例来构建学习曲线,我们以max_depth取值为横轴,以得分score为纵轴画图,图中包含训练集和测试集两条曲线。
可以看到,当max_depth越来越大的时候,模型的过拟合程度越来约严重,其中最好的点是在3附近,通过实验可以知道,最大的得分是0.81778,这个分数依然不是那么理想。
那么我们要找出最合适的剪枝方案(也就是每个参数设置成什么最好),难道要我们像max_depth一样一个一个去画图观察?那样工作量巨大!
当然不是!
因为在剪纸这一步有很多可以调节的参数,如何寻找到一个最优的参数组合就成了一个问题,网格搜索方法(GridSearchCV)就解决了这个问题。
GridSearchCV,它存在的意义就是自动调参,只要把参数输进去,就能给出最优化的结果和参数。但是这个方法适合于小数据集,一旦数据的量级上去了,很难得出结果。
这个时候就是需要动脑筋了。数据量比较大的时候可以使用一个快速调优的方法——坐标下降。它其实是一种贪心算法:拿当前对模型影响最大的参数调优,直到最优化;
再拿下一个影响最大的参数调优,如此下去,直到所有的参数调整完毕。
这个方法的缺点就是可能会调到局部最优而不是全局最优,但是省时间省力,巨大的优势面前,还是试一试吧,后续可以再拿bagging再优化。
本文利用该方法对模型进行处理,其中cv值设为10,即做10次交叉验证:
import numpy as np
gini_thresholds = np.linspace(0,0.5,20)
parameters = {'splitter':('best','random'),'criterion':("gini","entropy"),"max_depth":[*range(1,10)],'min_samples_leaf':[*range(1,50,5)],'min_impurity_decrease':[*np.linspace(0,0.5,20)]}
clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10)
GS.fit(Xtrain,Ytrain)
文章图片
我们来看看最好的剪枝方案
GS.best_params_
文章图片
再根据最好的剪枝方案得到最好的得分情况
GS.best_score_
文章图片
最后我们来画出决策树的图:
文章图片
4 决策树的优缺点 决策树优点1.易于理解和解释,因为树木可以画出来被看见。 2.需要很少的数据准备。其他很多算法通常都需要数据规范化,需要创建虚拟变量并删除空值等。但请注意, sklearn中的决策树模块不支持对缺失值的处理。3.使用树的成本(比如说,在预测数据的时候)是用于训练树的数据点的数量的对数,相比于其他算法,这是 一个很低的成本。4.能够同时处理数字和分类数据,既可以做回归又可以做分类。其他技术通常专门用于分析仅具有一种变量类 型的数据集。5.能够处理多输出问题,即含有多个标签的问题,注意与一个标签中含有多种标签分类的问题区别开。 6.是一个白盒模型,结果很容易能够被解释。如果在模型中可以观察到给定的情况,则可以通过布尔逻辑轻松 解释条件。相反,在黑盒模型中(例如,在人工神经网络中),结果可能更难以解释。7.可以使用统计测试验证模型,这让我们可以考虑模型的可靠性。8.即使其假设在某种程度上违反了生成数据的真实模型,也能够表现良好。决策树的缺点1.决策树学习者可能创建过于复杂的树,这些树不能很好地推广数据。这称为过度拟合。修剪,设置叶节点所 需的最小样本数或设置树的最大深度等机制是避免此问题所必需的,而这些参数的整合和调整对初学者来说 会比较晦涩。 2.决策树可能不稳定,数据中微小的变化可能导致生成完全不同的树,这个问题需要通过集成算法来解决。3.决策树的学习是基于贪婪算法,它靠优化局部最优(每个节点的最优)来试图达到整体的最优,但这种做法 不能保证返回全局最优决策树。这个问题也可以由集成算法来解决,在随机森林中,特征和样本会在分枝过 程中被随机采样。4.有些概念很难学习,因为决策树不容易表达它们,例如 XOR ,奇偶校验或多路复用器问题。5.如果标签中的某些类占主导地位,决策树学习者会创建偏向主导类的树。因此,建议在拟合决策树之前平衡 数据集。 5 附录 5.1 分类树参数列表
文章图片
文章图片
文章图片
文章图片
5.2 分类树属性列表
文章图片
5.3 分类树接口列表
文章图片
至此,关于决策树的全部内容整理完毕,接下来开始新的算法整理。
本文参考教程:菜菜的sk-learn课程
如有写的不合适,亦或是不精确的地方,望读者多包涵
如果大家对上述内容有任何不理解的地方,可以留言,和毛同学共同讨论,一起进步!
qq:1518887260
相关文章链接:
机器学习-万字长文介绍决策树之原理(一)
机器学习-决策树之分类树python实战(以红酒数据集为例)(二)
整理于2020年12月15日
推荐阅读
- 机器学习|《python机器学习基础教程》笔记(第2章监督学习)(第2部分)
- 数学建模算法|2022国赛数学建模思路算法分析—XGboost
- 数学建模算法|2022国赛数学建模思路算法案例k—means聚类分析
- 数学建模比赛|2022华为杯研究生数学建模赛题思路分析
- 机器学习|2021年华为杯数学建模比赛——二分类与回归问题(1)
- 学习|深度学习中(使用GPU和CPU的区别)
- 机器学习|【机器学习】为什么进行特征筛选时前要先用相关性筛一遍再进行降维()
- 机器学习|pytorch查看网络架构的几种方法
- 单细胞系列|SCS【6】单细胞转录组之细胞类型自动注释 (SingleR)