假设你去随机问很多人一个很复杂的问题,然后把它们的答案合并起来。通常情况下你会发现这个合并的答案比一个专家的答案要好。这就叫做群体智慧。同样的,如果你合并了一组分类器的预测(像分类或者回归),你也会得到一个比单一分类器更好的预测结果。这一组分类器就叫做集成;因此,这个技术就叫做集成学习,一个集成学习算法就叫做集成方法。
例如,你可以训练一组决策树分类器,每一个都在一个随机的训练集上。为了去做预测,你必须得到所有单一树的预测值,然后通过投票(例如第六章的练习)来预测类别。例如一种决策树的集成就叫做随机森林,它除了简单之外也是现今存在的最强大的机器学习算法之一。
向我们在第二章讨论的一样,我们会在一个项目快结束的时候使用集成算法,一旦你建立了一些好的分类器,就把他们合并为一个更好的分类器。事实上,在机器学习竞赛中获得胜利的算法经常会包含一些集成方法。
在本章中我们会讨论一下特别著名的集成方法,包括 bagging, boosting, stacking,和其他一些算法。我们也会讨论随机森林。
投票分类
假设你已经训练了一些分类器,每一个都有 80% 的准确率。你可能有了一个逻辑斯蒂回归、或一个 SVM、或一个随机森林,或者一个 KNN,或许还有更多(详见图 7-1)
一个非常简单去创建一个更好的分类器的方法就是去整合每一个分类器的预测然后经过投票去预测分类。这种分类器就叫做硬投票分类器(详见图 7-2)。
令人惊奇的是这种投票分类器得出的结果经常会比集成中最好的一个分类器结果更好。事实上,即使每一个分类器都是一个弱学习器(意味着它们也就比瞎猜好点),集成后仍然是一个强学习器(高准确率),只要有足够数量的弱学习者,他们就足够多样化。
这怎么可能?接下来的分析将帮助你解决这个疑问。假设你有一个有偏差的硬币,他有 51% 的几率为正面,49% 的几率为背面。如果你实验 1000 次,你会得到差不多 510 次正面,490 次背面,因此大多数都是正面。如果你用数学计算,你会发现在实验 1000 次后,正面概率为 51% 的人比例为 75%。你实验的次数越多,正面的比例越大(例如你试验了 10000 次,总体比例可能性就会达到 97%)。这是因为大数定律 :当你一直用硬币实验时,正面的比例会越来越接近 51%。图 7-3 展示了始终有偏差的硬币实验。你可以看到当实验次数上升时,正面的概率接近于 51%。最终所有 10 种实验都会收敛到 51%,它们都大于 50%。
同样的,假设你创建了一个包含 1000 个分类器的集成模型,其中每个分类器的正确率只有 51%(仅比瞎猜好一点点)。如果你用投票去预测类别,你可能得到 75% 的准确率!然而,这仅仅在所有的分类器都**运行的很好、不会发生有相关性的错误的情况下才会这样,然而每一个分类器都在同一个数据集上训练,导致其很可能会发生这样的错误。他们可能会犯同一种错误,所以也会有很多票投给了错误类别导致集成的准确率下降。
如果使每一个分类器都**自主的分类,那么集成模型会工作的很好。去得到多样的分类器的方法之一就是用完全不同的算法,这会使它们会做出不同种类的错误,这会提高集成的正确率
接下来的代码创建和训练了在 sklearn 中的投票分类器。这个分类器由三个不同的分类器组成(训练集是第五章中的 moons 数据集):
>>> 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)
让我们看一下在测试集上的准确率:
>>> 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(clf.__class__.__name__, accuracy_score(y_test, y_pred))
LogisticRegression 0.864
RandomForestClassifier 0.872
SVC 0.888
VotingClassifier 0.896
你看!投票分类器比其他单独的分类器表现的都要好。
如果所有的分类器都能够预测类别的概率(例如他们有一个predict_proba()
方法),那么你就可以让 sklearn 以最高的类概率来预测这个类,平均在所有的分类器上。这种方式叫做软投票。他经常比硬投票表现的更好,因为它给予高自信的投票更大的权重。你可以通过把voting="hard"
设置为voting="soft"
来保证分类器可以预测类别概率。然而这不是 SVC 类的分类器默认的选项,所以你需要把它的probability hyperparameter
设置为True
(这会使 SVC 使用交叉验证去预测类别概率,其降低了训练速度,但会添加predict_proba()
方法)。如果你修改了之前的代码去使用软投票,你会发现投票分类器正确率高达 91%
Bagging 和 Pasting
换句话说,Bagging 和 Pasting 都允许在多个分类器间对训练集进行多次采样,但只有 Bagging
就像之前讲到的,可以通过使用不同的训练算法去得到一些不同的分类器。另一种方法就是对每一个分类器都使用相同的训练算法,但是在不同的训练集上去训练它们。有放回采样被称为装袋(Bagging,是 bootstrap aggregating 的缩写)。无放回采样称为粘贴(pasting)。
换句话说,Bagging 和 Pasting 都允许在多个分类器上对训练集进行多次采样,但只有 Bagging 允许对同一种分类器上对训练集进行进行多次采样。采样和训练过程如图7-4所示。
当所有的分类器被训练后,集成可以通过对所有分类器结果的简单聚合来对新的实例进行预测。聚合函数通常对分类是统计模式(例如硬投票分类器)或者对回归是平均。每一个单独的分类器在如果在原始训练集上都是高偏差,但是聚合降低了偏差和方差。通常情况下,集成的结果是有一个相似的偏差,但是对比与在原始训练集上的单一分类器来讲有更小的方差。
正如你在图 7-4 上所看到的,分类器可以通过不同的 CPU 核或其他的服务器一起被训练。相似的,分类器也可以一起被制作。这就是为什么 Bagging 和 Pasting 是如此流行的原因之一:它们的可扩展性很好。
在 sklearn 中的 Bagging 和 Pasting
sklearn 为 Bagging 和 Pasting 提供了一个简单的API:BaggingClassifier
类(或者对于回归可以是BaggingRegressor
。接下来的代码训练了一个 500 个决策树分类器的集成,每一个都是在数据集上有放回采样 100 个训练实例下进行训练(这是 Bagging 的例子,如果你想尝试 Pasting,就设置bootstrap=False
)。n_jobs
参数告诉 sklearn 用于训练和预测所需要 CPU 核的数量。(-1 代表着 sklearn 会使用所有空闲核):
>>>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)
如果基分类器可以预测类别概率(例如它拥有predict_proba()
方法),那么BaggingClassifier
会自动的运行软投票,这是决策树分类器的情况。
图 7-5 对比了单一决策树的决策边界和 Bagging 集成 500 个树的决策边界,两者都在 moons 数据集上训练。正如你所看到的,集成的分类比起单一决策树的分类产生情况更好:集成有一个可比较的偏差但是有一个较小的方差(它在训练集上的错误数目大致相同,但决策边界较不规则)。
Bootstrap 在每个预测器被训练的子集中引入了更多的分集,所以 Bagging 结束时的偏差比 Pasting 更高,但这也意味着预测因子最终变得不相关,从而减少了集合的方差。总体而言,Bagging 通常会导致更好的模型,这就解释了为什么它通常是首选的。然而,如果你有空闲时间和 CPU 功率,可以使用交叉验证来评估 Bagging 和 Pasting 哪一个更好。
Out-of-Bag 评价
对于 Bagging 来说,一些实例可能被一些分类器重复采样,但其他的有可能不会被采样。BaggingClassifier
默认采样。BaggingClassifier
默认是有放回的采样m
个实例 (bootstrap=True
),其中m
是训练集的大小,这意味着平均下来只有63%的训练实例被每个分类器采样,剩下的37%个没有被采样的训练实例就叫做 Out-of-Bag 实例。注意对于每一个的分类器它们的 37% 不是相同的。
因为在训练中分类器从来没有看到过 oob 实例,所以它可以在这些实例上进行评估,而不需要单独的验证集或交叉验证。你可以拿出每一个分类器的 oob 来评估集成本身。
在 sklearn 中,你可以在训练后需要创建一个BaggingClassifier
来自动评估时设置oob_score=True
来自动评估。接下来的代码展示了这个操作。评估结果通过变量oob_score_
来显示:
>>> 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.93066666666666664
根据这个 obb 评估,BaggingClassifier
可以再测试集上达到93.1%的准确率,让我们修改一下:
>>> from sklearn.metrics import accuracy_score
>>> y_pred = bag_clf.predict(X_test)
>>> accuracy_score(y_test, y_pred)
0.93600000000000005
我们在测试集上得到了 93.6% 的准确率,足够接近了!
对于每个训练实例 oob 决策函数也可通过oob_decision_function_
变量来展示。在这种情况下(当基决策器有predict_proba()
时)决策函数会对每个训练实例返回类别概率。例如,oob 评估预测第二个训练实例有 60.6% 的概率属于正类(39.4% 属于负类):
>>> bag_clf.oob_decision_function_
array([[ 0., 1.], [ 0.60588235, 0.39411765],[ 1., 0. ],
... [ 1. , 0. ],[ 0., 1.],[ 0.48958333, 0.51041667]])