使用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((00), 00, 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(22, figsize=(97))
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秒)

Download Python source code: plot_stack_predictors.py

Download Jupyter notebook: plot_stack_predictors.ipynb