nan tensorflow

¿Tensorflow NaN error?



(12)

Estoy usando TensorFlow y modifiqué el ejemplo del tutorial para tomar mis imágenes RGB.

El algoritmo funciona perfectamente desde el primer momento en el nuevo conjunto de imágenes, hasta que de repente (aún convergiendo, generalmente tiene una precisión del 92%), falla con el error de que ReluGrad recibió valores no finitos. La depuración muestra que no ocurre nada inusual con los números hasta que de repente, por razones desconocidas, se produce el error. Agregando

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())

como código de depuración para cada bucle, produce el siguiente resultado:

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

Dado que ninguno de mis valores es muy alto, la única forma en que puede ocurrir un NaN es mediante un 0/0 mal manejado, pero como este código tutorial no hace divisiones u operaciones similares, no veo otra explicación que la que proviene de El código interno de TF.

No tengo idea de qué hacer con esto. ¿Alguna sugerencia? El algoritmo está convergiendo muy bien, su precisión en mi conjunto de validación estaba aumentando constantemente y solo alcanzó el 92.5% en la iteración 8600.


Una alternativa libre de sesgos.

Muchas de las otras soluciones usan recorte para evitar un gradiente indefinido. Dependiendo de su problema, el recorte presenta un sesgo y puede no ser aceptable en todos los casos. Como lo demuestra el siguiente código, solo necesitamos manejar el punto de discontinuidad, no la región cercana.

Respuesta especifica

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)

¿Pero funcionó?

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.

(Nota: publicación cruzada dup eliminada).

Receta general

Use un tf.where interno para asegurarse de que la función no tenga asíntota. Es decir, modifique la entrada a la función de generación de inf de modo que no se pueda crear inf. Luego use un segundo tf.where para seleccionar siempre la ruta de código válida. Es decir, implemente la condición matemática como lo haría "normalmente", es decir, la implementación "ingenua".

En el código Python, la receta es:

En lugar de esto:

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

Hacer esto:

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

Ejemplo

Suponga que desea calcular:

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

Una implementación ingenua da como resultado NaNs en el gradiente, es decir,

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))

¿Funciona?

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.

El patrón básico para evitar gradientes de NaN cuando se usa tf.where es llamar a tf.where dos veces. El tf.where más tf.where garantiza que el resultado f(x) sea ​​siempre finito. El tf.where más tf.where asegura que se tf.where el resultado correcto. Para el ejemplo en ejecución, el truco se desarrolla así:

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))

¿Pero funcionó?

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).


A veces usa la función tf.sqrt() sin agregar una pequeña constante 1e-10 , lo que induce este problema nan .


Además de todas las excelentes respuestas anteriores, agregaré las mías. Es un escenario menos común de encontrar, pero causa NaN: divide por cero .

En mi red para una tarea de PNL, hay una capa que agrupa en promedio . Es decir, cada dato es una secuencia de tokens. Mi capa incrusta algunos tokens y luego calcula el promedio del vector incrustado.

El cálculo promedio se codifica como

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

Aquí el pad es una ficha simulada que uso en el procesamiento por lotes.

Ahora, si algunos datos contienen una lista de tokens vacía (por cualquier razón), su longitud (el denominador en el fragmento de código anterior) sería 0. Luego causa un problema de división por cero y el NaN permanecerá en las siguientes capas / pasos de optimización .

En caso de que alguien se encuentre con este problema, utilicé tf.where para suavizar esa longitud:

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

Básicamente, esto trata todos los datos con la lista de tokens de longitud 0 como de longitud 1, y evita el problema de NaN.


Agregaré aquí uno de mis problemas anteriores con NaNs. Estaba usando la función sigmoidea como la activación de la última capa de mi red. Sin embargo, la función de activación sigmoidea usa la función exponencial para ser calculada y obtuve algunos números realmente grandes ingresando al sigmoide.

Resultó en gradientes infinitos y algunos NaN comenzaron a aparecer.


Aquí está la implementación de las pérdidas de entropía cruzada binarias (sigmoideas) y categóricas (softmax) en TensorFlow 1.1:

Como se puede ver en el caso binario, consideran algunos casos especiales para lograr la estabilidad numérica:

# 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)


En realidad, el recorte no es una buena idea ya que evitará que el gradiente se propague hacia atrás cuando se alcance el umbral. En cambio, podemos agregar un poco de constante a la salida de softmax.

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv + 1e-10))


En realidad, resultó ser algo estúpido. Estoy publicando esto en caso de que alguien más se encuentre con un error similar.

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

es en realidad una forma horrible de calcular la entropía cruzada. En algunas muestras, ciertas clases pueden excluirse con certeza después de un tiempo, lo que resulta en y_conv = 0 para esa muestra. Eso normalmente no es un problema, ya que no está interesado en ellos, pero en la forma en que se escribe cross_entropy allí, produce 0 * log (0) para esa muestra / clase en particular. De ahí el NaN.

Reemplazándolo con

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

Resuelto todos mis problemas.



He estado usando Tensorflow Estimator, que creo que explica esa división por cero y otros problemas de estabilidad numérica, y ocasionalmente obtengo este error ( ERROR:tensorflow:Model diverged with loss = NaN during training ). La mayoría de las veces cuando obtengo esto es porque mis entradas incluyen nan s. Entonces: asegúrese de que sus marcos de datos de entrada (o lo que sea que use) no tengan valores de NaN ocultos en algún lugar de ellos.


Me estaba volviendo loco algunas veces y no otras veces mientras trabajaba en una red de retroalimentación estándar. Anteriormente he usado un código TensorFlow similar y funcionó bien.

Resulta que importé los nombres de las variables por accidente. Entonces, tan pronto como se seleccionó la primera fila (los nombres de las variables) en un lote, comenzaron las pérdidas nan. ¿Quizás estar atento a eso?


Si y_conv es el resultado de un softmax, digamos, y_conv = tf.nn.softmax(x) , entonces una solución aún mejor es reemplazarlo con log_softmax :

y = tf.nn.log_softmax(x) cross_entropy = -tf.reduce_sum(y_*y)


Usé LSTM para secuencias largas y obtuve gradientes nan. Ninguna de estas respuestas me ayudó. Pero se me ocurrieron tres soluciones propias. Espero que sean útiles para otras personas que vinieron aquí desde la búsqueda de Google.

  1. El recorte de degradado no me ayudó porque los degradados se convirtieron en nan en una actualización por lotes. En este caso, puede reemplazar nans con ceros con tales líneas:

    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 desea rastrear si aparecieron nans, puede usar este código:

    was_nan = tf.reduce_any(tf.convert_to_tensor([tf.reduce_any(tf.is_nan(g)) for g in grads]))

  2. Reemplace LSTMCell con LayerNormBasicLSTMCell, una celda LSTM con norma de capa, algo similar a la norma de lote entre pasos de tiempo.

  3. Si usa un abandono de estado recurrente regular, puede reemplazarlo con "Abandono recurrente sin pérdida de memoria". Código:

    LayerNormBasicLSTMCell(neurons, dropout_keep_prob=0.8)

    Tenga en cuenta que también puede activar la función de abandono solo sin la normalización de capa:

    LayerNormBasicLSTMCell(neurons, layer_norm=False, dropout_keep_prob=0.8)