Je courais TensorFlow et il se trouve que quelque chose donne un NaN. J'aimerais savoir ce que c'est mais je ne sais pas comment faire ça. Le problème principal est que, dans un programme procédural "normal", je ne ferais qu'écrire une instruction print juste avant l'exécution de l'opération. Le problème avec TensorFlow est que je ne peux pas le faire car je déclare (ou définit) d’abord le graphique. Il est donc inutile d’ajouter des instructions print à la définition du graphique. Existe-t-il des règles, des conseils, des méthodes heuristiques et tout ce qui pourrait déterminer la cause du NaN?
Dans ce cas, je sais plus précisément quelle ligne à regarder car j'ai les éléments suivants:
Delta_tilde = 2.0*tf.matmul(x,W) - tf.add(WW, XX) #note this quantity should always be positive because its pair-wise euclidian distance
Z = tf.sqrt(Delta_tilde)
Z = Transform(Z) # potentially some transform, currently I have it to return Z for debugging (the identity)
Z = tf.pow(Z, 2.0)
A = tf.exp(Z)
quand cette ligne est présente, il est indiqué qu'elle renvoie NaN tel que déclaré par mes auteurs de résumé. Pourquoi est-ce? Existe-t-il un moyen d’explorer au moins la valeur que Z a après sa racine carrée?
Pour l'exemple spécifique que j'ai posté, j'ai essayé tf.Print(0,Z)
mais sans succès, rien n'a été imprimé. Un péché:
Delta_tilde = 2.0*tf.matmul(x,W) - tf.add(WW, XX) #note this quantity should always be positive because its pair-wise euclidian distance
Z = tf.sqrt(Delta_tilde)
tf.Print(0,[Z]) # <-------- TF PRINT STATMENT
Z = Transform(Z) # potentially some transform, currently I have it to return Z for debugging (the identity)
Z = tf.pow(Z, 2.0)
A = tf.exp(Z)
En fait, je ne comprends pas ce que tf.Print
Est censé faire. Pourquoi a-t-il besoin de deux arguments? Si je veux imprimer 1 tenseur, pourquoi devrais-je en passer 2? Cela me semble bizarre.
Je regardais la fonction tf.add_check_numerics_ops () mais il ne dit pas comment l'utiliser (plus la documentation ne semble pas être très utile). Est-ce que quelqu'un sait comment utiliser cela?
Depuis que j'ai eu des commentaires adressant les données pourraient être mauvais, j'utilise standard MNIST. Cependant, je calcule une quantité qui est positive (distance euclédienne par paires) et l’enracine ensuite. Ainsi, je ne verrais pas en quoi les données seraient un problème.
Il existe plusieurs raisons pour lesquelles vous pouvez obtenir un résultat NaN, souvent en raison d’un taux d’apprentissage trop élevé, mais de nombreuses autres raisons sont possibles, telles que des données corrompues dans votre file d’entrée ou un journal de 0.
Quoi qu'il en soit, le débogage avec une impression telle que vous la décrivez ne peut pas être effectué par une simple impression (ceci entraînerait uniquement l'impression des informations de tenseur à l'intérieur du graphique et n'imprimerait aucune valeur réelle).
Cependant, si vous utilisez tf.print comme une opération dans le graphe ( tf.print ), vous obtiendrez les valeurs réelles lorsque le graphe sera exécuté (et il IS un bon exercice pour observer ces valeurs pour déboguer et comprendre le comportement de votre réseau).
Cependant, vous utilisez l'instruction print de manière totalement incorrecte. Ceci est une opération, vous devez donc lui transmettre un tenseur et demander un tenseur de résultat avec lequel vous devez travailler ultérieurement dans le graphique en cours d'exécution. Sinon, l'opération ne sera pas exécutée et aucune impression ne sera effectuée. Essaye ça:
Z = tf.sqrt(Delta_tilde)
Z = tf.Print(Z,[Z], message="my Z-values:") # <-------- TF PRINT STATMENT
Z = Transform(Z) # potentially some transform, currently I have it to return Z for debugging (the identity)
Z = tf.pow(Z, 2.0)
J'avais l'habitude de trouver qu'il était beaucoup plus difficile de localiser les sources d'information et de résolution que de corriger le bogue. Pour compléter la réponse de @ scai, j'aimerais ajouter quelques points ici:
Le module de débogage, vous pouvez importer par:
from tensorflow.python import debug as tf_debug
est beaucoup mieux que toute impression ou affirmation.
Vous pouvez simplement ajouter la fonction de débogage en modifiant votre wrapper de session par:
sess = tf_debug.LocalCLIDebugWrapperSession(sess)
sess.add_tensor_filter("has_inf_or_nan", tf_debug.has_inf_or_nan)
Et vous allez proposer une interface en ligne de commande, puis vous entrez: run -f has_inf_or_nan
et lt -f has_inf_or_nan
pour trouver où sont les nans ou les infs. Le premier est le premier endroit où la catastrophe se produit. Par le nom de la variable, vous pouvez tracer l’origine dans votre code.
Référence: https://developers.googleblog.com/2017/02/debug-tensorflow-models-with-tfdbg.html
Il semble que vous puissiez l'appeler une fois que vous avez terminé de créer le graphique.
check = tf.add_check_numerics_ops()
Je pense que cela va ajouter la vérification pour toutes les opérations en virgule flottante. Ensuite, dans la fonction d'exécution de sessions, vous pouvez ajouter l'opération de vérification.
sess.run([check, ...])
À partir de la version 0.12, TensorFlow est livré avec un débogueur intégré appelé tfdbg
. Il optimise le flux de travail de débogage de ce type de problèmes de valeur numérique incorrecte (comme inf
et nan
). La documentation est à: https://www.tensorflow.org/programmers_guide/debugger
Tout d’abord, vous devez vérifier que vous entrez les données correctement. Dans la plupart des cas, c'est la raison. Mais pas toujours, bien sûr.
J'utilise habituellement Tensorboard pour voir ce qui se passe pendant l'entraînement. Ainsi, vous pouvez voir les valeurs sur chaque étape avec
Z = tf.pow(Z, 2.0)
summary_z = tf.scalar_summary('z', Z)
#etc..
summary_merge = tf.merge_all_summaries()
#on each desired step save:
summary_str = sess.run(summary_merge)
summary_writer.add_summary(summary_str, i)
Aussi, vous pouvez simplement évaluer et imprimer la valeur actuelle:
print(sess.run(Z))
J'ai pu résoudre mes problèmes avec NaN en éliminant toutes mes couches d'abandon dans le modèle de réseau. Je pensais que, pour une raison quelconque, une unité (neurone?) Du réseau avait perdu trop de connexions d'entrée (elle avait donc zéro après l'abandon), de sorte que lorsque l'information est transmise, sa valeur est NaN. Je ne vois pas comment cela pourrait se produire maintes et maintes avec abandon = 0,8 sur les couches de plus de cent unités chacune, le problème a donc probablement été résolu pour une raison différente. Quoi qu'il en soit, commenter les couches de décrochage a résolu mon problème.
EDIT: Oups! J'ai réalisé que j'avais ajouté une couche de suppression après ma dernière couche de sortie composée de trois unités. Maintenant cela a plus de sens. Alors ne fais pas ça!
Mise en œuvre actuelle de tfdbg.has_inf_or_nan
semble ne pas casser immédiatement après avoir touché un tenseur contenant NaN
. Lorsque cela s’arrête, la vaste liste de tenseurs affichée est non triée par ordre d’exécution. Un hack possible pour trouver la première apparition de Nan
s consiste à vider tous les tenseurs dans un répertoire temporaire et à les inspecter par la suite. Voici un exemple assez rapide exemple pour le faire. (En supposant que NaN
s apparaissent dans les premières exécutions)