集成学习实战

       如果你随机想几千个人询问一个复杂问题,然后汇总他们的回答。在许多情况下,你会发现,这个汇总的回答比专家的回答还要好。这被称为群体智慧。同样,如果你聚合一组预测器(比如分类器或回归器)的预测,得到的预测结果也比最好的单个预测要好。这样的一组预测器,我们称为集成,所以这种技术,也被称为集成学习,而一个集成学习的算法则被称为集成方法。

       目前最流行的几种集成方法,包括bagging、boosting、stacking等

投票分类器

       假设你已经训练好了一些分类器,每个分类器的准确率约为80%。要创建出一个更好的分类器,最简单的办法就是集合每个分类器的预测,然后将得票最多得结果作为预测类别。这个被称为硬投票分类器

       这个投票法分类器的准确率通常比集成中最好的分类器还要高。这个怎么可能呢?

       假设你创建一个包含1000个分类器的集成,每个分类器都只用51%的几率是正确。如果你以大多数投票的类别作为预测结果,你可以期待的准确率高达75%。但是前提是所有的分类器都是完全独立的,彼此的错误毫无相关。显然这是不可能的,因为它们都是在相同的数据上训练的。它们很可能会犯相同的错误,所以也会有很多次大多数投给了错误的类别,导致集成的准确率有所降低。

       注:当预测器尽可能互相独立时,集成方法的效果最优。获得多种分类器的方法之一就是使用不同的算法进行训练。这会增加它们犯不同类型错误的机会,从而提升集成的准确率。

       下面的代码用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.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

log_clf = LogisticRegression(solver="liblinear", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=10, random_state=42)
svm_clf = SVC(gamma="auto", random_state=42)

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

Bagging和Pasting

     前面提到的方法是使用不同的训练算法,还有另一种方法是每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。采样时如果将样本放回,这种方法叫做Bagging(bootstrap aggregating的缩写,也叫自举汇集法);采样时样本不放回,这种方法叫Pasting

总体来说,与直接在原始训练集上训练的单个预测器相比,集成的偏差相近,但是方差更低。你也可以通过不同的CPU甚至不同的服务器,并行地训练预测器。这正是bagging和pasting方法如此流行的原因之一,它们非常易于扩展。

    以下代码训练了一个包含500个决策树分类器的集成,每次随机从训练集中采样100个训练实例进行训练,然后放回。

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

bag_clf = BaggingClassifier(
    DecisionTreeClassifier(random_state=42), n_estimators=500,
    max_samples=100, bootstrap=True, n_jobs=-1, random_state=42)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)


from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, y_pred))
0.904
单个决策树
tree_clf = DecisionTreeClassifier(random_state=42)
tree_clf.fit(X_train, y_train)
y_pred_tree = tree_clf.predict(X_test)
print(accuracy_score(y_test, y_pred_tree))
0.856

比较单个决策树和bagging集成,如图所示。可以看出,集成预测的泛化效果很可能比单独的决策树要好一些;

二者偏差相近,但是集成的方差更小

from matplotlib.colors import ListedColormap

def plot_decision_boundary(clf, X, y, axes=[-1.5, 2.5, -1, 1.5], alpha=0.5, contour=True):
    x1s = np.linspace(axes[0], axes[1], 100)
    x2s = np.linspace(axes[2], axes[3], 100)
    x1, x2 = np.meshgrid(x1s, x2s)
    X_new = np.c_[x1.ravel(), x2.ravel()]
    y_pred = clf.predict(X_new).reshape(x1.shape)
    custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])
    plt.contourf(x1, x2, y_pred, alpha=0.3, cmap=custom_cmap)
    if contour:
        custom_cmap2 = ListedColormap(['#7d7d58','#4c4c7f','#507d50'])
        plt.contour(x1, x2, y_pred, cmap=custom_cmap2, alpha=0.8)
    plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", alpha=alpha)
    plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", alpha=alpha)
    plt.axis(axes)
    plt.xlabel(r"$x_1$", fontsize=18)
    plt.ylabel(r"$x_2$", fontsize=18, rotation=0)

plt.figure(figsize=(11,4))
plt.subplot(121)
plot_decision_boundary(tree_clf, X, y)
plt.title("Decision Tree", fontsize=14)
plt.subplot(122)
plot_decision_boundary(bag_clf, X, y)
plt.title("Decision Trees with Bagging", fontsize=14)
save_fig("decision_tree_without_and_with_bagging_plot")
plt.show()

提升法(Boosting)

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

AdaBoost

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

    例如,首先需要训练一个基础分类器(比如决策树),用它对训练集进行预测。然后对错误分类的训练实例增加其相对权重,接着,使用这个最新的权重对第二个分类器进行训练,然后再次对训练集进行预测,继续更新权重,并不断循环向前。

from sklearn.ensemble import AdaBoostClassifier

ada_clf = AdaBoostClassifier(
    DecisionTreeClassifier(max_depth=1), n_estimators=200,
    algorithm="SAMME.R", learning_rate=0.5, random_state=42)
ada_clf.fit(X_train, y_train)
plot_decision_boundary(ada_clf, X, y)

另一个非常受欢迎的提升法是梯度提升(Gradient Boosting

与AdaBoost不同之处在于,它不是在每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。

 

堆叠法(stacking)

它基于一个简单的想法:与其使用一些简单的函数来聚合集成中所有预测器的预测,我们为什么不训练一个模型来执行这个聚合呢?如下图底部的三个预测器分别预测了不同的值,然后最终的预测器(称为混合器或元学习器)将这些预测作为输入,进行最终预测。

   训练混合器的常用方法是使用留存集。首先,将训练集分为两个子集,第一个子集用来训练第一层的预测器。然后用第一层的预测器在第二个(留存)子集上进行预测。因为预测器在训练时从未见过这些实例,所以可以确保预测是“干净的”。那么现在对于留存集中的每个实例都有了三个预测值。我们可以使用这些预测值作为输入特征,创建一个新的训练集,并保留目标值。在这个新的训练集上训练混合器,让它学习根据第一层的预测来预测目标值。

Scikit-Learn不直接支持堆叠,但是自己堆出stacking实现并不太难,或者可以使用开源的实现方案,如brew(https://github.com/viisar/brew

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值