Boosting,Bagging和Stacking-使用sklearn和mlens的集成方法

Boosting,Bagging和Stacking-使用sklearn和mlens的集成方法

首页休闲益智午餐盒堆栈更新时间:2024-05-29

Binning、bagging和stacking是数据科学家工具包,也是一系列称为集成方法的统计技术的一部分。

有三个主要术语描述各种模型的集成(组合)到一个更有效的模型:

什么是集成方法?

这里的想法是训练多个模型,每个模型的目标是预测或分类一组结果。

模型学习中的大多数错误来自三个主要因素:方差、噪声和偏差。通过使用集成方法,我们能够提高最终模型的稳定性并减少前面提到的错误。通过组合许多模型,我们能够(大部分)减少方差,即使它们各自不是很好,因为我们不会受到来自单个源的随机错误的影响。

集成建模的主要原理是将弱学习者集中在一起形成一个强学习者。

考虑一下,我将在下面展示房屋价格,以及你如何考虑将一个人的房子的价格分类为低,中,或高。你会用一条规则来确定这个分类吗?或者你会根据不同的因素使用不同的规则吗?

你当然会使用各种规则,例如:

正如这几个问题不能通过一个分析来解决,而是通过不同的和重叠的分析方法来解决。这是集成建模的核心。

住房数据集

以下示例查看住房数据,以查看房屋的价格分类(低,中,高)是否可以通过以下几个特征来确定:

使用pydataset的住房数据来表示我们的各种集成方法在分类精度上的差异。需要注意的是,我认为这是一个很好的数据集,但是有太多的二进制变量。如果我回去重做这个项目,我可能会使用Boston housing dataset,它有更多的地理定位特性工程的特性和灵活性。

让我们来看看我们的数据,Python代码如下:

from pydataset import data

# Get the housing data

df = data('Housing')

df.head().values

# Check the data

array([[42000.0, 5850, 3, 1, 2, 'yes', 'no', 'yes', 'no', 'no', 1, 'no'],

[38500.0, 4000, 2, 1, 1, 'yes', 'no', 'no', 'no', 'no', 0, 'no'],

[49500.0, 3060, 3, 1, 1, 'yes', 'no', 'no', 'no', 'no', 0, 'no'], ...

# Create dictionary to label 'yes' and 'no'

d = dict(zip(['no', 'yes'], range(0,2)))

for i in zip(df.dtypes.index, df.dtypes):

if str(i[1]) == 'object':

df[i[0]] = df[i[0]].map(d)

在我们讨论价格之前,先看看价格的范围

for i, j in enumerate(np.unique(pd.qcut(df['price'], 3))):

print i, j

# Results

0 (24999.999, 53000.0]

1 (53000.0, 74500.0]

2 (74500.0, 190000.0]

看起来我们的数据集中最低的房子是25K美元,最高的是190K美元。虽然不是市面上最贵的房子,但对于下面的例子来说就足够了。现在我们来讨论一下价格。

df[‘price’] = pd.qcut(df[‘price’], 3, labels=[‘0’, ‘1’, ‘2’]).cat.codes

# Split into two sets

y = df['price']

X = df.drop('price', 1)

现在我们有事情要处理了。它应该足以说明集成模型的力量。

本文不打算对任何数据进行探究,但是让我们看一下快速的相关图,看看我们正在为住房数据做什么。

住房数据的相关图

这里没有什么有趣的东西,除了看起来像房子的stories 增加了地下室的机会减少了。车道和卧室的情况也是如此,但也只有一点点。

为了完整起见,这是上面的热图显示的一些更有趣的关系的成对图。

数据的选定部分的成对图

Bagging

引入的第一个术语bagging是Bootstrapping 和aggregating的结合的简写。Bootstrapping是一种帮助减少分类器的方差和减少过度拟合的方法,通过从训练集中重新采样数据,该训练集中的基数与原始集合相同。

一个模型的高方差是不好的,表明它的性能对所提供的训练数据是敏感的。因此,即使提供了更多的训练数据,模型也可能表现不佳。甚至不能减少模型的方差。

当你有有限的数据时,bagging是一种有效的方法,通过使用样本,你可以通过对许多样本的分数进行汇总得到一个估计。

bagging最简单的方法是使用几个小的子样本并将它们bag,如果集成精度比基本模型高得多,它就会工作;如果不是,使用较大的子样本。

# Bagging RandomForestClassifier at different subsamples

# Bagging classifier at .1 subsamples

Avg. Accuracy of: 0.641 ( /-) 0.080

# Bagging classifier at .3 subsamples

Avg. Accuracy of: 0.650 ( /-) 0.091 # Better accuracy

# Bagging classifier at .5 subsamples

Avg. Accuracy of: 0.639 ( /-) 0.091 # Worse accuracy

注意,使用较大的子样本不能保证改进结果。在bagging中,在基本模型精度和通过bagging获得的增益之间进行权衡。当您有一个不稳定的模型时,bagging可以极大地改善集合,但是当您的基础模型更稳定时——训练在更大的子样本上,具有更高的精度——bagging的改进减少了。

一旦bagging完成,所有模型都创建在(大部分)不同的数据上,然后使用加权平均来确定最终的分数。

# Get some classifiers to evaluate

from sklearn.model_selection import cross_val_score

from sklearn.ensemble import BaggingClassifier, ExtraTreesClassifier, RandomForestClassifier

from sklearn.neighbors import KNeighborsClassifier

from sklearn.linear_model import RidgeClassifier

from sklearn.svm import SVC

seed = 1075

np.random.seed(seed)

# Create classifiers

rf = RandomForestClassifier()

et = ExtraTreesClassifier()

knn = KNeighborsClassifier()

svc = SVC()

rg = RidgeClassifier()

clf_array = [rf, et, knn, svc, rg]

for clf in clf_array:

vanilla_scores = cross_val_score(clf, X, y, cv=10, n_jobs=-1)

bagging_clf = BaggingClassifier(clf,

max_samples=0.4, max_features=10, random_state=seed)

bagging_scores = cross_val_score(bagging_clf, X, y, cv=10,

n_jobs=-1)

print "mean of: {1:.3f}, std: ( /-) {2:.3f [{0}]"

.format(clf.__class__.__name__,

vanilla_scores.mean(), vanilla_scores.std())

print "Mean of: {1:.3f}, std: ( /-) {2:.3f} [Bagging {0}]\n"

.format(clf.__class__.__name__,

bagging_scores.mean(), bagging_scores.std())

我们的bagging和我们分类器的vanilla 版有什么不同?

Mean of: 0.632, std: ( /-) 0.081 [RandomForestClassifier]

Mean of: 0.639, std: ( /-) 0.069 [Bagging RandomForestClassifier]

Mean of: 0.636, std: ( /-) 0.080 [ExtraTreesClassifier]

Mean of: 0.654, std: ( /-) 0.073 [Bagging ExtraTreesClassifier]

Mean of: 0.500, std: ( /-) 0.086 [KNeighborsClassifier]

Mean of: 0.535, std: ( /-) 0.111 [Bagging KNeighborsClassifier]

Mean of: 0.465, std: ( /-) 0.085 [SVC]

Mean of: 0.535, std: ( /-) 0.083 [Bagging SVC]

Mean of: 0.639, std: ( /-) 0.050 [RidgeClassifier]

Mean of: 0.597, std: ( /-) 0.045 [Bagging RidgeClassifier]

除了一个分类器外,所有的分类器的方差都较低。同样,除了我们的脊分类器,我们的分类器的精度都增加了。看起来这个bagging的东西确实有用。

所以我们的bagged分类器(大部分)更好,但是我们选择哪一个呢?

如何选择

Sklearn的VotingClassifier允许您组合不同的机器学习分类器,并对预测的类标签对记录进行投票。

对于分类器,有两种投票方式:hard 和soft。

对于hard投票,你只需要大多数分类器来决定结果。如下图所示,各种bagged模型用H表示,分类器的结果显示在行上。在最右边,H1和H3投票给第一个记录为“no”(紫色),H2投票给“yes”(黄色)。因为有2个模型投了“no”的票,所以集成团队将该记录归类为“no”。

使用soft(加权),我们使用每个分类器计算百分比权重。收集每个记录的每个模型的预测类概率,并乘以分类器权重,最后进行平均。然后从具有最高平均概率的类标签导出最终的类标签。

实际上,如果你只是提供你认为应该加权或减少加权的模型的最佳猜测,很难找到权重。可以构建线性优化方程或神经网络以找到每个模型的正确加权以优化整体的准确性。

# Example of hard voting

from sklearn.ensemble import VotingClassifier

clf = [rf, et, knn, svc, rg]

eclf = VotingClassifier(estimators=[('Random Forests', rf), ('Extra Trees', et), ('KNeighbors', knn), ('SVC', svc), ('Ridge Classifier', rg)], voting='hard')

for clf, label in zip([rf, et, knn, svc, rg, eclf], ['Random Forest', 'Extra Trees', 'KNeighbors', 'SVC', 'Ridge Classifier', 'Ensemble']):

scores = cross_val_score(clf, X, y, cv=10, scoring='accuracy')

print("Accuracy: %0.2f ( /- %0.2f) [%s]" % (scores.mean(), scores.std(), label))

# Results

Mean: 0.627, std: ( /-) 0.082 [Random Forest]

Mean: 0.632, std: ( /-) 0.084 [Extra Trees]

Mean: 0.500, std: ( /-) 0.086 [KNeighbors]

Mean: 0.465, std: ( /-) 0.085 [SVC]

Mean: 0.639, std: ( /-) 0.050 [Ridge Classifier]

Mean: 0.641, std: ( /-) 0.094 [Ensemble]

# Compare this to the bagged results

Mean: 0.661, std: ( /-) 0.099 [Bagging Random Forest]

Mean: 0.650, std: ( /-) 0.082 [Bagging Extra Trees]

Mean: 0.535, std: ( /-) 0.117 [Bagging KNeighbors]

Mean: 0.528, std: ( /-) 0.068 [Bagging SVC]

Mean: 0.604, std: ( /-) 0.046 [Bagging Ridge Classifier]

Mean: 0.658, std: ( /-) 0.091 [Bagging Ensemble]

通过我们的bagged集成结果,我们提高了准确度(.658相对 .641)和方差(.091相对 .094)的减少,因此在我们将所有各种模型合并为一个之后,我们的集成模型正如预期的那样工作。

决策边界

现在我们已经知道我们的模型在单独和一起做得多好。我们如何看待分类器的边界并确定它们的截止点在哪里?

幸运的是,mlxtend有一个plot_decision_regions,显示每个分类器如何做出决定。因为在每个分类的决策过程中都有两个以上的特性,下面的图仅仅显示了lotsize和story的决策边界。

我们将使用eclf我们在项目的前一个投票阶段中定义的分类器,并使用分类结果将自己与我们之前vanilla模型进行比较。

以下是如何重新创建图表的Python实现方法

import matplotlib.pyplot as plt

from mlxtend.plotting import plot_decision_regions

import matplotlib.gridspec as gridspec

import itertools

gs = gridspec.GridSpec(3, 3)

fig = plt.figure(figsize=(14, 12))

labels = ['Random Forest', 'Extra Trees', 'KNN', 'Support Vector',

'Ridge Reg.', 'Ensemble']

for clf, lab, grd in zip([rf, et, knn, svc, rg, eclf],

labels,

itertools.product([0, 1, 2], repeat = 2)):

clf.fit(X[['lotsize', 'stories']], y)

ax = plt.subplot(gs[grd[0], grd[1]])

fig = plot_decision_regions(X=np.array(X[['lotsize', 'stories']]),

y=np.array(y), clf=clf)

plt.title(lab)

Boosting

考虑函数在函数空间上的优化,其中的优化可以使用梯度下降法来解决。Vanilla 梯度梯度下降用于最小化一组参数。例如,通过从误差函数的更新找到线性回归的参数的权重。

如果我们有一个光滑的凸参数空间,那么参数估计就显得微不足道,但是并不是所有的问题都能提供这样一个简单的平面来遍历。我们的问题是,因为有许多分类和二元变量,它创建了一个复杂的梯度,在优化过程中会遇到许多局部最小值。对于这些问题,我们可以使用一种不同的形式的梯度下降,叫做Boosting。

Boosting的主要思想是按顺序为整个集合模型添加其他模型。以前使用bagging,对创建的每个单独模型进行平均。这次每次迭代Boosting,创建一个新模型,并从先前学习者的误差中训练(更新)新的基础学习者模型。

该算法创建多个弱模型,其输出被加在一起以获得整体预测。这是早期的集成建模。现在boosted 梯度将当前预测移动到真实目标,其方式与梯度下降向真实值的移动方式类似。梯度下降优化发生在变化模型的输出上,而不是它们各自的参数。

有不同的方法来优化boosting算法,但它们超出了本文的范围。

与上面的bagging示例不同,经典的增强子集创建不是随机的,性能将取决于先前模型的性能。因为,迭代的每个新子集包含可能被先前模型误差分类的元素。我们还将使用我们之前使用的相同的hard投票来将模型组合在一起。

ada_boost = AdaBoostClassifier()

grad_boost = GradientBoostingClassifier()

xgb_boost = XGBClassifier()

boost_array = [ada_boost, grad_boost, xgb_boost]

eclf = EnsembleVoteClassifier(clfs=[ada_boost, grad_boost, xgb_boost], voting='hard')

labels = ['Ada Boost', 'Grad Boost', 'XG Boost', 'Ensemble']

for clf, label in zip([ada_boost, grad_boost, xgb_boost, eclf], labels):

scores = cross_val_score(clf, X, y, cv=10, scoring='accuracy')

print("Mean: {0:.3f}, std: ( /-) {1:.3f} [{2}]".format(scores.mean(), scores.std(), label))Let’s perform the same of voting on our boosting models

# Results

Mean: 0.641, std: ( /-) 0.082 [Ada Boost]

Mean: 0.654, std: ( /-) 0.113 [Grad Boost]

Mean: 0.663, std: ( /-) 0.101 [XG Boost]

Mean: 0.667, std: ( /-) 0.105 [Ensemble]

那么,决策边界是如何看待Boosting算法的呢?有趣的是,无论是boosted 还是bagged 效果都非常相似。

总的来说,似乎我们的boosting集成模型提供了准确性,0.667 这恰好超过了bagging的得分0.658。这并不是说boosting比bagging更好,因为在这些例子中没有任何优化。还应注意,boosting模型的标准偏差较高,这是预期的。

但是,还有一种集成方法可供选择。

Stacking

Stacking是另一个集成模型,在这个模型中,一个新的模型是从两个(或更多)先前模型的组合预测中训练出来的。模型中的预测作为每个序列层的输入,并结合起来形成一组新的预测。这些可以在其他层上使用,或者过程可以在这里停止,得到最终的结果。在下面的例子中,为了简单起见,我只使用一个层。

集成Stacking可以被称为混合,因为所有的数字都被混合以产生预测或分类。

记住,在Stacking算法中添加层和更多的模型并不意味着你会得到一个更好的预测器。机器学习没有免费的午餐。

例如,用Stacking模型我删除SVM从层,并最终从提高精度 .52到 .68。而且,这将在一瞬间变得更加清晰。

Stacking可视化

通常创建Stacking管道会很困难。然而,有一个很棒的ML Ensemble包(http://ml-ensemble.com/)简化了这个过程,让你可以专注于工作和优化模型,而不仅仅是编码。

我采用了[‘Random Forest’, ‘Extra Trees’, ‘KNeighbors’, ‘SVC’, ‘Ridge Classifier’]我们开始使用的vanilla分类器,并将它们组合成所有可能的组合,以测试哪种组合在我们的Stacking模型中表现最佳。

例如['SVC','Ridge Classifier'],['SVC'],['Random Forest','Extra Trees','KNeighbors']等。

一旦完成,我们应该有一些东西可以与我们之前的两个结果进行比较。Mlens确实打包了一个模型选择器,但我想手动显示该过程。

最后,我们将使用逻辑回归作为最终输出层,因为我们仍然希望对住房数据进行分类。Python代码如下:

from itertools import combinations

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()

names = ['Random Forest', 'Extra Trees', 'KNeighbors', 'SVC', 'Ridge Classifier']

def zip_stacked_classifiers(*args):

to_zip = []

for arg in args:

combined_items = sum([map(list, combinations(arg, i)) for i in range(len(arg) 1)], [])

combined_items = filter(lambda x: len(x) > 0, combined_items)

to_zip.append(combined_items)

return zip(to_zip[0], to_zip[1])

stacked_clf_list = zip_stacked_classifiers(clf_array, names)

best_combination = [0.00, ""]

for clf in stacked_clf_list:

ensemble = SuperLearner(scorer = accuracy_score,

random_state = seed,

folds = 10)

ensemble.add(clf[0])

ensemble.add_meta(lr)

ensemble.fit(X_train, y_train)

preds = ensemble.predict(X_test)

accuracy = accuracy_score(preds, y_test)

if accuracy > best_combination[0]:

best_combination[0] = accuracy

best_combination[1] = clf[1]

print("Accuracy score: {:.3f} {}").format(accuracy, clf[1])

print("\nBest stacking model is {} with accuracy of: {:.3f}").format(best_combination[1], best_combination[0])

# Output

Accuracy score: 0.674 ['Random Forest']

Accuracy score: 0.663 ['Extra Trees']

Accuracy score: 0.547 ['KNeighbors']

Accuracy score: 0.481 ['SVC']

...

Best stacking model is ['Extra Trees', 'KNeighbors', 'SVC'] with accuracy of: 0.691

看起来我们已经击败了bagging 和boosting模型,得分为 .691。

虽然这个例子只有一层,但mlens我们能够很容易地堆叠越来越多的层。

此外,您还可以对每个模型的参数空间执行伪网格搜索,使用该Evaluator包查找您在堆栈中使用的每个模型的最佳值。

最后

即使 .691是一个相当弱的分类器,我希望通过集成建模和投票的每个阶段的过程是有帮助的。与大多数数据科学一样,大部分工作将用于测试和调整超参数,以及用于改进模型的特征工程。

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved