一.概述
1.集成算法概述
集成学习(ensemble learning)是时下非常流行的机器学习算法,它本身不是一个单独的机器学习算法,而是通 过在数据上构建多个模型,集成所有模型的建模结果。基本上所有的机器学习领域都可以看到集成学习的身影,在 现实中集成学习也有相当大的作用,它可以用来做市场营销模拟的建模,统计客户来源,保留和流失,也可用来预 测疾病的风险和病患者的易感性。在现在的各种算法竞赛中,随机森林,梯度提升树(GBDT),Xgboost等集成 算法的身影也随处可见,可见其效果之好,应用之广。
集成算法的目标 |
---|
集成算法会考虑多个评估器的建模结果,汇总之后得到一个综合的结果,以此来获取比单个模型更好的回归或 分类表现。 |
多个模型集成成为的模型叫做集成评估器(ensemble estimator),组成集成评估器的每个模型都叫做基评估器 (base estimator)。通常来说,有三类集成算法:装袋法(Bagging),提升法(Boosting)和stacking。
装袋法的核心思想是构建多个相互独立的评估器,然后对其预测进行平均或多数表决原则来决定集成评估器的结 果。装袋法的代表模型就是随机森林。
提升法中,基评估器是相关的,是按顺序一一构建的。其核心思想是结合弱评估器的力量一次次对难以评估的样本 进行预测,从而构成一个强评估器。提升法的代表模型有Adaboost和梯度提升树。
2.sklearn中的集成算法
sklearn中的集成算法模块ensemble
类 | 类的功能 |
---|---|
ensemble.AdaBoostClassifie | AdaBoost分类 |
ensemble.AdaBoostRegressor | Adaboost回归 |
ensemble.BaggingClassifier | 装袋分类器 |
ensemble.BaggingRegressor | 装袋回归器 |
ensemble.ExtraTreesClassifier | Extra-trees分类(超树,极端随机树 |
ensemble.ExtraTreesRegressor | Extra-trees回归 |
ensemble.GradientBoostingClassifier | 梯度提升分类 |
ensemble.GradientBoostingRegressor | 梯度提升回归 |
ensemble.IsolationForest | 隔离森林 |
ensemble.RandomForestClassifier | 随机森林分类 |
ensemble.RandomForestRegressor | 随机森林回归 |
ensemble.RandomTreesEmbedding | 完全随机树的集成 |
ensemble.VotingClassifier | 用于不合适估算器的软投票/多数规则分类器 |
集成算法中,有一半以上都是树的集成模型,可以想见决策树在集成中必定是有很好的效果
3.回顾sklearn的基本建模流程
在这个流程下,随机森林对应的代码和决策树基本一致:
from sklearn.tree import RandomForestClassifier ##导入需要的模块
rfc = RandomForestClassifier() #实例化
rfc = rfc.fit(X_train,y_train) #用训练集数据训练模型
result = rfc.score(X_test,y_test) #导入测试集,从接口中调用需要的信息
**4. RandomForestClassifier **
随机森林是非常具有代表性的Bagging集成算法,它的所有基评估器都是决策树,分类树组成的森林就叫做随机森 林分类器,回归树所集成的森林就叫做随机森林回归器。这一节主要讲解RandomForestClassifier,随机森林分类 器。
二.重要参数
1. 控制基评估器的参数
参数 | 含义 |
---|---|
criterion | 不纯度的衡量指标,有基尼系数和信息熵两种选择 |
max_depth | 树的最大深度,超过最大深度的树枝都会被剪 |
min_samples_leaf | 一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样 本,否则分枝就不会发生 |
min_samples_split | 一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分 枝,否则分枝就不会发生 |
max_features | |
max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃, 默认值为总特征个数开平方取整 | |
min_impurity_decrease | 限制信息增益的大小,信息增益小于设定数值的分枝不会发生 |
单个决策树的准确率越高,随机森林的准确率也会越高,因为装袋法是依赖于平均值或 者少数服从多数原则来决定集成的结果的
2. n_estimators
这是森林中树木的数量,即基基评估器的数量。这个参数对随机森林模型的精确性影响是单调的,n_estimators 越大,模型的效果往往越好。但是相应的,任何模型都有决策边界,n_estimators达到一定的程度之后,随机森林 的精确性往往不在上升或开始波动,并且,n_estimators越大,需要的计算量和内存也越大,训练的时间也会越来 越长。对于这个参数,我们是渴望在训练难度和模型效果之间取得平衡。
n_estimators的默认值在现有版本的sklearn中是10,但是在即将更新的0.22版本中,这个默认值会被修正为 100。这个修正显示出了使用者的调参倾向:要更大的n_estimators。
建立一片森林
树模型的优点是简单易懂,可视化之后的树人人都能够看懂,可惜随机森林是无法被可视化的。所以为了更加直观 地让大家体会随机森林的效果,我们来进行一个随机森林和单个决策树效益的对比。我们依然使用红酒数据集
导入所需要的包
%matplotlib inline
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
导入需要的数据集
wine = load_wine()
wine.data
wine.target
复习:sklearn建模基本流程
from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest =train_test_split(wine.data,wine.target,test_size=0.3)
clf = DecisionTreeClassifier(random_state=0)
rfc = RandomForestClassifier(random_state=0)
clf = clf.fit(Xtrain,Ytrain) rfc = rfc.fit(Xtrain,Ytrain)
score_c = clf.score(Xtest,Ytest)
score_r = rfc.score(Xtest,Ytest
print("Single Tree:{}".format(score_c)
,"Random Forest:{}".format(score_r)
)
画出随机森林和决策树在十组交叉验证下的效果对比
#复习一下交叉验证
#交叉验证:是数据集划分为n分,依次取每一份做测试集,每n-1份做训练集,多次训练模型以观测模型稳定性的方法
rfc_l = []
clf_l = []
for i in range(10):
rfc = RandomForestClassifier(n_estimators=25)
rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
rfc_l.append(rfc_s)
clf = DecisionTreeClassifier()
clf_s = cross_val_score(clf,wine.data,wine.target,cv=10).mean()
clf_l.append(clf_s)
plt.plot(range(1,11),rfc_l,label = "Random Forest")
plt.plot(range(1,11),clf_l,label = "Decision Tree")
plt.legend() plt.show()
#是否有注意到,单个决策树的波动轨迹和随机森林一致? #再次验证了我们之前提到的,单个决策树的准确率越高,随机森林的准确率也会越高
** n_estimators的学习曲线**
#####【TIME WARNING: 2mins 30 seconds】#####
superpa = []
for i in range(200):
rfc = RandomForestClassifier(n_estimators=i+1,n_jobs=-1)
rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
superpa.append(rfc_s)
print(max(superpa),superpa.index(max(superpa)))
plt.figure(figsize=[20,5])
plt.plot(range(1,201),superpa)
plt.show()
思考:随机森林用了什么方法,来保证集成的效果一定好于单个分类器?
二.重要属性和接口
随机森林中有三个非常重要的属性:.estimators_,.oob_score_以及.feature_importances_。
.estimators_是用来查看随机森林中所有树的列表的。
oob_score_指的是袋外得分。随机森林为了确保林中的每棵树都不尽相同,所以采用了对训练集进行有放回抽样的 方式来不断组成信的训练集,在这个过程中,会有一些数据从来没有被随机挑选到,他们就被叫做“袋外数据”。这 些袋外数据,没有被模型用来进行训练,sklearn可以帮助我们用他们来测试模型,测试的结果就由这个属性 oob_score_来导出,本质还是模型的精确度。
而.feature_importances_和决策树中的.feature_importances_用法和含义都一致,是返回特征的重要性。
随机森林的接口与决策树完全一致,因此依然有四个常用接口:apply, fit, predict和score。除此之外,还需要注 意随机森林的predict_proba接口,这个接口返回每个测试样本对应的被分到每一类标签的概率,标签有几个分类 就返回几个概率。如果是二分类问题,则predict_proba返回的数值大于0.5的,被分为1,小于0.5的,被分为0。 传统的随机森林是利用袋装法中的规则,平均或少数服从多数来决定集成的结果,而sklearn中的随机森林是平均 每个样本对应的predict_proba返回的概率,得到一个平均概率,从而决定测试样本的分类。
#分别取尝试一下这些属性和接口
rfc = RandomForestClassifier(n_estimators=25)
rfc = rfc.fit(Xtrain, Ytrain)
rfc.score(Xtest,Ytest)
rfc.feature_importances_
rfc.apply(Xtest)
rfc.predict(Xtest)
rfc.predict_proba(Xtest)
掌握了上面的知识,基本上要实现随机森林分类已经是没问题了。从红酒数据集的表现上来看,随机森林的效用比 单纯的决策树要强上不少,大家可以自己更换其他数据来试试看(比如上周完整课案例中的泰坦尼克号数据)。
三.机器学习中调参的基本思想 **
大多数的机器学习相关的书都是遍历各种算法和案例,为大家讲解各种各样算法 的原理和用途,但却对调参探究甚少。这中间有许多原因,其一是因为,调参的方式总是根据数据的状况而定,所 以没有办法一概而论;其二是因为,其实大家也都没有特别好的办法
通过画学习曲线,或者网格搜索,我们能够探索到调参边缘(代价可能是训练一次模型要跑三天三夜),但是在现 实中,高手调参恐怕还是多依赖于经验,而这些经验,来源于:1)非常正确的调参思路和方法,2)对模型评估指 标的理解,3)对数据的感觉和经验,4)用洪荒之力去不断地尝试。
我们可以学习他们对模型评估指标的理解和调参的思路。
首先来讲讲正确的调参思路。模型调参,第一步是要找准目标:我们要做什么?一般来说,这个目标是提升 某个模型评估指标,比如对于随机森林来说,我们想要提升的是模型在未知数据上的准确率(由score或 oob_score_来衡量)。找准了这个目标,我们就需要思考:模型在未知数据上的准确率受什么因素影响?在机器学 习中,我们用来衡量模型在未知数据上的准确率的指标,叫做泛化误差**(Genelization error)
1.泛化误差
当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果 不好。泛化误差受到模型的结构(复杂度)影响。看下面这张图,它准确地描绘了泛化误差与模型复杂度的关系, 当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力 就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标
那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所 以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随 机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。当然了,调参 没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。
泛化误差的背后其实是“偏差-方差困境”,原理十分复杂,无论你翻开哪一本书,你都会看见长篇的数学论证和每个 字都能看懂但是连在一起就看不懂的文字解释。在下一节偏差vs方差中,我用最简单易懂的语言为大家解释了泛化 误差背后的原理,大家选读。那我们只需要记住这四点:
1)模型太复杂或者太简单,都会让泛化误差高,我们追求的是位于中间的平衡点
2)模型太复杂就会过拟合,模型太简单就会欠拟合
3)对树模型和树的集成模型来说,树的深度越深,枝叶越多,模型越复杂
4)树模型和树的集成模型的目标,都是减少模型复杂度,把模型往图像的左边移动
那具体每个参数,都如何影响我们的复杂度和模型呢?我们一直以来调参,都是在学习曲线上轮流找最优值,盼望 能够将准确率修正到一个比较高的水平。然而我们现在了解了随机森林的调参方向:降低复杂度,我们就可以将那 些对复杂度影响巨大的参数挑选出来,研究他们的单调性,然后专注调整那些能最大限度让复杂度降低的参数。对 于那些不单调的参数,或者反而会让复杂度升高的参数,我们就视情况使用,大多时候甚至可以退避。基于经验, 我对各个参数对模型的影响程度做了一个排序。在我们调参的时候,大家可以参考这个顺序。
我们现在也能够通过参数的变化来了解,模型什么时候到达了极限,当复杂度已经不能再降 低的时候,我们就不必再调整了,因为调整大型数据的参数是一件非常费时费力的事。除了学习曲线和网格搜索, 我们现在有了基于对模型和正确的调参思路的“推测”能力,这能够让我们的调参能力更上一层楼。
2.偏差vc方差
一个集成模型(f)在未知数据集(D)上的泛化误差E(f;D),由方差(var),偏差(bais)和噪声(ε)共同决定。
E ( f ; D ) = b i a s 2 ( x ) + v a r ( x ) + ϵ 2 E(f;D)=bias^2(x)+var(x)+\epsilon^2 E(f;D)=bias2(x)+var(x)+ϵ2
关键概念:偏差与方差 |
---|
观察下面的图像,每个点就是集成算法中的一个基评估器产生的预测值。红色虚线代表着这些预测值的均值, 而蓝色的线代表着数据本来的面貌。 偏差:模型的预测值与真实值之间的差异,即每一个红点到蓝线的距离。在集成算法中,每个基评估器都会有 自己的偏差,集成评估器的偏差是所有基评估器偏差的均值。模型越精确,偏差越低。 方差:反映的是模型每一次输出结果与模型预测值的平均水平之间的误差,即每一个红点到红色虚线的距离, 衡量模型的稳定性。模型越稳定,方差越低 |
其中偏差衡量模型是否预测得准确,偏差越小,模型越“准”;而方差衡量模型每次预测的结果是否接近,即是说方 差越小,模型越“稳”;噪声是机器学习无法干涉的部分,为了让世界美好一点,我们就不去研究了。一个好的模 型,要对大多数未知数据都预测得”准“又”稳“。即是说,当偏差和方差都很低的时候,模型的泛化误差就小,在未 知数据上的准确率就高。
通常来说,方差和偏差有一个很大,泛化误差都会很大。然而,方差和偏差是此消彼长的,不可能同时达到最小 值。这个要怎么理解呢?来看看下面这张图:
从图上可以看出,模型复杂度大的时候,方差高,偏差低。偏差低,就是要求模型要预测得“准”。模型就会更努力 去学习更多信息,会具体于训练数据,这会导致,模型在一部分数据上表现很好,在另一部分数据上表现却很糟 糕。模型泛化性差,在不同数据上表现不稳定,所以方差就大。而要尽量学习训练集,模型的建立必然更多细节, 复杂程度必然上升。所以,复杂度高,方差高,总泛化误差高。
相对的,复杂度低的时候,方差低,偏差高。方差低,要求模型预测得“稳”,泛化性更强,那对于模型来说,它就 不需要对数据进行一个太深的学习,只需要建立一个比较简单,判定比较宽泛的模型就可以了。结果就是,模型无 法在某一类或者某一组数据上达成很高的准确度,所以偏差就会大。所以,复杂度低,偏差高,总泛化误差高。
**我们调参的目标是,达到方差和偏差的完美平衡!**虽然方差和偏差不能同时达到最小值,但他们组成的泛化误差却 可以有一个最低点,而我们就是要寻找这个最低点。对复杂度大的模型,要降低方差,对相对简单的模型,要降低 偏差。随机森林的基评估器都拥有较低的偏差和较高的方差,因为决策树本身是预测比较”准“,比较容易过拟合的 模型,装袋法本身也要求基分类器的准确率必须要有50%以上。所以以随机森林为代表的装袋法的训练过程旨在降 低方差,即降低模型复杂度,所以随机森林参数的默认设定都是假设模型本身在泛化误差最低点的右边。
所以,我们在降低复杂度的时候,本质其实是在降低随机森林的方差,随机森林所有的参数,也都是朝着降低方差 的目标去。有了这一层理解,我们对复杂度和泛化误差的理解就更上一层楼了,对于我们调参,也有了更大的帮 助