Vergleich von Random Forests und Histogram Gradient Boosting Modellen#

In diesem Beispiel vergleichen wir die Leistung von Random Forest (RF) und Histogram Gradient Boosting (HGBT) Modellen in Bezug auf Score und Berechnungszeit für einen Regressionsdatensatz, obwohl alle hier vorgestellten Konzepte auch für Klassifikation gelten.

Der Vergleich wird durch Variation der Parameter durchgeführt, die die Anzahl der Bäume für jeden Estimator steuern

  • n_estimators steuert die Anzahl der Bäume im Forest. Dies ist eine feste Zahl.

  • max_iter ist die maximale Anzahl von Iterationen in einem Gradient Boosting-basierten Modell. Die Anzahl der Iterationen entspricht der Anzahl der Bäume für Regressions- und binäre Klassifikationsprobleme. Darüber hinaus hängt die tatsächlich benötigte Anzahl von Bäumen vom Stoppkriterium ab.

HGBT verwendet Gradient Boosting, um die Modellleistung iterativ zu verbessern, indem jeder Baum an den negativen Gradienten der Verlustfunktion in Bezug auf den vorhergesagten Wert angepasst wird. RF hingegen basieren auf Bagging und verwenden eine Mehrheitsentscheidung zur Vorhersage des Ergebnisses.

Weitere Informationen zu Ensemble-Modellen finden Sie im Benutzerhandbuch oder sehen Sie sich Merkmale in Histogram Gradient Boosting Bäumen für ein Beispiel an, das einige andere Merkmale von HGBT-Modellen zeigt.

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

Datensatz laden#

from sklearn.datasets import fetch_california_housing

X, y = fetch_california_housing(return_X_y=True, as_frame=True)
n_samples, n_features = X.shape

HGBT verwendet einen histogrammbasierten Algorithmus auf gebinnte Merkmalswerte, der große Datensätze (Zehntausende von Stichproben oder mehr) mit einer hohen Anzahl von Merkmalen effizient verarbeiten kann (siehe Warum es schneller ist). Die scikit-learn Implementierung von RF verwendet keine Binning und stützt sich auf exakte Teilung, was rechenintensiv sein kann.

print(f"The dataset consists of {n_samples} samples and {n_features} features")
The dataset consists of 20640 samples and 8 features

Score und Berechnungszeiten berechnen#

Beachten Sie, dass viele Teile der Implementierung von HistGradientBoostingClassifier und HistGradientBoostingRegressor standardmäßig parallelisiert sind.

Die Implementierung von RandomForestRegressor und RandomForestClassifier kann durch die Verwendung des Parameters n_jobs ebenfalls auf mehreren Kernen ausgeführt werden, hier auf die Anzahl der physischen Kerne des Host-Rechners eingestellt. Weitere Informationen finden Sie unter Parallelität.

import joblib

N_CORES = joblib.cpu_count(only_physical_cores=True)
print(f"Number of physical cores: {N_CORES}")
Number of physical cores: 2

Im Gegensatz zu RF bieten HGBT-Modelle eine Option zum frühen Stoppen (siehe Frühes Stoppen in Gradient Boosting), um keine unnötigen Bäume mehr hinzuzufügen. Intern verwendet der Algorithmus eine Out-of-Sample-Menge, um die Generalisierungsleistung des Modells bei jeder Hinzufügung eines Baumes zu berechnen. Wenn sich die Generalisierungsleistung für mehr als n_iter_no_change Iterationen nicht verbessert, werden keine weiteren Bäume hinzugefügt.

Die anderen Parameter beider Modelle wurden optimiert, das Verfahren wird hier jedoch vereinfacht dargestellt, um das Beispiel einfach zu halten.

import pandas as pd

from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor
from sklearn.model_selection import GridSearchCV, KFold

models = {
    "Random Forest": RandomForestRegressor(
        min_samples_leaf=5, random_state=0, n_jobs=N_CORES
    ),
    "Hist Gradient Boosting": HistGradientBoostingRegressor(
        max_leaf_nodes=15, random_state=0, early_stopping=False
    ),
}
param_grids = {
    "Random Forest": {"n_estimators": [10, 20, 50, 100]},
    "Hist Gradient Boosting": {"max_iter": [10, 20, 50, 100, 300, 500]},
}
cv = KFold(n_splits=4, shuffle=True, random_state=0)

results = []
for name, model in models.items():
    grid_search = GridSearchCV(
        estimator=model,
        param_grid=param_grids[name],
        return_train_score=True,
        cv=cv,
    ).fit(X, y)
    result = {"model": name, "cv_results": pd.DataFrame(grid_search.cv_results_)}
    results.append(result)

Hinweis

Das Optimieren von n_estimators für RF führt im Allgemeinen zu einer Verschwendung von Computerleistung. In der Praxis muss man nur sicherstellen, dass es groß genug ist, so dass eine Verdoppelung seines Wertes nicht zu einer signifikanten Verbesserung des Testergebnisses führt.

Ergebnisse plotten#

Wir können plotly.express.scatter verwenden, um den Kompromiss zwischen verstrichener Rechenzeit und mittlerem Test-Score zu visualisieren. Das Bewegen des Cursors über einen bestimmten Punkt zeigt die entsprechenden Parameter. Fehlerbalken entsprechen einer Standardabweichung, wie sie in den verschiedenen Folds der Kreuzvalidierung berechnet wurde.

import plotly.colors as colors
import plotly.express as px
from plotly.subplots import make_subplots

fig = make_subplots(
    rows=1,
    cols=2,
    shared_yaxes=True,
    subplot_titles=["Train time vs score", "Predict time vs score"],
)
model_names = [result["model"] for result in results]
colors_list = colors.qualitative.Plotly * (
    len(model_names) // len(colors.qualitative.Plotly) + 1
)

for idx, result in enumerate(results):
    cv_results = result["cv_results"].round(3)
    model_name = result["model"]
    param_name = next(iter(param_grids[model_name].keys()))
    cv_results[param_name] = cv_results["param_" + param_name]
    cv_results["model"] = model_name

    scatter_fig = px.scatter(
        cv_results,
        x="mean_fit_time",
        y="mean_test_score",
        error_x="std_fit_time",
        error_y="std_test_score",
        hover_data=param_name,
        color="model",
    )
    line_fig = px.line(
        cv_results,
        x="mean_fit_time",
        y="mean_test_score",
    )

    scatter_trace = scatter_fig["data"][0]
    line_trace = line_fig["data"][0]
    scatter_trace.update(marker=dict(color=colors_list[idx]))
    line_trace.update(line=dict(color=colors_list[idx]))
    fig.add_trace(scatter_trace, row=1, col=1)
    fig.add_trace(line_trace, row=1, col=1)

    scatter_fig = px.scatter(
        cv_results,
        x="mean_score_time",
        y="mean_test_score",
        error_x="std_score_time",
        error_y="std_test_score",
        hover_data=param_name,
    )
    line_fig = px.line(
        cv_results,
        x="mean_score_time",
        y="mean_test_score",
    )

    scatter_trace = scatter_fig["data"][0]
    line_trace = line_fig["data"][0]
    scatter_trace.update(marker=dict(color=colors_list[idx]))
    line_trace.update(line=dict(color=colors_list[idx]))
    fig.add_trace(scatter_trace, row=1, col=2)
    fig.add_trace(line_trace, row=1, col=2)

fig.update_layout(
    xaxis=dict(title="Train time (s) - lower is better"),
    yaxis=dict(title="Test R2 score - higher is better"),
    xaxis2=dict(title="Predict time (s) - lower is better"),
    legend=dict(x=0.72, y=0.05, traceorder="normal", borderwidth=1),
    title=dict(x=0.5, text="Speed-score trade-off of tree-based ensembles"),
)


Sowohl HGBT als auch RF Modelle verbessern sich mit zunehmender Anzahl von Bäumen im Ensemble. Die Scores erreichen jedoch ein Plateau, an dem das Hinzufügen weiterer Bäume das Anpassen und Bewerten nur verlangsamt. Das RF-Modell erreicht ein solches Plateau früher und kann nie den Test-Score des größten HGBDT-Modells erreichen.

Beachten Sie, dass die im obigen Plot gezeigten Ergebnisse bei verschiedenen Durchläufen leicht und bei Ausführung auf anderen Maschinen noch erheblich variieren können: Versuchen Sie, dieses Beispiel auf Ihrem eigenen lokalen Computer auszuführen.

Insgesamt ist festzustellen, dass die Histogramm-basierten Gradient Boosting-Modelle die Random Forest-Modelle im "Test-Score vs. Trainingsgeschwindigkeit"-Kompromiss durchweg übertreffen (die HGBDT-Kurve sollte sich oben links von der RF-Kurve befinden, ohne sich jemals zu kreuzen). Der "Test-Score vs. Vorhersagegeschwindigkeit"-Kompromiss kann ebenfalls umstrittener sein, aber er ist meistens zugunsten von HGBDT. Es ist immer eine gute Idee, beide Arten von Modellen (mit Hyperparameter-Tuning) zu prüfen und ihre Leistung für Ihr spezifisches Problem zu vergleichen, um festzustellen, welches Modell am besten geeignet ist, aber HGBT bietet fast immer einen günstigeren Geschwindigkeits-Genauigkeits-Kompromiss als RF, sei es mit den Standard-Hyperparametern oder einschließlich der Kosten für das Hyperparameter-Tuning.

Es gibt jedoch eine Ausnahme von dieser Faustregel: Beim Trainieren eines Multiklassen-Klassifikationsmodells mit einer großen Anzahl möglicher Klassen passt HGBDT intern einen Baum pro Klasse bei jeder Boosting-Iteration an, während die von den RF-Modellen verwendeten Bäume naturgemäß multiklass sind, was den Geschwindigkeits-Genauigkeits-Kompromiss der RF-Modelle in diesem Fall verbessern sollte.

Gesamte Laufzeit des Skripts: (0 Minuten 39,620 Sekunden)

Verwandte Beispiele

Beispiel-Pipeline für Textmerkmal-Extraktion und -Bewertung

Beispiel-Pipeline für Textmerkmal-Extraktion und -Bewertung

Merkmals Transformationen mit Ensemble von Bäumen

Merkmals Transformationen mit Ensemble von Bäumen

Permutations-Wichtigkeit vs. Random Forest Merkmals-Wichtigkeit (MDI)

Permutations-Wichtigkeit vs. Random Forest Merkmals-Wichtigkeit (MDI)

Merkmale in Histogram Gradient Boosting Bäumen

Merkmale in Histogram Gradient Boosting Bäumen

Galerie generiert von Sphinx-Gallery