MISE À JOUR: Cette question concernait Tensorflow 1.x. J'ai mis à niveau vers 2.0 et (au moins sur le code simple ci-dessous) le problème de reproductibilité semble résolu sur 2.0. Donc cela résout mon problème; mais je suis toujours curieux de savoir quelles "meilleures pratiques" ont été utilisées pour ce problème sur 1.x.
Entraîner exactement le même modèle / paramètres / données sur keras / tensorflow ne donne pas de résultats reproductibles et la perte est significativement différente à chaque fois que vous entraînez le modèle. Il y a beaucoup de questions sur le stackoverflow à ce sujet (par exemple, Comment obtenir des résultats reproductibles dans les keras ) mais les solutions de contournement recommandées ne semblent pas fonctionner pour moi ou pour beaucoup d'autres personnes sur StackOverflow. OK, c'est ce que c'est.
Mais étant donné cette limitation de la non-reproductibilité avec les keras sur tensorflow - quelle est la meilleure pratique pour comparer des modèles et choisir des hyper paramètres? Je teste différentes architectures et activations, mais comme l'estimation de la perte est différente à chaque fois, je ne suis jamais sûr qu'un modèle soit meilleur que l'autre. Existe-t-il des meilleures pratiques pour résoudre ce problème?
Je ne pense pas que le problème ait quoi que ce soit à voir avec mon code, mais juste au cas où cela aiderait; voici un exemple de programme:
import os #stackoverflow says turning off the GPU helps reproducibility, but it doesn't help for me os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" os.environ["CUDA_VISIBLE_DEVICES"] = "" os.environ['PYTHONHASHSEED']=str(1) import tensorflow as tf import tensorflow.keras as keras import tensorflow.keras.layers import random import pandas as pd import numpy as np #StackOverflow says this is needed for reproducibility but it doesn't help for me from tensorflow.keras import backend as K config = tf.ConfigProto(intra_op_parallelism_threads=1,inter_op_parallelism_threads=1) sess = tf.Session(graph=tf.get_default_graph(), config=config) K.set_session(sess) #make some random data NUM_ROWS = 1000 NUM_FEATURES = 10 random_data = np.random.normal(size=(NUM_ROWS, NUM_FEATURES)) df = pd.DataFrame(data=random_data, columns=['x_' + str(ii) for ii in range(NUM_FEATURES)]) y = df.sum(axis=1) + np.random.normal(size=(NUM_ROWS)) def run(x, y): #StackOverflow says you have to set the seeds but it doesn't help for me tf.set_random_seed(1) np.random.seed(1) random.seed(1) os.environ['PYTHONHASHSEED']=str(1) model = keras.Sequential([ keras.layers.Dense(40, input_dim=df.shape[1], activation='relu'), keras.layers.Dense(20, activation='relu'), keras.layers.Dense(10, activation='relu'), keras.layers.Dense(1, activation='linear') ]) NUM_EPOCHS = 500 model.compile(optimizer='adam', loss='mean_squared_error') model.fit(x, y, epochs=NUM_EPOCHS, verbose=0) predictions = model.predict(x).flatten() loss = model.evaluate(x, y) #This prints out the loss by side-effect #Each time we run it gives a wildly different loss. :-( run(df, y) run(df, y) run(df, y)
Compte tenu de la non-reproductibilité, comment puis-je évaluer si les modifications de mes hyper-paramètres et de mon architecture aident ou non?
4 Réponses :
C'est sournois, mais il manque en fait à votre code une étape pour une meilleure reproductibilité: réinitialiser les graphiques Keras et TensorFlow avant chaque exécution. Sans cela, tf.set_random_seed()
ne fonctionnera pas correctement - voir l'approche correcte ci-dessous.
J'épuisais toutes les options avant de jeter l'éponge sur la non-reproductibilité; actuellement, je ne connais qu'une seule instance de ce type , et c'est probablement un bogue. Néanmoins, il est possible que vous obteniez des résultats sensiblement différents même si vous suivez toutes les étapes - dans ce cas, voir "Si rien ne fonctionne", mais chacune n'est clairement pas très productive, il est donc préférable de se concentrer sur la reproductibilité:
Améliorations définitives :
reset_seeds(K)
ci-dessousK.set_floatx('float64')
PYTHONHASHSEED
avant le démarrage du noyau Python - par exemple à partir du terminaltf.python.keras
- voir icifrom keras.layers import ...
et from tensorflow.keras.optimizers import ...
)Voir aussi SO connexe sur la reproductibilité
Si rien ne fonctionne :
Méthode de réinitialisation correcte :
session_conf = tf.ConfigProto( intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) sess = tf.Session(config=session_conf)
Exécution de TF sur un seul thread CPU : (code pour TF1 uniquement)
def reset_seeds(reset_graph_with_backend=None): if reset_graph_with_backend is not None: K = reset_graph_with_backend K.clear_session() tf.compat.v1.reset_default_graph() print("KERAS AND TENSORFLOW GRAPHS RESET") # optional np.random.seed(1) random.seed(2) tf.compat.v1.set_random_seed(3) print("RANDOM SEEDS RESET") # optional
Vous avez quelques options pour stabiliser les performances ...
1) Définissez la valeur de départ de vos initiateurs afin qu'ils soient toujours initialisés aux mêmes valeurs.
2) Plus de données se traduisent généralement par une convergence plus stable.
3) Des taux d'apprentissage plus faibles et des lots de plus grande taille sont également bons pour un apprentissage plus prévisible.
4) Formation basée sur un nombre fixe d'époques au lieu d'utiliser des rappels pour modifier les hyperparams pendant le train.
5) Validation du pli K pour s'entraîner sur différents sous-ensembles. La moyenne de ces plis devrait aboutir à une métrique assez prévisible.
6) Vous avez également la possibilité de vous entraîner plusieurs fois et de prendre une moyenne de cela.
Le problème semble être résolu dans Tensorflow 2.0 (du moins sur les modèles simples)! Voici un extrait de code qui semble donner des résultats reproductibles.
import os ####*IMPORANT*: Have to do this line *before* importing tensorflow os.environ['PYTHONHASHSEED']=str(1) import tensorflow as tf import tensorflow.keras as keras import tensorflow.keras.layers import random import pandas as pd import numpy as np def reset_random_seeds(): os.environ['PYTHONHASHSEED']=str(1) tf.random.set_seed(1) np.random.seed(1) random.seed(1) #make some random data reset_random_seeds() NUM_ROWS = 1000 NUM_FEATURES = 10 random_data = np.random.normal(size=(NUM_ROWS, NUM_FEATURES)) df = pd.DataFrame(data=random_data, columns=['x_' + str(ii) for ii in range(NUM_FEATURES)]) y = df.sum(axis=1) + np.random.normal(size=(NUM_ROWS)) def run(x, y): reset_random_seeds() model = keras.Sequential([ keras.layers.Dense(40, input_dim=df.shape[1], activation='relu'), keras.layers.Dense(20, activation='relu'), keras.layers.Dense(10, activation='relu'), keras.layers.Dense(1, activation='linear') ]) NUM_EPOCHS = 500 model.compile(optimizer='adam', loss='mean_squared_error') model.fit(x, y, epochs=NUM_EPOCHS, verbose=0) predictions = model.predict(x).flatten() loss = model.evaluate(x, y) #This prints out the loss by side-effect #With Tensorflow 2.0 this is now reproducible! run(df, y) run(df, y) run(df, y)
En ne mettant que le code ci-dessous, ça marche. La CLÉ de la question, TRÈS IMPORTANTE, est d'appeler la fonction reset_seeds () à chaque fois avant d'exécuter le modèle. En faisant cela, vous obtiendrez des résultats reproductibles comme je l'ai vérifié dans Google Collab.
import numpy as np import tensorflow as tf import random as python_random def reset_seeds(): np.random.seed(123) python_random.seed(123) tf.random.set_seed(1234) reset_seeds()