机器学习算法笔记(三十六):Bagging

虽然我们上文实现了简单的集成学习,但是它还是存在着问题——从投票的角度来看,这些算法仍然不够。如果我们想尽量保证最终有好的结果的话,我们希望有成千上万的投票者来保证最终的结果更加可信(概率论中的“大数定理”)。所以我们就需要创建更多子模型、集成更多子模型的意见,且子模型之间要用差异性,创建差异性的一个重要思路就是对子模型运用放回取样(Bagging)不放回取样(Pasting)

一、如何创建差异性

创建差异性的最简单的方法就是让每个子模型只看样本数据的一部分。例如我们有 500 个样本数据,那么我们就能让每个子模型只处理 100 个样本数据,每个子模型所使用的算法可以是一个算法,这样我们就创建了很多子模型,且这些子模型之间存在差异性(子模型所处理的样本数据不同)。由于将样本数据平分成 5 份,每份 100 个样本数据,每份样本数据之间有差异,因此所训练出的 5 个子模型之间也存在差异,虽然每个子模型的准确率可能不高,但是我们最终是要通过所有子模型投票来产生结果,对子模型本身的准确率的要求并不高。

即使子模型的准确率不高,使用集成学习后的分类效果也非常好。

二、Bagging 和 Pasting

那么如何只处理样本数据集的一部分呢?一种方式是放回取样(Bagging),就是每个子模型从所有的样本数据中随机抽取一定数量的样本,训练完成后将数据放回样本数据中,下个子模型再从所有的样本数据中随机抽取同样数量的子模型;另一种是不放回取样(Pasting),即在前一个子模型抽取样本后,下一个子模型不能再选用前一个子模型选取过的样本,只能在剩下的样本中抽取。通常来说,Bagging 更加常用,因为它可以训练更多的子模型,不受样本数据量的限制,并且在 train_test_split 时,不那么强烈的依赖随机,而 Pasting 的方式,会受随机的影响。

在统计学中,放回取样被称为 bootstrap。

三、使用 Bagging 进行集成学习

下面我们就通过 Bagging 来进行集成学习。新建一个工程,创建一个 main.py 文件,实现如下:

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=42)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier

bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
                           n_estimators=500, max_samples=100, #n_estimators代表集成几个模型,max_samples代表每个模型所处理的样本个数
                           bootstrap=True) #若为true,则为放回抽样bagging
bagging_clf.fit(X_train, y_train)
print(bagging_clf.score(X_test, y_test)) #prints: 0.92

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier

bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
                           n_estimators=5000, max_samples=100,
                           bootstrap=True)
bagging_clf.fit(X_train, y_train)
print(bagging_clf.score(X_test, y_test)) #prints: 0.912

可以看到随着集成模型的增多,最终的准确率会变高。当然,我们还可以对 n_estimators、max_samples、子模型的参数进行调整,以获得更高的准确率。

四、OOB(Out of bag)

所谓 OOB,就是当我们做有限次放回取样时,有一定的概率某些样本根本没有取到。经过严格的数学计算,平均有37%的样本是无法取到的,这些样本就被称为 Out of bag,也就是从来没取出来的样本。既然如此,我们在使用 Bagging 的时候就不需要进行训练集和测试集的分离了,我们可以直接使用这些从来没取过的样本作为测试集。sklearn 也为我们提供了一个 oob_score_ 属性,就能很方便的计算 Bagging 时使用这些 Out of bag 验证的结果。

下面我们通过代码测试一下sklearn 中的 Out of bag,实现如下:

import numpy as np

from sklearn import datasets

X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=666)

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier

bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
                               n_estimators=500, max_samples=100,
                               bootstrap=True, oob_score=True) #设置oob_score为True,这个属性默认为False
bagging_clf.fit(X, y)
print(bagging_clf.oob_score_) #prints: 0.91

五、使用 n_jobs 进行并行化处理

Bagging 取样方式,使得计算机极易进行并行化处理,因为对于每一个子模型都是独立的随机抽取训练数据集,而且每个子模型的训练也都是独立的,所以系统可以对所有的子模型并行处理。我们可以在构造 BaggingClassifier 时,传入一个 n_jobs 参数,来决定使用几个核心来处理 Bagging,若传入-1,代表使用所有的核心,例如:

bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
                               n_estimators=500, max_samples=100,
                               bootstrap=True, oob_score=True,
                               n_jobs=-1) #设置将所有的核心用于 Bagging

六、其他的让子模型产生差异化的方式

Bagging 创建每一个子模型时,我们要让这些子模型有差异,之前我们让子模型产生差异的方式是我们让每一个子模型去看更小的一个样本数据集(即对样本进行随机取样)。其实还有其他方法让这些子模型产生差异化,例如我们在样本数据特征非常多的时候,对特征进行随机取样(Random Subspaces)。例如在图像识别时,每一张图片的每一个像素点都是一个特征,那么每一个样本的特征就比较大,Bagging 时就可以对特征进行随机采样。

本身样本所有的特征构成了特征空间,每次对特征进行取样时相当于在一个子空间中进行取样。

另外,若我们既对样本进行随机取样,又对特征空间进行随机取样,这样的采样方式又被称为 Random Patches。

如上图所示,我们把数据本身看作一个矩阵,每一行是一个样本,每一列代表一个特征,Random Patches 的意思是既在行的方向上随机,又在列的方向上随机,得到的结果就好像是一块布上的很多补丁,这些补丁是随机的,就有了 Random Patches 这个名字。

下面我们用 sklearn 提供的接口来快速实现一下特征取样,代码如下:

random_subspaces_clf = BaggingClassifier(DecisionTreeClassifier(),
                               n_estimators=500, max_samples=500, #max_samples设为500,关闭对样本的随机采样,即每次随机取500个,相当于没对样本随机
                               bootstrap=True, oob_score=True,
                               max_features=1, bootstrap_features=True) #max_features决定一次取多少个特征;bootstrap_features决定是否使用放回采样
random_subspaces_clf.fit(X, y)
print(random_subspaces_clf.oob_score_) #prints: 0.82

若我们把 max_samples 设为 100,就相当于既对样本数进行采样,又对特征进行采样,即 Random Patches,最终模型的准确率会有所提高。

发表评论

电子邮件地址不会被公开。 必填项已用*标注