使用stacking的组合预测器¶
stacking是一种组合估计器的方法。在该策略中,一些估计器单独的在一些训练数据上进行拟合,而最终估计器则是使用这些基估计器的预测融合训练的。
在这个例子中,我们说明了一个用例,在这个用例中,不同的回归器被融合在一起,并使用一个最终的线性惩罚回归器来输出预测。我们将每一个回归器的表现与融合策略进行比较。融合稍微提高了整体性能。
print(__doc__)
# Authors: Guillaume Lemaitre <g.lemaitre58@gmail.com>
# Maria Telenczuk <https://github.com/maikia>
# License: BSD 3 clause
下载数据集
我们将使用 Ames Housing数据集, 这个数据集是由Dean De Cock制作的并且在Kaggle挑战中被使用后很有名。这是一个有1460套在Ames的房子,每个房子都有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[features]
X, y = shuffle(X, y, random_state=0)
X = X[:600]
y = y[:600]
return X, np.log(y)
X, y = load_ames_housing()
制作数据预处理工作流
在使用Ames数据集之前,我们还需要进行一些预处理。首先,数据集有许多缺失的值。为了计算它们,我们将用新的类别 ‘missing’来替换分类缺失的值,而用列的“平mean”来表示缺少的数值。我们也会使用sklearn.preprocessing.OneHotEncoder
或者sklearn.preprocessing.OrdinalEncoder
来编码这些类别, 这取决于我们所使用模型的类型(线性或者非线性模型)。为了伪造这一预处理, 我们将制造两个工作流(pipelines)。如果您的数据已准备好使用且不需要预处理,则可以跳过本节。
from sklearn.compose import make_column_transformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import OrdinalEncoder
from sklearn.preprocessing import StandardScaler
cat_cols = X.columns[X.dtypes == 'O']
num_cols = X.columns[X.dtypes == 'float64']
categories = [
X[column].unique() for column in X[cat_cols]]
for cat in categories:
cat[cat == None] = 'missing' # noqa
cat_proc_nlin = make_pipeline(
SimpleImputer(missing_values=None, strategy='constant',
fill_value='missing'),
OrdinalEncoder(categories=categories)
)
num_proc_nlin = make_pipeline(SimpleImputer(strategy='mean'))
cat_proc_lin = make_pipeline(
SimpleImputer(missing_values=None,
strategy='constant',
fill_value='missing'),
OneHotEncoder(categories=categories)
)
num_proc_lin = make_pipeline(
SimpleImputer(strategy='mean'),
StandardScaler()
)
# transformation to use for non-linear estimators
processor_nlin = make_column_transformer(
(cat_proc_nlin, cat_cols),
(num_proc_nlin, num_cols),
remainder='passthrough')
# transformation to use for linear estimators
processor_lin = make_column_transformer(
(cat_proc_lin, cat_cols),
(num_proc_lin, num_cols),
remainder='passthrough')
单个数据集预测器的融合
有时候,在给定数据集上找到一个表现最好的模型是很乏味的。Stacking通过组合几个学习器的输出提供了一个替代方案,而不需要具体地选择一个模型。Stacking的性能通常接近最佳模型,有时甚至可以超过各个模型的预测性能。
在这里,我们结合了三个学习器(线性和非线性),并使用岭回归器将他们的输出组合在一起。
注意:虽然我们将建立一个新的处理工作流, 这个处理就是之前三个学习期的预处理过程, 但是最终的估计器RidgeCV()并不需要数据的预处理,因为它将被输入三个学习器已经预处理的输出。
from sklearn.experimental import enable_hist_gradient_boosting # noqa
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import StackingRegressor
from sklearn.linear_model import LassoCV
from sklearn.linear_model import RidgeCV
lasso_pipeline = make_pipeline(processor_lin,
LassoCV())
rf_pipeline = make_pipeline(processor_nlin,
RandomForestRegressor(random_state=42))
gradient_pipeline = make_pipeline(
processor_nlin,
HistGradientBoostingRegressor(random_state=0))
estimators = [('Random Forest', rf_pipeline),
('Lasso', lasso_pipeline),
('Gradient Boosting', gradient_pipeline)]
stacking_regressor = StackingRegressor(estimators=estimators,
final_estimator=RidgeCV())
现在我们可以使用Ames住房数据集进行预测。我们检查每个预测器的性能以及回归器的融合。
函数图 plot_regression_results
用于绘制预测和真实的目标。
import time
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_validate, cross_val_predict
def plot_regression_results(ax, y_true, y_pred, title, scores, elapsed_time):
"""Scatter plot of the predicted vs true targets."""
ax.plot([y_true.min(), y_true.max()],
[y_true.min(), y_true.max()],
'--r', linewidth=2)
ax.scatter(y_true, y_pred, alpha=0.2)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
ax.spines['left'].set_position(('outward', 10))
ax.spines['bottom'].set_position(('outward', 10))
ax.set_xlim([y_true.min(), y_true.max()])
ax.set_ylim([y_true.min(), y_true.max()])
ax.set_xlabel('Measured')
ax.set_ylabel('Predicted')
extra = plt.Rectangle((0, 0), 0, 0, fc="w", fill=False,
edgecolor='none', linewidth=0)
ax.legend([extra], [scores], loc='upper left')
title = title + '\n Evaluation in {:.2f} seconds'.format(elapsed_time)
ax.set_title(title)
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)]):
start_time = time.time()
score = cross_validate(est, X, y,
scoring=['r2', 'neg_mean_absolute_error'],
n_jobs=-1, verbose=0)
elapsed_time = time.time() - start_time
y_pred = cross_val_predict(est, X, y, n_jobs=-1, verbose=0)
plot_regression_results(
ax, y, y_pred,
name,
(r'$R^2={:.2f} \pm {:.2f}$' + '\n' + r'$MAE={:.2f} \pm {:.2f}$')
.format(np.mean(score['test_r2']),
np.std(score['test_r2']),
-np.mean(score['test_neg_mean_absolute_error']),
np.std(score['test_neg_mean_absolute_error'])),
elapsed_time)
plt.suptitle('Single predictors versus stacked predictors')
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
融合的回归器将结合不同的回归者的优势。然而,我们也看到,训练融合的回归器在计算上要昂贵得多。
脚本的总运行时间:(0分25.100秒)