Grid Search per l'ottimizzazione degli iperparametri in Python
Scopri come usare GridSearchCV e RandomizedSearchCV in Python per ottimizzare gli iperparametri dei modelli di machine learning con scikit-learn.
L'ottimizzazione degli iperparametri è il processo di ricerca dei valori di configurazione per un modello di machine learning che non vengono appresi dai dati — come la profondità di un albero decisionale, la forza di regolarizzazione di una regressione logistica o il numero di neuroni in una rete neurale. La grid search è l'approccio più diretto: si definisce un insieme discreto di valori per ciascun iperparametro, si prova ogni combinazione e si mantiene quella migliore.
Questa pagina tratta:
- Cosa sono gli iperparametri e perché sono importanti
- Come
GridSearchCVesplora esaustivamente una griglia di parametri - Come leggere
cv_results_e capire cosa è stato testato - L'uso di
n_jobs=-1per parallelizzare la ricerca RandomizedSearchCVcome alternativa più veloce per griglie di grandi dimensioni- La combinazione della grid search con una
Pipelineper evitare la fuga di dati - Quando usare la grid search rispetto ad alternative più veloci
Tutti gli esempi usano i dataset integrati di scikit-learn, quindi puoi eseguirli immediatamente.
Cosa sono gli iperparametri?
Ogni modello di machine learning ha due tipi di parametri:
- I parametri del modello vengono appresi automaticamente durante l'addestramento (es. i pesi in una rete neurale, le soglie di divisione in un albero decisionale).
- Gli iperparametri vengono impostati da te prima che l'addestramento inizi. Controllano il processo di apprendimento stesso.
Scegliere iperparametri errati può lasciare un modello capace con prestazioni gravemente insufficienti. Ad esempio, un albero decisionale senza limite di profondità vada in overfitting sui dati di addestramento; uno con un limite di profondità pari a 1 andrà in underfitting. Il limite corretto dipende dai tuoi dati, e la grid search lo trova in modo sistematico anziché per tentativi.
Come funziona GridSearchCV
GridSearchCV di scikit-learn combina due idee:
- Enumerazione della griglia — genera ogni combinazione dei valori degli iperparametri che specifichi.
- Validazione incrociata — per ciascuna combinazione esegue la k-fold cross-validation (vedi Cross-Validation in Python) e registra il punteggio medio.
Al termine della ricerca, GridSearchCV memorizza la migliore combinazione e ridefinisce automaticamente un modello con quelle impostazioni sull'intero set di addestramento.
Il numero di addestramento è (combinazioni) × (fold di CV). Una griglia con 3 × 3 × 3 = 27 combinazioni e cv=5 esegue 135 addestramento — gestibile per modelli veloci, costoso per quelli lenti.
Esempio base di GridSearchCV
L'esempio seguente ottimizza un Decision Tree Classifier sul dataset Iris.
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_))Cosa fa ciascun argomento:
| Argomento | Scopo |
|---|---|
estimator | Il modello da ottimizzare. Funziona con qualsiasi estimator di scikit-learn. |
param_grid | Dict che mappa i nomi dei parametri a liste di valori candidati. |
cv | Numero di fold nella cross-validation (5 è un valore predefinito comune). |
scoring | Metrica da ottimizzare. Per impostazione predefinita usa il metodo .score() dell'estimator. |
n_jobs | Numero di job paralleli. -1 usa tutti i core CPU disponibili. |
Output tipico:
Best parameters: {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV accuracy: 0.967Lettura di cv_results_
Dopo l'addestramento, grid_search.cv_results_ è un dict di array — una voce per ogni combinazione testata. Le chiavi più utili sono:
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))Colonne chiave:
mean_test_score— il punteggio CV medio su tutti i fold per quella combinazione.std_test_score— deviazione standard; un valore elevato indica che il punteggio è instabile tra i fold.rank_test_score— il rango 1 è il vincitore.
Opzioni di scoring
Per impostazione predefinita GridSearchCV ottimizza la metrica predefinita dell'estimator. Puoi specificare qualsiasi scorer integrato o uno personalizzato:
# 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,
)Usare una Pipeline per prevenire la fuga di dati
Quando il preprocessamento dipende dai dati di addestramento (scaling, imputazione, selezione delle feature), devi addestrare il preprocessore solo sui fold di addestramento — mai sull'intero dataset prima della divisione. Una Pipeline gestisce questo automaticamente e funziona perfettamente con 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_))La sintassi con il doppio underscore (svc__C) è la chiave: indica a scikit-learn di passare C allo step svc all'interno della pipeline. Senza una pipeline, scalare l'intero dataset prima della cross-validation farebbe trapelare informazioni dal fold di test nello scaler, producendo un punteggio eccessivamente ottimistico.
RandomizedSearchCV: più veloce per griglie grandi
La grid search esaustiva diventa impraticabile quando ciascun iperparametro ha molti valori candidati. RandomizedSearchCV campiona un numero fisso di combinazioni casuali invece di testarle tutte:
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:
GridSearchCV | RandomizedSearchCV | |
|---|---|---|
| Strategia di ricerca | Esaustiva (tutte le combinazioni) | Campionamento casuale |
| Riproducibilità | Completamente deterministica | Imposta random_state |
| Ideale per | Griglie piccole e ben definite | Spazi di ricerca ampi |
| Distribuzioni continue | Non supportate | Supportate tramite scipy.stats |
| Garantisce il miglior risultato | Sì (all'interno della griglia) | No, ma spesso vicino |
Per griglie grandi, RandomizedSearchCV con n_iter=50–100 trova frequentemente una soluzione quasi ottimale in una frazione del tempo di calcolo.
Fare previsioni con il modello migliore
Dopo l'addestramento, GridSearchCV si comporta come un estimator normale. Il suo attributo best_estimator_ contiene il modello ri-addestrato:
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)Importante: mantieni sempre un set di test separato e mai visto durante la grid search. Il punteggio di cross-validation all'interno di GridSearchCV stima la generalizzazione, ma la vera valutazione finale dovrebbe essere effettuata su dati che la ricerca non ha mai toccato.
Quando usare la grid search
La grid search è una scelta predefinita solida quando:
- Hai un modello di piccole o medie dimensioni che si addestra rapidamente (da secondi a pochi minuti per addestramento).
- Sai all'incirca quali iperparametri contano di più e hai un insieme sensato di valori candidati.
- La riproducibilità e l'esaustività sono importanti.
Considera alternative quando:
- La griglia è grande (molti parametri con molti valori) — usa prima
RandomizedSearchCVper restringere lo spazio, poi affina conGridSearchCV. - L'addestramento è costoso (deep learning, grandi ensemble) — librerie di ottimizzazione bayesiana come
scikit-optimizeoOptunafanno scelte più intelligenti del campionamento casuale. - Vuoi un arresto automatico — le strategie di dimezzamento (
HalvingGridSearchCV) eliminano i candidati con prestazioni basse in anticipo e richiedono meno addestramento complessivi.
Consigli pratici
- Inizia in modo grossolano. Usa una griglia piccola con valori distribuiti su ordini di grandezza (es.
C: [0.01, 0.1, 1, 10, 100]). Una volta trovata una regione promettente, affina con una griglia più fine. - Osserva le deviazioni standard. Se
std_test_scoreè elevato, il modello è sensibile alla particolare divisione dei dati. Considera di aumentarecvo raccogliere più dati. - Imposta
n_jobs=-1per usare tutti i core CPU — non costa nulla e spesso offre un'accelerazione da 4 a 8 volte su una macchina moderna. - Abbina a una Pipeline. Racchiudi sempre il preprocessamento e il modello insieme in una
Pipelineprima di passarla aGridSearchCV. Questa è la pratica più importante per ottenere punteggi affidabili. - Usa fold stratificati per la classificazione.
GridSearchCVusaStratifiedKFoldautomaticamente per i classificatori, preservando le proporzioni delle classi tra i fold.
Argomenti correlati
- Cross-Validation in Python — comprendi la valutazione k-fold che alimenta
GridSearchCV - Decision Tree Classifier — un modello comune da ottimizzare con la grid search
- Logistic Regression — un altro modello veloce adatto alla grid search
- K-Nearest Neighbors —
n_neighborsemetricsono obiettivi classici della grid search - Linear Regression — ottimizzazione degli iperparametri per varianti di regressione regolarizzata