学习《机器学习实战》七

集成学习和随机森林

*集成学习

集成学习:聚合一组预测器(如分类器或回归器)的技术。

在这里插入图片描述

投票分类器

比如,我们已经用一组训练集训练好了一些分类器:逻辑回归分类器、SVM分类器、随机森林分类器、K-近邻分类器等多种预测器。将这些预测器预测出来的分类结果聚合,得到票数最多的结果作为预测的类别。
其中:如果聚合的是预测的类别则叫做硬投票分类器;如果聚合的是类别的概率(即,训练好的模型具有predict_proba()方法)则叫做软投票分类器。
硬头票分类器的实例:
训练集是卫星数据集

import warnings
warnings.filterwarnings('ignore')
from sklearn.datasets import make_moons
X,y=make_moons(n_samples=1000,noise=0.1) 
X_train, X_test = X[:800],X[800:]
y_train, y_test = y[:800],y[800:]

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)

此段代码表示。训练了三个模型、分别是:逻辑回归分类器、随机森林分类器、SVM分类器。然后将其通过投票分类器的方法聚合起来。然后训练数据。其中:voting = 'hard’表示硬投票;voting = ‘soft’表示软投票分类器。

from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf):
    clf.fit(X_train,y_train)
    y_pred = clf.predict(X_test)
    print( accuracy_score(y_test, y_pred))

结果:
0.88
0.995
0.995
0.99
这段代码表示的是每个分类器在测试集上的准确率。逻辑回归的准确率为0.88、随机森林的准确率为0.995、SVM的准确率为0.995.投票分类器的准确率为0.99。原则上投票分类器应略胜于所有的单个分类器。但这里emm…不知道是不是数据集有点问题。又或者单个分类器的准确率已经足够高了。

关于软投票分类器:
要确保单个模型都可以估算出概率才行。它会求出每个类别的平均概率。最高的平均概率作为预测类别。

随机抽样训练集

前面的投票分类法是用同一个训练集的数据来训练多个模型。而如今要讨论的是使用的模型唯一(算法不变),然后在不同的训练集随机子集上进行训练(也就是将训练集上的数据随机抽样为若干个子集,然后用这些不同子集在同一种模型上训练,这样就形成了不一样的预测器)
其中:采样时如果将样本放回叫bagging;如果采样时样本不放回叫pasting。
当多个预测器训练完成后。集成就可以简单地聚合所有预测器的预测。聚合函数通常是统计法用于分类,或者平均分用于回归。(这里用不同的训练子集训练出来的预测器和前面投票分类单个预测器是一个意思。然后后续的方法也和投票分类器一样,可根据类别预测,也可根据类别的概率预测)

scikit-Learn提供的API:BaggingClassifier类用于分类;BaggingRegressor用于回归。

from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators = 500,
    max_samples = 100, bootstrap = True, n_jobs = -1
)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)

此段代码训练了一个包含500个决策树分类器的集成。每次从训练集中采样100个实例。然后放回。一个形成了500个子集。bootstrap=True表示的是这是一个bagging实例。(bootstrap=False表示的是pasting)。参数n_jobs用来指示Scikit-Learn用多少CPU内核进行训练和预测(-1表示让Scilkit-Learn使用所有可用内核)

包外评估
对于给定的预测器。使用bagging,有些实例可能会被采样多次,而有些实例则可能根本不被采样。平均只对63%的训练实例进行采样,剩余37%未被采样的训练实例称为包外(oob)实例。既然预测器从未见过这些包外实例,正好可以用这些实例进行评估,从而不需要单独的验证集或者交叉验证。将每个预测器在其包外实例上的评估结果进行平均,就得到最终的评估分数。

bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators = 500,
bootstrap = True, n_jobs = -1, oob_score = True)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_

结果:
0.99
根据包外评估的结果,这个BaggingClassifier分类器很可能在测试集上达到约99%的准确率。只需要设置 obb_score = True即可自动完成对这个集成的评估。通过变量oob_score_可以得到最后的评估分数。
我们按照常规的评估方法来验证这个集成的准确率:

from sklearn.metrics import accuracy_score
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred)

结果:
0.99
可见在测试集上的准确率为99%(可证)
每个实例的包外决策函数可通过变量oob_decision_function_获得

bag_clf.oob_decision_function_

结果:
array([[1. , 0. ],
[0. , 1. ],
[1. , 0. ],
…,
[1. , 0. ],
[0. , 1. ],
[0.0106383, 0.9893617]])

前面提到的代码都是对实例来抽样。BaggingClassifer也支持对特征进行抽样,这通过两个参数控制:max_feature和bootstrap_features。
1、Random Patches方法:对训练实例和特征都进行抽样。
2、随机子空间法:保留所有训练实例。但对特征进行抽样。(即BaggingClassifer的参数要设置为:bootstrap = False且max_samples = 1.0;bootstrap_fratures = True且max_features<1.0)

提升法

提升法的总体思路是:循环训练预测器,也就是说,每一次训练都对前一个预测器做出改正。
其中:AdaBoost是对前一个预测器错误分类(拟合不足)的训练实例增加其相对权重。
梯度提升是对前一个预测器的残差来进行训练拟合。
一、AdaBoost
例如:要构建一个AdaBoost分类器,首先训练一个基础分类器(比如决策树),用它对训练集进行预测。然后对错误分类的训练实例增加其相对权重,接着,使用这个最新的权重对第二个分类器进行训练,然后再次对训练集进行预测,继续更新权重,并不断循环向前。
局限:无法并行,每个预测器只能在前一个预测器训练完成并评估之后才能开始训练。
具体AdaBoost的原理参照书本P175~P177内容。

scikit-Learn提供的API:AdaBoostClassifier类用于分类;AdaBoostRegressor用于回归。

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分类器,它基于200个单层决策树,也就是max_depth = 1。
对于algorithm = “SAMME.R” :SAMME表示基于多类指数损失函数的逐步添加模型。当只有两个模型时,SAMME即等同于AdaBoost。如果预测器可以估算类别的概率(含有predict_proba()方法),scikit-Learn会使用SAMME.R(R代表"Real"),它依赖的是类别概率而不是类别预测。

二、梯度提升(GBRT)
梯度提升也是逐步在集成中添加预测器,每一个都对前一个做出改正。它是让新的预测器针对前一个预测器的残差进行拟合 。
例如:回归示例,使用决策树作为基础预测器,这被称为梯度树提升或者是梯度提升回归树(GBRT)
梯度提升的思路过程:
训练集为带噪声的二次训练集

import numpy as np
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)
X_new = 4 * np.random.rand(m, 1) - 3
from sklearn.tree import DecisionTreeRegressor

tree_reg1 = DecisionTreeRegressor(max_depth = 2)
tree_reg1.fit(X, y)
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))

这个过程清晰地展现了梯度提升的过程。首先在训练集上拟合一个决策树模型。然后针对第一个预测器的残差,训练第二个决策树模型,然后针对第二个预测器的残差,训练第三个决策树。最后将三个回归器的预测相加从而可以对新的数据进行预测。
以上过程可以用Sklearn的API实现:GradientBoostingRegressor类

from sklearn.ensemble import GradientBoostingRegressor

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

它具有控制决策树生长的超参数(例如max_depth、min_samples_leaf等),以及控制集成训练的超参数(树的数量:n_estimators)。
超参数learning_rate对每棵树的贡献进行缩放。如果将learning_rate设置成为最低值0.1,则需要更多的树来拟合训练集,但是预测的泛化效果通常更好。这是一种被称为收缩的正则化技术。
当降低了学习率的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)

这段代码表示训练了一颗拥有120棵树的GBRT集成,然后测量,每个训练阶段的验证误差,从而找到树的最优数量,最后使用最优树树重新训练了一个GBRT集成。bst_n_estimators得出最优树的数量。

实际上,还有另外的方法可以实现提前停止训练,不需要训练大量的树再回头找最优数量。
设置warm_start = True,当fit()方法被调用时,Scikit-Learn会保留现有的树,从而允许增量训练。

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

此代码表示在验证误差连续5次迭代未改善时,直接停止训练。
随机梯度提升
GradientBoostingRegressor类还可以支持超参数subsample,指定用于训练每棵树的实际比例。例如,如果subsample=0.25,则表示每棵树用25%的随机选择的示例进行训练。

堆叠法(stacking)

堆叠法的思路:训练多个预测器得出来的数据,集合成为一个数据集。将这些值组成的集合当作输入来训练一个最终的预测器(称为混合器或元学习器)以得到最终的预测。
但Scikit-Learn不支持堆叠。

*随机森林

随机森林是决策树树的集成,通常用bagging(有时也可能是pasting)方法训练。Scikit-Learn提供了API可以直接调用:RandomForestClassifier类RandomForestRegressor类

from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators = 500, max_leaf_nodes = 16,n_jobs=-1)
rnd_clf.fit(X_train, y_train)

y_pred_rf = rnd_clf.predict(X_test)

此段代码表示使用所有可用的CPU内核,训练了一个拥有500棵树的随机森林分类器(每棵树限制为最多16个叶节点)

bag_clf = BaggingClassifier(
DecisionTreeClassifier(splitter = "random", max_leaf_nodes = 16),
n_estimators = 500, max_samples = 1.0, bootstrap = True, n_jobs = -1)

此段代码等同于前面使用随机森林API的代码。

极端随机树

随机森林里单棵树在生长过程中,每个节点在分类时仅考虑道一个随机子集所包含的特征,那如果我们对每个特征使用随机阈值,而不是像常规决策树一样搜索得出的最佳阈值,那么就会让决策树生长得更随机。这种极端随机得决策树组成得森林叫做极端随机数的集成。
Scikit-Learn提供了API可以直接调用:ExtraTreesClassifier类ExtraTreesRegressor类。其与随机森林的API使用相同。

特征的重要性
单个决策树,重要的特征出现在靠近根节点的位置,而不重要的特征通常出现在靠近叶节点的位置(甚至不出现)。因此,可以通过计算一个特征在森林中所有树上的平均深度可以估算出一个特征的重要程度。

Scikit-Learn在训练结束后自动计算每个特征的重要性。通过变量feature_importances_就可以得到结果。

from sklearn.datasets import load_iris
iris = load_iris()
rnd_clf = RandomForestClassifier(n_estimators = 500, n_jobs = -1)
rnd_clf.fit(iris["data"], iris["target"])
for name, score in zip(iris["feature_names"],rnd_clf.feature_importances_):
    print(name, score)

结果:
sepal length (cm) 0.09415064334371878
sepal width (cm) 0.024666595569612062
petal length (cm) 0.45384140169754206
petal width (cm) 0.4273413593891271
此段代码表示,在鸢尾花数据集上训练一个随机森林分类器,输出了每个特征的重要程度,可以看到结果表示最重要的特征是花瓣长度(45%)和宽度(42%),而花萼的长度(9%)和宽度(2%)则相对不那么重要。

总结:当你需要执行特征选择的时候,找到那个特征更重要的时候,随机森林是一个非常便利的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值