如何在TensorFlow中调试NaN值?

Pin*_*hio 45 python machine-learning neural-network conv-neural-network tensorflow

我正在运行TensorFlow,而我碰巧有一些产生NaN的东西.我想知道它是什么,但我不知道该怎么做.主要问题是在"正常"程序程序中,我只是在执行操作之前编写一个print语句.TensorFlow的问题在于我不能这样做,因为我首先声明(或定义)图形,因此向图形定义添加print语句没有帮助.是否有任何规则,建议,启发式方法,以及追踪可能导致NaN的原因的任何事情?


在这种情况下,我更准确地知道要查看哪一行,因为我有以下内容:

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) 
Run Code Online (Sandbox Code Playgroud)

当这一行存在时,我知道它返回由我的摘要编写者声明的NaN.为什么是这样?有没有办法至少探索Z在其平方根后具有什么价值?


对于我发布的具体示例,我试过tf.Print(0,Z)但没有成功,它什么都没打印.如:

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) 
Run Code Online (Sandbox Code Playgroud)

我实际上不明白tf.Print应该做什么.为什么需要两个参数?如果我想打印1张张,为什么我需要传递2?对我来说似乎很奇怪.


我正在查看函数tf.add_check_numerics_ops()但它没有说明如何使用它(加上文档似乎没有超级帮助).有谁知道如何使用它?


由于我对评论数据的评论可能不好,我使用的是标准的MNIST.但是,我正在计算一个积极的数量(成对的eucledian距离)然后平方根.因此,我不会看到数据具体如何成为一个问题.

Phi*_*ock 23

有几个原因为什么你可以获得NaN结果,通常是因为学习率太高,但还有很多其他原因,例如输入队列中的损坏数据或0计算日志.

无论如何,用你所描述的打印调试不能通过简单的打印来完成(因为这只会导致在图形内打印张量信息而不打印任何实际值).

但是,如果使用tf.print作为构建图形的操作(tf.print),那么当图形执行时,您将获得打印的实际值(并且观察这些值以调试和理解行为是一个很好的练习你的网).

但是,您使用print-statement并不完全正确.这是一个op,所以你需要传递一个张量并请求你需要在执行图中使用的结果张量.否则,op将不会被执行并且不会发生打印.试试这个:

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)
Run Code Online (Sandbox Code Playgroud)

  • 如果第二个Z是数据,为什么必须传递第一个Z?从本质上讲,`tf.Print`的API令人困惑.为什么我们需要两个输入参数来打印一个单独的东西? (6认同)
  • 这是一个对某些张量x有用的小片段:DEBUGGING = False`x = x,如果不是DEBUGGING其他tf.Print(x,[x],'x的值:')` (2认同)

Ler*_*ang 11

我曾经发现,精确定位nan和infs的位置要比修复bug困难得多。作为@scai答案的补充,我想在这里补充几点:

您可以通过以下方式导入调试模块:

from tensorflow.python import debug as tf_debug
Run Code Online (Sandbox Code Playgroud)

比任何印刷品或断言要好得多。

您可以通过以下方式更改会话的包装器来添加调试功能:

sess = tf_debug.LocalCLIDebugWrapperSession(sess)
sess.add_tensor_filter("has_inf_or_nan", tf_debug.has_inf_or_nan)
Run Code Online (Sandbox Code Playgroud)

然后,您将提示命令行界面,然后输入: run -f has_inf_or_nanlt -f has_inf_or_nan查找nans或infs的位置。第一个是发生灾难的第一个地方。通过变量名,您可以跟踪代码中的原点。

参考:https : //developers.googleblog.com/2017/02/debug-tensorflow-models-with-tfdbg.html


cha*_*255 8

看起来你可以在完成图表后调用它.

check = tf.add_check_numerics_ops()

我认为这将添加所有浮点运算的检查.然后在会话运行功能中,您可以添加检查操作.

sess.run([check, ...])


sca*_*cai 8

从版本0.12开始,TensorFlow附带一个名为的内置调试器tfdbg.它优化了调试此类错误数值问题(如infnan)的工作流程.文档位于:https: //www.tensorflow.org/programmers_guide/debugger


小智 5

首先,您需要检查您输入的数据是否正确。在大多数情况下,这就是原因。但并非总是如此,当然。

我通常使用 Tensorboard 来查看训练时发生的情况。所以你可以看到每一步的值

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)
Run Code Online (Sandbox Code Playgroud)

您也可以简单地评估并打印当前值:

 print(sess.run(Z))
Run Code Online (Sandbox Code Playgroud)


fxt*_*cle 5

对于 TensorFlow 2,将一些注入x=tf.debugging.check_numerics(x,'x is nan')到您的代码中。InvalidArgument如果x有任何不是数字 (NaN) 或无穷大 (Inf) 的值,它们将抛出错误。

哦,对于下一个在寻找 TF2 NaN 问题时发现这个问题的人,我的情况证明是一个爆炸梯度。梯度本身达到了 1e+20,这还不是 NaN,但是将其添加到变量中却变得太大了。我所做的诊断是

gradients = tape.gradient(loss, training_variables)
for g,v in zip(gradients, training_variables):
  tf.print(v.name, tf.reduce_max(g))
optimizer.apply_gradients(zip(gradients, training_variables))
Run Code Online (Sandbox Code Playgroud)

这揭示了过大的数字。在 CPU 上运行完全相同的网络运行良好,但在我工作站的 GTX 1080 TI 上却失败了,因此导致 CUDA 数值稳定性问题可能是根本原因。但由于它只是偶尔发生,我通过以下方式对整个事情进行了录音:

gradients = tape.gradient(loss, training_variables)
gradients = [tf.clip_by_norm(g, 10.0) for g in gradients]
optimizer.apply_gradients(zip(gradients, training_variables))
Run Code Online (Sandbox Code Playgroud)

这只会将爆炸梯度剪辑到一个合理的值。对于梯度始终很高的网络,这无济于事,但由于幅度只是偶尔高,这解决了问题,现在网络在 GPU 上也能很好地训练。