[机器学习实战] 阅读第七章

集成学习和随机森林

集成

第一种,在相同数据集上训练不同得模型,然后集成
例如:

from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
log_clf = LogisticRegression()
rnd_clf = RandomForestClassifier()
svm_clf = SVC()
voting_clf = VotingClassifier(
	estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
	voting='hard'
)
voting_clf.fit(X_train, y_train)

第二种,在不同数据集上训练相同得模型,然后集成
可以参照随机森林。

1 投票分类器

class sklearn.ensemble.VotingClassifier(
		estimators, 
		voting=’hard’, #默认使用硬投票,设置为soft使用软投票
		weights=None, 
		n_jobs=None, 
		flatten_transform=True)

1.1 硬(hard)投票

聚合每个分类器的预测,然后将得票最多得结果作为预测类别。

1.2 软(soft)投票

如果所有分类器都能够估算出类别得概率(即有predict_proba()方法),那么你可以将概率在所有单个分类器上平均,然后让scikit-learn给出平均概率最高得类别作为预测。

2 bagging 和 pasting

获得不同种类分类器的方法之一是使用不同的训练算法。还有另一种方法是每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。采样时如果将样本放回,这种方法叫作bagging(bootstrap aggregating的缩写,也叫自举汇聚法);采样时样本不放回,这种方法则叫用pasting。
换句话说,bagging和pasting都允许训练实例在多个预测器中被多次采样,但是只有bagging允许训练实例被同一个预测器多次采样。
一旦预测器训练完成,集成就可以通过简单地聚合所有预测器的预测,来对新实例做出预测。聚合函数通常是统计法(即最多数的预测好比硬投票分类器一样)用于分类,或是平均法用于回归。每个预
测器单独的偏差都高于在原始训练集上训练的偏差,但是通过聚合,同时降低了偏差和方差。[4]总体来说,最终结果是,与直接在原始训练集上训练的单个预测器相比,集成的偏差相近,但是方差更低。
可以通过不同的CPU内核甚至是不同的服务器,并行地训练预测器。类似地,预测也可以并行。这正是bagging和pasting方法如此流行的原因之一,它们非常易于拓展。

Scikit-Learn 的 bagging 和 pasting

Scikit-Learn提供了一个简单的API,可用BaggingClassifier类进行bagging和pasting(或BaggingRegressor用于回归)

注:默认使用bagging(自举汇聚法),若想使用pasting可设置bootstrap=False;
参数n_jobs用来指示scikit-learn用多少CPU内核进行训练和预测(-1表示让Scikit-Learn使用所有可用内核)

3 包外评估

对于任意给定的预测器,使用bagging,有些实例可能会被采样多次,而有些实例则可能根本不被采样。BaggingClassifier默认采样m个训练实例,然后放回样本(bootstrap=True),m是训练集的大小。这意味着对于每个预测器来说,平均只对63%的训练实例进行采样。剩余37%未被采样的训练实例称为包外(oob)实例。注意,对所有预测器来说,这是不一样的37%

既然预测器在训练的时候从未见过这些包外实例,正好可以用这些实例进行评估,从而不需要单独的验证集或是交叉验证。将每个预测器在其包外实例上的评估结果进行平均,你就可以得到对集成的评估。

在Scikit-Learn中,创建BaggingClassifier时,设置oob_score=True,就可以请求在训练结束后自动进行包外评估。

每个训练实例的包外决策函数也可以通过变量oob_decision_function_获得。决策函数返回的是每个实例的类别概率。

4 Random Patches 和随机子空间

BaggingClassifier也支持对特征进行抽样,这通过两个超参数控制:max_features和bootstrap_features。它们的工作方式跟max_samples和bootstrap相同,只是抽样对象不再是实例,而是特征。因此,每个预测器将用输入特征的随机子集进行训练。

这对于处理高维输入(例如图像)特别有用。对训练实例和特征都进行抽样,被称为Random Patches方法。[1]而保留所有训练实例(即bootstrap=False并且max_samples=1.0)但是对特征进行抽样(即bootstrap_features=True并且/或max_features<1.0),这被称为随机子空间法。

对特征抽样给预测器带来更大的多样性,所以以略高一点的偏差换取了更低的方差。

5 随机森林

前面已经提到,随机森林是决策树的集成,通常用bagging(有时也可能是pasting)方法训练,训练集大小通过max_samples来设置。除了先构建一个BaggingClassifier然后将结果传输到DecisionTreeClassifier,还有一种方法就是使用RandomForestClassifier类,这种方法更方便,对决策树更优化(同样,对于回归任务也有一个RandomForestRegressor类)。

除了少数例外,RandomForestClassifier具有DecisionTreeClassifier的所有超参数,以及BaggingClassifier的所有超参数,前者用来控制树的生长,后者用来控制集成本身。

有几个值得注意的例外:没有splitter(强制为random),没有presort(强制为False),没有max_samples(强制为1.0),没有base_estimator(强制为DecisionTreeClassifier与给定超参数)。

随机森林在树的生长上引入了更多的随机性:分裂节点时不再是搜索最好的特征,而是在一个随机生成的特征子集里搜索最好的特征。这导致决策树具有更大的多样性,(再一次)用更
高的偏差换取更低的方差,总之,还是产生了一个整体性能更优的模型。

极端随机树

如前所述,随机森林里单棵树的生长过程中,每个节点在分裂时仅考虑到了一个随机子集所包含的特征。如果我们对每个特征使用随机阈值,而不是搜索得出的最佳阈值(如常规决策树),则可能让决
策树生长得更加随机。这种极端随机的决策树组成的森林,被称为极端随机树集成[4](或简称Extra-Trees)。同样,它也是以更高的偏差换取了更低的方差。极端随机树训练起来比常规随机森林要快很多,因为在每个节点上找到每个特征的最佳阈值是决策树生长中最耗时的任务之一。

使用Scikit-Learn的ExtraTreesClassifier可以创建一个极端随机树分类器。它的API与RandomForestClassifier相同。同理,ExtraTreesRegressor与RandomForestRegressor的API也相同。

通常来说,很难预先知道一个RandomForestClassifier是否会比一个ExtraTreesClassifier更好或是更差。唯一的方法是两种都尝试一遍,然后使用交叉验证(还需要使用网格搜索调整超参数)进行比
较。

6 特征重要性

最后,如果你查看单个决策树会发现,重要的特征更可能出现在靠近根节点的位置,而不重要的特征通常出现在靠近叶节点的位置(甚至根本不出现)。因此,通过计算一个特征在森林中所有树上的平均深度,可以估算出一个特征的重要程度。Scikit-Learn在训练结束后自动计算每个特征的重要性。通过变量feature_importances_你就可以访问到这个计算结果。

所以,如果想快速了解什么是真正重要的特征,随机森林是一个
非常便利的方法,特别是当你需要执行特征选择的时候。

7 提升法(集成的方法之一)

提升法(Boosting,最初被称为假设提升)是指可以将几个弱学习器结合成一个强学习器的任意集成方法。大多数提升法的总体思路是循环训练预测器,每一次都对其前序做出一些改正。可用的提升法有很多,但目前最流行的方法是AdaBoost(自适应提升法,Adaptive Boosting的缩写)和梯度提升。先从AdaBoost开始介绍。

AdaBoost

新预测器对其前序进行纠正的办法之一,就是更多地关注前序拟合不足的训练实例。从而使新的预测器不断地越来越专注于难缠的问题,这就是AdaBoost使用的技术。

AdaBoost这种依序循环的学习技术跟梯度下降有一些异曲同工之处,差别只在于——不再是调整单个预测器的参数使成本函数最小化,而是不断在集成中加入预测器,使模型越来越好。

这种依序学习技术有一个重要的缺陷就是无法并行(哪怕只是一部分),因为每个预测器只能在前一个预测器训练完成并评估之后才能开始训练。因此,在拓展方面,它的表现不如bagging和pasting方法。

Scikit-Learn使用的其实是AdaBoost的一个多分类版本,叫作SAMME(基于多类指数损失函数的逐步添加模型)。当只有两个类别时,SAMME即等同于AdaBoost。此外,如果预测器可以估算类别概率(即具有predict_proba()方法),Scikit-Learn会使用一种SAMME的变体,称为SAMME.R(R代表“Real”),它依赖的是类别概率而不是类别预测,通常表现更好。AdaBoostClassifier默认使用的基础估算器是单层决策树(是max_depth=1的决策树)。

如果你的AdaBoost集成过度拟合训练集,你可以试试减少估算器数量,或是提高基础估算器的正则化程度。

梯度提升

另一个非常受欢迎的提升法是梯度提升(Gradient Boosting)。跟AdaBoost一样,梯度提升也是逐步在集成中添加预测器,每一个都对其前序做出改正。不同之处在于,它不是像AdaBoost那样在每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合

一个简单的回归示例,使用决策树作为基础预测器(梯度提升当然也适用于回归任务),这被称为梯度树提升或者是梯度提升回归树(GBRT)。首先,在训练集(比如带噪声的二次训练集)上拟合一个DecisionTreeRegressor:

from sklearn.tree import DecisionTreeRegressor
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X, y)

现在,针对第一个预测器的残差,训练第二个
DecisionTreeRegressor:
y2 = y - tree_reg1.predict(X)  #针对上一个模型未能准确预测的实例,重新构建模型进行预测
tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X, y2)

然后,针对第二个预测器的残差,训练第三个回归器:
y3 = y2 - tree_reg2.predict(X) #再次进行
tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X, y3)

现在,我们有了一个包含三棵树的集成。它将所有树的预测相
加,从而对新实例进行预测:
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3

训练GBRT集成有个简单的方法,就是使用Scikit-Learn的GradientBoosting-Regressor类。与RandomForestRegressor类似,它具有控制决策树生长的超参数(例如max_depth、min_samples_leaf等),以及控制集成训练的超参数,例如树的数量(n_estimators)。

等效于上面的方法:

from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X, y)

超参数learning_rate对每棵树的贡献进行缩放。如果你将其设置为低值,比如0.1,则需要更多的树来拟合训练集,但是预测的泛化效果通常更好。这是一种被称为收缩的正则化技术。

要找到树的最佳数量,可以使用早期停止法(参见第4章)。简单的实现方法就是使用staged_predict()方法:它在训练的每个阶段(一棵树时,两棵树时,等等)都对集成的预测返回一个迭代器。

实际上,要实现早期停止法,不一定需要先训练大量的树,然后再回头找最优的数字,还可以真的提前停止训练。设置warm_start=True,当fit()方法被调用时,Scikit-Learn会保留现有的树,从而允许增量训练。以下代码会在验证误差连续5次迭代未改善时,直接停止训练:

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%的随机选择的实例进行训练。现在你可以猜到,这也是用更高的偏差换取了更低的方差,同时在相当大的程度上加速了训练过程。这种技术被称为随机梯度提升

堆叠法(集成的方法之二)

最后一个集成方法叫作堆叠法(stacking),又称层叠泛化法。它基于一个简单的想法:与其使用一些简单的函数(比如硬投票)来聚合集成中所有预测器的预测,我们为什么不训练一个模型来执行这个聚合呢?

训练混合器的常用方法是使用留存集。

scikit-learn不直接支持堆叠,不过可以自行堆出stacking,或者也可以借助开源的实现方案,例如brew

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值