# fundamentos-de-scikit-learn
Fundamentos de Scikit-learn
Volver a la portada del cursoGrid Search para Optimización
Sección 5 · Tuning de Hiperparámetros
Grid Search para optimización
Un RandomForestClassifier trae decenas de perillas: cuántos árboles, qué tan profundos, cuántas entregas mínimas por hoja. Los valores por omisión son razonables, pero no están pensados para las entregas de PyEnvíos. Probar combinaciones a mano, mirando el test cada vez, es la forma más rápida de engañarse. GridSearchCV automatiza esa búsqueda con validación cruzada y deja el test intacto para el final.
Parámetros e hiperparámetros
Los parámetros los aprende el modelo con fit: los coeficientes de la regresión logística, los cortes de cada árbol. Terminan en guion bajo (coef_). Los hiperparámetros los eliges tú antes de entrenar y se pasan al constructor: n_estimators, max_depth, min_samples_leaf, el C de la logística. Buscar hiperparámetros es entrenar muchas veces con valores distintos y quedarse con los que mejor generalizan.
La rejilla y los nombres paso__parametro
El modelo vive dentro de un Pipeline con pre, así que para llegar a un hiperparámetro hay que decir en qué paso está: el nombre del paso, dos guiones bajos y el hiperparámetro. pipe.get_params().keys() lista todos los nombres válidos.
rf = Pipeline([("pre", pre),
("modelo", RandomForestClassifier(random_state=42))])
grid = {
"modelo__n_estimators": [100, 300],
"modelo__max_depth": [4, 8, None],
"modelo__min_samples_leaf": [1, 5, 20],
}
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
gs = GridSearchCV(rf, grid, cv=skf, scoring="roc_auc", n_jobs=-1)
gs.fit(X_tr, y_tr)
La rejilla tiene $2 \times 3 \times 3 = 18$ combinaciones. Cada una se evalúa con 5 pliegues, así que se entrenan $18 \times 5 = 90$ modelos, más uno final. El costo es el producto de todas las listas: agregar un cuarto hiperparámetro con 4 valores lo multiplica por 4. n_jobs=-1 reparte los ajustes entre todos los núcleos. Se usa scoring="roc_auc" porque, como viste en métricas, la exactitud premia al modelo que nunca avisa.
Leer el resultado
print(gs.best_params_)
print(round(gs.best_score_, 4)) # 0.9354
res = pd.DataFrame(gs.cv_results_)
cols = ["params", "mean_test_score", "rank_test_score"]
print(res[cols].sort_values("rank_test_score").head())
best_params_: la mejor combinación,max_depth=None,min_samples_leaf=5,n_estimators=100.best_score_: el AUC medio de validación cruzada de esa combinación, 0.9354. No es una cifra de test.cv_results_: un diccionario con una fila por combinación (params,mean_test_score,std_test_score,rank_test_score, tiempos y la nota de cada pliegue). En DataFrame se ordena por rango. La segunda combinación (profundidad 8, hoja 1, 300 árboles) da 0.9336: la diferencia con la primera es menor que la variación entre pliegues.
refit y la evaluación final
Con refit=True (el valor por omisión), al terminar la búsqueda GridSearchCV vuelve a entrenar la mejor combinación con todo X_tr y la guarda en best_estimator_. Por eso el propio objeto gs sabe predecir. Ahora, y solo ahora, se mira el test, una vez:
proba = gs.predict_proba(X_te)[:, 1]
print(round(roc_auc_score(y_te, proba), 4)) # 0.9108
print(round(gs.score(X_te, y_te), 4)) # 0.9108 (AUC)
El AUC de test (0.9108) es menor que el de validación cruzada (0.9354). Es normal: al elegir la mejor de 18 combinaciones, el best_score_ queda un poco optimista. Fíjate en la segunda línea: gs.score usa el scoring de la búsqueda, así que devuelve AUC, no exactitud.
No siempre la búsqueda cambia mucho. Con la regresión logística y {"modelo__C": [0.01, 0.1, 1, 10, 100]}, el mejor es C=1 (el valor por omisión) con 0.9317, y desde C=0.1 la curva es casi plana (0.9313 a 0.9317). Una búsqueda que confirma el valor por omisión también es información.
Trampas comunes
- Buscar con el test. Si eliges hiperparámetros mirando el test, su cifra deja de ser una estimación honesta.
- Preprocesar fuera del pipeline. Escalar
X_trcompleto antes de la búsqueda filtra información de cada pliegue de validación al de entrenamiento. - Reportar
best_score_como resultado final. Es una cifra de selección; la del modelo es la del test. - Rejillas enormes. 4 valores en 4 hiperparámetros ya son 256 combinaciones y 1280 ajustes; para eso está la búsqueda aleatoria de la próxima lección.
- Nombres mal escritos.
max_depthsin el prefijomodelo__lanzaValueError: Invalid parameter.
El notebook busqueda-de-hiperparametros repite esta búsqueda paso a paso con la salida a la vista. Empieza con rejillas pequeñas y valores espaciados en escala logarítmica; afina solo alrededor de lo que funcionó.