堆叠集成方法
原文towardsdatascience.com/the-stacking-ensemble-method-984f5134463a发现堆叠在机器学习中的力量——一种将多个模型组合成一个单一强大预测器的技术。本文从基础知识到高级技术探讨了堆叠揭示了它是如何结合不同模型的优势以提高准确性的。无论你是堆叠的新手还是寻求优化策略本指南提供了实用的见解和技巧帮助你用 scikit-learn 提升预测建模水平。虽然这篇文章基于 scikit-learn但我提供了一个纯 Python 类该类实现了并模仿了 scikit-learn 的堆叠模型。审查这个纯 Python 实现是检验和测试你理解的一个极好方式。在本文中我们将看到堆叠在机器学习中的集成技术中的地位堆叠是如何在内部工作以提供预测的它是如何拟合的什么是“重堆叠”如何创建多层堆叠为什么以及如何检查基础模型的表现如何调整和优化堆叠模型的使用https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fe094039d326e50e35f6cad8640fd8ab.png由Brigitte Tohm在Unsplash拍摄的照片如果你喜欢或想用 scikit-learn 学习机器学习请查看我关于这个神奇包的教程系列Sklearn 教程所有图片均为作者拍摄。堆叠作为集成技术堆叠是机器学习中的一个集成技术意味着它将多个“基础模型”组合成一个单一的“超级模型”。存在许多不同的集成技术它们是传统机器学习中最有效的技术之一。当我说“基础模型”时我指的是你可能遇到过的任何传统模型——那些你可以从 scikit-learn 导入、拟合和直接预测的模型。这些基础模型例如线性回归或逻辑回归及其变体如 LASSO 或岭回归支持向量回归器或分类器决策树回归器或分类器K-最近邻回归器或分类器单独来看所有这些模型都有它们自己的优缺点它们自己的超参数它们自己的“舒适区”。因此它们可能在你的机器学习问题上的表现不同它们可以在你的数据集上得到不同的分数以及在某些样本/数据集区域表现更好而在其他地方表现更差。集成方法的一般思想是将多个这样的基础模型组合成一个更好的模型。对于堆叠技术我们将使用这些模型作为构建块以及我们结合预测的手段。让我们快速回顾一些常见的集成模型技术voting从一组基础模型创建一个投票集成最终预测是通过平均基础模型的预测对于回归或最预测的类别对于分类来计算的。这是最简单的一种集成技术也是最容易理解和解释的。boosting一系列弱学习器的预测几乎是使用权重递归调整的。这种技术导致了 Adaboost 算法和 GradientBoosting并且是表现最好的现成模型之一。bagging也称为 bootstrap-aggregating一种并行集成技术其中许多单个模型在不同的数据集子集上拟合。然后将这些模型的预测进行组合使用投票/平均。当单个模型是决策树时这种技术导致随机森林算法。stacking这是本文解释的集成技术请继续阅读因此stacking 只是集成技术中的一种选择。正如我们将看到的stacking 在许多方面与上述集成技术不同它不会以顺序方式组合基础模型它不会以递归的方式更新模型的权重它不会简单地平均基础模型的预测它使用各种类型的模型集合线性回归、KNN、SVM 等而 bagging 和 boosting 通常使用相同的基础模型集合通常是决策树stacking 的工作原理堆叠方法的最大差异和特殊性之一是基础模型的构建方式和最终预测的计算方式。堆叠集成的结构如下一组基础模型构成了集成它们独立拟合和预测。这些模型也被称为“基础层”或“堆叠结构的第 0 层”类似于投票集成。这些基础模型可能被称为“弱学习器”因为在 boosting/bagging 的上下文中但请注意它们不是严格意义上的弱学习器。另一个单独的模型将包装基础模型的预测并计算最终预测。这个模型也被称为“最终层”或“第 1 层”概括地看它看起来像这样我们现在关注预测过程https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d73f87e580834b1177487d86060c2eb7.png堆叠模型的结构示例这里是一个回归器。使 stacking 具有特殊性的重要事情是以下内容在内部堆叠的最终模型不使用通常的输入数据集 X而是从基础模型的预测中学习/预测。想法是使用一个模型来拟合/学习基础模型如何预测输出 y并将这些预测与实际的 truey_true进行比较。模型拟合完成后预测过程如下对于一个新的 X 数据集X或甚至单个样本x所有基础模型的预测都是独立计算的对于基础模型中的每个模型y_predmodel.predict(X)所有这些y_pred向量都是水平拼接的以便创建一个新的二维数据集其中每一列代表给定基础模型的预测我们将这个矩阵称为X_final因为它代表了最终估计器的二维输入数据集。这个X_final二维数据集被最终估计器用来预测实际的最终预测final.predict(X_final)https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f273fa3e1df92e20a5466a2b8dbdf920.png在拟合的堆叠模型上计算预测的方式。基础模型的 y 预测水平组合成一个二维矩阵用作最终估计器的输入。换句话说有一个中间的二维数据集矩阵在内部创建由基础模型的 y 预测组成这个矩阵被用作最终模型的输入——这意味着最终模型接受一个 y 空间的二维矩阵值并输出实际的 y 空间值的 1D 预测。另一种看待堆叠模型的方式是将其视为一种投票模型只不过我们不是简单地取平均/最高票数的预测而是将这些预测输入到另一个模型中。换句话说我们可以通过使用一个简单的平均/argmax 模型作为最终模型来模拟堆叠投票。通常用于回归问题的最终模型是一个简单的线性回归用于分类问题的逻辑回归这些是 scikit-learnStackingClassifier和StackingRegressor的默认值。通过最终估计器组合基础模型预测的步骤有时被称为“混合”因为所有模型的预测都被混合成一个单一的预测。正确拟合堆叠模型堆叠模型工作的特定方式使得必须使用特定的拟合过程。与任何估计器一样这是由 scikit-learn 在幕后自动完成的因此您不必完全理解这个拟合过程就可以使用堆叠模型。另一方面了解您的模型是如何工作的总是好事——学习新事物既有趣又令人满足用于拟合堆叠模型的算法可以分解为 2 个步骤。第一个步骤相当简单而第二个步骤需要更多的解释第 1 步所有基础模型来自第 0 层的那些都是完全独立地使用整个 X/y 数据集进行拟合的。换句话说第一步等价于for model in base_models: model.fit(X,y)第 2 步使用未拟合的基础模型通过交叉验证方案拟合最终估计器第一步相当直观当在堆叠模型stack.fit(X,y)上调用 fit 方法时X 和 y 被处理到每个基础模型中并且它们是相互独立地拟合的。这一步等价于for model in base_models: model.fit(X,y)。那些拟合的估计器被存储在堆叠模型的stack.estimators_属性中。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c8346c08fd6f797626f4c5bf03825041.png拟合堆叠的基础模型。然后为了训练最终估计器使用了一种不同的方法。我们已经有了那个拟合的目标输出 y但我们仍然需要生成由基础模型的预测创建的中间X_final数据集。使用已经拟合的估计器的.predict方法会导致某种过拟合因为输入 X 已经被基础估计器看到并训练过。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/470d21fe78099b178e8af0629999764b.png如何将 X/y 数据集分解以创建最终模型拟合的分割。换句话说我们不能使用已经训练好的基础估计器来将输入数据集馈送到拟合最终估计器。相反每个估计器都被馈送到cross_val_predict函数的相同交叉验证折叠中以生成所有基础估计器的y_pred。然后这些基础预测在X_final训练数据集中水平连接以便最终估计器使用final_model.fit(X_final, y)进行拟合。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/74f124206abe6a325add21fd8eeed2cb.png第一个基础模型这里是一个岭回归的预测是通过连接交叉验证方案中所有折叠的预测来计算的。这样我们构建一个与输入 X 完全对应的 y 向量。现在我们知道将问题分解为“最终估计器用于拟合目标 y 的 X 数据集是通过连接由cross_val_predict函数给出的所有估计器的预测值创建的”对所有估计器使用相同的 cv 策略以便考虑到所有基础估计器总是看到相同的输入/输出。这意味着第一步中拟合的基础模型那些使用整个 X 数据集拟合的模型不会被用来拟合最终估计器。相反每个折叠使用基础模型的一个副本并且每个折叠的预测被连接起来对于每个模型是垂直连接以创建X_final。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/6cd9956fa34154dd372a091098b1841e.png为所有基础模型计算 y 预测并将其水平连接以创建“X_final”矩阵用作最终模型的 X 输入。这样最终的估计器使用基础模型在尚未被基础模型看到的样本上的预测进行拟合。另一种查看此拟合过程的方法是表示来自交叉验证折叠分解的中间 2D 数据集X_finalX 数据集被分成 N 个折叠X_trainX_testy_trainy_test。这些折叠必须遵守“分区”标准即每个单独的样本只能作为测试集使用一次。对于这些分割中的每一个使用.fit(X_train, y_train)对未拟合的基础模型进行拟合并使用.predict(X_test)计算相应的预测。这样我们得到了所有基础模型的y_pred以及所有分割。最终的X_train仅仅是所有分割预测的垂直连接。重新堆叠堆叠架构允许对模型进行额外的定制你可以将其视为一个超参数允许我们调整模型的工作方式。这个想法真的很简单除了基础模型的水平堆叠预测外原始输入 X 数据集也被添加到最终估计器的输入中。这样最终估计器可以在数据中找到更多信息包括基础模型的预测和原始数据。换句话说通过增加最终模型输入的特征数量堆叠模型的复杂性增加。在 scikit-learn 中这是通过堆叠模型的passthroughTrue参数来实现的。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0d262f82fc9eeb68b3671b0f644353a4.png重新堆叠仅仅意味着将原始 X 数据集添加到基础模型的预测中。这增加了最终模型的特征空间复杂性。显然X 矩阵在拟合时间和预测时间都添加到 X_final 中。多层堆叠模型的设计方式允许简单地创建额外的堆叠从而创建更复杂的模型。这可以通过嵌套堆叠模型来实现其中任何新的堆叠都被设置为前一个堆叠的最终估计器。请注意这将增加模型的复杂性以增加拟合/预测时间和潜在过拟合为代价。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3890b04eba1f0064fa6e61748cd4c862.png多层堆叠意味着使用多个基础模型层。这可以通过嵌套堆叠实现。调整堆叠模型如上所述Stacking 的目标是保留每个基础模型的最好部分以便它们可以相互补偿各自的弱点。这意味着如果所有模型在相同的样本上表现相同那么堆叠模型将不会更好因此我们可以通过使用表现不同的基础模型来优化堆叠模型理想情况下是在数据集的不同拓扑区域。因此检查基础模型的表现以及它们的预测误差是否相关可能是一个好主意。如果两个模型几乎共享相同的误差那么可能可以丢弃其中一个——在保持整体性能的同时降低计算复杂度。由于堆叠封装了所有基础模型和最终模型它继承了它们的超参数。由于我们感兴趣的是堆叠的性能而不是基础模型的表现因此它们的超参数必须在堆叠的上下文中进行调整而不是在堆叠之前。换句话说基础模型在超参数A 的情况下可能表现更好但堆叠的性能可能因为超参数B 而更好。最后使用passthroughTrue或False是调整堆叠模型的另一种方式。总体而言你应该始终检查是否有任何基础模型的表现与堆叠模型相当如果是这样则可以丢弃或调整堆叠模型因为其高计算复杂度必须伴随着非微不足道的性能提升。Stacking 是一种集成模型的形式因此拥有不完美的单个模型并不总是坏事目标在于模型性能相互补充——就像弱学习器结合在一起以创建一个更大更好的模型。在实践中堆叠预测器预测的效果与基础层中最好的预测器相当有时甚至通过结合这些预测器的不同优势而优于它。然而使用堆叠预测器进行训练/预测是计算密集型的。使用 scikit-learn 进行堆叠现在我们已经了解了所有关于堆叠的信息让我们看看如何使用 scikit-learn 来实现它。你需要记住的唯一一件事是在 scikit-learn 中堆叠被实现为一个模型/元模型在某种意义上它是一个模型因为它具有.fit 和.predict 方法。在某种意义上它是一个元模型因为它将其他模型作为输入。Scikit-learn 在集成模块中提供了用于分类和回归的堆叠模型。让我们看看一个回归的简单示例%matplotlib qtimportnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltimportseabornassnsfromsklearn.datasetsimportload_iris,make_moons,make_circlesfromsklearn.model_selectionimportStratifiedKFold,train_test_split,cross_validatefromsklearn.svmimportSVC,LinearSVCfromsklearn.naive_bayesimportGaussianNBfromsklearn.neighborsimportKNeighborsClassifierfromsklearn.linear_modelimportLogisticRegressionCV,LogisticRegressionfromsklearn.ensembleimportStackingClassifier X,ymake_circles(n_samples500,noise0.3,factor0.5,random_state1,shuffleTrue)# We use 3 base modelsbase_models[(knn,KNeighborsClassifier(n_neighbors3)),(logreg,LogisticRegression(max_iter1000)),(svc,SVC(C0.05)),]# Create a stack with the base models, and a LogisticRegression as the final estimatorstackStackingClassifier(estimatorsbase_models,final_estimatorLogisticRegressionCV(max_iter1000))restackStackingClassifier(estimatorsbase_models,final_estimatorLogisticRegressionCV(max_iter1000),passthroughTrue,)# Then we have a nice list to compare all modelsmodelsbase_models[(stack,stack),(restack,restack)]X_train,X_test,y_train,y_testtrain_test_split(X,y,stratifyy)forname,modelinmodels:model.fit(X_train,y_train)# decision boundary for stacking classifierx_min,x_maxX[:,0].min()-1,X[:,0].max()1y_min,y_maxX[:,1].min()-1,X[:,1].max()1xx,yynp.meshgrid(np.arange(x_min,x_max,0.2),np.arange(y_min,y_max,0.2))fig,axesplt.subplots(1,len(models),sharexTrue,shareyTrue)forax,(name,model)inzip(axes,models):Zmodel.predict(np.c_[xx.ravel(),yy.ravel()])ZZ.reshape(xx.shape)ax.contourf(xx,yy,Z,alpha0.8)ax.scatter(X_test[:,0],X_test[:,1],cy_test,edgecolorsk)ax.set_title(f{name}score{model.score(X_test,y_test):.2f})ax.set_xlabel(Feature 1)ax.set_ylabel(Feature 2)fig.tight_layout()为了完整性我添加了堆叠的重新堆叠版本。如图所示结合基础模型将堆叠的性能提升到基础模型中最佳性能的略高位置。使用重新堆叠可以带来性能的微小提升。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9f532d516a3b77c5bec3d3e9c38689e8.png3 个基础模型的分类性能以及这些基础模型的堆叠和重新堆叠。注意上述内容仅演示了如何创建堆叠模型 - 实际的性能评估需要调整和交叉验证。从零开始堆叠现在我们来看一个简单的堆叠实现使用 scikit-learn API。这只是一个玩具示例用于演示堆叠并不复杂。除了学习/解释目的外您应该使用 scikit-learn 的实现importnumpyasnpfromsklearn.datasetsimportload_diabetesfromsklearn.ensembleimportRandomForestRegressorfromsklearn.linear_modelimportRidgefromsklearn.model_selectionimportcross_validatefromsklearn.baseimportBaseEstimator,RegressorMixin,TransformerMixin,clonefromsklearn.model_selectionimportcross_val_predictclassHomeMadeStackingRegressor(BaseEstimator,RegressorMixin,TransformerMixin):def__init__(self,base_estimators,meta_estimator):self.base_estimatorsbase_estimators self.meta_estimatormeta_estimatordeffit(self,X,y):self.base_estimators_[clone(est)forestinself.base_estimators]self.meta_estimator_clone(self.meta_estimator)# Fit base estimators independantelyforestinself.base_estimators_:est.fit(X,y)# Generate X_final feature dataset for the final estimator# as the concatenation of the cross_val_predict restults of the base estimatorsX_finalnp.array([cross_val_predict(est,X,y,cv5)forestinself.base_estimators_]).T# Fit final estimatorself.meta_estimator_.fit(X_final,y)returnselfdefpredict(self,X):meta_featuresnp.array([est.predict(X)forestinself.base_estimators_]).Treturnself.meta_estimator_.predict(meta_features)bostonload_diabetes()X,yboston.data,boston.target base_estimators[RandomForestRegressor(n_estimators20,max_depth3,random_state1),Ridge()]meta_estimatorRidge()stackHomeMadeStackingRegressor(base_estimators,meta_estimator)modelslist(zip([rf,ridge],base_estimators))[(stack,stack)]results[]forname,basemodelinmodels:cv_resultscross_validate(basemodel,X,y,return_estimatorTrue,return_train_scoreTrue)dfpd.DataFrame.from_dict(cv_results)df.reset_index(namesrun,inplaceTrue)df[model]name results.append(df)cv_resultspd.concat(results).reset_index(dropTrue)cv_results_meltcv_results.melt(id_vars[model,run],value_vars[test_score,train_score,fit_time,score_time],var_namemetric,value_namevalue)sns.catplot(cv_results_melt,ymodel,xvalue,kindbar,huemodel,colmetric,sharexFalse)https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8027d25f9a2374c9b0dafb34df841ecb.png堆叠模型的表现优于其任何基础模型。如您所见堆叠模型的测试分数优于其任何基础模型。另一方面它需要更高的拟合时间。总结这里是您应该记住的关于堆叠的内容这是一种集成技术它使用最终模型来结合基础模型的预测最终模型使用基础模型预测的水平堆叠作为其输入当基础模型不同且相互补偿彼此的弱点时它效果最佳。它应该至少表现得比基础模型中的最佳模型更好。它可以通过使用基础模型和最终模型的超参数以及其 passthrough 参数来调整以启用或不启用重新堆叠调整过程涉及使用交叉验证方案来调整最终估计量基础估计量通常独立调整如果您喜欢这篇文章请查看我的其他帖子朴素贝叶斯清晰解释科学/数值 Python时间序列的傅里叶变换数据科学和机器学习