Especificación explícita de conjuntos de prueba / tren en GridSearchCV

Tengo una pregunta sobre el parámetro cv de GrlearSearchCV de GridSearchCV .

Estoy trabajando con datos que tienen un componente de tiempo, por lo que no creo que la mezcla aleatoria dentro de la validación cruzada de KFold parezca sensata.

En su lugar, quiero especificar explícitamente los puntos de corte para el entrenamiento, la validación y los datos de prueba dentro de un GridSearchCV . ¿Puedo hacer esto?

Para iluminar mejor la pregunta, he aquí cómo lo haría manualmente.

 import numpy as np import pandas as pd from sklearn.linear_model import Ridge np.random.seed(444) index = pd.date_range('2014', periods=60, freq='M') X, y = make_regression(n_samples=60, n_features=3, random_state=444, noise=90.) X = pd.DataFrame(X, index=index, columns=list('abc')) y = pd.Series(y, index=index, name='y') # Train on the first 30 samples, validate on the next 10, test on # the final 10. X_train, X_val, X_test = np.array_split(X, [35, 50]) y_train, y_val, y_test = np.array_split(y, [35, 50]) param_grid = {'alpha': np.linspace(0, 1, 11)} model = None best_param_ = None best_score_ = -np.inf # Manual implementation for alpha in param_grid['alpha']: ridge = Ridge(random_state=444, alpha=alpha).fit(X_train, y_train) score = ridge.score(X_val, y_val) if score > best_score_: best_score_ = score best_param_ = alpha model = ridge print('Optimal alpha parameter: {:0.2f}'.format(best_param_)) print('Best score (on validation data): {:0.2f}'.format(best_score_)) print('Test set score: {:.2f}'.format(model.score(X_test, y_test))) # Optimal alpha parameter: 1.00 # Best score (on validation data): 0.64 # Test set score: 0.22 

El proceso aquí es:

  • Tanto para X como para Y, quiero un conjunto de entrenamiento, un conjunto de validación y un conjunto de pruebas. El conjunto de entrenamiento son las primeras 35 muestras en la serie de tiempo. El conjunto de validación son las siguientes 15 muestras. El conjunto de prueba es el 10 final.
  • Los conjuntos de tren y validación se usan para determinar el parámetro alpha óptimo dentro de la regresión Ridge. Aquí pruebo alpha de (0.0, 0.1, …, 0.9, 1.0).
  • El conjunto de pruebas se mantiene para las pruebas “reales” como datos invisibles.

De todos modos … Parece que estoy buscando hacer algo como esto, pero no estoy seguro de qué pasar con el cv aquí:

 from sklearn.model_selection import GridSearchCV grid_search = GridSearchCV(Ridge(random_state=444), param_grid, cv= ???) grid_search.fit(...?) 

Los documentos, que estoy teniendo problemas para interpretar, especifican:

cv : int, generador de validación cruzada o un iterable, opcional

Determina la estrategia de división de validación cruzada. Las posibles entradas para cv son:

  • Ninguno, para utilizar la validación cruzada de 3 veces por defecto,
  • entero, para especificar el número de pliegues en un KFold (estratificado),
  • Un objeto para ser utilizado como un generador de validación cruzada.
  • Un tren de rendimiento iterable, prueba de fracturas.

Para entradas enteras / Ninguna, si el estimador es un clasificador y y es binario o multiclase, se usa StratifiedKFold. En todos los demás casos, se usa KFold.

Como dijo @MaxU, es mejor dejar que GridSearchCV maneje las divisiones, pero si desea imponer la división como se ha establecido en la pregunta, puede usar el PredefinedSplit que hace esto.

Entonces necesitas hacer los siguientes cambios en tu código.

 # Here X_test, y_test is the untouched data # Validation data (X_val, y_val) is currently inside X_train, which will be split using PredefinedSplit inside GridSearchCV X_train, X_test = np.array_split(X, [50]) y_train, y_test = np.array_split(y, [50]) # The indices which have the value -1 will be kept in train. train_indices = np.full((35,), -1, dtype=int) # The indices which have zero or positive values, will be kept in test test_indices = np.full((15,), 0, dtype=int) test_fold = np.append(train_indices, test_indices) print(test_fold) # OUTPUT: array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) from sklearn.model_selection import PredefinedSplit ps = PredefinedSplit(test_fold) # Check how many splits will be done, based on test_fold ps.get_n_splits() # OUTPUT: 1 for train_index, test_index in ps.split(): print("TRAIN:", train_index, "TEST:", test_index) # OUTPUT: ('TRAIN:', array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]), 'TEST:', array([35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49])) # And now, send this `ps` to cv param in GridSearchCV from sklearn.model_selection import GridSearchCV grid_search = GridSearchCV(Ridge(random_state=444), param_grid, cv=ps) # Here, send the X_train and y_train grid_search.fit(X_train, y_train) 

El X_train, y_train enviado a fit() se dividirá en train y test (val en su caso) usando la división que definimos y, por lo tanto, Ridge se entrenará con datos originales de los índices [0:35] y se probará el [35 : 50].

Espero que esto borre el trabajo.

¿Has probado TimeSeriesSplit ?

Se hizo explícitamente para esto.

 tscv = TimeSeriesSplit(n_splits=3) grid_search = GridSearchCV(clf, param_grid, cv=tscv.split(X)) 

En los datos de series de tiempo, Kfold no es un enfoque correcto, ya que kfold cv te barajará los datos y perderás el patrón dentro de la serie. Aquí hay un enfoque

 import xgboost as xgb from sklearn.model_selection import TimeSeriesSplit, GridSearchCV import numpy as np X = np.array([[4, 5, 6, 1, 0, 2], [3.1, 3.5, 1.0, 2.1, 8.3, 1.1]]).T y = np.array([1, 6, 7, 1, 2, 3]) tscv = TimeSeriesSplit(n_splits=2) model = xgb.XGBRegressor() param_search = {'max_depth' : [3, 5]} my_cv = TimeSeriesSplit(n_splits=2).split(X) gsearch = GridSearchCV(estimator=model, cv=my_cv, param_grid=param_search) gsearch.fit(X, y) 

referencia – ¿Cómo uso un TimeSeriesSplit con un objeto GridSearchCV para ajustar un modelo en scikit-learn?

Creo que la forma más fácil es usar un número entero para cv – parámetro:

 grid_search = GridSearchCV(Ridge(random_state=444), param_grid, cv=5) 

GridSearchCV esto, cuando utilice GridSearchCV , no desea dividir sus datos en conjuntos de datos de entrenamiento y prueba. GridSearchCV lo hará por usted automáticamente:

 grid_search.fit(X_all_data, y_all_data) 

este utilizará el método (Stratified)KFold para dividir sus datos en conjuntos de prueba y entrenamiento 5 veces y elegirá los parámetros óptimos especificados en el diccionario param_grid .

También es posible que desee usar GroupShuffleSplit y especificar grupos para respetar los grupos dividiendo :