Comment griller les hyperparamètres de recherche pour les modèles d'apprentissage profond en Python avec Keras
L'optimisation des hyperparamètres est une partie importante du deep learning.
La raison en est que les réseaux de neurones sont notoirement difficiles à configurer et que de nombreux paramètres doivent être définis. De plus, l’entraînement des modèles individuels peut être très lent.
Dans cet article, vous découvrirez comment utiliser la capacité de recherche de grille de la bibliothèque d'apprentissage automatique Python scikit-learn pour régler les hyperparamètres des modèles d'apprentissage profond de Keras.
Après avoir lu cet article, vous saurez :
- Comment envelopper les modèles Keras pour les utiliser dans scikit-learn et comment utiliser la recherche par grille
- Comment rechercher sur une grille les paramètres courants du réseau neuronal, tels que le taux d'apprentissage, le taux d'abandon, les époques et le nombre de neurones
- Comment définir vos propres expériences de réglage d'hyperparamètres sur vos propres projets
Démarrez votre projet avec mon nouveau livre Deep Learning With Python, comprenant des tutoriels pas à pas et les fichiers code source Python pour tous exemples.
Commençons.
- Août 2016 : première publication
- Mise à jour de novembre 2016 : correction d'un problème mineur lors de l'affichage des résultats de recherche de grille dans les exemples de code
- Mise à jour d'octobre 2016 : exemples mis à jour pour Keras 1.1.0, TensorFlow 0.10.0 et scikit-learn v0.18
- Mise à jour de mars 2017 : exemple mis à jour pour Keras 2.0.2, TensorFlow 1.0.1 et Theano 0.9.0
- Mise à jour de septembre 2017 : exemple mis à jour pour utiliser les « époques » Keras 2 au lieu de « nb_epochs » Keras 1
- Mise à jour de mars 2018 : ajout d'un autre lien pour télécharger l'ensemble de données
- Mise à jour d'octobre 2019 : mise à jour pour l'API Keras 2.3.0
- Mise à jour de juillet 2022 : mise à jour pour TensorFlow/Keras et SciKeras 0.8
Aperçu
Dans cet article, vous découvrirez comment utiliser la capacité de recherche de grille scikit-learn. Vous recevrez une suite d’exemples que vous pourrez copier et coller dans votre propre projet comme point de départ.
Vous trouverez ci-dessous une liste des sujets abordés par cet article :
- Comment utiliser les modèles Keras dans scikit-learn
- Comment utiliser la recherche par grille dans scikit-learn
- Comment régler la taille des lots et les époques de formation
- Comment régler les algorithmes d'optimisation
- Comment régler le taux et l'élan d'apprentissage
- Comment régler l'initialisation du poids du réseau
- Comment régler les fonctions d'activation
- Comment régler la régularisation des abandons
- Comment régler le nombre de neurones dans la couche cachée
Comment utiliser les modèles Keras dans scikit-learn
Les modèles Keras peuvent être utilisés dans scikit-learn en les encapsulant avec la classe KerasClassifier
ou KerasRegressor
du module SciKeras. Vous devrez peut-être d'abord exécuter la commande pip install scikeras
pour installer le module.
Pour utiliser ces wrappers, vous devez définir une fonction qui crée et renvoie votre modèle séquentiel Keras, puis transmettre cette fonction à l'argument model
lors de la construction de la classe KerasClassifier
.
Par exemple:
def create_model():
...
return model
model = KerasClassifier(model=create_model)
Le constructeur de la classe KerasClassifier
peut prendre des arguments par défaut qui sont transmis aux appels à model.fit()
, tels que le nombre d'époques et la taille du lot.
Par exemple:
def create_model():
...
return model
model = KerasClassifier(model=create_model, epochs=10)
Le constructeur de la classe KerasClassifier
peut également prendre de nouveaux arguments qui peuvent être transmis à votre fonction personnalisée create_model()
. Ces nouveaux arguments doivent également être définis dans la signature de votre fonction create_model()
avec les paramètres par défaut.
Par exemple:
def create_model(dropout_rate=0.0):
...
return model
model = KerasClassifier(model=create_model, dropout_rate=0.2)
Vous pouvez en savoir plus à ce sujet dans la documentation SciKeras.
Comment utiliser la recherche par grille dans scikit-learn
La recherche de grille est une technique d'optimisation d'hyperparamètres de modèle.
Dans scikit-learn, cette technique est fournie dans la classe GridSearchCV
.
Lors de la construction de cette classe, vous devez fournir un dictionnaire d'hyperparamètres à évaluer dans l'argument param_grid
. Il s'agit d'une carte du nom du paramètre du modèle et d'un tableau de valeurs à essayer.
Par défaut, la précision est le score optimisé, mais d'autres scores peuvent être spécifiés dans l'argument score
du constructeur GridSearchCV
.
Par défaut, la recherche dans la grille n'utilisera qu'un seul thread. En définissant l'argument n_jobs
dans le constructeur GridSearchCV
sur -1, le processus utilisera tous les cœurs de votre machine. Cependant, cela peut parfois interférer avec le processus principal de formation du réseau neuronal.
Le processus GridSearchCV
construira et évaluera ensuite un modèle pour chaque combinaison de paramètres. La validation croisée est utilisée pour évaluer chaque modèle individuel, et la valeur par défaut de validation croisée triple est utilisée, bien que vous puissiez la remplacer en spécifiant l'argument cv
au constructeur GridSearchCV
. .
Vous trouverez ci-dessous un exemple de définition d'une recherche de grille simple :
param_grid = dict(epochs=[10,20,30])
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
Une fois terminé, vous pouvez accéder au résultat de la recherche dans la grille dans l'objet résultat renvoyé par grid.fit()
. Le membre best_score_
donne accès au meilleur score observé lors de la procédure d'optimisation, et le membre best_params_
décrit la combinaison de paramètres qui a permis d'obtenir les meilleurs résultats.
Vous pouvez en savoir plus sur la classe GridSearchCV dans la documentation de l'API scikit-learn.
Description du problème
Maintenant que vous savez comment utiliser les modèles Keras avec scikit-learn et comment utiliser la recherche par grille dans scikit-learn, examinons quelques exemples.
Tous les exemples seront démontrés sur un petit ensemble de données d’apprentissage automatique standard appelé ensemble de données de classification de l’apparition du diabète chez les Indiens Pima. Il s'agit d'un petit ensemble de données avec tous les attributs numériques avec lequel il est facile de travailler.
- Téléchargez l'ensemble de données et placez-le directement dans votre travail actuel sous le nom
pima-indians-diabetes.csv
(mise à jour : télécharger à partir d'ici).
Au fur et à mesure que vous parcourez les exemples de cet article, vous regrouperez les meilleurs paramètres. Ce n'est pas la meilleure façon d'effectuer une recherche sur une grille car les paramètres peuvent interagir, mais c'est une bonne méthode à des fins de démonstration.
Remarque sur la parallélisation de la recherche par grille
Tous les exemples sont configurés pour utiliser le parallélisme (n_jobs=-1
).
Si vous obtenez une erreur comme celle ci-dessous :
INFO (theano.gof.compilelock): Waiting for existing lock by process '55614' (I am process '55613')
INFO (theano.gof.compilelock): To manually release the lock, delete ...
Tuez le processus et modifiez le code pour ne pas effectuer la recherche dans la grille en parallèle ; définissez n_jobs=1
.
Comment régler la taille du lot et le nombre d'époques
Dans ce premier exemple simple, vous examinerez le réglage de la taille du lot et du nombre d'époques utilisés lors de l'ajustement du réseau.
La taille du lot en descente de gradient itérative correspond au nombre de modèles affichés sur le réseau avant la mise à jour des pondérations. C'est aussi une optimisation dans l'entraînement du réseau, définissant le nombre de modèles à lire à la fois et à conserver en mémoire.
Le nombre d'époques correspond au nombre de fois où l'intégralité de l'ensemble de données d'entraînement est affichée sur le réseau pendant l'entraînement. Certains réseaux sont sensibles à la taille du lot, comme les réseaux de neurones récurrents LSTM et les réseaux de neurones convolutifs.
Ici, vous évaluerez une suite de différentes tailles de mini-lots de 10 à 100 par étapes de 20.
La liste complète des codes est fournie ci-dessous :
# Use scikit-learn to grid search the batch size and epochs
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model():
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, verbose=0)
# define the grid search parameters
batch_size = [10, 20, 40, 60, 80, 100]
epochs = [10, 50, 100]
param_grid = dict(batch_size=batch_size, epochs=epochs)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.705729 using {'batch_size': 10, 'epochs': 100}
0.597656 (0.030425) with: {'batch_size': 10, 'epochs': 10}
0.686198 (0.017566) with: {'batch_size': 10, 'epochs': 50}
0.705729 (0.017566) with: {'batch_size': 10, 'epochs': 100}
0.494792 (0.009207) with: {'batch_size': 20, 'epochs': 10}
0.675781 (0.017758) with: {'batch_size': 20, 'epochs': 50}
0.683594 (0.011049) with: {'batch_size': 20, 'epochs': 100}
0.535156 (0.053274) with: {'batch_size': 40, 'epochs': 10}
0.622396 (0.009744) with: {'batch_size': 40, 'epochs': 50}
0.671875 (0.019918) with: {'batch_size': 40, 'epochs': 100}
0.592448 (0.042473) with: {'batch_size': 60, 'epochs': 10}
0.660156 (0.041707) with: {'batch_size': 60, 'epochs': 50}
0.674479 (0.006639) with: {'batch_size': 60, 'epochs': 100}
0.476562 (0.099896) with: {'batch_size': 80, 'epochs': 10}
0.608073 (0.033197) with: {'batch_size': 80, 'epochs': 50}
0.660156 (0.011500) with: {'batch_size': 80, 'epochs': 100}
0.615885 (0.015073) with: {'batch_size': 100, 'epochs': 10}
0.617188 (0.039192) with: {'batch_size': 100, 'epochs': 50}
0.632812 (0.019918) with: {'batch_size': 100, 'epochs': 100}
Vous pouvez voir que la taille de lot de 10 et 100 époques a permis d'obtenir le meilleur résultat, avec une précision d'environ 70 %.
Comment régler l'algorithme d'optimisation de la formation
Keras propose une suite de différents algorithmes d'optimisation de pointe.
Dans cet exemple, vous ajusterez l'algorithme d'optimisation utilisé pour entraîner le réseau, chacun avec des paramètres par défaut.
C'est un exemple étrange car souvent, vous choisirez une approche a priori et vous concentrerez plutôt sur l'ajustement de ses paramètres sur votre problème (voir l'exemple suivant).
Ici, vous évaluerez la suite d'algorithmes d'optimisation pris en charge par l'API Keras.
La liste complète des codes est fournie ci-dessous :
# Use scikit-learn to grid search the batch size and epochs
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model():
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# return model without compile
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, loss="binary_crossentropy", epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
optimizer = ['SGD', 'RMSprop', 'Adagrad', 'Adadelta', 'Adam', 'Adamax', 'Nadam']
param_grid = dict(optimizer=optimizer)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
Notez que la fonction create_model()
définie ci-dessus ne renvoie pas de modèle compilé comme celui de l'exemple précédent. En effet, la définition d'un optimiseur pour un modèle Keras est effectuée dans l'appel de fonction compile()
; il est donc préférable de s'en remettre au wrapper KerasClassifier
et au modèle GridSearchCV
. Notez également que vous avez spécifié loss="binary_crossentropy"
dans le wrapper car il doit également être défini lors de l'appel de la fonction compile()
.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.697917 using {'optimizer': 'Adam'}
0.674479 (0.033804) with: {'optimizer': 'SGD'}
0.649740 (0.040386) with: {'optimizer': 'RMSprop'}
0.595052 (0.032734) with: {'optimizer': 'Adagrad'}
0.348958 (0.001841) with: {'optimizer': 'Adadelta'}
0.697917 (0.038051) with: {'optimizer': 'Adam'}
0.652344 (0.019918) with: {'optimizer': 'Adamax'}
0.684896 (0.011201) with: {'optimizer': 'Nadam'}
Le wrapper KerasClassifier
ne compilera pas à nouveau votre modèle si le modèle est déjà compilé. Par conséquent, l'autre façon d'exécuter GridSearchCV
consiste à définir l'optimiseur comme argument de la fonction create_model()
, qui renvoie un modèle compilé de manière appropriée comme le suivant :
# Use scikit-learn to grid search the batch size and epochs
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model(optimizer='adam'):
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
optimizer = ['SGD', 'RMSprop', 'Adagrad', 'Adadelta', 'Adam', 'Adamax', 'Nadam']
param_grid = dict(model__optimizer=optimizer)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
Notez que dans ce qui précède, vous avez le préfixe model__
dans le dictionnaire de paramètres param_grid
. Ceci est requis pour le KerasClassifier
dans le module SciKeras pour indiquer clairement que le paramètre doit route dans la fonction create_model()
en tant qu'arguments, plutôt qu'un paramètre à configurer dans compile()
ou fit()
. Voir également la section des paramètres routés de la documentation SciKeras.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.697917 using {'model__optimizer': 'Adam'}
0.636719 (0.019401) with: {'model__optimizer': 'SGD'}
0.683594 (0.020915) with: {'model__optimizer': 'RMSprop'}
0.585938 (0.038670) with: {'model__optimizer': 'Adagrad'}
0.518229 (0.120624) with: {'model__optimizer': 'Adadelta'}
0.697917 (0.049445) with: {'model__optimizer': 'Adam'}
0.652344 (0.027805) with: {'model__optimizer': 'Adamax'}
0.686198 (0.012890) with: {'model__optimizer': 'Nadam'}
Les résultats suggèrent que l’algorithme d’optimisation ADAM est le meilleur avec un score de précision d’environ 70 %.
Comment régler le taux d'apprentissage et l'élan
Il est courant de présélectionner un algorithme d'optimisation pour entraîner votre réseau et ajuster ses paramètres.
De loin, l’algorithme d’optimisation le plus courant est le vieux Stochastic Gradient Descent (SGD), car il est très bien compris. Dans cet exemple, vous examinerez l'optimisation des paramètres de taux d'apprentissage et d'élan de SGD.
Le taux d'apprentissage contrôle dans quelle mesure il faut mettre à jour le poids à la fin de chaque lot, et l'élan contrôle dans quelle mesure laisser la mise à jour précédente influencer la mise à jour du poids en cours.
Vous essaierez une suite de petits taux d'apprentissage standard et de valeurs d'élan allant de 0,2 à 0,8 par pas de 0,2, ainsi que de 0,9 (car cela peut être une valeur populaire dans la pratique). Dans Keras, la manière de définir le taux et l'élan d'apprentissage est la suivante :
...
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.2)
Dans le wrapper SciKeras, vous acheminer les paramètres vers l'optimiseur avec le préfixe optimizer__
.
Généralement, c'est une bonne idée d'inclure également le nombre d'époques dans une optimisation comme celle-ci car il existe une dépendance entre la quantité d'apprentissage par lot (taux d'apprentissage), le nombre de mises à jour par époque (taille du lot) et le nombre de mises à jour par époque. des époques.
La liste complète des codes est fournie ci-dessous :
# Use scikit-learn to grid search the learning rate and momentum
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import SGD
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model():
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, loss="binary_crossentropy", optimizer="SGD", epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
learn_rate = [0.001, 0.01, 0.1, 0.2, 0.3]
momentum = [0.0, 0.2, 0.4, 0.6, 0.8, 0.9]
param_grid = dict(optimizer__learning_rate=learn_rate, optimizer__momentum=momentum)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.686198 using {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.0}
0.686198 (0.036966) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.0}
0.651042 (0.009744) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.2}
0.652344 (0.038670) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.4}
0.656250 (0.065907) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.6}
0.671875 (0.022326) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.8}
0.661458 (0.015733) with: {'optimizer__learning_rate': 0.001, 'optimizer__momentum': 0.9}
0.665365 (0.021236) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.0}
0.671875 (0.003189) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.2}
0.640625 (0.008438) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.4}
0.648438 (0.003189) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.6}
0.649740 (0.003683) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.8}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.01, 'optimizer__momentum': 0.9}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.0}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.2}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.4}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.6}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.8}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.1, 'optimizer__momentum': 0.9}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.0}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.2}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.4}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.6}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.8}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.2, 'optimizer__momentum': 0.9}
0.652344 (0.003189) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.0}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.2}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.4}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.6}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.8}
0.651042 (0.001841) with: {'optimizer__learning_rate': 0.3, 'optimizer__momentum': 0.9}
Vous pouvez voir que SGD n’est pas très bon sur ce problème ; néanmoins, les meilleurs résultats ont été obtenus en utilisant un taux d'apprentissage de 0,001 et une impulsion de 0,0 avec une précision d'environ 68 %.
Comment régler l'initialisation du poids du réseau
L'initialisation du poids du réseau neuronal était simple : utilisez de petites valeurs aléatoires.
Il existe désormais une suite de techniques différentes parmi lesquelles choisir. Keras fournit une liste de blanchisserie.
Dans cet exemple, vous examinerez la sélection de l'initialisation du poids du réseau en évaluant toutes les techniques disponibles.
Vous utiliserez la même méthode d’initialisation du poids sur chaque couche. Idéalement, il serait peut-être préférable d’utiliser différents schémas d’initialisation de poids en fonction de la fonction d’activation utilisée sur chaque couche. Dans l'exemple ci-dessous, vous utiliserez un redresseur pour la couche cachée. Utilisez sigmoïde pour la couche de sortie car les prédictions sont binaires. L'initialisation du poids est maintenant un argument de la fonction create_model()
, où vous devez utiliser le préfixe model__
pour demander au KerasClassifier
d'acheminer le paramètre. à la fonction de création de modèle.
La liste complète des codes est fournie ci-dessous :
# Use scikit-learn to grid search the weight initialization
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model(init_mode='uniform'):
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), kernel_initializer=init_mode, activation='relu'))
model.add(Dense(1, kernel_initializer=init_mode, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
init_mode = ['uniform', 'lecun_uniform', 'normal', 'zero', 'glorot_normal', 'glorot_uniform', 'he_normal', 'he_uniform']
param_grid = dict(model__init_mode=init_mode)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.716146 using {'model__init_mode': 'uniform'}
0.716146 (0.034987) with: {'model__init_mode': 'uniform'}
0.678385 (0.029635) with: {'model__init_mode': 'lecun_uniform'}
0.716146 (0.030647) with: {'model__init_mode': 'normal'}
0.651042 (0.001841) with: {'model__init_mode': 'zero'}
0.695312 (0.027805) with: {'model__init_mode': 'glorot_normal'}
0.690104 (0.023939) with: {'model__init_mode': 'glorot_uniform'}
0.647135 (0.057880) with: {'model__init_mode': 'he_normal'}
0.665365 (0.026557) with: {'model__init_mode': 'he_uniform'}
Nous pouvons voir que les meilleurs résultats ont été obtenus avec un schéma d'initialisation à poids uniforme, atteignant une performance d'environ 72 %.
Comment régler la fonction d'activation des neurones
La fonction d'activation contrôle la non-linéarité des neurones individuels et le moment où se déclencher.
Généralement, la fonction d’activation du redresseur est la plus populaire. Cependant, il s’agissait auparavant des fonctions sigmoïde et tanh, et ces fonctions peuvent encore être plus adaptées à différents problèmes.
Dans cet exemple, vous évaluerez la suite de différentes fonctions d'activation disponibles dans Keras. Vous n'utiliserez ces fonctions que dans la couche cachée, car une fonction d'activation sigmoïde est requise dans la sortie du problème de classification binaire. Semblable à l'exemple précédent, il s'agit d'un argument de la fonction create_model()
, et vous utiliserez le préfixe model__
pour la grille de paramètres GridSearchCV
. .
En général, il est judicieux de préparer les données en fonction des différentes fonctions de transfert, ce que vous ne ferez pas dans ce cas.
La liste complète des codes est fournie ci-dessous :
# Use scikit-learn to grid search the activation function
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model(activation='relu'):
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), kernel_initializer='uniform', activation=activation))
model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
activation = ['softmax', 'softplus', 'softsign', 'relu', 'tanh', 'sigmoid', 'hard_sigmoid', 'linear']
param_grid = dict(model__activation=activation)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L'exécution de cet exemple produit le résultat suivant :
Best: 0.710938 using {'model__activation': 'linear'}
0.651042 (0.001841) with: {'model__activation': 'softmax'}
0.703125 (0.012758) with: {'model__activation': 'softplus'}
0.671875 (0.009568) with: {'model__activation': 'softsign'}
0.710938 (0.024080) with: {'model__activation': 'relu'}
0.669271 (0.019225) with: {'model__activation': 'tanh'}
0.675781 (0.011049) with: {'model__activation': 'sigmoid'}
0.677083 (0.004872) with: {'model__activation': 'hard_sigmoid'}
0.710938 (0.034499) with: {'model__activation': 'linear'}
Étonnamment (du moins pour moi), la fonction d'activation « linéaire » a obtenu les meilleurs résultats avec une précision d'environ 71 %.
Comment régler la régularisation des abandons
Dans cet exemple, vous examinerez l’ajustement du taux d’abandon pour la régularisation dans le but de limiter le surapprentissage et d’améliorer la capacité du modèle à généraliser.
Pour de meilleurs résultats, il est préférable de combiner l'abandon avec une contrainte de poids telle que la contrainte de norme maximale.
Pour en savoir plus sur l'utilisation du dropout dans les modèles d'apprentissage profond avec Keras, consultez l'article :
- Régularisation des abandons dans les modèles d'apprentissage profond avec Keras
Cela implique d’ajuster à la fois le pourcentage d’abandon et la contrainte de poids. Nous essaierons des pourcentages d'abandon compris entre 0,0 et 0,9 (1,0 n'a pas de sens) et des valeurs de contrainte de poids maxnorm entre 0 et 5.
La liste complète des codes est fournie ci-dessous.
# Use scikit-learn to grid search the dropout rate
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.constraints import MaxNorm
from scikeras.wrappers import KerasClassifier
# Function to create model, required for KerasClassifier
def create_model(dropout_rate, weight_constraint):
# create model
model = Sequential()
model.add(Dense(12, input_shape=(8,), kernel_initializer='uniform', activation='linear', kernel_constraint=MaxNorm(weight_constraint)))
model.add(Dropout(dropout_rate))
model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
print(dataset.dtype, dataset.shape)
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# create model
model = KerasClassifier(model=create_model, epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
weight_constraint = [1.0, 2.0, 3.0, 4.0, 5.0]
dropout_rate = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
param_grid = dict(model__dropout_rate=dropout_rate, model__weight_constraint=weight_constraint)
#param_grid = dict(model__dropout_rate=dropout_rate)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L’exécution de cet exemple produit le résultat suivant.
Best: 0.766927 using {'model__dropout_rate': 0.2, 'model__weight_constraint': 3.0}
0.729167 (0.021710) with: {'model__dropout_rate': 0.0, 'model__weight_constraint': 1.0}
0.746094 (0.022326) with: {'model__dropout_rate': 0.0, 'model__weight_constraint': 2.0}
0.753906 (0.022097) with: {'model__dropout_rate': 0.0, 'model__weight_constraint': 3.0}
0.750000 (0.012758) with: {'model__dropout_rate': 0.0, 'model__weight_constraint': 4.0}
0.751302 (0.012890) with: {'model__dropout_rate': 0.0, 'model__weight_constraint': 5.0}
0.739583 (0.026748) with: {'model__dropout_rate': 0.1, 'model__weight_constraint': 1.0}
0.733073 (0.001841) with: {'model__dropout_rate': 0.1, 'model__weight_constraint': 2.0}
0.753906 (0.030425) with: {'model__dropout_rate': 0.1, 'model__weight_constraint': 3.0}
0.748698 (0.031466) with: {'model__dropout_rate': 0.1, 'model__weight_constraint': 4.0}
0.753906 (0.030425) with: {'model__dropout_rate': 0.1, 'model__weight_constraint': 5.0}
0.760417 (0.024360) with: {'model__dropout_rate': 0.2, 'model__weight_constraint': 1.0}
nan (nan) with: {'model__dropout_rate': 0.2, 'model__weight_constraint': 2.0}
0.766927 (0.021710) with: {'model__dropout_rate': 0.2, 'model__weight_constraint': 3.0}
0.755208 (0.010253) with: {'model__dropout_rate': 0.2, 'model__weight_constraint': 4.0}
0.750000 (0.008438) with: {'model__dropout_rate': 0.2, 'model__weight_constraint': 5.0}
0.725260 (0.015073) with: {'model__dropout_rate': 0.3, 'model__weight_constraint': 1.0}
0.738281 (0.008438) with: {'model__dropout_rate': 0.3, 'model__weight_constraint': 2.0}
0.748698 (0.003683) with: {'model__dropout_rate': 0.3, 'model__weight_constraint': 3.0}
0.740885 (0.023073) with: {'model__dropout_rate': 0.3, 'model__weight_constraint': 4.0}
0.735677 (0.008027) with: {'model__dropout_rate': 0.3, 'model__weight_constraint': 5.0}
0.743490 (0.009207) with: {'model__dropout_rate': 0.4, 'model__weight_constraint': 1.0}
0.751302 (0.006639) with: {'model__dropout_rate': 0.4, 'model__weight_constraint': 2.0}
0.750000 (0.024910) with: {'model__dropout_rate': 0.4, 'model__weight_constraint': 3.0}
0.744792 (0.030314) with: {'model__dropout_rate': 0.4, 'model__weight_constraint': 4.0}
0.751302 (0.010253) with: {'model__dropout_rate': 0.4, 'model__weight_constraint': 5.0}
0.757812 (0.006379) with: {'model__dropout_rate': 0.5, 'model__weight_constraint': 1.0}
0.740885 (0.030978) with: {'model__dropout_rate': 0.5, 'model__weight_constraint': 2.0}
0.742188 (0.003189) with: {'model__dropout_rate': 0.5, 'model__weight_constraint': 3.0}
0.718750 (0.016877) with: {'model__dropout_rate': 0.5, 'model__weight_constraint': 4.0}
0.726562 (0.019137) with: {'model__dropout_rate': 0.5, 'model__weight_constraint': 5.0}
0.725260 (0.013279) with: {'model__dropout_rate': 0.6, 'model__weight_constraint': 1.0}
0.738281 (0.013902) with: {'model__dropout_rate': 0.6, 'model__weight_constraint': 2.0}
0.743490 (0.001841) with: {'model__dropout_rate': 0.6, 'model__weight_constraint': 3.0}
0.722656 (0.009568) with: {'model__dropout_rate': 0.6, 'model__weight_constraint': 4.0}
0.747396 (0.024774) with: {'model__dropout_rate': 0.6, 'model__weight_constraint': 5.0}
0.729167 (0.006639) with: {'model__dropout_rate': 0.7, 'model__weight_constraint': 1.0}
0.717448 (0.012890) with: {'model__dropout_rate': 0.7, 'model__weight_constraint': 2.0}
0.710938 (0.027621) with: {'model__dropout_rate': 0.7, 'model__weight_constraint': 3.0}
0.718750 (0.014616) with: {'model__dropout_rate': 0.7, 'model__weight_constraint': 4.0}
0.743490 (0.021236) with: {'model__dropout_rate': 0.7, 'model__weight_constraint': 5.0}
0.713542 (0.009207) with: {'model__dropout_rate': 0.8, 'model__weight_constraint': 1.0}
nan (nan) with: {'model__dropout_rate': 0.8, 'model__weight_constraint': 2.0}
0.721354 (0.009207) with: {'model__dropout_rate': 0.8, 'model__weight_constraint': 3.0}
0.716146 (0.009207) with: {'model__dropout_rate': 0.8, 'model__weight_constraint': 4.0}
0.716146 (0.015073) with: {'model__dropout_rate': 0.8, 'model__weight_constraint': 5.0}
0.682292 (0.018688) with: {'model__dropout_rate': 0.9, 'model__weight_constraint': 1.0}
0.696615 (0.011201) with: {'model__dropout_rate': 0.9, 'model__weight_constraint': 2.0}
0.696615 (0.026557) with: {'model__dropout_rate': 0.9, 'model__weight_constraint': 3.0}
0.694010 (0.001841) with: {'model__dropout_rate': 0.9, 'model__weight_constraint': 4.0}
0.696615 (0.022628) with: {'model__dropout_rate': 0.9, 'model__weight_constraint': 5.0}
Nous pouvons voir que le taux d'abandon de 20 % et la contrainte de poids MaxNorm de 3 ont abouti à la meilleure précision d'environ 77 %. Vous remarquerez peut-être qu'une partie du résultat est nan
. Cela est probablement dû au problème que l'entrée n'est pas normalisée et vous pouvez rencontrer par hasard un modèle dégénéré.
Comment régler le nombre de neurones dans la couche cachée
Le nombre de neurones dans une couche est un paramètre important à régler. Généralement, le nombre de neurones dans une couche contrôle la capacité de représentation du réseau, du moins à ce stade de la topologie.
De plus, en général, un réseau monocouche suffisamment grand peut se rapprocher de n’importe quel autre réseau neuronal, du moins en théorie.
Dans cet exemple, nous examinerons le réglage du nombre de neurones dans une seule couche cachée. Nous allons essayer les valeurs de 1 à 30 par pas de 5.
Un réseau plus grand nécessite plus de formation et au moins la taille du lot et le nombre d’époques devraient idéalement être optimisés en fonction du nombre de neurones.
La liste complète des codes est fournie ci-dessous.
# Use scikit-learn to grid search the number of neurons
import numpy as np
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from scikeras.wrappers import KerasClassifier
from tensorflow.keras.constraints import MaxNorm
# Function to create model, required for KerasClassifier
def create_model(neurons):
# create model
model = Sequential()
model.add(Dense(neurons, input_shape=(8,), kernel_initializer='uniform', activation='linear', kernel_constraint=MaxNorm(4)))
model.add(Dropout(0.2))
model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# fix random seed for reproducibility
seed = 7
tf.random.set_seed(seed)
# load dataset
dataset = np.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(model=create_model, epochs=100, batch_size=10, verbose=0)
# define the grid search parameters
neurons = [1, 5, 10, 15, 20, 25, 30]
param_grid = dict(model__neurons=neurons)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1, cv=3)
grid_result = grid.fit(X, Y)
# 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))
Remarque : Vos résultats peuvent varier en raison de la nature stochastique de l'algorithme ou de la procédure d'évaluation, ou des différences de précision numérique. Pensez à exécuter l’exemple plusieurs fois et comparez le résultat moyen.
L’exécution de cet exemple produit le résultat suivant.
Best: 0.729167 using {'model__neurons': 30}
0.701823 (0.010253) with: {'model__neurons': 1}
0.717448 (0.011201) with: {'model__neurons': 5}
0.717448 (0.008027) with: {'model__neurons': 10}
0.720052 (0.019488) with: {'model__neurons': 15}
0.709635 (0.004872) with: {'model__neurons': 20}
0.708333 (0.003683) with: {'model__neurons': 25}
0.729167 (0.009744) with: {'model__neurons': 30}
Nous pouvons voir que les meilleurs résultats ont été obtenus avec un réseau comportant 30 neurones dans la couche cachée avec une précision d'environ 73 %.
Conseils pour l'optimisation des hyperparamètres
Cette section répertorie quelques conseils pratiques à prendre en compte lors du réglage des hyperparamètres de votre réseau neuronal.
- Validation croisée k-fold. Vous pouvez voir que les résultats des exemples de cet article montrent une certaine variance. Une validation croisée par défaut de 3 a été utilisée, mais peut-être que k=5 ou k=10 serait plus stable. Choisissez soigneusement votre configuration de validation croisée pour garantir la stabilité de vos résultats.
- Examinez l'ensemble de la grille. Ne vous concentrez pas uniquement sur le meilleur résultat, examinez l'ensemble de la grille de résultats et recherchez des tendances pour prendre en charge les décisions de configuration.
- Paralléliser. Utilisez tous vos cœurs si vous le pouvez, les réseaux de neurones sont lents à s'entraîner et nous voulons souvent essayer beaucoup de paramètres différents. Pensez à créer de nombreuses instances AWS.
- Utilisez un échantillon de votre ensemble de données. Étant donné que les réseaux sont lents à s'entraîner, essayez de les entraîner sur un échantillon plus petit de votre ensemble de données d'entraînement, juste pour avoir une idée des orientations générales des paramètres plutôt que des configurations optimales.
- Commencez avec des grilles grossières. Commencez avec des grilles à gros grain et zoomez sur des grilles à grain plus fin une fois que vous pouvez réduire la portée.
- Ne pas transférer les résultats. Les résultats sont généralement spécifiques au problème. Essayez d'éviter les configurations favorites pour chaque nouveau problème que vous rencontrez. Il est peu probable que les résultats optimaux que vous découvrez sur un problème soient transférés à votre prochain projet. Recherchez plutôt des tendances plus larges, comme le nombre de couches ou les relations entre les paramètres.
- La reproductibilité est un problème. Bien que nous ayons défini la valeur de départ du générateur de nombres aléatoires dans NumPy, les résultats ne sont pas reproductibles à 100 %. La reproductibilité lors de la recherche dans une grille de modèles Keras encapsulés est plus complexe que ce qui est présenté dans cet article.
Résumé
Dans cet article, vous avez découvert comment régler les hyperparamètres de vos réseaux d'apprentissage profond en Python à l'aide de Keras et scikit-learn.
Concrètement, vous avez appris :
- Comment envelopper les modèles Keras pour les utiliser dans scikit-learn et comment utiliser la recherche par grille.
- Comment rechercher sur une grille une suite de différents paramètres de réseau neuronal standard pour les modèles Keras.
- Comment concevoir vos propres expériences d'optimisation d'hyperparamètres.
Avez-vous de l'expérience dans le réglage des hyperparamètres de grands réseaux de neurones ? Veuillez partager vos histoires ci-dessous.
Avez-vous des questions sur l'optimisation des hyperparamètres des réseaux de neurones ou sur cet article ? Posez vos questions dans les commentaires et je ferai de mon mieux pour y répondre.