Permutations-Wichtigkeit im Vergleich zur Zufallswald-Feature-Wichtigkeit (MDI)#

In diesem Beispiel vergleichen wir die auf Verunreinigung basierende Feature-Wichtigkeit von RandomForestClassifier mit der Permutations-Wichtigkeit auf dem Titanic-Datensatz unter Verwendung von permutation_importance. Wir werden zeigen, dass die auf Verunreinigung basierende Feature-Wichtigkeit die Wichtigkeit numerischer Merkmale aufblähen kann.

Darüber hinaus leidet die auf Verunreinigung basierende Feature-Wichtigkeit von Zufallswäldern darunter, dass sie auf Statistiken berechnet wird, die aus dem Trainingsdatensatz abgeleitet sind: Die Wichtigkeiten können auch für Merkmale hoch sein, die das Zielvariable nicht vorhersagen, solange das Modell die Fähigkeit hat, sie zum Überanpassen zu nutzen.

Dieses Beispiel zeigt, wie Permutations-Wichtigkeiten als Alternative verwendet werden können, um diese Einschränkungen zu mildern.

Referenzen

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

Daten laden und Feature Engineering#

Wir verwenden pandas, um eine Kopie des Titanic-Datensatzes zu laden. Das Folgende zeigt, wie separate Vorverarbeitung auf numerische und kategorische Merkmale angewendet wird.

Wir fügen zwei Zufallsvariablen hinzu, die keinerlei Korrelation mit der Zielvariable (survived) aufweisen.

  • random_num ist eine numerische Variable mit hoher Kardinalität (so viele eindeutige Werte wie Datensätze).

  • random_cat ist eine kategoriale Variable mit geringer Kardinalität (3 mögliche Werte).

import numpy as np

from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True)
rng = np.random.RandomState(seed=42)
X["random_cat"] = rng.randint(3, size=X.shape[0])
X["random_num"] = rng.randn(X.shape[0])

categorical_columns = ["pclass", "sex", "embarked", "random_cat"]
numerical_columns = ["age", "sibsp", "parch", "fare", "random_num"]

X = X[categorical_columns + numerical_columns]
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

Wir definieren ein prädiktives Modell, das auf einem Zufallswald basiert. Daher werden wir die folgenden Vorverarbeitungsschritte durchführen:

  • Verwendung von OrdinalEncoder zur Kodierung der kategorialen Merkmale;

  • Verwendung von SimpleImputer zum Auffüllen fehlender Werte für numerische Merkmale mit einer Mittelwertstrategie.

from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder

categorical_encoder = OrdinalEncoder(
    handle_unknown="use_encoded_value", unknown_value=-1, encoded_missing_value=-1
)
numerical_pipe = SimpleImputer(strategy="mean")

preprocessing = ColumnTransformer(
    [
        ("cat", categorical_encoder, categorical_columns),
        ("num", numerical_pipe, numerical_columns),
    ],
    verbose_feature_names_out=False,
)

rf = Pipeline(
    [
        ("preprocess", preprocessing),
        ("classifier", RandomForestClassifier(random_state=42)),
    ]
)
rf.fit(X_train, y_train)
Pipeline(steps=[('preprocess',
                 ColumnTransformer(transformers=[('cat',
                                                  OrdinalEncoder(encoded_missing_value=-1,
                                                                 handle_unknown='use_encoded_value',
                                                                 unknown_value=-1),
                                                  ['pclass', 'sex', 'embarked',
                                                   'random_cat']),
                                                 ('num', SimpleImputer(),
                                                  ['age', 'sibsp', 'parch',
                                                   'fare', 'random_num'])],
                                   verbose_feature_names_out=False)),
                ('classifier', RandomForestClassifier(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.


Genauigkeit des Modells#

Bevor die Feature-Wichtigkeiten inspiziert werden, ist es wichtig zu prüfen, ob die prädiktive Leistung des Modells ausreichend hoch ist. Tatsächlich wäre es von geringem Interesse, die wichtigen Merkmale eines nicht-prädiktiven Modells zu inspizieren.

print(f"RF train accuracy: {rf.score(X_train, y_train):.3f}")
print(f"RF test accuracy: {rf.score(X_test, y_test):.3f}")
RF train accuracy: 1.000
RF test accuracy: 0.814

Hier kann man beobachten, dass die Trainingsgenauigkeit sehr hoch ist (der Wald-Modell hat genügend Kapazität, um den Trainingssatz vollständig zu merken), aber er kann dank der integrierten Bagging-Methode von Zufallswäldern immer noch gut auf den Testdatensatz generalisieren.

Es ist möglicherweise möglich, einige Genauigkeit auf dem Trainingssatz gegen eine etwas bessere Genauigkeit auf dem Testdatensatz einzutauschen, indem die Kapazität der Bäume begrenzt wird (z. B. durch Setzen von min_samples_leaf=5 oder min_samples_leaf=10), um Überanpassung zu begrenzen, ohne zu viel Unteranpassung einzuführen.

Wir behalten jedoch vorerst unser Zufallswaldmodell mit hoher Kapazität bei, um einige Tücken bei der Feature-Wichtigkeit für Variablen mit vielen eindeutigen Werten zu veranschaulichen.

Baum-Feature-Wichtigkeit aus mittlerer Verringerung der Verunreinigung (MDI)#

Die auf Verunreinigung basierende Feature-Wichtigkeit ordnet die numerischen Merkmale als die wichtigsten Merkmale ein. Infolgedessen wird die nicht-prädiktive Variable random_num als eines der wichtigsten Merkmale eingestuft!

Dieses Problem ergibt sich aus zwei Einschränkungen der auf Verunreinigung basierenden Feature-Wichtigkeiten:

  • auf Verunreinigung basierende Wichtigkeiten sind gegenüber Merkmalen mit hoher Kardinalität voreingenommen;

  • auf Verunreinigung basierende Wichtigkeiten werden auf Trainingssatz-Statistiken berechnet und spiegeln daher nicht die Fähigkeit eines Merkmals wider, nützlich für Vorhersagen zu sein, die auf den Testdatensatz generalisieren (wenn das Modell über genügend Kapazität verfügt).

Die Voreingenommenheit gegenüber Merkmalen mit hoher Kardinalität erklärt, warum random_num eine wirklich große Wichtigkeit im Vergleich zu random_cat hat, während wir erwarten würden, dass beide Zufallsmerkmale eine Null-Wichtigkeit haben.

Die Tatsache, dass wir Trainingssatz-Statistiken verwenden, erklärt, warum sowohl die Merkmale random_num als auch random_cat eine nicht-null Wichtigkeit aufweisen.

import pandas as pd

feature_names = rf[:-1].get_feature_names_out()

mdi_importances = pd.Series(
    rf[-1].feature_importances_, index=feature_names
).sort_values(ascending=True)
ax = mdi_importances.plot.barh()
ax.set_title("Random Forest Feature Importances (MDI)")
ax.figure.tight_layout()
Random Forest Feature Importances (MDI)

Als Alternative werden die Permutations-Wichtigkeiten von rf auf einem zurückgehaltenen Testdatensatz berechnet. Dies zeigt, dass das kategoriale Merkmal mit geringer Kardinalität, sex und pclass, die wichtigsten Merkmale sind. Tatsächlich führt das Vertauschen der Werte dieser Merkmale zu der größten Verringerung der Genauigkeitsbewertung des Modells auf dem Testdatensatz.

Beachten Sie auch, dass beide Zufallsmerkmale sehr geringe Wichtigkeiten (nahe 0) haben, wie erwartet.

from sklearn.inspection import permutation_importance

result = permutation_importance(
    rf, X_test, y_test, n_repeats=10, random_state=42, n_jobs=2
)

sorted_importances_idx = result.importances_mean.argsort()
importances = pd.DataFrame(
    result.importances[sorted_importances_idx].T,
    columns=X.columns[sorted_importances_idx],
)
ax = importances.plot.box(vert=False, whis=10)
ax.set_title("Permutation Importances (test set)")
ax.axvline(x=0, color="k", linestyle="--")
ax.set_xlabel("Decrease in accuracy score")
ax.figure.tight_layout()
Permutation Importances (test set)

Es ist auch möglich, die Permutations-Wichtigkeiten auf dem Trainingsdatensatz zu berechnen. Dies zeigt, dass random_num und random_cat eine signifikant höhere Wichtigkeitsbewertung erhalten als wenn sie auf dem Testdatensatz berechnet werden. Der Unterschied zwischen diesen beiden Diagrammen ist eine Bestätigung dafür, dass das RF-Modell über genügend Kapazität verfügt, um diese zufälligen numerischen und kategorialen Merkmale zum Überanpassen zu nutzen.

result = permutation_importance(
    rf, X_train, y_train, n_repeats=10, random_state=42, n_jobs=2
)

sorted_importances_idx = result.importances_mean.argsort()
importances = pd.DataFrame(
    result.importances[sorted_importances_idx].T,
    columns=X.columns[sorted_importances_idx],
)
ax = importances.plot.box(vert=False, whis=10)
ax.set_title("Permutation Importances (train set)")
ax.axvline(x=0, color="k", linestyle="--")
ax.set_xlabel("Decrease in accuracy score")
ax.figure.tight_layout()
Permutation Importances (train set)

Wir können das Experiment erneut versuchen, indem wir die Kapazität der Bäume zum Überanpassen begrenzen, indem wir min_samples_leaf auf 20 Datenpunkte setzen.

rf.set_params(classifier__min_samples_leaf=20).fit(X_train, y_train)
Pipeline(steps=[('preprocess',
                 ColumnTransformer(transformers=[('cat',
                                                  OrdinalEncoder(encoded_missing_value=-1,
                                                                 handle_unknown='use_encoded_value',
                                                                 unknown_value=-1),
                                                  ['pclass', 'sex', 'embarked',
                                                   'random_cat']),
                                                 ('num', SimpleImputer(),
                                                  ['age', 'sibsp', 'parch',
                                                   'fare', 'random_num'])],
                                   verbose_feature_names_out=False)),
                ('classifier',
                 RandomForestClassifier(min_samples_leaf=20, 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.


Wenn wir die Genauigkeitsbewertung auf dem Trainings- und Testdatensatz betrachten, stellen wir fest, dass die beiden Metriken jetzt sehr ähnlich sind. Daher überanpasst unser Modell nicht mehr. Wir können dann die Permutations-Wichtigkeiten mit diesem neuen Modell überprüfen.

print(f"RF train accuracy: {rf.score(X_train, y_train):.3f}")
print(f"RF test accuracy: {rf.score(X_test, y_test):.3f}")
RF train accuracy: 0.810
RF test accuracy: 0.832
train_result = permutation_importance(
    rf, X_train, y_train, n_repeats=10, random_state=42, n_jobs=2
)
test_results = permutation_importance(
    rf, X_test, y_test, n_repeats=10, random_state=42, n_jobs=2
)
sorted_importances_idx = train_result.importances_mean.argsort()
train_importances = pd.DataFrame(
    train_result.importances[sorted_importances_idx].T,
    columns=X.columns[sorted_importances_idx],
)
test_importances = pd.DataFrame(
    test_results.importances[sorted_importances_idx].T,
    columns=X.columns[sorted_importances_idx],
)
for name, importances in zip(["train", "test"], [train_importances, test_importances]):
    ax = importances.plot.box(vert=False, whis=10)
    ax.set_title(f"Permutation Importances ({name} set)")
    ax.set_xlabel("Decrease in accuracy score")
    ax.axvline(x=0, color="k", linestyle="--")
    ax.figure.tight_layout()
  • Permutation Importances (train set)
  • Permutation Importances (test set)

Jetzt können wir beobachten, dass auf beiden Sätzen die Merkmale random_num und random_cat eine geringere Wichtigkeit haben als beim überanpassenden Zufallswald. Die Schlussfolgerungen bezüglich der Wichtigkeit der anderen Merkmale bleiben jedoch gültig.

Gesamtlaufzeit des Skripts: (0 Minuten 6,265 Sekunden)

Verwandte Beispiele

Merkmalswichtigkeiten mit einem Wald von Bäumen

Merkmalswichtigkeiten mit einem Wald von Bäumen

Permutations-Wichtigkeit bei multikollinearen oder korrelierten Merkmalen

Permutations-Wichtigkeit bei multikollinearen oder korrelierten Merkmalen

Release Highlights für scikit-learn 0.22

Release Highlights für scikit-learn 0.22

Gradient Boosting Regression

Gradient Boosting Regression

Galerie generiert von Sphinx-Gallery