赞
踩
集成方法:聚合一组预测器(比如分类器或回归器)的预测,得到的预测结果也比最好的单个预测器要好。
例如,你可以训练一组决策树分类器,每一棵树都基于训练集不同的随机子集进行训练。做出预测时,你只需要获得所有树各自的预测,然后给出得票最多的类别作为预测结果。这样一组决策树的集成被称为随机森林,尽管很简单,但它是迄今可用的最强大的机器学习算法之一。
我们将探讨最流行的几种集成方法,包括bagging、boosting、stacking、随机森林。
已经训练好了一些分类器,包括:一个逻辑回归分类器、一个SVM分类器、一个随机森林分类器、一个K-近邻分类器,每个分类器的准确率约为80%。这时,要创建出一个更好的分类器,最简单的办法就是聚合每个分类器的预测,然后将得票最多的结果作为预测类别。这种大多数投票分类器被称为硬投票分类器。
这个投票法分类器的准确率通常比集成中最好的分类器还要高。事实上,即使每个分类器都是弱学习器(意味着它仅比随机猜测好一点),通过集成依然可以实现一个强学习器(高准确率),只要有足够大数量并且足够多种类的弱学习器就可以。
下面这个类比可以帮助你掀开这层神秘面纱。假设你有一个略微偏倚的硬币,它有51%的可能正面数字朝上,49%的
可能背面花朝上。如果你掷1000次,你大致会得到差不多510次数字和490次花,所以正面是大多数。“在1000次投掷后,大多数为正面朝上”的概率接近75%。投掷硬币的次数越多,这个概率越高(例如,投掷10000次后,这个概率攀升至97%)。
这是因为大数定理导致的:随着你不断投掷硬币,正面朝上的比率越来越接近于正面的概率(51%)。图7-3显示了10条偏倚硬币的投掷结果。可以看出随着投掷次数的增加,正面的比率逐渐接近51%,最终所有10条线全都接近51%,并且始终位于50%以上。
假设你创建了一个包含1000个分类器的集成,每个分类器都只有51%的几率是正确的(几乎没比随机猜测强多少)。如果你以大多数投票的类别作为预测结果,你可以期待的准确率高达75%。但是,这基于的前提是:
所有的分类器都是完全独立的,彼此的错误毫不相关。显然这是不可能的,因为它们都是在相同的数据上训练的。它们很可能会犯相同的错误,所以也会有很多次大多数投给了错误的类别,导致集成的准确率有所降低。
因此,当预测器尽可能互相独立时,集成方法的效果最优。
获得多种分类器的方法之一就是使用不同的算法进行训练。这会增加它们犯不同类型错误的机会,从而提升集成的准确率。
1、硬投票分类器
用Scikit-Learn创建并训练一个投票分类器,由三种不同的分类器组成(训练集是卫星数据集
- from sklearn.model_selection import train_test_split
- from sklearn.datasets import make_moons
-
- X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
- X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)#顺序不要写错
- from sklearn.linear_model import LogisticRegression
- from sklearn.svm import SVC
- from sklearn.ensemble import RandomForestClassifier
- from sklearn.ensemble import VotingClassifier
-
- log_clf=LogisticRegression(solver="liblinear",random_state=42)
- svm_clf=SVC(gamma="auto",random_state=42) #SVC默认使用RBF核函数
- rnd_clf=RandomForestClassifier(n_estimators=10,random_state=42)
- voting_clf=VotingClassifier(
- estimators=[('svc',svm_clf),('lr',log_clf),('rl',rnd_clf)],
- voting='hard'
- )
- voting_clf.fit(X_train, y_train)
- from sklearn.metrics import accuracy_score
- for clf in(svm_clf,log_clf,rnd_clf,voting_clf):
- clf.fit(X_train,y_train)
- y_pred=clf.predict(X_test)
- print(clf.__class__.__name__,accuracy_score(y_pred,y_test))
看看每个分类器在测试集上的准确率,输出:
投票分类器略胜于所有单个分类器。
2、软投票分类器
如果所有分类器都能够估算出类别的概率(即有predict_proba()方法),那么你可以将概率在所有单个分类器上平均,然后给出平均概率最高的类别作为预测。这被称为软投票法。通常来说,它比硬投票法的表现更优,因为它给予那些高度自信的投票更高的权重。而所有你需要做的就是用voting="soft"代替voting="hard",并确保所有分类器都可以估算出概率。默认情况下,SVC类是不行的,所以你需要将其超参数probability设置为True(这会导致SVC使用交叉验证来估算类别概率,减慢训练速度,并会添加predict_proba()方法)。如果修改上面代码为使用软投票,你会发现投票分类器的准确率达到91%以上!
- voting_clf = VotingClassifier(
- estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
- voting='soft')
结果/:
获得不同种类分类器的方法之一是使用不同的训练算法。还有另一种方法是每个预测器使用的算法相同,但是在不同的训
练集随机子集上进行训练。采样时如果将样本放回,这种方法叫作bagging (自举汇聚法);采样时样本不放回,这种方法则叫用pasting。bagging和pasting都允许训练实例在多个预测器中被多次采样,但是只有bagging允许训练实例被同一个预测器多次采样。
一旦预测器训练完成,集成就可以通过简单地聚合所有预测器的预测,来对新实例做出预测。聚合函数通常是统计法(即最多数的预测好比硬投票分类器一样)用于分类,或是平均法用于回归。每个预测器单独的偏差都高于在原始训练集上训练的偏差,但是通过聚合,同时降低了偏差和方差。
总体来说,最终结果是,与直接在原始训练集上训练的单个预测器相比,集成的偏差相近,但是方差更低。
可以通过不同的CPU内核甚至是不同的服务器,并行地训练预测器,预测也可以并行。这正是bagging和pasting方法如此流行的原因之一,它们非常易于拓展。
代码实现:
用BaggingClassifier类进行bagging和pasting(或BaggingRegressor用于回归)。
- from sklearn.ensemble import BaggingClassifier
- from sklearn.tree import DecisionTreeClassifier
- bag_clf=BaggingClassifier(
- DecisionTreeClassifier(),n_estimators=100,max_samples=100,bootstrap=True,n_jobs=-1,oob_score=True)
- bag_clf.fit(X_train,y_train)
- y_pred=bag_clf.predict(X_test)
参数:
如果基础分类器能够估算类别概率(也就是具备predict_proba()方法),比如决策树分类器,那么BaggingClassifier自动执行的就是软投票法而不是硬投票法。
下图,一个是单个的决策树,一个是由500个决策树组成的bagging集成(来自前面的代码)。可以看出,集成预测的泛化效果很可能会比单独的决策树要好一些:二者偏差相近,但是集成的方差更小(两边训练集上的错误数量差不多,但是集成的决策边界更规则)。
由于”样本放回“给每个预测器的训练子集引入了更高的多样性,所以bagging比pasting的偏差略高,但这也意味着预测器之间的关联度更低,所以集成的方差降低。总之,bagging生成的模型通常更好。
包外评估oob_score:
对于任意给定的预测器,使用bagging,有些实例可能会被采样多次,而有些实例则可能根本不被采样。未被采样的训练实例称为包外(oob)实例。既然预测器在训练的时候从未见过这些包外实例,正好可以用这些实例进行评估,从而不需要单独的验证集或是交叉验证。将每个预测器在其包外实例上的评估结果进行平均,你就可以得到对集成的评估。
- bag_clf.oob_score_ #包外实例的评估结果,与测试集的评估结果基本一致
-
- #每个训练实例的包外决策函数也可以通过变量oob_decision_function_获得。
- bag_clf.oob_decision_function_ #基础预测器具备predict_proba()方法,决策函数返回的是每个实例的类别概率。
随机贴片与随机子空间
BaggingClassifier
也支持采样特征。它被两个超参数max_features
和bootstrap_features
控制。他们的工作方式和max_samples
和bootstrap
一样,但这是对于特征采样而不是实例采样。因此,每一个分类器都会被在随机的输入特征内进行训练。
当你在处理高维度输入下(例如图片)此方法尤其有效。对训练实例和特征的采样被叫做随机贴片。保留了所有的训练实例(例如bootstrap=False
和max_samples=1.0
),但是对特征采样(bootstrap_features=True
并且/或者max_features
小于 1.0)叫做随机子空间。采样特征导致更多的预测多样性,用高偏差换低方差。
随机森林是决策树的集成,通常用bagging(有时也可能是pasting)方法训练,训练集大小通过max_samples来设置。(同
样,对于回归任务也有一个RandomForestRegressor类)RandomForestClassifier具有DecisionTreeClassifier的所有超参数,以及BaggingClassifier的所有超参数,前者用来控制树的生长,后者用来控制集成本身
1、先构建一个BaggingClassifier然后将结果传输到DecisionTreeClassifier,
2、使用RandomForestClassifier类,这种方法更方便,对决策树更优化 。
- from sklearn.ensemble import RandomForestClassifier
-
- rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1, random_state=42)
- rnd_clf.fit(X_train, y_train)
- y_pred_rf = rnd_clf.predict(X_test)
-
-
- #构建一个BaggingClassifier然后将结果传输到DecisionTreeClassifier
- bag_clf = BaggingClassifier(
- DecisionTreeClassifier(splitter="random", max_leaf_nodes=16, random_state=42),
- n_estimators=500, max_samples=1.0, bootstrap=True, n_jobs=-1, random_state=42)
- bag_clf.fit(X_train,y_train)
- y_pred = bag_clf.predict(X_test)
随机森林不仅可以预测类别predict():方法,还可以输出预测类别的概率 predict_proba()方法,predict_proba(X):预测X的类别的概率。输入样本的预测类别概率被计算为森林中树木的平均预测类别概率。单个树的类概率是叶中同一类的样本的比率(参见决策树如何输出类别概率)。
随机森林在树的生长上引入了更多的随机性:分裂节点时不再是搜索最好的特征,而是在一个随机生成的特征子集里搜索最好的特征。这导致决策树具有更大的多样性,(再一次)用更高的偏差换取更低的方差
极端随机树
如果我们对每个特征使用随机阈值,而不是搜索得出的最佳阈值(如常规决策树),则可能让决策树生长得更加随机。这种极端随机的决策树组成的森林,被称为极端随机树,它也是以更高的偏差换取了更低的方差。极端随机树训练起来比常规随机
森林要快很多,因为在每个节点上找到每个特征的最佳阈值是决策树生长中最耗时的任务之一。
使用Scikit-Learn的ExtraTreesClassifier可以创建一个极端随机树分类器。它的API与RandomForestClassifier相同。同理,
ExtraTreesRegressor与RandomForestRegressor的API也相同。
特征重要性
查看单个决策树会发现,重要的特征更可能出现在靠近根节点的位置,而不重要的特征通常出现在靠近叶节点的位置(甚至根本不出现)。因此,通过计算一个特征在森林中所有树上的平均深度,可以估算出一个特征的重要程度。Scikit-Learn通过变量feature_importances_你就可以访问到这个计算结果。
所以,如果想快速了解什么是真正重要的特征,随机森林是一个非常便利的方法,特别是当你需要执行特征选择的时候。
scikit-learn中随机森林使用详解(参数,属性和方法)可参考博客:
https://blog.csdn.net/memoryheroli/article/details/80920260
提升(Boosting)指的是可以将几个弱学习者组合成强学习者的集成方法。现如今已经有很多的提升方法了,但最著名的就是 Adaboost(适应性提升,是 Adaptive Boosting 的简称) 和 Gradient Boosting(梯度提升)。
1、AdaBoost
新预测器对其前序进行纠正的办法之一,就是更多地关注前序拟合不足的训练实例。从而使新的预测器不断地越来越专注于难缠的问题,这就是AdaBoost使用的技术。
例如,要构建一个AdaBoost分类器,首先需要训练一个基础分类器(比如决策树),用它对训练集进行预测。然后对错误分类的训练实例增加其相对权重,接着,使用这个最新的权重对第二个分类器进行训练,然后再次对训练集进行预测,继续更新权重,并不断循环向前。
下图显示连续五次预测的 moons 数据集的决策边界( RBF 核的 SVM)。第一个分类器误分类了很多实例,所以它们的权重被提升了。第二个分类器因此对这些误分类的实例分类效果更好,以此类推。右边的图代表了除了学习率减半外(误分类实例权重每次迭代上升一半)相同的预测序列。你可以看出,序列学习技术与梯度下降很相似,除了调整单个预测因子的参数以最小化代价函数之外,AdaBoost 增加了集合的预测器,逐渐使其更好。
一旦所有的分类器都被训练后,除了分类器根据整个训练集上的准确率被赋予的权重外,集成预测就非常像Bagging和Pasting了。序列学习技术的一个重要的缺点就是:它不能被并行化(只能按步骤),因为每个分类器只能在之前的分类器已经被训练和评价后再进行训练。因此,它不像Bagging和Pasting一样。
Adaboost 算法原理:
每一个实例的权重wi
初始都被设为1/m,
第一个分类器被训练,然后他的权重误差率在训练集上算出,
1、第j
个分类器的权重误差率:
其中 j
个分类器对于第i
实例的预测。
2、分类器的权重
其中η
是超参数学习率(默认为 1)。分类器准确率越高,它的权重就越高。如果它只是瞎猜,那么它的权重会趋近于 0。然而,如果它总是出错(比瞎猜的几率都低),它的权重会使负数。
3、权重更新规则
随后所有实例的权重都被归一化。最后,一个新的分类器通过更新过的权重训练,整个过程被重复(新的分类器权重被计算,实例的权重被更新,随后另一个分类器被训练,以此类推)。当规定的分类器数量达到或者最好的分类器被找到后算法就会停止。进行预测,预测类别会是权重投票中主要的类别。
4、 预测
其中N
是分类器的数量。
sklearn 通常使用 Adaboost 的多分类版本 SAMME(这就代表了 分段加建模使用多类指数损失函数)。如果只有两类别,那么 SAMME 是与 Adaboost 相同的。如果分类器可以预测类别概率(例如如果它们有predict_proba()
),如果 sklearn 可以使用 SAMME 叫做SAMME.R
的变量(R 代表“REAL”),这种依赖于类别概率的通常比依赖于分类器的更好。
接下来的代码训练了使用 sklearn 的AdaBoostClassifier
基于 200 个决策树桩 Adaboost 分类器(正如你说期待的,对于回归也有AdaBoostRegressor
)。一个决策树桩是max_depth=1
的决策树-换句话说,是一个单一的决策节点加上两个叶子结点。这就是AdaBoostClassifier
的默认基分类器
- >>>from sklearn.ensemble import AdaBoostClassifier
- >>>ada_clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1), n_estimators=200,algorithm="SAMME.R", learning_rate=0.5)
- >>>ada_clf.fit(X_train, y_train)
Adaboost 集成过拟合了训练集,你可以尝试减少基分类器的数量或者对基分类器使用更强的正则化。
2、梯度提升
与 Adaboost 一样,梯度提升也是通过向集成中逐步增加分类器运行的,每一个分类器都修正之前的分类结果。然而,它并不像 Adaboost 那样每一次迭代都更改实例的权重,这个方法是去使用新的分类器去拟合前面分类器预测的残差 。
让我们通过一个使用决策树当做基分类器的简单的回归例子(回归当然也可以使用梯度提升)。这被叫做梯度提升回归树(GBRT,Gradient Boosted Regression Trees)
- from sklearn.ensemble import GradientBoostingRegressor
- >>>gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0) #3个分类器
- >>>gbrt.fit(X, y)
超参数learning_rate对每棵树的贡献进行缩放。如果你将其设置为低值,比如0.1,则需要更多的树来拟合训练集,但是预测的泛化效果通常更好。这是一种被称为收缩的正则化技术。拟合训练集的树数量过多会导致过度拟合。
寻找树的最佳数量可以使用早期停止法。
1、简单的实现方法就是使用staged_predict()方法:它在训练的每个阶段(一棵树时,两棵树时,等等)都对集成的预测返回一个迭代器。先训练一个GBRT集成,然后测量每个训练阶段的验证误差,从而找到树的最优数量,最后使用最优树数重新训练一个GBRT集成。
- >>>import numpy as np
- >>>from sklearn.model_selection import train_test_split
- >>>from sklearn.metrics import mean_squared_error
-
- >>>X_train, X_val, y_train, y_val = train_test_split(X, y)
- >>>gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
- >>>gbrt.fit(X_train, y_train)
- >>>errors = [mean_squared_error(y_val, y_pred)
- for y_pred in gbrt.staged_predict(X_val)]
- >>>bst_n_estimators = np.argmin(errors)
- >>>gbrt_best = GradientBoostingRegressor(max_depth=2,n_estimators=bst_n_estimators)
- >>>gbrt_best.fit(X_train, y_train)
2、通过设置warm_start=True
来实现 ,这使得当fit()
方法被调用时 sklearn 保留现有树,并允许增量训练。接下来的代码在当一行中的五次迭代验证错误没有改善时会停止训练:
- gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True)
- min_val_error = float("inf")
- error_going_up = 0
- for n_estimators in range(1, 120):
- gbrt.n_estimators = n_estimators
- gbrt.fit(X_train, y_train)
- y_pred = gbrt.predict(X_val)
- val_error = mean_squared_error(y_val, y_pred)
- if val_error < min_val_error:
- min_val_error = val_error
- error_going_up = 0
- else:
- error_going_up += 1
- if error_going_up == 5:
- break # early stopping
随机梯度提升
GradientBoostingRegressor
也支持指定用于训练每棵树的训练实例比例的超参数subsample
。例如如果subsample=0.25
,那么每个树都会在 25% 随机选择的训练实例上训练。这也是个高偏差换低方差的作用。它同样也加速了训练。
也可能对其他损失函数使用梯度提升。这是由损失超参数控制。
这个算法基于一个简单的想法:不使用琐碎的函数(如硬投票)来聚合集合中所有分类器的预测,而是训练一个模型来执行这个聚合,下图展示了这样一个在新的回归实例上预测的集成。底部三个分类器每一个都有不同的值(3.1,2.7 和 2.9),然后最后一个分类器(混合器)把这三个分类器的结果当做输入然后做出最终决策(3.0)。
训练混合器的常用方法是使用留存集。
首先,将训练集分为两个子集,第一个子集用来训练第一层的预测器,假设训练3个不同的预测器;
然后,用第一层的3个预测器在第二个(留存)子集上进行预测,这时对于留存集中的每个实例都有了三个预测值;
最后使用这些预测值作为输入特征,创建一个新的训练集(新的训练集有三个维度),并保留目标值。随后在这个新的训练集上训练混合器。
通过这种方法可以训练多种不同的混合器(例如,一个使用线性回归,另一个使用随机森林回归,等等):于是我们可以得
到一个混合器层。诀窍在于将训练集分为三个子集:第一个用来训练第一层,第二个用来创造训练第二层的新训练集(使用第一层的预测),而第三个用来创造训练第三层的新训练集(使用第二层的预测)。一旦训练完成,我们可以按照顺序遍历每层来对新实例进行预测,如右图所示。
sklearn 并不直接支持 stacking ,但是你自己组建是很容易的,或者使用开源的项目例如 brew: https://github.com/viisar/brew
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。