Kombinieren von Prädiktoren mit Stacking#

Stacking bezieht sich auf eine Methode, um Estimators zu vermischen. Bei dieser Strategie werden einige Estimators individuell auf Trainingsdaten angepasst, während ein endgültiger Estimator mithilfe der gestackten Vorhersagen dieser Basis-Estimators trainiert wird.

In diesem Beispiel veranschaulichen wir den Anwendungsfall, bei dem verschiedene Regressoren zusammen gestapelt werden und ein endgültiger linearer, bestrafter Regressor zur Ausgabe der Vorhersage verwendet wird. Wir vergleichen die Leistung jedes einzelnen Regressors mit der Stacking-Strategie. Stacking verbessert die Gesamtleistung geringfügig.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Datensatz herunterladen#

Wir verwenden den Ames Housing-Datensatz, der zuerst von Dean De Cock zusammengestellt und nach seiner Verwendung in einer Kaggle-Herausforderung bekannter wurde. Es handelt sich um eine Sammlung von 1460 Wohnhäusern in Ames, Iowa, die jeweils durch 80 Merkmale beschrieben werden. Wir werden ihn verwenden, um den endgültigen logarithmischen Preis der Häuser vorherzusagen. In diesem Beispiel werden nur die 20 interessantesten Merkmale verwendet, die mithilfe von GradientBoostingRegressor() ausgewählt wurden, und die Anzahl der Einträge begrenzt (hier gehen wir nicht ins Detail, wie die interessantesten Merkmale ausgewählt werden).

Der Ames Housing-Datensatz wird nicht mit scikit-learn mitgeliefert, daher werden wir ihn von OpenML abrufen.

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()

Pipeline zur Vorverarbeitung der Daten erstellen#

Bevor wir den Ames-Datensatz verwenden können, müssen wir noch einige Vorverarbeitungsschritte durchführen. Zuerst wählen wir die kategorialen und numerischen Spalten des Datensatzes aus, um den ersten Schritt der Pipeline zu konstruieren.

from sklearn.compose import make_column_selector

cat_selector = make_column_selector(dtype_include=[object, "string"])
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']

Anschließend müssen wir Vorverarbeitungs-Pipelines entwerfen, die vom endgültigen Regressor abhängen. Wenn der endgültige Regressor ein lineares Modell ist, muss man die Kategorien per One-Hot-Encoding kodieren. Wenn der endgültige Regressor ein baumbasiertes Modell ist, reicht ein ordinaler Encoder aus. Außerdem müssen numerische Werte für ein lineares Modell standardisiert werden, während die rohen numerischen Daten von einem baumbasierten Modell unverändert behandelt werden können. Beide Modelle benötigen jedoch einen Imputer zur Behandlung fehlender Werte.

Wir entwerfen zunächst die Pipeline, die für baumbasierte Modelle erforderlich ist.

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 0x7fb4868f6d10>),
                                ('ordinalencoder',
                                 OrdinalEncoder(encoded_missing_value=-2,
                                                handle_unknown='use_encoded_value',
                                                unknown_value=-1),
                                 <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


Dann definieren wir nun den Präprozessor, der verwendet wird, wenn der endgültige Regressor ein lineares Modell ist.

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 0x7fb4868f6d10>),
                                ('onehotencoder',
                                 OneHotEncoder(handle_unknown='ignore'),
                                 <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


Stack von Prädiktoren auf einem einzelnen Datensatz#

Es ist manchmal mühsam, das Modell zu finden, das auf einem gegebenen Datensatz am besten abschneidet. Stacking bietet eine Alternative, indem es die Ausgaben mehrerer Lerner kombiniert, ohne dass ein Modell speziell ausgewählt werden muss. Die Leistung des Stackings liegt normalerweise nahe am besten Modell und kann manchmal die Vorhersageleistung jedes einzelnen Modells übertreffen.

Hier kombinieren wir 3 Lerner (linear und nicht-linear) und verwenden einen Ridge-Regressor, um ihre Ausgaben zu kombinieren.

Hinweis

Obwohl wir neue Pipelines mit den Präprozessoren erstellen, die wir im vorherigen Abschnitt für die 3 Lerner geschrieben haben, benötigt der endgültige Estimator RidgeCV() keine Vorverarbeitung der Daten, da er mit der bereits vorverarbeiteten Ausgabe der 3 Lerner gespeist wird.

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 0x7fb4868f6d10>),
                                                 ('onehotencoder',
                                                  OneHotEncoder(handle_unknown='ignore'),
                                                  <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])),
                ('lassocv', LassoCV())])
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


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 0x7fb4868f6d10>),
                                                 ('ordinalencoder',
                                                  OrdinalEncoder(encoded_missing_value=-2,
                                                                 handle_unknown='use_encoded_value',
                                                                 unknown_value=-1),
                                                  <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])),
                ('randomforestregressor',
                 RandomForestRegressor(random_state=42))])
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


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 0x7fb4868f6d10>),
                                                 ('ordinalencoder',
                                                  OrdinalEncoder(encoded_missing_value=-2,
                                                                 handle_unknown='use_encoded_value',
                                                                 unknown_value=-1),
                                                  <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])),
                ('histgradientboostingregressor',
                 HistGradientBoostingRegressor(random_state=0))])
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


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 0x7fb4868f6d10>),
                                                                                ('ordinalencoder',
                                                                                 OrdinalEncoder(encoded_missing_value=-2,
                                                                                                handle_unknown='use_encoded_value',
                                                                                                unknown_v...
                                                                                 <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6d10>),
                                                                                ('ordinalencoder',
                                                                                 OrdinalEncoder(encoded_missing_value=-2,
                                                                                                handle_unknown='use_encoded_value',
                                                                                                unknown_value=-1),
                                                                                 <sklearn.compose._column_transformer.make_column_selector object at 0x7fb4868f6010>)])),
                                               ('histgradientboostingregressor',
                                                HistGradientBoostingRegressor(random_state=0))]))],
                  final_estimator=RidgeCV())
In einer Jupyter-Umgebung führen Sie diese Zelle bitte erneut aus, um die HTML-Darstellung anzuzeigen, oder vertrauen Sie dem Notebook.
Auf GitHub kann die HTML-Darstellung nicht gerendert werden. Versuchen Sie bitte, diese Seite mit nbviewer.org zu laden.


Ergebnisse messen und plotten#

Jetzt können wir den Ames Housing-Datensatz verwenden, um Vorhersagen zu treffen. Wir prüfen die Leistung jedes einzelnen Prädiktors sowie des Stacks der Regressoren.

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()
Single predictors versus stacked predictors, Random Forest Evaluation in 0.90 seconds, Lasso Evaluation in 0.21 seconds, Gradient Boosting Evaluation in 0.41 seconds, Stacking Regressor Evaluation in 8.07 seconds

Der gestapelte Regressor kombiniert die Stärken der verschiedenen Regressoren. Wir sehen jedoch auch, dass das Trainieren des gestapelten Regressors wesentlich rechenintensiver ist.

Gesamtlaufzeit des Skripts: (0 Minuten 19,441 Sekunden)

Verwandte Beispiele

Vorhersagen von einzelnen und abstimmenden Regressionsmodellen plotten

Vorhersagen von einzelnen und abstimmenden Regressionsmodellen plotten

Schätzer und komplexe Pipelines anzeigen

Schätzer und komplexe Pipelines anzeigen

Unterstützung für kategorische Merkmale in Gradient Boosting

Unterstützung für kategorische Merkmale in Gradient Boosting

Entscheidungsbaum-Regression mit AdaBoost

Entscheidungsbaum-Regression mit AdaBoost

Galerie generiert von Sphinx-Gallery