Je souhaite implémenter un arrêt anticipé avec Keras et sklean's GridSearchCV
.
L'exemple de code de travail ci-dessous est modifié à partir de How to Grid Search Hyperparameters for Deep Learning Models in Python With Keras . L'ensemble de données peut être téléchargé à partir d'ici .
La modification ajoute la classe de rappel Keras EarlyStopping
pour éviter un ajustement excessif. Pour que cela soit efficace, il faut le monitor='val_acc'
argument pour surveiller la précision de la validation. Pour val_acc
pour être disponible KerasClassifier
nécessite le validation_split=0.1
pour générer une précision de validation, sinon EarlyStopping
lève RuntimeWarning: Early stopping requires val_acc available!
. Noter la FIXME:
commentaire de code!
Notez que nous pourrions remplacer val_acc
par val_loss
!
Question: Comment puis-je utiliser l'ensemble de données de validation croisée généré par l'algorithme GridSearchCV
k-fold au lieu de perdre 10% de la des données de formation pour un ensemble de validation d'arrêt précoce?
# Use scikit-learn to grid search the learning rate and momentum
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
from keras.optimizers import SGD
# Function to create model, required for KerasClassifier
def create_model(learn_rate=0.01, momentum=0):
# create model
model = Sequential()
model.add(Dense(12, input_dim=8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
optimizer = SGD(lr=learn_rate, momentum=momentum)
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
return model
# Early stopping
from keras.callbacks import EarlyStopping
stopper = EarlyStopping(monitor='val_acc', patience=3, verbose=1)
# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)
# load dataset
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# create model
model = KerasClassifier(
build_fn=create_model,
epochs=100, batch_size=10,
validation_split=0.1, # FIXME: Instead use GridSearchCV k-fold validation data.
verbose=2)
# define the grid search parameters
learn_rate = [0.01, 0.1]
momentum = [0.2, 0.4]
param_grid = dict(learn_rate=learn_rate, momentum=momentum)
grid = GridSearchCV(estimator=model, param_grid=param_grid, verbose=2, n_jobs=1)
# Fitting parameters
fit_params = dict(callbacks=[stopper])
# Grid search.
grid_result = grid.fit(X, Y, **fit_params)
# summarize results
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in Zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
[Réponse après modification et clarification de la question:]
Avant de se précipiter sur les problèmes de mise en œuvre, il est toujours bon de prendre le temps de réfléchir à la méthodologie et à la tâche elle-même; sans doute, mêler l'arrêt précoce à la procédure de validation croisée est pas une bonne idée.
Faisons un exemple pour mettre en évidence l'argument.
Supposons que vous utilisez en effet un arrêt précoce avec 100 époques et une validation croisée (CV) 5 fois pour la sélection d'hyperparamètre. Supposons également que vous vous retrouvez avec un ensemble d'hyperparamètres X offrant les meilleures performances, par exemple une précision de classification binaire de 89,3%.
Supposons maintenant que votre deuxième ensemble d'hyperparamètres, Y, donne une précision de 89,2%. En examinant de près les plis CV individuels, vous voyez que, dans votre meilleur cas X, 3 des 5 plis CV ont épuisé les 100 époques maximum, tandis que dans les 2 autres arrêts précoces, disons dans 89 et 93 époques respectivement.
Imaginez maintenant qu'en examinant votre deuxième meilleur ensemble Y, vous voyez que 4 des 5 plis de CV ont épuisé les 100 époques, tandis que le 5e s'est arrêté tôt à environ 80 époques.
Quelle serait votre conclusion d'une telle expérience?
Sans doute, vous vous seriez retrouvé dans une situation non concluante ; d'autres expériences pourraient révéler quel est en fait le meilleur ensemble d'hyperparamètres, à condition bien sûr que vous ayez pensé à examiner ces détails des résultats en premier lieu. Et inutile de dire que si tout cela a été automatisé via un rappel, vous avez peut-être manqué votre meilleur modèle malgré le fait que vous l'auriez réellement essayé .
L'idée de CV dans son ensemble est implicitement basée sur l'argument "toutes les autres étant égales" (ce qui bien sûr n'est jamais vrai dans la pratique, mais uniquement approché de la meilleure façon possible). Si vous pensez que le nombre d'époques devrait être un hyperparamètre, incluez-le simplement explicitement dans votre CV en tant que tel, plutôt que de l'insérer par la porte arrière de l'arrêt précoce, ce qui pourrait compromettre l'ensemble du processus (sans parler de cet arrêt précoce possède lui-même un hyperparamètre , patience
).
Ne pas mélanger ces deux techniques ne signifie bien sûr pas que vous ne pouvez pas les utiliser séquentiellement : une fois que vous avez obtenu vos meilleurs hyperparamètres par CV, vous pouvez toujours utiliser arrêt précoce lors de l'ajustement du modèle dans l'ensemble de votre ensemble de formation (à condition bien sûr que vous ayez un ensemble de validation distinct).
Le domaine des réseaux neuronaux profonds est encore (très) jeune et il est vrai qu'il n'a pas encore établi ses lignes directrices sur les "meilleures pratiques"; Ajoutez le fait que, grâce à une communauté incroyable, il existe toutes sortes d'outils disponibles dans les implémentations open source, et vous pouvez facilement vous retrouver dans la position (certes tentante) de mélanger les choses simplement parce qu'elles sont disponibles. Je ne dis pas nécessairement que c'est ce que vous essayez de faire ici - je demande simplement plus de prudence lorsque vous combinez des idées qui n'ont peut-être pas été conçues pour fonctionner ensemble ...
Je suis en désaccord avec desertnaut (mais manque de réputation pour commenter). Avec un arrêt précoce, il est vrai que pour un ensemble de décomptes d'époques, vous ne pouvez pas dire lequel d'entre eux a contribué au meilleur ensemble d'hyperparamètres trouvé. Mais ce n'était pas la question pour commencer. Ce que la méthode a demandé était "Étant donné au maximum n époques et en utilisant un arrêt précoce, quels sont les meilleurs hyperparamètres?". Oui, un arrêt anticipé introduira d'autres paramètres hyper que vous pourriez souhaiter ou non optimiser avec la recherche de grille, mais cela est vrai pour tout hyperparamètre de votre modèle. En fait, je pense que l'arrêt précoce pendant la recherche de grille est beaucoup plus logique que de ne pas le faire d'abord mais après la recherche de grille, car vous pouvez (au moins légèrement) raisonner sur les hyperparamètres qu'il introduit.
[Ancienne réponse, avant que la question ne soit modifiée et clarifiée - voir la réponse mise à jour et acceptée ci-dessus]
Je ne suis pas sûr d'avoir compris votre problème exact (votre question n'est pas claire, et vous incluez de nombreux détails non liés, ce qui n'est jamais bon lorsque vous posez une question SO - voir ici ).
Vous n'avez pas à (et en fait ne devriez pas) inclure d'arguments sur les données de validation dans votre appel de fonction model = KerasClassifier()
(c'est intéressant pourquoi vous ne ressentez pas le même besoin de formation des données ici aussi). Votre grid.fit()
se chargera des plis de validation de la formation et . Donc, à condition que vous souhaitiez conserver les valeurs d'hyperparamètre telles qu'elles sont incluses dans votre exemple, cet appel de fonction doit être simplement
model = KerasClassifier(build_fn=create_model,
epochs=100, batch_size=32,
shuffle=True,
verbose=1)
Vous pouvez voir quelques exemples clairs et bien expliqués concernant l'utilisation de GridSearchCV
avec Keras ici .
Voici comment le faire avec un seul fractionnement.
fit_params['cl__validation_data'] = (X_val, y_val)
X_final = np.concatenate((X_train, X_val))
y_final = np.concatenate((y_train, y_val))
splits = [(range(len(X_train)), range(len(X_train), len(X_final)))]
GridSearchCV(estimator=model, param_grid=param_grid, cv=splits)I
Si vous voulez plus de divisions, vous pouvez utiliser 'cl__validation_split' avec un rapport fixe et construire des divisions qui répondent à ces critères.
Cela peut être trop paranoïaque, mais je n'utilise pas l'ensemble de données à arrêt anticipé comme ensemble de données de validation car il a été indirectement utilisé pour créer le modèle.
Je pense également que si vous utilisez l'arrêt précoce avec votre modèle final, cela devrait également être fait lorsque vous effectuez une recherche d'hyper-paramètre.