html
Optimización del Ajuste de Modelos de Aprendizaje Automático: Adoptando RandomizedSearchCV en Lugar de GridSearchCV
En el dinámico mundo del aprendizaje automático, el ajuste de modelos es fundamental para lograr un rendimiento óptimo. Tradicionalmente, GridSearchCV ha sido el método preferido para la optimización de hiperparámetros. Sin embargo, a medida que los conjuntos de datos crecen en tamaño y complejidad, GridSearchCV puede convertirse en un cuello de botella intensivo en recursos. Entra RandomizedSearchCV: una alternativa más eficiente que ofrece resultados comparables con una sobrecarga computacional significativamente reducida. Este artículo profundiza en las complejidades de ambos métodos, destacando las ventajas de adoptar RandomizedSearchCV para proyectos de datos a gran escala.
Tabla de Contenidos
- Entendiendo GridSearchCV y sus Limitaciones
- Introduciendo RandomizedSearchCV
- Análisis Comparativo: GridSearchCV vs. RandomizedSearchCV
- Preparación y Preprocesamiento de Datos
- Construcción de Modelos y Ajuste de Hiperparámetros
- Resultados y Evaluación del Rendimiento
- Conclusión: Cuándo Elegir RandomizedSearchCV
- Recursos y Lecturas Adicionales
Entendiendo GridSearchCV y sus Limitaciones
GridSearchCV es una herramienta poderosa en scikit-learn utilizada para el ajuste de hiperparámetros. Realiza una búsqueda exhaustiva a través de un conjunto predefinido de hiperparámetros para identificar la combinación que ofrece el mejor rendimiento del modelo basado en una métrica especificada.
Características Clave:
- Búsqueda Exhaustiva: Evalúa todas las combinaciones posibles en la cuadrícula de parámetros.
- Integración de Validación Cruzada: Utiliza validación cruzada para asegurar la robustez del modelo.
- Selección del Mejor Estimador: Devuelve el mejor modelo basado en las métricas de rendimiento.
Limitaciones:
- Intensivo Computacionalmente: A medida que la cuadrícula de parámetros crece, el número de combinaciones aumenta exponencialmente, lo que lleva a tiempos de cómputo más largos.
- Consumo de Memoria: Manejar grandes conjuntos de datos con numerosas combinaciones de parámetros puede sobrecargar los recursos del sistema.
- Retornos Decrecientes: No todas las combinaciones de parámetros contribuyen significativamente al rendimiento del modelo, haciendo que la búsqueda exhaustiva sea ineficiente.
Ejemplo: Procesar un conjunto de datos con más de 129,000 registros usando GridSearchCV tomó aproximadamente 12 horas, incluso con hardware robusto. Esto demuestra su impracticabilidad para aplicaciones a gran escala.
Introduciendo RandomizedSearchCV
RandomizedSearchCV ofrece una alternativa pragmática a GridSearchCV al muestrear un número fijo de combinaciones de hiperparámetros de las distribuciones especificadas, en lugar de evaluar todas las combinaciones posibles.
Ventajas:
- Eficiencia: Reduce significativamente el tiempo de cómputo al limitar el número de evaluaciones.
- Flexibilidad: Permite especificar distribuciones para cada hiperparámetro, lo que permite un muestreo más diverso.
- Escalabilidad: Mejor adaptado para grandes conjuntos de datos y modelos complejos.
Cómo Funciona:
RandomizedSearchCV selecciona aleatoriamente un subconjunto de combinaciones de hiperparámetros, las evalúa utilizando validación cruzada y identifica la combinación de mejor rendimiento basada en la métrica elegida.
Análisis Comparativo: GridSearchCV vs. RandomizedSearchCV
Aspecto
GridSearchCV
RandomizedSearchCV
Método de Búsqueda
Exhaustivo
Muestreo Aleatorio
Tiempo de Cómputo
Alto
Bajo a Medio
Uso de Recursos
Alto
Moderado a Bajo
Rendimiento
Potencialmente el Mejor
Comparable con Menos Esfuerzo
Flexibilidad
Combinaciones Fijas
Muestreo Basado en Probabilidad
Visualización: En la práctica, RandomizedSearchCV puede reducir el tiempo de ajuste de modelos de horas a meros minutos sin una caída significativa en el rendimiento.
Preparación y Preprocesamiento de Datos
Un preprocesamiento efectivo de los datos sienta las bases para un entrenamiento exitoso del modelo. A continuación, se presenta una guía paso a paso basada en el Notebook de Jupyter proporcionado.
Cargando el Conjunto de Datos
El conjunto de datos utilizado es Airline Passenger Satisfaction de Kaggle. Contiene 5,000 registros con 23 características relacionadas con las experiencias de los pasajeros y los niveles de satisfacción.
123456
import pandas as pd import seaborn as sns # Loading the small datasetdata = pd.read_csv('Airline2_tiny.csv')print(data.shape) # Output: (4999, 23)
Manejando Datos Faltantes
Datos Numéricos
Los valores numéricos faltantes se imputan utilizando la estrategia de media.
1234567
import numpy as npfrom sklearn.impute import SimpleImputer imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')numerical_cols = list(np.where((X.dtypes == np.int64) | (X.dtypes == np.float64))[0])imp_mean.fit(X.iloc[:, numerical_cols])X.iloc[:, numerical_cols] = imp_mean.transform(X.iloc[:, numerical_cols])
Datos Categóricos
Los valores categóricos faltantes se imputan utilizando la estrategia de más frecuente.
1234
imp_mode = SimpleImputer(missing_values=np.nan, strategy='most_frequent')string_cols = list(np.where((X.dtypes == 'object'))[0])imp_mode.fit(X.iloc[:, string_cols])X.iloc[:, string_cols] = imp_mode.transform(X.iloc[:, string_cols])
Codificando Variables Categóricas
Las características categóricas se codifican utilizando una combinación de One-Hot Encoding y Label Encoding basada en el número de categorías únicas.
1234567891011121314151617181920212223242526272829
from sklearn.compose import ColumnTransformerfrom sklearn.preprocessing import OneHotEncoder, LabelEncoder def OneHotEncoderMethod(indices, data): columnTransformer = ColumnTransformer([('encoder', OneHotEncoder(), indices )], remainder='passthrough') return columnTransformer.fit_transform(data) def LabelEncoderMethod(series): le = LabelEncoder() le.fit(series) return le.transform(series) def EncodingSelection(X, threshold=10): string_cols = list(np.where((X.dtypes == 'object'))[0]) one_hot_encoding_indices = [] for col in string_cols: length = len(pd.unique(X[X.columns[col]])) if length == 2 or length > threshold: X[X.columns[col]] = LabelEncoderMethod(X[X.columns[col]]) else: one_hot_encoding_indices.append(col) X = OneHotEncoderMethod(one_hot_encoding_indices, X) return X # Apply encodingX = EncodingSelection(X)print(X.shape) # Output: (4999, 24)
Selección de Características
Seleccionar las características más relevantes mejora el rendimiento del modelo y reduce la complejidad.
12345678910111213
from sklearn.feature_selection import SelectKBest, chi2from sklearn.preprocessing import MinMaxScaler kbest = SelectKBest(score_func=chi2, k='all')MMS = MinMaxScaler()K_features = 10 x_temp = MMS.fit_transform(X)x_temp = kbest.fit(x_temp, y)best_features = np.argsort(x_temp.scores_)[-K_features:]features_to_delete = np.argsort(x_temp.scores_)[:-K_features]X = np.delete(X, features_to_delete, axis=1)print(X.shape) # Output: (4999, 10)
División de Entrenamiento y Prueba
Dividir el conjunto de datos asegura que el modelo se evalúe con datos no vistos, facilitando métricas de rendimiento imparciales.
12345
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=1)print(X_train.shape) # Output: (3999, 10)print(X_test.shape) # Output: (1000, 10)
Escalado de Características
Escalar las características asegura que todas las características contribuyan por igual al rendimiento del modelo.
123456
from sklearn.preprocessing import StandardScaler sc = StandardScaler(with_mean=False)sc.fit(X_train)X_train = sc.transform(X_train)X_test = sc.transform(X_test)
Construcción de Modelos y Ajuste de Hiperparámetros
Con los datos preprocesados, es hora de construir y optimizar varios modelos de aprendizaje automático utilizando RandomizedSearchCV.
K-Nearest Neighbors (KNN)
KNN es un algoritmo de aprendizaje basado en instancias, simple.
12345678910111213141516171819202122232425
from sklearn.neighbors import KNeighborsClassifierfrom sklearn.model_selection import RandomizedSearchCV, StratifiedKFold model = KNeighborsClassifier() params = { 'n_neighbors': [4, 5, 6, 7], 'leaf_size': [1, 3, 5], 'algorithm': ['auto', 'kd_tree'], 'weights': ['uniform', 'distance']} cv = StratifiedKFold(n_splits=2)random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: KNeighborsClassifier(leaf_size=1)print("Best score", random_search_cv.best_score_) # Output: 0.8774673417446253
Regresión Logística
Un modelo probabilístico utilizado para tareas de clasificación binaria.
12345678910111213141516171819202122
from sklearn.linear_model import LogisticRegression model = LogisticRegression() params = { 'solver': ['newton-cg', 'lbfgs', 'liblinear'], 'penalty': ['l1', 'l2'], 'C': [100, 10, 1.0, 0.1, 0.01]} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: LogisticRegression(C=0.01)print("Best score", random_search_cv.best_score_) # Output: 0.8295203666687819
Gaussian Naive Bayes (GaussianNB)
Un clasificador probabilístico simple pero efectivo basado en el teorema de Bayes.
12345678
from sklearn.naive_bayes import GaussianNBfrom sklearn.metrics import accuracy_score, classification_report model_GNB = GaussianNB()model_GNB.fit(X_train, y_train)y_pred = model_GNB.predict(X_test)print(accuracy_score(y_pred, y_test)) # Output: 0.84print(classification_report(y_pred, y_test))
Salida:
12345678
precision recall f1-score support 0 0.86 0.86 0.86 564 1 0.82 0.81 0.82 436 accuracy 0.84 1000 macro avg 0.84 0.84 0.84 1000weighted avg 0.84 0.84 0.84 1000
Support Vector Machine (SVM)
Un clasificador robusto efectivo en espacios de alta dimensionalidad.
123456789101112131415161718192021222324
from sklearn.svm import SVC model = SVC() params = { 'kernel': ['linear', 'poly', 'rbf', 'sigmoid'], 'C': [1, 5, 10], 'degree': [3, 8], 'coef0': [0.01, 10, 0.5], 'gamma': ['auto', 'scale']} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: SVC(C=10, coef0=0.5, degree=8)print("Best score", random_search_cv.best_score_) # Output: 0.9165979221213969
Árbol de Decisión
Un modelo jerárquico que toma decisiones basadas en divisiones de características.
123456789101112131415161718192021
from sklearn.tree import DecisionTreeClassifier model = DecisionTreeClassifier() params = { 'max_leaf_nodes': list(range(2, 100)), 'min_samples_split': [2, 3, 4]} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: DecisionTreeClassifier(max_leaf_nodes=30, min_samples_split=4)print("Best score", random_search_cv.best_score_) # Output: 0.9069240944070234
Bosque Aleatorio
Un método de ensamblaje que aprovecha múltiples árboles de decisión para mejorar el rendimiento predictivo.
12345678910111213141516171819202122232425
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier() params = { 'bootstrap': [True], 'max_depth': [80, 90, 100, 110], 'max_features': [2, 3], 'min_samples_leaf': [3, 4, 5], 'min_samples_split': [8, 10, 12], 'n_estimators': [100, 200, 300, 1000]} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: RandomForestClassifier(max_leaf_nodes=96, min_samples_split=3)print("Best score", random_search_cv.best_score_) # Output: 0.9227615146702333
AdaBoost
Un método de ensamblaje de boosting que combina múltiples aprendices débiles para formar un aprendiz fuerte.
123456789101112131415161718192021
from sklearn.ensemble import AdaBoostClassifier model = AdaBoostClassifier() params = { 'n_estimators': np.arange(10, 300, 10), 'learning_rate': [0.01, 0.05, 0.1, 1]} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: AdaBoostClassifier(learning_rate=0.1, n_estimators=200)print("Best score", random_search_cv.best_score_) # Output: 0.8906331862757826
XGBoost
Un marco optimizado de gradient boosting conocido por su rendimiento y velocidad.
1234567891011121314151617181920212223242526272829303132333435
import xgboost as xgbfrom sklearn.metrics import accuracy_score, classification_report model = xgb.XGBClassifier() params = { 'min_child_weight': [1, 5, 10], 'gamma': [0.5, 1, 1.5, 2, 5], 'subsample': [0.6, 0.8, 1.0], 'colsample_bytree': [0.6, 0.8, 1.0], 'max_depth': [3, 4, 5], 'n_estimators': [100, 500, 1000], 'learning_rate': [0.01, 0.3, 0.5, 0.1], 'reg_lambda': [1, 2]} random_search_cv = RandomizedSearchCV( estimator=model, param_distributions=params, verbose=1, cv=cv, scoring='f1', n_jobs=-1) random_search_cv.fit(X_train, y_train)print("Best Estimator", random_search_cv.best_estimator_) # Output: XGBClassifier with best parametersprint("Best score", random_search_cv.best_score_) # Output: 0.922052180776655 # Model Evaluationmodel_best = random_search_cv.best_estimator_model_best.fit(X_train, y_train)y_pred = model_best.predict(X_test)print(accuracy_score(y_pred, y_test)) # Output: 0.937print(classification_report(y_pred, y_test))
Salida:
12345678
precision recall f1-score support 0 0.96 0.93 0.95 583 1 0.91 0.94 0.93 417 accuracy 0.94 1000 macro avg 0.93 0.94 0.94 1000weighted avg 0.94 0.94 0.94 1000
Resultados y Evaluación del Rendimiento
La efectividad de RandomizedSearchCV es evidente a partir del rendimiento de los modelos:
- KNN logró un F1-score de ~0.877.
- Regresión Logística entregó un F1-score de ~0.830.
- GaussianNB mantuvo una precisión del 84%.
- SVM destacó con un impresionante F1-score de ~0.917.
- Árbol de Decisión obtuvo un F1-score de ~0.907.
- Bosque Aleatorio lideró con un F1-score de ~0.923.
- AdaBoost logró un F1-score de ~0.891.
- XGBoost sobresalió con un F1-score de ~0.922 y una precisión del 93.7%.
Observaciones Clave:
- RandomForestClassifier y XGBoost demostraron un rendimiento superior.
- RandomizedSearchCV redujo efectivamente el tiempo de cómputo de más de 12 horas (GridSearchCV) a meros minutos sin comprometer la precisión del modelo.
Conclusión: Cuándo Elegir RandomizedSearchCV
Si bien GridSearchCV ofrece un ajuste exhaustivo de hiperparámetros, sus demandas computacionales pueden ser prohibitivas para conjuntos de datos grandes. RandomizedSearchCV surge como una solución pragmática, equilibrando eficiencia y rendimiento. Es particularmente ventajoso cuando:
- El Tiempo es una Restricción: Se requiere un ajuste rápido del modelo.
- Los Recursos Computacionales son Limitados: Reduce la carga sobre los recursos del sistema.
- Espacios de Hiperparámetros de Alta Dimensionalidad: Simplifica el proceso de búsqueda.
Adoptar RandomizedSearchCV puede optimizar el flujo de trabajo de aprendizaje automático, permitiendo que los profesionales se enfoquen en la interpretación y despliegue del modelo en lugar de en procedimientos de ajuste prolongados.
Recursos y Lecturas Adicionales
- Documentación de Scikit-learn: RandomizedSearchCV
- Kaggle: Conjunto de Datos de Satisfacción de Pasajeros de Aerolínea
- Documentación Oficial de XGBoost
- Una Guía Completa para el Ajuste de Hiperparámetros
Al aprovechar RandomizedSearchCV, los profesionales de aprendizaje automático pueden lograr un ajuste de modelo eficiente y efectivo, asegurando soluciones escalables y de alto rendimiento en aplicaciones impulsadas por datos.