注意
转到末尾 下载完整示例代码。或者通过 JupyterLite 或 Binder 在浏览器中运行此示例
使用堆叠(stacking)组合预测器#
堆叠(Stacking)是指一种融合估计器的方法。在这种策略中,一些估计器会独立地在训练数据上进行拟合,而最终估计器则使用这些基础估计器的堆叠预测进行训练。
在本示例中,我们展示了将不同回归器堆叠在一起,并使用最终的线性正则化回归器输出预测结果的用例。我们比较了每个单独回归器与堆叠策略的性能。堆叠略微提升了整体性能。
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
下载数据集#
我们将使用 Ames Housing 数据集,该数据集最初由 Dean De Cock 整理,在 Kaggle 挑战中使用后变得更为人所知。它包含爱荷华州埃姆斯市的 1460 栋住宅,每栋住宅由 80 个特征描述。我们将使用它来预测房屋的最终对数价格。在本示例中,我们仅使用通过 GradientBoostingRegressor() 选择的 20 个最有趣的特征,并限制条目数量(此处我们不深入探讨如何选择最有趣的特征)。
Ames Housing 数据集未随 scikit-learn 提供,因此我们将从 OpenML 获取。
import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.utils import shuffle
def load_ames_housing():
df = fetch_openml(name="house_prices", as_frame=True)
X = df.data
y = df.target
features = [
"YrSold",
"HeatingQC",
"Street",
"YearRemodAdd",
"Heating",
"MasVnrType",
"BsmtUnfSF",
"Foundation",
"MasVnrArea",
"MSSubClass",
"ExterQual",
"Condition2",
"GarageCars",
"GarageType",
"OverallQual",
"TotalBsmtSF",
"BsmtFinSF1",
"HouseStyle",
"MiscFeature",
"MoSold",
]
X = X.loc[:, features]
X, y = shuffle(X, y, random_state=0)
X = X.iloc[:600]
y = y.iloc[:600]
return X, np.log(y)
X, y = load_ames_housing()
构建数据预处理流水线#
在使用 Ames 数据集之前,我们仍然需要进行一些预处理。首先,我们将选择数据集中的分类和数值列,以构建流水线的第一步。
from sklearn.compose import make_column_selector
cat_selector = make_column_selector(dtype_include=object)
num_selector = make_column_selector(dtype_include=np.number)
cat_selector(X)
['HeatingQC', 'Street', 'Heating', 'MasVnrType', 'Foundation', 'ExterQual', 'Condition2', 'GarageType', 'HouseStyle', 'MiscFeature']
num_selector(X)
['YrSold', 'YearRemodAdd', 'BsmtUnfSF', 'MasVnrArea', 'MSSubClass', 'GarageCars', 'OverallQual', 'TotalBsmtSF', 'BsmtFinSF1', 'MoSold']
然后,我们需要设计依赖于最终回归器的预处理流水线。如果最终回归器是线性模型,则需要对类别进行独热编码。如果最终回归器是基于树的模型,则序数编码器就足够了。此外,对于线性模型,数值需要进行标准化,而对于基于树的模型,原始数值数据可以直接处理。然而,这两种模型都需要一个填充器来处理缺失值。
我们首先设计基于树的模型所需的流水线。
from sklearn.compose import make_column_transformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import OrdinalEncoder
cat_tree_processor = OrdinalEncoder(
handle_unknown="use_encoded_value",
unknown_value=-1,
encoded_missing_value=-2,
)
num_tree_processor = SimpleImputer(strategy="mean", add_indicator=True)
tree_preprocessor = make_column_transformer(
(num_tree_processor, num_selector), (cat_tree_processor, cat_selector)
)
tree_preprocessor
ColumnTransformer(transformers=[('simpleimputer', SimpleImputer(add_indicator=True), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('ordinalencoder', OrdinalEncoder(encoded_missing_value=-2, handle_unknown='use_encoded_value', unknown_value=-1), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
转换器 | [('simpleimputer', ...), ('ordinalencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'use_encoded_value' | |
未知值 | -1 | |
编码缺失值 | -2 | |
最小频率 | None | |
最大类别数 | None |
然后,我们现在将定义当最终回归器是线性模型时使用的预处理器。
from sklearn.preprocessing import OneHotEncoder, StandardScaler
cat_linear_processor = OneHotEncoder(handle_unknown="ignore")
num_linear_processor = make_pipeline(
StandardScaler(), SimpleImputer(strategy="mean", add_indicator=True)
)
linear_preprocessor = make_column_transformer(
(num_linear_processor, num_selector), (cat_linear_processor, cat_selector)
)
linear_preprocessor
ColumnTransformer(transformers=[('pipeline', Pipeline(steps=[('standardscaler', StandardScaler()), ('simpleimputer', SimpleImputer(add_indicator=True))]), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('onehotencoder', OneHotEncoder(handle_unknown='ignore'), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
转换器 | [('pipeline', ...), ('onehotencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
复制 | True | |
带均值 | True | |
带标准差 | True |
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
丢弃 | None | |
稀疏输出 | True | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'ignore' | |
最小频率 | None | |
最大类别数 | None | |
特征名称组合器 | 'concat' |
单个数据集上的预测器堆叠#
有时,为给定数据集找到最佳性能模型是很繁琐的。堆叠(Stacking)提供了一种替代方案,它通过组合多个学习器的输出来实现,无需专门选择某个模型。堆叠的性能通常接近最佳模型,有时甚至可以超越每个单独模型的预测性能。
在这里,我们组合了 3 个学习器(线性和非线性),并使用一个岭回归器将它们的输出结合起来。
注意
尽管我们将为这 3 个学习器使用前面部分编写的处理器来构建新的流水线,但最终估计器 RidgeCV()
不需要对数据进行预处理,因为它将直接接收来自 3 个学习器已预处理过的输出。
from sklearn.linear_model import LassoCV
lasso_pipeline = make_pipeline(linear_preprocessor, LassoCV())
lasso_pipeline
Pipeline(steps=[('columntransformer', ColumnTransformer(transformers=[('pipeline', Pipeline(steps=[('standardscaler', StandardScaler()), ('simpleimputer', SimpleImputer(add_indicator=True))]), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('onehotencoder', OneHotEncoder(handle_unknown='ignore'), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])), ('lassocv', LassoCV())])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
步骤 | [('columntransformer', ...), ('lassocv', ...)] | |
transform_input | None | |
内存 | None | |
详细模式 | False |
参数
转换器 | [('pipeline', ...), ('onehotencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
复制 | True | |
带均值 | True | |
带标准差 | True |
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
丢弃 | None | |
稀疏输出 | True | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'ignore' | |
最小频率 | None | |
最大类别数 | None | |
特征名称组合器 | 'concat' |
参数
eps | 0.001 | |
n_alphas | 'deprecated' | |
alphas | 'warn' | |
fit_intercept | True | |
预计算 | 'auto' | |
max_iter | 1000 | |
tol | 0.0001 | |
copy_X | True | |
cv | None | |
详细模式 | False | |
并行作业数 | None | |
positive | False | |
random_state | None | |
选择 | 'cyclic' |
from sklearn.ensemble import RandomForestRegressor
rf_pipeline = make_pipeline(tree_preprocessor, RandomForestRegressor(random_state=42))
rf_pipeline
Pipeline(steps=[('columntransformer', ColumnTransformer(transformers=[('simpleimputer', SimpleImputer(add_indicator=True), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('ordinalencoder', OrdinalEncoder(encoded_missing_value=-2, handle_unknown='use_encoded_value', unknown_value=-1), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])), ('randomforestregressor', RandomForestRegressor(random_state=42))])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
步骤 | [('columntransformer', ...), ('randomforestregressor', ...)] | |
transform_input | None | |
内存 | None | |
详细模式 | False |
参数
转换器 | [('simpleimputer', ...), ('ordinalencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'use_encoded_value' | |
未知值 | -1 | |
编码缺失值 | -2 | |
最小频率 | None | |
最大类别数 | None |
参数
n_estimators | 100 | |
criterion | 'squared_error' | |
max_depth | None | |
min_samples_split | 2 | |
min_samples_leaf | 1 | |
min_weight_fraction_leaf | 0.0 | |
max_features | 1.0 | |
max_leaf_nodes | None | |
min_impurity_decrease | 0.0 | |
bootstrap | True | |
oob_score | False | |
并行作业数 | None | |
random_state | 42 | |
详细模式 | 0 | |
warm_start | False | |
ccp_alpha | 0.0 | |
max_samples | None | |
monotonic_cst | None |
from sklearn.ensemble import HistGradientBoostingRegressor
gbdt_pipeline = make_pipeline(
tree_preprocessor, HistGradientBoostingRegressor(random_state=0)
)
gbdt_pipeline
Pipeline(steps=[('columntransformer', ColumnTransformer(transformers=[('simpleimputer', SimpleImputer(add_indicator=True), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('ordinalencoder', OrdinalEncoder(encoded_missing_value=-2, handle_unknown='use_encoded_value', unknown_value=-1), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])), ('histgradientboostingregressor', HistGradientBoostingRegressor(random_state=0))])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
步骤 | [('columntransformer', ...), ('histgradientboostingregressor', ...)] | |
transform_input | None | |
内存 | None | |
详细模式 | False |
参数
转换器 | [('simpleimputer', ...), ('ordinalencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'use_encoded_value' | |
未知值 | -1 | |
编码缺失值 | -2 | |
最小频率 | None | |
最大类别数 | None |
参数
损失 | 'squared_error' | |
分位数 | None | |
学习率 | 0.1 | |
max_iter | 100 | |
max_leaf_nodes | 31 | |
max_depth | None | |
min_samples_leaf | 20 | |
l2_regularization | 0.0 | |
max_features | 1.0 | |
最大箱数 | 255 | |
categorical_features | 'from_dtype' | |
monotonic_cst | None | |
interaction_cst | None | |
warm_start | False | |
early_stopping | 'auto' | |
评分 | 'loss' | |
验证分数 | 0.1 | |
n_iter_no_change | 10 | |
tol | 1e-07 | |
详细模式 | 0 | |
random_state | 0 |
from sklearn.ensemble import StackingRegressor
from sklearn.linear_model import RidgeCV
estimators = [
("Random Forest", rf_pipeline),
("Lasso", lasso_pipeline),
("Gradient Boosting", gbdt_pipeline),
]
stacking_regressor = StackingRegressor(estimators=estimators, final_estimator=RidgeCV())
stacking_regressor
StackingRegressor(estimators=[('Random Forest', Pipeline(steps=[('columntransformer', ColumnTransformer(transformers=[('simpleimputer', SimpleImputer(add_indicator=True), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('ordinalencoder', OrdinalEncoder(encoded_missing_value=-2, handle_unknown='use_encoded_value', unknown_v... <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>), ('ordinalencoder', OrdinalEncoder(encoded_missing_value=-2, handle_unknown='use_encoded_value', unknown_value=-1), <sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>)])), ('histgradientboostingregressor', HistGradientBoostingRegressor(random_state=0))]))], final_estimator=RidgeCV())在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示或信任此笔记本。
在 GitHub 上,HTML 表示无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
估计器 | [('Random Forest', ...), ('Lasso', ...), ...] | |
最终估计器 | RidgeCV() | |
cv | None | |
并行作业数 | None | |
直通 | False | |
详细模式 | 0 |
参数
转换器 | [('simpleimputer', ...), ('ordinalencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'use_encoded_value' | |
未知值 | -1 | |
编码缺失值 | -2 | |
最小频率 | None | |
最大类别数 | None |
参数
n_estimators | 100 | |
criterion | 'squared_error' | |
max_depth | None | |
min_samples_split | 2 | |
min_samples_leaf | 1 | |
min_weight_fraction_leaf | 0.0 | |
max_features | 1.0 | |
max_leaf_nodes | None | |
min_impurity_decrease | 0.0 | |
bootstrap | True | |
oob_score | False | |
并行作业数 | None | |
random_state | 42 | |
详细模式 | 0 | |
warm_start | False | |
ccp_alpha | 0.0 | |
max_samples | None | |
monotonic_cst | None |
参数
转换器 | [('pipeline', ...), ('onehotencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
复制 | True | |
带均值 | True | |
带标准差 | True |
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
丢弃 | None | |
稀疏输出 | True | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'ignore' | |
最小频率 | None | |
最大类别数 | None | |
特征名称组合器 | 'concat' |
参数
eps | 0.001 | |
n_alphas | 'deprecated' | |
alphas | 'warn' | |
fit_intercept | True | |
预计算 | 'auto' | |
max_iter | 1000 | |
tol | 0.0001 | |
copy_X | True | |
cv | None | |
详细模式 | False | |
并行作业数 | None | |
positive | False | |
random_state | None | |
选择 | 'cyclic' |
参数
转换器 | [('simpleimputer', ...), ('ordinalencoder', ...)] | |
剩余 | 'drop' | |
稀疏阈值 | 0.3 | |
并行作业数 | None | |
转换器权重 | None | |
详细模式 | False | |
verbose_feature_names_out | True | |
force_int_remainder_cols | 'deprecated' |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291bdde0>
参数
缺失值 | nan | |
策略 | 'mean' | |
填充值 | None | |
复制 | True | |
添加指示符 | True | |
保留空特征 | False |
<sklearn.compose._column_transformer.make_column_selector object at 0x7fad291be1a0>
参数
类别 | 'auto' | |
数据类型 | <class 'numpy.float64'> | |
处理未知 | 'use_encoded_value' | |
未知值 | -1 | |
编码缺失值 | -2 | |
最小频率 | None | |
最大类别数 | None |
参数
损失 | 'squared_error' | |
分位数 | None | |
学习率 | 0.1 | |
max_iter | 100 | |
max_leaf_nodes | 31 | |
max_depth | None | |
min_samples_leaf | 20 | |
l2_regularization | 0.0 | |
max_features | 1.0 | |
最大箱数 | 255 | |
categorical_features | 'from_dtype' | |
monotonic_cst | None | |
interaction_cst | None | |
warm_start | False | |
early_stopping | 'auto' | |
评分 | 'loss' | |
验证分数 | 0.1 | |
n_iter_no_change | 10 | |
tol | 1e-07 | |
详细模式 | 0 | |
random_state | 0 |
参数
alphas | (0.1, ...) | |
fit_intercept | True | |
评分 | None | |
cv | None | |
gcv_mode | None | |
store_cv_results | False | |
alpha_per_target | False |
测量并绘制结果#
现在我们可以使用 Ames Housing 数据集进行预测。我们检查了每个单独预测器以及回归器堆叠的性能。
import time
import matplotlib.pyplot as plt
from sklearn.metrics import PredictionErrorDisplay
from sklearn.model_selection import cross_val_predict, cross_validate
fig, axs = plt.subplots(2, 2, figsize=(9, 7))
axs = np.ravel(axs)
for ax, (name, est) in zip(
axs, estimators + [("Stacking Regressor", stacking_regressor)]
):
scorers = {"R2": "r2", "MAE": "neg_mean_absolute_error"}
start_time = time.time()
scores = cross_validate(
est, X, y, scoring=list(scorers.values()), n_jobs=-1, verbose=0
)
elapsed_time = time.time() - start_time
y_pred = cross_val_predict(est, X, y, n_jobs=-1, verbose=0)
scores = {
key: (
f"{np.abs(np.mean(scores[f'test_{value}'])):.2f} +- "
f"{np.std(scores[f'test_{value}']):.2f}"
)
for key, value in scorers.items()
}
display = PredictionErrorDisplay.from_predictions(
y_true=y,
y_pred=y_pred,
kind="actual_vs_predicted",
ax=ax,
scatter_kwargs={"alpha": 0.2, "color": "tab:blue"},
line_kwargs={"color": "tab:red"},
)
ax.set_title(f"{name}\nEvaluation in {elapsed_time:.2f} seconds")
for name, score in scores.items():
ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.suptitle("Single predictors versus stacked predictors")
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()

堆叠回归器将结合不同回归器的优势。然而,我们也发现训练堆叠回归器的计算成本要高得多。
脚本总运行时间: (0 分 21.747 秒)
相关示例