使用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秒)