Vergleich des Target Encoders mit anderen Encodern#

Der TargetEncoder verwendet den Wert des Ziels (target), um jedes kategoriale Merkmal zu kodieren. In diesem Beispiel werden wir drei verschiedene Ansätze zur Behandlung kategorialer Merkmale vergleichen: TargetEncoder, OrdinalEncoder, OneHotEncoder und das Verwerfen der Kategorie.

Hinweis

fit(X, y).transform(X) ist nicht gleich fit_transform(X, y), da in fit_transform ein Cross-Fitting-Schema für die Kodierung verwendet wird. Details finden Sie im Benutzerhandbuch.

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

Laden von Daten von OpenML#

Zuerst laden wir den Weinbewertungsdatensatz, bei dem das Ziel die von einem Rezensenten vergebenen Punkte sind.

from sklearn.datasets import fetch_openml

wine_reviews = fetch_openml(data_id=42074, as_frame=True)

df = wine_reviews.frame
df.head()
Land Beschreibung Bezeichnung Punkte Preis Provinz Region_1 Region_2 Sorte Weingut
0 USA Dieser beeindruckende, 100% sortenreine Wein stammt aus ... Martha's Vineyard 96 235.0 Kalifornien Napa Valley Napa Cabernet Sauvignon Heitz
1 Spanien Reife Aromen von Feige, Brombeere und Cassis sind ... Carodorum Selección Especial Reserva 96 110.0 Nordspanien Toro NaN Tinta de Toro Bodega Carmen Rodríguez
2 USA Mac Watson ehrt die Erinnerung an einen Wein, der einst ... Special Selected Late Harvest 96 90.0 Kalifornien Knights Valley Sonoma Sauvignon Blanc Macauley
3 USA Dieser reifte 20 Monate in 30% neuem französischem Eichenholz, und ... Reserve 96 65.0 Oregon Willamette Valley Willamette Valley Pinot Noir Ponzi
4 Frankreich Dies ist der Spitzenwein von La Bégude, benannt nach ... La Brûlade 95 66.0 Provence Bandol NaN Provence Rotwein-Blend Domaine de la Bégude


Für dieses Beispiel verwenden wir die folgende Untermenge von numerischen und kategorialen Merkmalen in den Daten. Die Zielwerte sind kontinuierliche Werte von 80 bis 100.

numerical_features = ["price"]
categorical_features = [
    "country",
    "province",
    "region_1",
    "region_2",
    "variety",
    "winery",
]
target_name = "points"

X = df[numerical_features + categorical_features]
y = df[target_name]

_ = y.hist()
plot target encoder

Trainieren und Bewerten von Pipelines mit verschiedenen Encodern#

In diesem Abschnitt werden wir Pipelines mit HistGradientBoostingRegressor mit verschiedenen Kodierungsstrategien auswerten. Zuerst listen wir die Encoder auf, die wir zur Vorverarbeitung der kategorialen Merkmale verwenden werden.

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, TargetEncoder

categorical_preprocessors = [
    ("drop", "drop"),
    ("ordinal", OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1)),
    (
        "one_hot",
        OneHotEncoder(handle_unknown="ignore", max_categories=20, sparse_output=False),
    ),
    ("target", TargetEncoder(target_type="continuous")),
]

Als Nächstes werten wir die Modelle anhand von Kreuzvalidierung aus und erfassen die Ergebnisse.

from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.model_selection import cross_validate
from sklearn.pipeline import make_pipeline

n_cv_folds = 3
max_iter = 20
results = []


def evaluate_model_and_store(name, pipe):
    result = cross_validate(
        pipe,
        X,
        y,
        scoring="neg_root_mean_squared_error",
        cv=n_cv_folds,
        return_train_score=True,
    )
    rmse_test_score = -result["test_score"]
    rmse_train_score = -result["train_score"]
    results.append(
        {
            "preprocessor": name,
            "rmse_test_mean": rmse_test_score.mean(),
            "rmse_test_std": rmse_train_score.std(),
            "rmse_train_mean": rmse_train_score.mean(),
            "rmse_train_std": rmse_train_score.std(),
        }
    )


for name, categorical_preprocessor in categorical_preprocessors:
    preprocessor = ColumnTransformer(
        [
            ("numerical", "passthrough", numerical_features),
            ("categorical", categorical_preprocessor, categorical_features),
        ]
    )
    pipe = make_pipeline(
        preprocessor, HistGradientBoostingRegressor(random_state=0, max_iter=max_iter)
    )
    evaluate_model_and_store(name, pipe)

Native Unterstützung für kategoriale Merkmale#

In diesem Abschnitt bauen und bewerten wir eine Pipeline, die die native Unterstützung für kategoriale Merkmale in HistGradientBoostingRegressor verwendet, die nur bis zu 255 eindeutige Kategorien unterstützt. In unserem Datensatz haben die meisten kategorialen Merkmale mehr als 255 eindeutige Kategorien.

n_unique_categories = df[categorical_features].nunique().sort_values(ascending=False)
n_unique_categories
winery      14810
region_1     1236
variety       632
province      455
country        48
region_2       18
dtype: int64

Um die oben genannte Einschränkung zu umgehen, gruppieren wir die kategorialen Merkmale in Merkmale mit niedriger Kardinalität und Merkmale mit hoher Kardinalität. Die Merkmale mit hoher Kardinalität werden mit Target Encoding kodiert, und die Merkmale mit niedriger Kardinalität verwenden die native kategoriale Unterstützung im Gradient Boosting.

high_cardinality_features = n_unique_categories[n_unique_categories > 255].index
low_cardinality_features = n_unique_categories[n_unique_categories <= 255].index
mixed_encoded_preprocessor = ColumnTransformer(
    [
        ("numerical", "passthrough", numerical_features),
        (
            "high_cardinality",
            TargetEncoder(target_type="continuous"),
            high_cardinality_features,
        ),
        (
            "low_cardinality",
            OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1),
            low_cardinality_features,
        ),
    ],
    verbose_feature_names_out=False,
)

# The output of the of the preprocessor must be set to pandas so the
# gradient boosting model can detect the low cardinality features.
mixed_encoded_preprocessor.set_output(transform="pandas")
mixed_pipe = make_pipeline(
    mixed_encoded_preprocessor,
    HistGradientBoostingRegressor(
        random_state=0, max_iter=max_iter, categorical_features=low_cardinality_features
    ),
)
mixed_pipe
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(transformers=[('numerical', 'passthrough',
                                                  ['price']),
                                                 ('high_cardinality',
                                                  TargetEncoder(target_type='continuous'),
                                                  Index(['winery', 'region_1', 'variety', 'province'], dtype='object')),
                                                 ('low_cardinality',
                                                  OrdinalEncoder(handle_unknown='use_encoded_value',
                                                                 unknown_value=-1),
                                                  Index(['country', 'region_2'], dtype='object'))],
                                   verbose_feature_names_out=False)),
                ('histgradientboostingregressor',
                 HistGradientBoostingRegressor(categorical_features=Index(['country', 'region_2'], dtype='object'),
                                               max_iter=20, 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.


Schließlich werten wir die Pipeline anhand von Kreuzvalidierung aus und erfassen die Ergebnisse.

evaluate_model_and_store("mixed_target", mixed_pipe)

Darstellung der Ergebnisse#

In diesem Abschnitt zeigen wir die Ergebnisse, indem wir die Test- und Trainingsergebnisse plotten.

import matplotlib.pyplot as plt
import pandas as pd

results_df = (
    pd.DataFrame(results).set_index("preprocessor").sort_values("rmse_test_mean")
)

fig, (ax1, ax2) = plt.subplots(
    1, 2, figsize=(12, 8), sharey=True, constrained_layout=True
)
xticks = range(len(results_df))
name_to_color = dict(
    zip((r["preprocessor"] for r in results), ["C0", "C1", "C2", "C3", "C4"])
)

for subset, ax in zip(["test", "train"], [ax1, ax2]):
    mean, std = f"rmse_{subset}_mean", f"rmse_{subset}_std"
    data = results_df[[mean, std]].sort_values(mean)
    ax.bar(
        x=xticks,
        height=data[mean],
        yerr=data[std],
        width=0.9,
        color=[name_to_color[name] for name in data.index],
    )
    ax.set(
        title=f"RMSE ({subset.title()})",
        xlabel="Encoding Scheme",
        xticks=xticks,
        xticklabels=data.index,
    )
RMSE (Test), RMSE (Train)

Bei der Bewertung der prädiktiven Leistung auf dem Testdatensatz schneidet das Verwerfen der Kategorien am schlechtesten ab, und die Target Encoder schneiden am besten ab. Dies lässt sich wie folgt erklären:

  • Das Verwerfen der kategorialen Merkmale macht die Pipeline weniger ausdrucksstark und führt zu Underfitting;

  • Aufgrund der hohen Kardinalität und zur Reduzierung der Trainingszeit verwendet das One-Hot-Encoding-Schema max_categories=20, was verhindert, dass sich die Merkmale zu stark erweitern, was zu Underfitting führen kann.

  • Wenn wir max_categories=20 nicht gesetzt hätten, hätte das One-Hot-Encoding-Schema wahrscheinlich zu Überanpassung geführt, da die Anzahl der Merkmale mit seltenen Kategorien explodiert, die zufällig mit dem Ziel korrelieren (nur im Trainingsdatensatz);

  • Das Ordinal-Encoding erzwingt eine willkürliche Reihenfolge der Merkmale, die dann vom HistGradientBoostingRegressor als numerische Werte behandelt werden. Da dieses Modell numerische Merkmale in 256 Bins pro Merkmal gruppiert, können viele nicht zusammenhängende Kategorien zusammen gruppiert werden, und als Ergebnis kann die gesamte Pipeline underfitten;

  • Bei Verwendung des Target Encoders geschieht dasselbe Binning, aber da die kodierten Werte statistisch nach ihrer marginalen Assoziation mit der Zielvariablen geordnet sind, ist das Binning, das vom HistGradientBoostingRegressor verwendet wird, sinnvoll und führt zu guten Ergebnissen: Die Kombination aus geglättetem Target Encoding und Binning wirkt als gute regularisierende Strategie gegen Überanpassung, ohne die Ausdrucksstärke der Pipeline zu sehr einzuschränken.

Gesamtlaufzeit des Skripts: (0 Minuten 21,022 Sekunden)

Verwandte Beispiele

Unterstützung für kategorische Merkmale in Gradient Boosting

Unterstützung für kategorische Merkmale in Gradient Boosting

Target Encoders interne Kreuzanpassung

Internes Cross-Fitting des Target Encoders

Column Transformer mit gemischten Typen

Column Transformer mit gemischten Typen

Release Highlights für scikit-learn 1.4

Release Highlights für scikit-learn 1.4

Galerie generiert von Sphinx-Gallery