La validation croisée imbriquée estime l'erreur de généralisation d'un modèle, c'est donc un bon moyen de choisir le meilleur modèle à partir d'une liste de modèles candidats et de leurs grilles de paramètres associées. Le poste d'origine est proche de faire un CV imbriqué: plutôt que de faire un seul fractionnement train-test, on devrait plutôt utiliser un deuxième séparateur de validation croisée. C'est-à-dire que l'on "imbrique" un séparateur de validation croisée "interne" à l'intérieur d'un séparateur de validation croisée "externe".
Le séparateur de validation croisée interne est utilisé pour choisir les hyperparamètres. Le séparateur de validation croisée externe fait la moyenne de l'erreur de test sur plusieurs fractionnements de train-test. La moyenne de l'erreur de généralisation sur plusieurs divisions train-test fournit une estimation plus fiable de la précision du modèle sur des données invisibles.
J'ai modifié le code de l'article d'origine pour le mettre à jour vers la dernière version de sklearn
(avec sklearn.cross_validation
remplacé par sklearn.model_selection
et avec 'mean_squared_error'
remplacé par 'neg_mean_squared_error'
), et j'ai utilisé deux KFold
répartiteurs de validation croisée pour sélectionner le meilleur modèle. Pour en savoir plus sur la validation croisée imbriquée, voir le sklearn
l » exemple sur la validation croisée imbriquée .
from sklearn.model_selection import KFold, cross_val_score, GridSearchCV
from sklearn.datasets import make_regression
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
import numpy as np
# `outer_cv` creates 3 folds for estimating generalization error
outer_cv = KFold(3)
# when we train on a certain fold, we use a second cross-validation
# split in order to choose hyperparameters
inner_cv = KFold(3)
# create some regression data
X, y = make_regression(n_samples=1000, n_features=10)
# give shorthand names to models and use those as dictionary keys mapping
# to models and parameter grids for that model
models_and_parameters = {
'svr': (SVR(),
{'C': [0.01, 0.05, 0.1, 1]}),
'rf': (RandomForestRegressor(),
{'max_depth': [5, 10, 50, 100, 200, 500]})}
# we will collect the average of the scores on the 3 outer folds in this dictionary
# with keys given by the names of the models in `models_and_parameters`
average_scores_across_outer_folds_for_each_model = dict()
# find the model with the best generalization error
for name, (model, params) in models_and_parameters.items():
# this object is a regressor that also happens to choose
# its hyperparameters automatically using `inner_cv`
regressor_that_optimizes_its_hyperparams = GridSearchCV(
estimator=model, param_grid=params,
cv=inner_cv, scoring='neg_mean_squared_error')
# estimate generalization error on the 3-fold splits of the data
scores_across_outer_folds = cross_val_score(
regressor_that_optimizes_its_hyperparams,
X, y, cv=outer_cv, scoring='neg_mean_squared_error')
# get the mean MSE across each of outer_cv's 3 folds
average_scores_across_outer_folds_for_each_model[name] = np.mean(scores_across_outer_folds)
error_summary = 'Model: {name}\nMSE in the 3 outer folds: {scores}.\nAverage error: {avg}'
print(error_summary.format(
name=name, scores=scores_across_outer_folds,
avg=np.mean(scores_across_outer_folds)))
print()
print('Average score across the outer folds: ',
average_scores_across_outer_folds_for_each_model)
many_stars = '\n' + '*' * 100 + '\n'
print(many_stars + 'Now we choose the best model and refit on the whole dataset' + many_stars)
best_model_name, best_model_avg_score = max(
average_scores_across_outer_folds_for_each_model.items(),
key=(lambda name_averagescore: name_averagescore[1]))
# get the best model and its associated parameter grid
best_model, best_model_params = models_and_parameters[best_model_name]
# now we refit this best model on the whole dataset so that we can start
# making predictions on other data, and now we have a reliable estimate of
# this model's generalization error and we are confident this is the best model
# among the ones we have tried
final_regressor = GridSearchCV(best_model, best_model_params, cv=inner_cv)
final_regressor.fit(X, y)
print('Best model: \n\t{}'.format(best_model), end='\n\n')
print('Estimation of its generalization error (negative mean squared error):\n\t{}'.format(
best_model_avg_score), end='\n\n')
print('Best parameter choice for this model: \n\t{params}'
'\n(according to cross-validation `{cv}` on the whole dataset).'.format(
params=final_regressor.best_params_, cv=inner_cv))