TensorFlow官方教程翻译:TensorFlow调试器
TensorFlow调试器是TensorFlow专门的调试器。它提供运行的TensorFlow的图其内部的结构和状态的可见性。从这种可见性中获得的洞察力有利于调试各种模型在训练和推断中出现的错误。
这个教程将展现tfdbg的命令行界面的功能,并聚焦于如何调试在TensorFLow的模型开发中经常发生的一种错误:错误数值(nan和inf)导致的训练失败。
为了观察这个问题,在没有调试器的情况下,运行下列代码:
python -mtensorflow.python.debug.examples.debug_mnist
这个代码训练了一个简单的神经网络用来识别MNIST数字图片。请注意,准确率在第一次训练后,微微上升,但是接着停滞在了一个比较低(近机会)的水平:
抓抓脑袋,你怀疑肯定是在训练中,图中的一些节点产生了比如inf和nan这样的坏的数值。TensorFlow的计算图模型使得其不用用类似于Python的pdb等多用途的调试器来调试例如模型内部状态。tfdbg专门用来诊断这中类型的问题,并查明问题首先暴露出来的那个确切的节点。
01 用tfdbg包装TensorFlow会话
为了在我们的样例中添加tfdbg的支持,我们只需要添加下列三行代码,这三行代码会在提供了—debug标识的时候,用一个调试器包装器来包装会话对象。
这个包装器有会话对象相同的接口,因此启动调试不需要对于代码做其他的修改。但是包装器提供其他的功能,包括:
l在每次run()调用之前或者之后,提供一个基于终端的用户接口,让你控制运行,并检查图的内部状态
l让你可以为张量的数值注册特殊的过滤器,以此来方便问题的诊断。
在这个例子中,我们将注册一个称作tfdbg.has_inf_or_nan的张量过滤器,它仅仅确定了图中的任何一个中间张量,是否存在任何的nan或者inf数值。(这个过滤器是一个普遍的样例,我们使用debug_data模块来运行它)
defhas_inf_or_nan(datum,tensor):
returnnp.any(np.isnan(tensor))ornp.any(np.isinf(tensor))
建议:你可以写你自己定义的过滤器。参见DebugDumpDir.find()的API文档获取额外的信息。
02 用TFdbg调试模型训练
让我们在开启调试的情况下,再次训练模型。运行上面提到的指令,这次增加—debug标志:
python-m tensorflow.python.debug.examples.debug_mnist--debug
调试包装器会话会在将要运行第一次run()调用的时候,弹出给你,并有关于获取的张量和供给字典的信息显示在屏幕上。
这就是我们所提到的运行-启动用户接口。如果屏幕尺寸太小,不足以显示消息的整个内容,你可以调整它的大小,或者用PageUp/PageDown/Home/End键来浏览屏幕上的输出。
正如屏幕所显示的,第一次调用run()使用测试集计算准确率,也就是一个图上的前向传递。你可以输入run(或者它的简写r)来启动run()的调用。在终端上同样支持鼠标事件,你可以只点击屏幕左上角的带下划线的run来运行。
这会在run()调用刚结束的时候启动另外一个屏幕,它会显示所有这次运行中被转储的中间张量。(这些张量也可以通过在你执行run之后,运行命令lt来获取。)这被称作运行-结束用户接口。
03 tfdbg CLI常用指令
在tfdbg>弹出界面尝试下列命令(参考代码tensorflow/python/debug/examples/debug_mnist.py)
在第一调用run()的时候,没有有问题的数据值。你可以使用run命令或者他的缩写r,来接着运行下一个run。
建议:如果你重复的输入run或者r,你将会让run()调用进入序列模式。
你也可以使用-t参数来指定一次运行run()的次数,例如
tfdbg>run-t10
除了重复的输入run,并在每次run()之后,手动的在运行-结束用户界面搜索nan和inf,你还可以使用下列命令让调试器不用再运行前和运行后停止并弹出,并重复的执行run()调用知道nan或者inf第一次出现在图中。这类似于一些程序语言调试器的条件中断:
tfdbg>run-f has_inf_or_nan
注意:这个能成功是因为我们之前为nan和inf注册了一个名为has_inf_or_nan的过滤器(如前所述)。如果你注册了其他的过滤器,那么你也可以让tfdbg运行直到任何张量被传递给过滤器,比如:
# In python code:
sess.add_tensor_filter('my_filter',my_filter_callable)
# Run at tfdbg run-start prompt:
tfdbg>run-f my_filter
在你输入run –f has_inf_or_nan之后,你会看到如下显示,红色标题行意味着tfdbg在一个run()调用后立即停止了,因为这个run调用产生了中间张量,传递给了指定的过滤器has_inf_or_nan:
如屏幕显示所示,has_inf_or_nan过滤器在第四次运行run()的时候,第一次被传值:一个Adam优化器前馈训练在图中传递了这个值。在这次运行中,36个(总共95个)中间张量包含nan或者inf值。这些张量按照时间先后顺序被列出,并且左边显示了他们的时间戳。在列表顶部,你可以看到首先出现坏的数据值的第一个张量:cross_entropy/Log:0
查看张量的数值,点击下划线的张量的名字cross_entropy/Log:0,或者输入相同的指令
tfdbg>pt cross_entropy/Log:0
向下滚动一点,你会注意到一些分散的inf数值。如果inf和nan的例子很难用肉眼看出,你可以使用下列指令运行正则表达式搜索,并且高亮输出:
tfdbg>/inf
或者:
tfdbg>/(inf|nan)
为什么出现了这些无穷大?为了更进一步的调试,显示更多关于节点cross_entropy/Log的信息,可以通过点击顶部下划线的node_info菜单选项或者输出相同的命令:
tfdbg>ni cross_entropy/Log
你可以看到这个节点由个操作类型为Log,并且它的输入节点是softmax/Softmax。运行下列指令来更进一步的查看输入张量:
tfdbg>pt softmax/Softmax:0
检查输入张量的值,并搜索检查其是否有零:
tfdbg>/0\.000
确实有零的存在。现在清楚了,坏的数据值的源头是节点cross_entropy/Log计算零的对数值。为了找到相关的Python源码,使用-t标志的ni参数命令来追溯节点的构建:
tfdbg>ni-t cross_entropy/Log
如果你使用在屏幕顶部的可点击的node_info菜单项目,那么-t标志是默认使用的。
从追溯中可以看到,操作是在代码debug_mnist.py:105-106行创建的:
diff=y_*tf.log(y)
*tfdbg的功能使得追溯张亮和操作到Python源文件中每行变得容易。它可以用操作或者张量注释创建它们的Python文件的每行。为了使用这个功能,只需点击ni –t 指令的堆栈输出中的有下划线的数字,或者使用ps(或者print_source)命令,比如ps /path/to/source.py。下面的屏幕截图就是一个ps输出的例子:
对于tf.log的输入运用一个数值剪切可以解决这个问题:
diff=y_*tf.log(tf.clip_by_value(y,1e-8,1.0))
现在,再次尝试训练,并使用--debug:
python-m tensorflow.python.debug.examples.debug_mnist--debug
输入在弹出的tfdbg>界面,输入run –f has_inf_or_nan,从而确定没有张量被标记为含有nan或者inf的数值,这样准确率就不再停滞不前了。成功!
调试tf-learn评估器
对于在tfdbg上调试tf.contrib.learn评估器和实验的文档,请参考How to Use TensorFlow
Debugger (tfdbg) with tf.contrib.learn.
04 离线调试远程运行的会话
有时候,你的模型运行在远程的机器或者进程上,你无法通过终端接触到。为了在这种情况下运行模型调试,你可以使用tfdbg的offline_analyzer。它运行在转储的数据字典上。如果你运行的进程是用Python写的,你可以使用tf.dbg.watch_graph方法,在调用Session.run()的时候,配置RunOption的原型。这会导致,在Session.run()被调用时,中间的张量和运行时的图被转储到你选择的一个共享存储位置上。例如:
fromtensorflow.python.debugimportdebug_utils
# ... Code where your session and graph are set up...
run_options=tf.RunOptions()
debug_utils.watch_graph(
run_options,
session.graph,
debug_urls=["file:///shared/storage/location/tfdbg_dumps_1"])
# Be sure to use different directories for different run() calls.