TensorFlow Nan bug?

j'utilise TensorFlow et j'ai modifié le tutoriel exemple pour prendre mes images RGB.

l'algorithme fonctionne parfaitement hors de la boîte sur le nouvel ensemble d'image, jusqu'à ce que tout à coup (encore convergeant, il est d'environ 92% de précision Habituellement), il se brise avec l'erreur qui a reçu des valeurs non-finies. Débogage montre que rien d'inhabituel se passe avec les nombres jusqu'à très soudain, pour une raison inconnue, l'erreur est levée. Ajouter

print "max W vales: %g %g %g %g"%(tf.reduce_max(tf.abs(W_conv1)).eval(),tf.reduce_max(tf.abs(W_conv2)).eval(),tf.reduce_max(tf.abs(W_fc1)).eval(),tf.reduce_max(tf.abs(W_fc2)).eval())
print "max b vales: %g %g %g %g"%(tf.reduce_max(tf.abs(b_conv1)).eval(),tf.reduce_max(tf.abs(b_conv2)).eval(),tf.reduce_max(tf.abs(b_fc1)).eval(),tf.reduce_max(tf.abs(b_fc2)).eval())

comme code de débogage pour chaque boucle, donne la sortie suivante:

Step 8600
max W vales: 0.759422 0.295087 0.344725 0.583884
max b vales: 0.110509 0.111748 0.115327 0.124324
Step 8601
max W vales: 0.75947 0.295084 0.344723 0.583893
max b vales: 0.110516 0.111753 0.115322 0.124332
Step 8602
max W vales: 0.759521 0.295101 0.34472 0.5839
max b vales: 0.110521 0.111747 0.115312 0.124365
Step 8603
max W vales: -3.40282e+38 -3.40282e+38 -3.40282e+38 -3.40282e+38
max b vales: -3.40282e+38 -3.40282e+38 -3.40282e+38 -3.40282e+38

comme aucune de mes valeurs n'est très élevée, la seule façon qu'une NaN peut arriver est par un 0/0 mal manipulé, mais puisque ce code tutoriel ne fait pas de divisions ou opérations similaires, Je ne vois pas d'autre explication que celle qui vient du code TF interne.

Je ne sais pas quoi faire avec ça. Toutes les suggestions? L'algorithme est convergent joliment, son la précision de mon jeu de validation augmentait régulièrement et atteignait tout juste 92,5% à l'itération 8600.

48
demandé sur Salvador Dali 2015-11-14 22:01:58

9 réponses

en Fait, il s'est avéré être quelque chose de stupide. Je poste ça au cas où quelqu'un d'autre rencontrerait une erreur similaire.

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))

est en fait une façon horrible de calculer l'entropie croisée. Dans certains échantillons, certaines classes peuvent être exclues avec certitude au bout d'un certain temps, ce qui donne y_conv=0 pour cet échantillon. Ce n'est normalement pas un problème puisque vous n'êtes pas intéressé par ceux-ci, mais de la façon dont cross_entropy est écrit là, il donne 0*log(0) pour cela échantillon particulier/classe. D'où la NaN.

le remplaçant par

cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv,1e-10,1.0)))

a résolu tous mes problèmes.

116
répondu user1111929 2015-11-14 20:49:12

en fait, la coupure n'est pas une bonne idée car elle empêchera le gradient de se propager vers l'arrière lorsque le seuil est atteint. Au lieu de cela, nous pouvons ajouter un peu de constante à la sortie softmax.

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-10))
24
répondu Young Geng 2016-07-30 11:04:25

si y_conv est le résultat d'un softmax, dire, y_conv = tf.nn.softmax(x) , alors une solution encore meilleure est de le remplacer par log_softmax :

y = tf.nn.log_softmax(x)
cross_entropy = -tf.reduce_sum(y_*y)
12
répondu mathguyjohn 2016-07-20 19:52:28
"1519160920 Un" parti pris alternative libre.

beaucoup d'autres solutions utilisent le découpage pour éviter un gradient Non défini. Selon votre problème, la coupure introduit un biais et peut ne pas être acceptable dans tous les cas. Comme le montre le code suivant, nous n'avons besoin que de gérer le point de discontinuité, pas la région qui l'entoure.

Réponse Spécifique

def cross_entropy(x, y, axis=-1):
  safe_y = tf.where(tf.equal(x, 0.), tf.ones_like(y), y)
  return -tf.reduce_sum(x * tf.log(safe_y), axis)

def entropy(x, axis=-1):
  return cross_entropy(x, x, axis)

mais ça a marché?

x = tf.constant([0.1, 0.2, 0., 0.7])
e = entropy(x)
# ==> 0.80181855
g = tf.gradients(e, x)[0]
# ==> array([1.30258512,  0.60943794, 0., -0.64332503], dtype=float32)  Yay! No NaN.

(Note: supprimé dup cross-post .)

Recette Générale

utilisez un tf.where interne pour s'assurer que la fonction n'a pas d'asymptote. C'est-à-dire modifier l'entrée de la fonction génératrice inf de sorte qu'aucun inf ne puisse être créé. Ensuite, utilisez un second tf.where pour toujours sélectionner le chemin de code valide. C'est-à-dire, mettre en œuvre la condition mathématique comme vous le feriez "normalement", c'est-à-dire l'implémentation "naïve".

en code Python, la recette est:

au lieu de ceci:

tf.where(x_ok, f(x), safe_f(x))

Faites ceci:

safe_x = tf.where(x_ok, x, safe_x)
tf.where(x_ok, f(safe_x), safe_f(x))

exemple

supposons que vous voulez calculer:

f(x) = { 1/x, x!=0
       { 0,   x=0

naïve de la mise en œuvre des résultats de NaNs le dégradé, c'est à dire,

def f(x):
  x_ok = tf.not_equal(x, 0.)
  f = lambda x: 1. / x
  safe_f = tf.zeros_like
  return tf.where(x_ok, f(x), safe_f(x))

est-ce que ça marche?

x = tf.constant([-1., 0, 1])
tf.gradients(f(x), x)[0].eval()
# ==> array([ -1.,  nan,  -1.], dtype=float32)
#  ...bah! We have a NaN at the asymptote despite not having
# an asymptote in the non-differentiated result.

le schéma de base pour éviter les gradients NaN en utilisant tf.where est d'appeler tf.where deux fois. Le plus profond tf.where assure que le résultat f(x) est toujours fini. Le tf.where latéral assure le choix du résultat correct. Pour l'exemple, le tour se joue comme ceci:

def safe_f(x):
  x_ok = tf.not_equal(x, 0.)
  f = lambda x: 1. / x
  safe_f = tf.zeros_like
  safe_x = tf.where(x_ok, x, tf.ones_like(x))
  return tf.where(x_ok, f(safe_x), safe_f(x))

mais ça a marché?

x = tf.constant([-1., 0, 1])
tf.gradients(safe_f(x), x)[0].eval()
# ==> array([-1.,  0., -1.], dtype=float32)
# ...yay! double-where trick worked. Notice that the gradient
# is now a constant at the asymptote (as opposed to being NaN).
11
répondu jvdillon 2017-11-20 16:48:59

vous essayez de calculer entropie croisée en utilisant la formule standard. Non seulement la valeur n'est pas définie quand x=0 , mais elle est aussi numériquement instable.

il est préférable d'utiliser tf.nn.softmax_cross_entropy_with_logits ou si vous voulez vraiment utiliser la formule artisanale, à tf.clip_by_value zéros à très petit nombre dans le journal.

1
répondu Salvador Dali 2017-04-29 05:32:47

Voici la mise en œuvre de l'entropie binaire (sigmoid) et catégorique (softmax) pertes dans TensorFlow 1.1:

Comme on peut le voir dans le binaire cas ils considèrent quelques cas spéciaux pour atteindre la stabilité numérique:

# The logistic loss formula from above is
#   x - x * z + log(1 + exp(-x))
# For x < 0, a more numerically stable formula is
#   -x * z + log(1 + exp(x))
# Note that these two expressions can be combined into the following:
#   max(x, 0) - x * z + log(1 + exp(-abs(x)))
# To allow computing gradients at zero, we define custom versions of max and
# abs functions.
zeros = array_ops.zeros_like(logits, dtype=logits.dtype)
cond = (logits >= zeros)
relu_logits = array_ops.where(cond, logits, zeros)
neg_abs_logits = array_ops.where(cond, -logits, logits)
return math_ops.add(relu_logits - logits * labels,
                    math_ops.log1p(math_ops.exp(neg_abs_logits)),
                    name=name)
1
répondu Lenar Hoyt 2017-05-16 09:37:11

j'ai utilisé LSTM pour de longues séquences et j'ai eu des gradients de nan. Aucune de ces réponses m'ont aidé. Mais je suis venu avec trois propres solutions. J'espère qu'ils seront utiles à d'autres gens qui sont venus ici de recherche de google.

  1. la coupure de Gradient ne m'a pas aidé parce que les gradients ont transformé nan dans une mise à jour de lot. Dans ce cas, vous pouvez remplacer nans par des zéros avec de telles lignes:

    opt = tf.train.AdamOptimizer(args.lr)
    grads = opt.compute_gradients(loss)
    grads2 = [(tf.where(tf.is_nan(grad), tf.zeros(grad.shape), grad), var) for grad, var in grads]
    opt_op = opt.apply_gradients(grads2)
    

    si vous voulez pister si nans vous pouvez utiliser ce code:

    was_nan = tf.reduce_any(tf.convert_to_tensor([tf.reduce_any(tf.is_nan(g)) for g in grads]))
    
  2. remplacer LSTMCell avec LayerNormBasicLSTMCell - an LSTM cell with layer norm - something similar to batch norm between timesteps.

  3. si vous utilisez l'abandon régulier de l'état récidivant, vous pouvez le remplacer par "abandon récidivant sans perte de mémoire". Code:

    LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)
    

    notez que vous pouvez également activer la fonction abandon seul sans normalisation de la couche:

    LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)
    
0
répondu alyaxey 2017-12-06 19:33:17

je recevais nans parfois et pas d'autres fois tout en travaillant sur un réseau de feed-forward standard. J'ai déjà utilisé un code TensorFlow similaire et il a fonctionné très bien.

il s'avère que j'ai importé les noms variables par accident. Ainsi, dès que la première ligne (les noms des variables) a été sélectionnée dans un lot, les pertes de nan ont commencé. Peut-être garder un oeil pour qui?

0
répondu tf.nn.michael 2018-02-27 01:45:08

outre toutes les grandes réponses ci-dessus, j'ajouterai la mienne. C'est un scénario moins commun à rencontrer, mais qui cause NaN: diviser par zéro .

dans mon réseau pour une tâche NLP, il y a une couche qui fait pooling moyenne . À savoir, chaque donnée est une séquence de jetons. Ma couche fait un peu d'embedding de jeton et calcule ensuite la moyenne du vecteur embedded.

le calcul moyen est codé comme suit:

tf.reduce_sum(embedded)/tf.reduce_sum(tf.not_equal(input, pad)) 

ici pad est un jeton factice que j'utilise dans le traitement par lots.

maintenant si certaines données contiennent une liste de tokens vide (pour quelque raison que ce soit), sa longueur (le dénominateur dans l'extrait de code ci-dessus) serait 0. Ensuite, il provoque un diviser par zéro problème et la NaN restera dans toutes les couches suivantes/ étapes d'optimisation.

au cas où quelqu'un rencontrerait ce problème, j'ai utilisé tf.where pour lissez ces longueurs:

sum_embedding = tf.reduce_sum(embedded, 1)
embedding_length = tf.reduce_sum(tf.cast(tf.not_equal(input, pad), dtype=tf.float32), axis=1, keep_dims=True)
embedding_length_smoothed = tf.where(tf.greater(embedding_length, 0.0), embedding_length, tf.ones(tf.shape(embedding_length)))
avg_embedding = sum_embedding / embedding_length_smoothed

essentiellement ceci traite toutes les données avec 0-length token list comme étant de longueur 1, et évite la question de NaN.

0
répondu Camuslu 2018-07-02 14:49:04