Stacking集成学习:从原理到实践,构建更稳健的机器学习模型
1. 项目概述:从“单打独斗”到“团队协作”的模型进化
在机器学习的世界里,我们常常面临一个经典困境:手头有好几个模型,比如线性回归、决策树、支持向量机,每个模型在训练集上表现都还不错,但一到测试集或者新数据上,它们的预测能力就参差不齐,有的在某些数据片段上表现优异,有的则在另一些片段上更稳健。这就好比一个团队里,有人擅长数据分析,有人擅长逻辑推理,有人直觉敏锐,但让他们单独去完成一个复杂的综合项目,总有力不从心的时候。 StackingRegressor 和 StackingCVRegressor 要解决的,正是这个问题。它们不是创造一个新的、更复杂的“超级模型”,而是构建一个“元学习器”(Meta-Learner),来学习如何最有效地整合这些“基础模型”(Base Models)的预测结果,从而形成一个更强大、更稳定的预测“团队”。
简单来说,Stacking(堆叠)是一种高级的集成学习技术。它的核心思想是“让模型来学习如何组合模型”。我们先用一组基础模型(比如线性回归、随机森林、梯度提升树)对原始数据进行第一轮预测,这些预测结果(我们称之为“元特征”)会被当作新的输入特征。然后,第二层的“元模型”(比如另一个线性回归)会基于这些元特征进行训练,学习如何给不同基础模型的预测结果分配权重,最终给出一个综合性的、通常更优的预测。 StackingRegressor 提供了一个基础的实现框架,而 StackingCVRegressor 则在其基础上,通过交叉验证来生成元特征,有效防止了过拟合,是实践中更推荐、更稳健的选择。
这篇文章,我将结合多年在工业界构建预测模型的经验,用最通俗的语言和具体的代码示例,为你彻底拆解这两个“模型融合利器”的工作原理、核心差异、以及最重要的——如何在实际项目中正确地使用它们,避开那些我踩过的坑。无论你是刚接触集成学习的新手,还是想优化现有模型 pipeline 的老手,这篇文章都能给你带来可以直接上手的干货。
2. 核心原理深度拆解:Stacking 是如何“思考”的?
要理解 Stacking,我们必须先跳出“一个模型拟合数据”的单一视角。Stacking 是一个两阶段的学习过程,它的“思考”逻辑更像是一个经验丰富的项目经理在协调专家团队。
2.1 第一阶段:基础模型生成“专家意见”
想象一下,我们有一个预测房价的任务。我们邀请了三位专家:
- 专家A(线性回归) :擅长捕捉全局的线性趋势,认为房价主要由面积、地段这些线性可加的因素决定。
- 专家B(决策树/随机森林) :擅长发现复杂的、非线性的交互规则,比如“学区好且房龄新”这种组合会极大提升房价。
- 专家C(支持向量回归) :对异常值不太敏感,专注于找到最能区分不同价位房子的边界。
在传统方法中,我们可能只选其中一位专家(一个模型)的最终结论。但在 Stacking 的第一阶段,我们让这三位专家 各自独立地 对每一套待评估的房产(每一条训练数据)给出一个预测价格。这些预测价格,就是来自不同视角的“专家意见”。
这里有一个 极其关键且容易混淆的细节 :为了防止“数据泄露”和严重的过拟合,这些专家意见(即元特征) 绝对不能 基于他们自己用来训练的数据来生成。如果专家用自己学过的例题(训练集)来给自己打分,那肯定都是高分,这没有任何参考价值。因此,我们需要一种机制,确保专家在给出对某条数据的意见时,没有见过这条数据。
StackingRegressor 的朴素做法 :它将训练集简单地分成两部分。比如,用 70% 的数据训练基础模型,然后用训练好的模型去预测剩下的 30% 数据,用这 30% 数据的预测结果作为元特征。这种方法简单,但有两个明显缺点:1) 浪费了 30% 的数据用于训练基础模型;2) 元特征只基于一部分数据生成,可能不稳定。
StackingCVRegressor 的聪明做法 :它采用了 K折交叉验证(K-Fold Cross-Validation) 的策略。这是理解其优越性的核心。具体流程如下:
- 将完整的训练数据平均分成 K 份(例如5份)。
- 对于第 i 折(i从1到K):
- 将第 i 份数据作为“验证折”,剩下的 K-1 份数据作为“训练折”。
- 用“训练折”数据训练 每一个 基础模型。
- 用训练好的基础模型去预测“验证折”数据。这样,我们就得到了“验证折”数据对应的、来自各个基础模型的预测值。
- 循环完 K 折之后, 每一份原始训练数据 ,都恰好有一次作为“验证折”被所有基础模型预测过。我们把所有预测结果按原始顺序拼接起来,就得到了完整的、与原始训练数据一一对应的元特征矩阵。这个矩阵的每一列代表一个基础模型的预测结果。
- 同时 ,在每一折中,我们还会用全部 K-1 折数据训练一个最终的基础模型副本(共训练了K组模型),在预测阶段,我们会用这K个模型对真正的测试集进行预测,然后取平均值作为该基础模型对测试集的最终预测,用于生成测试集的元特征。
注意 :这个交叉验证过程 仅用于生成元特征 。它确保了元特征是在“未见过的数据”上生成的,完美避免了数据泄露。之后,我们还会用 全部训练数据 重新训练一遍所有基础模型和元模型,用于最终的预测。这是标准流程,
mlxtend库的StackingCVRegressor已经帮我们封装好了。
2.2 第二阶段:元模型学习“意见权重”
现在我们有了一个全新的数据集:它的特征不再是原始的“面积”、“地段”、“房龄”,而是三位专家的“预测价格”。我们的目标变量仍然是真实的房价。
接下来,我们引入一位“元专家”(元模型,通常是相对简单的模型,如线性回归、岭回归或简单的神经网络)。这位元专家的任务不是去研究房子本身的属性,而是去研究三位专家(基础模型)的预测规律。它要学习的是:当专家A预测偏高、专家B预测偏低时,真实房价更可能接近谁?三位专家的意见应该如何加权平均?
元模型在这个新的元特征数据集上进行训练。训练完成后,它就掌握了一套“组合拳法”。当有新房源需要预测时,流程是这样的:
- 三位基础专家(用全部数据训练好的最终版本)先各自给出预测价。
- 元专家拿到这三个预测价,运用它学到的“组合拳法”,计算出一个最终的、综合的预测价。
这个过程的强大之处在于,它通过数据驱动的方式,自动发现了不同基础模型在不同数据模式下的相对优势,并进行了最优线性或非线性组合。理论上,只要基础模型足够多样(误差相关性低),元模型就能将它们的长板结合起来,抵消各自的短板,从而获得超越任何单一模型的预测性能。
3. 核心细节解析与实操要点
理解了原理,我们来看看在具体实现时,有哪些必须关注的细节和要点。这些细节往往决定了你的 Stacking 模型是“神器”还是“废铁”。
3.1 基础模型的选择:多样性高于一切
选择基础模型的第一原则不是“每个模型都要最强”,而是“模型之间要有差异性”。如果所有基础模型都犯同样的错误(误差高度相关),那么元模型也无法纠正这个错误。
推荐的基础模型组合策略:
- 不同算法家族 :混合线性模型、树模型、基于核的模型、神经网络等。例如:
LinearRegression,Ridge,RandomForestRegressor,GradientBoostingRegressor,SVR,KNeighborsRegressor。 - 同算法不同配置 :例如,使用不同最大深度的随机森林,或者不同正则化强度的岭回归。但这带来的多样性通常不如不同算法家族。
- 使用预处理不同的数据 :这不是
mlxtend库直接提供的功能,但你可以通过构建不同的 Pipeline 来实现。例如,一个模型使用原始特征,另一个模型使用多项式特征扩展后的数据。
一个常见的误区 :加入一个很弱的模型会拉低整体性能吗?不一定。只要这个弱模型的错误与其他模型不高度相关,元模型会学习到给它分配一个很小的权重,甚至负权重(如果它的预测与真实值呈某种负相关关系)。元模型的强大之处就在于这种自适应的权重学习。
3.2 元模型的选择:保持简单
元模型通常应该选择一个相对简单、稳定的模型。原因如下:
- 防止过拟合 :元特征已经是高层抽象的特征了,数据量(行数)和原始数据一样,但特征数(列数)等于基础模型的数量,通常很少(比如3-10个)。在这种小特征空间下,使用复杂的模型(如深度森林、未正则化的复杂神经网络)很容易过拟合。
- 可解释性 :简单的元模型(如线性回归)的系数可以直接解释为各个基础模型的“投票权重”,有助于我们理解模型融合的逻辑。
最常用的元模型 :
LinearRegression:最直接,但可能受多重共线性影响(如果基础模型预测结果高度相关)。Ridge或Lasso:带正则化的线性回归,是 最推荐 的默认选择。它们可以处理共线性,并通过正则化防止过拟合,尤其当基础模型较多时。ElasticNet:结合了 L1 和 L2 正则化。- 简单的
MLPRegressor(浅层神经网络):如果怀疑基础模型间存在非线性组合关系可以尝试,但需要仔细调参以防过拟合。
3.3 参数详解与避坑指南
以 mlxtend 库中的 StackingCVRegressor 为例,我们来剖析几个关键参数:
from mlxtend.regressor import StackingCVRegressor
from sklearn.linear_model import Ridge, LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
# 定义基础模型
lr = LinearRegression()
ridge = Ridge(alpha=1.0)
rf = RandomForestRegressor(n_estimators=100, random_state=42)
svr = SVR(kernel='rbf', C=100, gamma=0.1)
# 创建 StackingCVRegressor
stack = StackingCVRegressor(regressors=[lr, ridge, rf, svr], # 基础模型列表
meta_regressor=Ridge(alpha=1.0), # 元模型
cv=5, # 交叉验证折数,用于生成元特征
shuffle=True, # 在交叉验证前打乱数据
random_state=42,
use_features_in_secondary=False) # 重要参数!
-
cv:这个参数控制生成元特征时的交叉验证折数。 一般设置为5或10 。折数太少(如2),元特征估计可能不稳定;折数太多(如20),计算成本会急剧增加,且可能因为训练折数据量太大而让基础模型在训练折上“学得太好”,导致生成的元特征与用全部数据训练时产生的预测有差异,影响最终性能。这是一个需要权衡的参数。 -
use_features_in_secondary:这是最容易出错和最有价值的参数之一。False(默认):元模型 仅使用 基础模型的预测结果作为特征。这是最经典、最常用的模式。它迫使元模型专注于学习如何组合基础模型。True:元模型将使用 基础模型的预测结果 + 原始特征 一起作为特征。这相当于给了元模型一个“后门”,让它可以直接接触原始数据。 这非常危险! 除非你的元模型非常简单(并且你使用了强正则化),否则极容易导致元模型严重过拟合——它可能会直接忽略基础模型的预测,自己去拟合原始数据中的噪声。在实践中,我几乎从不使用True这个选项,除非在非常严格的正则化和验证下进行实验。
-
shuffle和random_state:为了确保交叉验证分割的可重复性,务必设置random_state。shuffle=True通常在数据没有明显顺序时是推荐的。
实操心得 :在项目初期,建议先用 cv=5 和 use_features_in_secondary=False 建立一个基线模型。这是一个稳健的起点。只有在模型性能达到平台期,并且你有充足的计算资源和时间进行严谨验证时,才去尝试调整 cv 或冒险开启 use_features_in_secondary 。
4. 完整实操流程与核心环节实现
让我们通过一个完整的、可复现的示例,将理论落地。我们将使用波士顿房价数据集(虽然已弃用,但用于演示非常清晰),并对比单一模型、投票集成与 Stacking 的效果。
4.1 环境准备与数据加载
# 导入必要库
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing # 使用加州房价数据集替代波士顿
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
from sklearn.metrics import mean_squared_error, r2_score
import warnings
warnings.filterwarnings('ignore')
# 加载数据
housing = fetch_california_housing()
X, y = housing.data, housing.target
feature_names = housing.feature_names
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 数据标准化:对SVR、线性模型等非常重要
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"训练集样本数:{X_train_scaled.shape[0]}, 特征数:{X_train_scaled.shape[1]}")
print(f"测试集样本数:{X_test_scaled.shape[0]}")
4.2 构建并训练 StackingCVRegressor
from mlxtend.regressor import StackingCVRegressor
# 1. 定义多样化的基础模型
# 注意:为了演示速度,这里参数设得比较简单。实际项目中需要单独调优。
base_models = [
LinearRegression(),
Ridge(alpha=10.0), # 与简单线性回归形成对比
RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42, n_jobs=-1),
GradientBoostingRegressor(n_estimators=100, max_depth=5, random_state=42),
SVR(kernel='rbf', C=10, gamma='scale') # SVR对尺度敏感,所以前面做了标准化
]
# 2. 定义元模型 - 选择稳健的岭回归
meta_model = Ridge(alpha=1.0)
# 3. 创建 StackingCVRegressor
stacking_model = StackingCVRegressor(
regressors=base_models,
meta_regressor=meta_model,
cv=5, # 使用5折交叉验证生成元特征
shuffle=True,
random_state=42,
use_features_in_secondary=False, # 关键:仅使用预测值作为元特征
n_jobs=-1 # 并行化以加速计算
)
# 4. 训练模型
print("开始训练 StackingCVRegressor...")
stacking_model.fit(X_train_scaled, y_train)
print("训练完成!")
4.3 性能评估与对比
现在,让我们看看 Stacking 的威力,并与单一模型以及简单的平均法集成进行对比。
# 定义一个评估函数
def evaluate_model(model, model_name, X_train, y_train, X_test, y_test):
"""训练并评估模型,返回性能指标字典"""
model.fit(X_train, y_train)
y_pred_train = model.predict(X_train)
y_pred_test = model.predict(X_test)
scores = {
'Model': model_name,
'Train_R2': r2_score(y_train, y_pred_train),
'Test_R2': r2_score(y_test, y_pred_test),
'Test_RMSE': np.sqrt(mean_squared_error(y_test, y_pred_test))
}
return scores
# 评估所有基础模型和Stacking模型
results = []
for i, model in enumerate(base_models):
name = model.__class__.__name__
if hasattr(model, 'alpha'):
name += f"(a={model.alpha})"
print(f"正在评估 {name}...")
scores = evaluate_model(model, name, X_train_scaled, y_train, X_test_scaled, y_test)
results.append(scores)
# 评估Stacking模型
print("正在评估 StackingCVRegressor...")
scores_stacking = evaluate_model(stacking_model, 'StackingCV(Ridge)', X_train_scaled, y_train, X_test_scaled, y_test)
results.append(scores_stacking)
# 创建一个简单的平均集成作为基线对比
from sklearn.base import BaseEstimator, RegressorMixin
class AverageEnsemble(BaseEstimator, RegressorMixin):
"""一个简单的平均集成模型"""
def __init__(self, models):
self.models = models
def fit(self, X, y):
for model in self.models:
model.fit(X, y)
return self
def predict(self, X):
predictions = np.column_stack([model.predict(X) for model in self.models])
return np.mean(predictions, axis=1)
avg_ensemble = AverageEnsemble([m for m in base_models])
scores_avg = evaluate_model(avg_ensemble, 'Simple Average', X_train_scaled, y_train, X_test_scaled, y_test)
results.append(scores_avg)
# 将结果转为DataFrame并排序
results_df = pd.DataFrame(results)
results_df.sort_values(by='Test_R2', ascending=False, inplace=True)
print("\n========== 模型性能对比 ==========")
print(results_df.to_string(index=False))
运行上述代码,你可能会得到类似下面的结果(具体数值因随机性会有波动):
========== 模型性能对比 ==========
Model Train_R2 Test_R2 Test_RMSE
StackingCV(Ridge) 0.925 0.825 0.512
GradientBoostingRegressor 0.938 0.817 0.523
RandomForestRegressor 0.961 0.808 0.534
Ridge(a=10.0) 0.606 0.601 0.989
Simple Average 0.927 0.820 0.517
LinearRegression 0.606 0.601 0.989
SVR 0.772 0.768 0.718
结果分析 :
- StackingCVRegressor 胜出 :在测试集R²上,Stacking模型取得了最好的成绩(0.825),并且其测试集RMSE也是最低的。这说明元模型(岭回归)成功地学习了如何加权组合基础模型的预测,得到了比任何单一模型都更稳健的泛化性能。
- 对比简单平均 :简单的平均集成(
Simple Average)表现也不错,但略逊于Stacking。这是因为平均法给所有模型赋予了相等的权重,而Stacking通过数据学习到了更优的权重。例如,它可能发现SVR在某些情况下噪音较大,从而降低了其权重。 - 基础模型差异 :
RandomForest和GradientBoosting这两个树模型本身就很强大,但Stacking依然能在此基础上有所提升。而Ridge和LinearRegression表现一般,但它们的加入提供了不同的“视角”(线性假设),可能帮助元模型更好地校正其他模型的非线性偏差。 - 过拟合观察 :注意
RandomForest的训练集R²高达0.961,但测试集只有0.808,存在明显的过拟合。而Stacking的训练集R²(0.925)更接近测试集R²,说明其泛化能力更好。
4.4 解读元模型:权重分析
我们可以查看训练好的元模型(岭回归)的系数,来理解它是如何“信任”各个基础模型的。
# 获取元模型的系数(权重)
# stacking_model.meta_regressor_ 就是训练好的元模型
if hasattr(stacking_model.meta_regressor_, 'coef_'):
meta_coef = stacking_model.meta_regressor_.coef_
meta_intercept = stacking_model.meta_regressor_.intercept_
print("\n========== 元模型(岭回归)学习到的权重 ==========")
for i, (coef, model) in enumerate(zip(meta_coef, base_models)):
model_name = model.__class__.__name__
print(f"{model_name:30s}: {coef:.4f}")
print(f"{'截距(Intercept)':30s}: {meta_intercept:.4f}")
输出可能类似于:
========== 元模型(岭回归)学习到的权重 ==========
LinearRegression : 0.0512
Ridge : 0.1285
RandomForestRegressor : 0.4123
GradientBoostingRegressor : 0.3810
SVR : 0.0270
截距(Intercept) : 0.0005
解读 :
RandomForestRegressor和GradientBoostingRegressor获得了最高的正权重(0.41和0.38),说明元模型最依赖这两个强预测器的输出。LinearRegression和SVR的权重很低(0.05和0.03),说明它们单独的预测能力对最终结果的直接贡献较小。但这并不意味着它们没用,它们的加入可能起到了“正则化”或提供不同偏差的作用,帮助元模型做出更准确的调整。- 所有权重之和接近1,截距接近0,这符合“加权平均”的直观感觉。元模型本质上学习了一个最优的加权平均方案。
5. 常见问题与排查技巧实录
在实际项目中应用 Stacking 时,你几乎一定会遇到下面这些问题。这里是我总结的排查清单和解决方案。
5.1 问题:Stacking 效果还不如最好的单一模型
可能原因与解决方案:
-
基础模型同质化严重 :你的基础模型可能都是同一类(比如全是树模型),它们的预测误差高度相关。元模型无法从高度相关的信息中提取额外价值。
- 检查 :计算各个基础模型在验证集上预测值的相关系数矩阵。如果相关系数普遍高于0.9,说明同质化严重。
- 解决 :引入不同原理的模型,如线性模型、支持向量机、K近邻、简单的神经网络等。
-
元模型过拟合 :你可能使用了过于复杂的元模型(如深度神经网络),或者在启用
use_features_in_secondary=True时没有施加足够的正则化。- 解决 :始终从简单的元模型开始(
Ridge或Lasso)。如果使用use_features_in_secondary=True,必须对元模型使用强正则化,并通过交叉验证严格评估。
- 解决 :始终从简单的元模型开始(
-
数据泄露或交叉验证设置不当 :如果你错误地手动实现了Stacking,可能在生成元特征时发生了数据泄露。或者在使用
StackingCVRegressor时,cv折数设置得太小,导致元特征质量不高。- 解决 :坚持使用
StackingCVRegressor而不是自己手动实现。尝试增加cv值(如从5增加到10),观察性能是否稳定提升。
- 解决 :坚持使用
-
基础模型未充分调优 :如果基础模型本身都处于欠拟合状态,那么它们的“专家意见”质量就很低,元模型巧妇难为无米之炊。
- 解决 :在构建Stacking之前,应该对每个基础模型进行独立的、初步的超参数调优,确保它们各自都能达到一个不错的基线性能。不需要调到极致,但至少要达到可接受的水平。
5.2 问题:训练速度非常慢
原因 :Stacking 的训练成本是基础模型训练成本的 K+1 倍(K折CV生成元特征 + 最终全量训练)。如果基础模型本身很慢(如未剪枝的大规模随机森林、SVR在大数据集上),Stacking 会慢得无法接受。
优化策略:
- 减少基础模型数量 :从最重要的、差异性最大的2-3个模型开始,而不是堆砌5个以上的模型。
- 使用快速模型或简化模型 :用
HistGradientBoostingRegressor替代GradientBoostingRegressor(速度更快);减少树模型的n_estimators或max_depth;对SVR使用线性核。 - 利用并行计算 :确保基础模型和
StackingCVRegressor都设置了n_jobs=-1来利用所有CPU核心。mlxtend的StackingCVRegressor在fit阶段可以并行化训练不同折的基础模型。 - 降低
cv折数 :在开发阶段,可以先用cv=3进行快速实验和原型验证,最终确定方案后再用cv=5或cv=10进行最终训练。
5.3 问题:如何对 Stacking 模型进行超参数调优?
这是一个高级话题。Stacking 有两层模型,调优空间很大。一个系统的方法是:
-
分层调优 :
- 第一步:调优基础模型 。像对待独立模型一样,使用网格搜索或随机搜索,为每个基础模型找到一组好的超参数。 注意 :这个调优应该在 原始训练集 上进行,使用交叉验证评估。
- 第二步:固定基础模型,调优元模型 。在第一步得到的基础模型上,构建
StackingCVRegressor,然后对元模型的超参数(如Ridge的alpha)进行调优。 - 第三步(可选):联合微调 。如果计算资源允许,可以以第一步得到的参数作为中心,在一个较小的联合参数空间中进行搜索,同时调整某些关键的基础模型参数和元模型参数。
-
使用管道(Pipeline) :
scikit-learn的Pipeline和GridSearchCV可以与StackingCVRegressor结合。但要注意,这会导致“交叉验证套交叉验证”,计算成本极高。通常只用于小数据集或最终模型。
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
# 示例:调优元模型(Ridge)的alpha参数
pipeline = Pipeline([
('scaler', StandardScaler()),
('stacking', StackingCVRegressor(
regressors=base_models, # 使用预定义的基础模型
meta_regressor=Ridge(), # 待调优的元模型
cv=5,
random_state=42))
])
param_grid = {
'stacking__meta_regressor__alpha': [0.01, 0.1, 1.0, 10.0, 100.0]
}
grid_search = GridSearchCV(pipeline, param_grid, cv=3, scoring='r2', n_jobs=-1, verbose=1)
grid_search.fit(X_train, y_train)
print(f"最佳参数:{grid_search.best_params_}")
print(f"最佳验证分数:{grid_search.best_score_:.4f}")
5.4 一个重要的提醒:Stacking 与特征工程
Stacking 是模型层的集成,它不替代特征工程。事实上, 高质量的特征工程是 Stacking 发挥效力的前提 。基础模型在垃圾特征上只能产生垃圾预测,元模型也无法点石成金。在应用 Stacking 之前,务必做好数据清洗、缺失值处理、特征缩放、特征编码以及必要的特征构造工作。你可以将特征工程步骤放入一个 Pipeline 中,并置于 StackingCVRegressor 之前,确保整个流程的整洁和可复现性。
我个人在时间序列预测和营销响应建模中多次应用 Stacking,它的确能稳定地带来 1%-5% 的性能提升(以 RMSE 或 AUC 衡量)。但记住,它不是银弹。它的价值在于以可接受的计算成本,系统性地榨取现有模型集的最后一滴性能。对于追求极致性能的竞赛(如 Kaggle)和关键的业务场景,Stacking 及其变体(如 Blending)仍然是顶级选手工具箱中的标配。对于日常项目,当单一模型性能遇到瓶颈,且你有多个表现各异的好模型时,就是尝试 Stacking 的最佳时机。
更多推荐


所有评论(0)