W3docs

Grid Search zur Hyperparameter-Optimierung in Python

GridSearchCV und RandomizedSearchCV in Python verwenden, um Hyperparameter von Machine-Learning-Modellen zu optimieren – mit scikit-learn-Beispielen.

Hyperparameter-Optimierung ist der Prozess, die Konfigurationswerte eines Machine-Learning-Modells zu finden, die nicht aus den Daten gelernt werden – zum Beispiel die Tiefe eines Entscheidungsbaums, die Regularisierungsstärke einer logistischen Regression oder die Anzahl der Neuronen in einem neuronalen Netz. Grid Search ist der direkteste Ansatz: Man definiert einen diskreten Satz von Werten für jeden Hyperparameter, probiert jede Kombination aus und behält die beste.

Diese Seite behandelt:

  • Was Hyperparameter sind und warum sie wichtig sind
  • Wie GridSearchCV ein Parameterraster erschöpfend durchsucht
  • Wie man cv_results_ liest und versteht, was getestet wurde
  • Verwendung von n_jobs=-1 zur Parallelisierung der Suche
  • RandomizedSearchCV als schnellere Alternative für große Raster
  • Kombination von Grid Search mit einer Pipeline zur Vermeidung von Data Leakage
  • Wann Grid Search gegenüber schnelleren Alternativen vorzuziehen ist

Alle Beispiele verwenden die eingebauten Datensätze von scikit-learn, sodass sie sofort ausgeführt werden können.

Was sind Hyperparameter?

Jedes Machine-Learning-Modell hat zwei Arten von Parametern:

  • Modellparameter werden beim Training automatisch gelernt (z. B. die Gewichte in einem neuronalen Netz, die Schwellenwerte für Splits in einem Entscheidungsbaum).
  • Hyperparameter werden von Ihnen vor dem Training festgelegt. Sie steuern den Lernprozess selbst.

Die Wahl falscher Hyperparameter kann dazu führen, dass ein leistungsfähiges Modell erheblich schlechter abschneidet. Ein Entscheidungsbaum ohne Tiefenbegrenzung wird die Trainingsdaten überanpassen; einer mit einer Tiefenbegrenzung von 1 wird zu wenig anpassen. Die korrekte Begrenzung hängt von den Daten ab – Grid Search findet sie systematisch statt durch Raten.

Wie GridSearchCV funktioniert

GridSearchCV aus scikit-learn kombiniert zwei Konzepte:

  1. Rasterenumeration — es erzeugt jede Kombination der von Ihnen angegebenen Hyperparameter-Werte.
  2. Kreuzvalidierung — für jede Kombination führt es eine k-fache Kreuzvalidierung durch (siehe Kreuzvalidierung in Python) und speichert den Durchschnittswert.

Nach der Suche speichert GridSearchCV die beste Kombination und passt automatisch ein Modell mit diesen Einstellungen an den vollständigen Trainingsdatensatz an.

Die Anzahl der Anpassungen beträgt (Kombinationen) × (CV-Folds). Ein Raster mit 3 × 3 × 3 = 27 Kombinationen und cv=5 führt 135 Anpassungen durch – handhabbar für schnelle Modelle, aufwendig für langsame.

Einfaches GridSearchCV-Beispiel

Das folgende Beispiel optimiert einen Entscheidungsbaum-Klassifikator auf dem Iris-Datensatz.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

# Model with no hyperparameters set yet
model = DecisionTreeClassifier(random_state=42)

# Define the grid of values to try
param_grid = {
    'max_depth': [2, 3, 5, None],
    'min_samples_split': [2, 5, 10],
    'criterion': ['gini', 'entropy'],
}

# cv=5 means 5-fold cross-validation for each combination
grid_search = GridSearchCV(
    estimator=model,
    param_grid=param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1,       # use all CPU cores
    verbose=0,
)

grid_search.fit(X, y)

print("Best parameters:", grid_search.best_params_)
print("Best CV accuracy: {:.3f}".format(grid_search.best_score_))

Was jedes Argument bewirkt:

ArgumentZweck
estimatorDas zu optimierende Modell. Jeder scikit-learn-Estimator funktioniert.
param_gridDict, das Parameternamen auf Listen von Kandidatenwerten abbildet.
cvAnzahl der Folds bei der Kreuzvalidierung (5 ist ein üblicher Standardwert).
scoringZu optimierende Metrik. Standardmäßig die .score()-Methode des Estimators.
n_jobsAnzahl der parallelen Jobs. -1 verwendet alle verfügbaren CPU-Kerne.

Typische Ausgabe:

Best parameters: {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV accuracy: 0.967

cv_results_ lesen

Nach der Anpassung ist grid_search.cv_results_ ein Dict aus Arrays – ein Eintrag pro getesteter Kombination. Die nützlichsten Schlüssel sind:

import pandas as pd

results = pd.DataFrame(grid_search.cv_results_)

# Show top 5 combinations by mean test score
cols = ['param_max_depth', 'param_min_samples_split', 'param_criterion',
        'mean_test_score', 'std_test_score', 'rank_test_score']
print(results[cols].sort_values('rank_test_score').head(5).to_string(index=False))

Wichtige Spalten:

  • mean_test_score — der durchschnittliche CV-Score über alle Folds für diese Kombination.
  • std_test_score — Standardabweichung; ein hoher Wert bedeutet, dass der Score über Folds hinweg instabil ist.
  • rank_test_score — Rang 1 ist der Gewinner.

Scoring-Optionen

Standardmäßig optimiert GridSearchCV die Standardmetrik des Estimators. Sie können einen beliebigen eingebauten Scorer oder einen eigenen angeben:

# Common scoring strings
scoring_options = [
    'accuracy',       # classification
    'f1_weighted',    # F1 for multi-class
    'roc_auc',        # binary classification
    'neg_mean_squared_error',  # regression (note: negative so higher = better)
    'r2',             # regression
]

# Evaluate multiple metrics at once (refit on the one you care most about)
grid_search = GridSearchCV(
    estimator=DecisionTreeClassifier(random_state=42),
    param_grid={'max_depth': [2, 3, 5]},
    cv=5,
    scoring={'acc': 'accuracy', 'f1': 'f1_weighted'},
    refit='acc',       # use accuracy to pick the best model
    n_jobs=-1,
)

Eine Pipeline verwenden, um Data Leakage zu verhindern

Wenn Ihre Vorverarbeitung von den Trainingsdaten abhängt (Skalierung, Imputation, Feature-Selektion), müssen Sie den Preprocessor nur auf Trainings-Folds anpassen – niemals auf dem vollständigen Datensatz vor der Aufteilung. Eine Pipeline übernimmt das automatisch und funktioniert nahtlos mit GridSearchCV.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import GridSearchCV

X, y = load_breast_cancer(return_X_y=True)

# Build a pipeline: scale then classify
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('svc', SVC()),
])

# Reference pipeline steps with double-underscore: step__param
param_grid = {
    'svc__C': [0.1, 1, 10],
    'svc__kernel': ['linear', 'rbf'],
    'svc__gamma': ['scale', 'auto'],
}

grid_search = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X, y)

print("Best params:", grid_search.best_params_)
print("Best CV accuracy: {:.3f}".format(grid_search.best_score_))

Die Doppelunterstrich-Syntax (svc__C) ist entscheidend: Sie teilt scikit-learn mit, C an den svc-Schritt innerhalb der Pipeline weiterzugeben. Ohne eine Pipeline würde die Skalierung des vollständigen Datensatzes vor der Kreuzvalidierung Informationen aus dem Test-Fold in den Scaler einführen und einen zu optimistischen Score liefern.

RandomizedSearchCV: Schneller bei großen Rastern

Erschöpfende Grid Search wird unpraktisch, wenn jeder Hyperparameter viele Kandidatenwerte hat. RandomizedSearchCV zieht eine feste Anzahl zufälliger Kombinationen, anstatt alle zu testen:

from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from scipy.stats import randint

X, y = load_iris(return_X_y=True)

# Use distributions instead of discrete lists
param_dist = {
    'n_estimators': randint(50, 500),   # random integer in [50, 500)
    'max_depth': [3, 5, 10, None],
    'min_samples_split': randint(2, 20),
    'max_features': ['sqrt', 'log2'],
}

rand_search = RandomizedSearchCV(
    estimator=RandomForestClassifier(random_state=42),
    param_distributions=param_dist,
    n_iter=30,        # try 30 random combinations instead of all
    cv=5,
    scoring='accuracy',
    n_jobs=-1,
    random_state=42,
)

rand_search.fit(X, y)

print("Best params:", rand_search.best_params_)
print("Best CV accuracy: {:.3f}".format(rand_search.best_score_))

GridSearchCV vs. RandomizedSearchCV:

GridSearchCVRandomizedSearchCV
SuchstrategieErschöpfend (alle Kombinationen)Zufällige Stichprobe
ReproduzierbarkeitVollständig deterministischrandom_state setzen
Am besten geeignet fürKleine, klar definierte RasterGroße Suchräume
Kontinuierliche VerteilungenNicht unterstütztUnterstützt via scipy.stats
Findet garantiert das BesteJa (innerhalb des Rasters)Nein, aber oft nah dran

Bei großen Rastern findet RandomizedSearchCV mit n_iter=50–100 häufig eine nahezu optimale Lösung in einem Bruchteil der Rechenzeit.

Vorhersagen mit dem besten Modell treffen

Nach der Anpassung verhält sich GridSearchCV wie ein normaler Estimator. Das Attribut best_estimator_ enthält das neu angepasste Modell:

from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

param_grid = {'max_depth': [2, 3, 5], 'criterion': ['gini', 'entropy']}
grid_search = GridSearchCV(
    DecisionTreeClassifier(random_state=42),
    param_grid, cv=5, n_jobs=-1
)
grid_search.fit(X_train, y_train)

# Evaluate on the held-out test set
test_score = grid_search.score(X_test, y_test)
print("Test accuracy: {:.3f}".format(test_score))

# Access the best model directly
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test[:5])
print("Predictions for first 5 test samples:", predictions)

Wichtig: Halten Sie immer einen separaten zurückgehaltenen Testdatensatz bereit, der während der Grid Search nie verwendet wird. Der Kreuzvalidierungs-Score innerhalb von GridSearchCV schätzt die Generalisierungsfähigkeit, aber die eigentliche abschließende Bewertung sollte auf Daten erfolgen, die die Suche nie berührt hat.

Wann Grid Search verwendet werden sollte

Grid Search ist eine gute Standardwahl, wenn:

  • Sie ein kleines oder mittelgroßes Modell haben, das schnell trainiert (Sekunden bis wenige Minuten pro Anpassung).
  • Sie grob wissen, welche Hyperparameter am wichtigsten sind, und einen sinnvollen Satz von Kandidatenwerten haben.
  • Reproduzierbarkeit und Vollständigkeit wichtig sind.

Erwägen Sie Alternativen, wenn:

  • Das Raster groß ist (viele Parameter mit vielen Werten) — verwenden Sie zuerst RandomizedSearchCV, um den Raum einzugrenzen, und verfeinern Sie dann mit GridSearchCV.
  • Das Training aufwendig ist (Deep Learning, große Ensembles) — Bayesianische Optimierungsbibliotheken wie scikit-optimize oder Optuna treffen klügere Entscheidungen als zufälliges Sampling.
  • Sie automatisches Stoppen wünschen — Halving-Strategien (HalvingGridSearchCV) scheiden schwach performende Kandidaten früh aus und benötigen insgesamt weniger Anpassungen.

Praktische Tipps

  • Beginnen Sie grob. Verwenden Sie ein kleines Raster mit Werten, die sich über Größenordnungen erstrecken (z. B. C: [0.01, 0.1, 1, 10, 100]). Sobald Sie eine vielversprechende Region gefunden haben, verfeinern Sie mit einem feineren Raster.
  • Beobachten Sie Standardabweichungen. Wenn std_test_score groß ist, ist das Modell empfindlich gegenüber der jeweiligen Datenteilung. Erwägen Sie, cv zu erhöhen oder mehr Daten zu sammeln.
  • Setzen Sie n_jobs=-1, um alle CPU-Kerne zu nutzen – das kostet nichts und bringt auf einem modernen Rechner oft einen 4–8-fachen Geschwindigkeitsvorteil.
  • Kombinieren Sie mit einer Pipeline. Wickeln Sie Vorverarbeitung und Modell immer in einer Pipeline ein, bevor Sie sie an GridSearchCV übergeben. Dies ist die wichtigste Praxis für zuverlässige Scores.
  • Verwenden Sie stratifizierte Folds bei der Klassifikation. GridSearchCV verwendet für Klassifikatoren automatisch StratifiedKFold, wodurch die Klassenanteile über Folds hinweg erhalten bleiben.

Verwandte Themen

Was this page helpful?