前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >将Tensorflow调试时间减少90%

将Tensorflow调试时间减少90%

作者头像
IT大咖说
发布2020-04-21 14:22:33
1.2K0
发布2020-04-21 14:22:33
举报
文章被收录于专栏:IT大咖说IT大咖说

今天为大家介绍可应用于Tensorflow代码的VeriTensor代码方法,以使调试起来更加有效。

Image from Pixabay

Tensorflow代码很难调试。我以前花了数周时间调试代码。更糟糕的是,在大多数情况下,我不知道如何进行-我可以看到我的代码没有训练好,但是我不知道是因为该模型无法学习,或者是由于实现存在错误。如果是后者,错误在哪里?

这是许多机器学习从业者面临的挫败感。本文介绍了我设计用来调试Tensorflow代码的VeriTensor方法。VeriTensor基于"Design by Contract"方法。它包括三种技术。这种方法将我的调试时间从数周缩短至数小时,提高了90%以上。更好的是,在完成调试后,我知道代码中没有错误。真是太好了!

通过断言进行规范

有效调试的关键是编写规范以定义代码的正确性。规范描述了代码应该执行的操作,而实现则描述了如何执行代码。一段代码仅在其规范方面是正确的。在Python中,您可以使用断言来编写规范,如下面所示。

代码语言:javascript
复制
def square_root(x):
    assert x >= 0
    result = math.sqrt(x)
    assert result == math.sqrt(x)
    return result

你说,但是很难写断言。我同意你的看法。我花了15年的时间用断言验证代码。我开发了基于断言的技术,Microsoft将其包含在Bing搜索引擎中。我知道规格可能会很棘手。这就是为什么当我开发VeriTensor时,我确保它是实用的。

有效调试的关键是通过断言告诉调试器代码应该做什么。

VeriTensor方法

VeriTensor包括3种技术。您可以在编写Tensorflow代码后应用它们。这意味着这些技术是很简单的,您无需从头开始就可以使用它们。

技术1:张量形状断言

引入张量时,需要编写断言以检查其形状。关于张量形状的错误假设通常会导致棘手的错误。而且TensorFlow的广播机制可以将它们隐藏得很深。

例如,在诸如Deep Q Network(DQN)之类的回归算法中,您有一个来自神经网络的预测张量,目标张量和损失张量:

代码语言:javascript
复制
prediction = q_function.output_tensor
target = reward + gamma* bootstrapped_q
loss = tf.reduce_mean(tf.square(target - prediction))

该预测代表预测值。目标张量表示期望值,由奖励张量和bootstrapped_q张量计算得出,而γ是浮点数。损失张量表示我们的训练损失为均方误差。

现在,我们为引入的张量添加断言,如下清单所示。这些断言检查预测的形状和目标的形状必须在batch_size和action_dimension方面相同。这些是DQN算法中使用的一些数量。如果您不熟悉它们,不必担心。这里重要的是我们编写断言来检查张量形状。最后,由于损失评估为数字,因此断言声明其形状为[]。

代码语言:javascript
复制
prediction = q_function.output_tensor
assert prediction.shape.to_list() == [batch_size, action_dimension]

target = reward + gamma* bootstrapped_q
assert target.shape.to_list() == [batch_size, action_dimension]

loss = tf.reduce_mean(tf.square(target - prediction))
assert loss.shape.to_list() == []

如果张量的形状与它们的期望值不匹配,则会违反声明。您不会相信违反形状声明的可能性会如此的大!

技术2:张量间的依赖

Tensorflow程序是一个计算图。因此,您需要确保正确构建张量图。如果张量B的值取决于张量A的值(例如B = A + 1),则图中的节点B到节点A之间应该有一条边。

您使用TensorBoard可视化Tensorflow图。但是,了解此图很困难,因为实际的张量图通常具有数百个节点和边。下图显示了典型的TensorBoard可视化。

这里的关键见解是:要检查张量图的结构,只需要可视化所引入的张量之间的关系即可。而且,您通常可以将许多张量分组到一个节点中。例如,在具有许多变量的多层神经网络中,每个变量都是张量。但是您只需要将整个网络可视化为一个节点。

我开发了Python包VeriTensor,以简化张量图的可视化。我将很快将此程序包开源。它包含一个TensorGroupDependency类。此类允许您仅注册要可视化的张量。它为注册的张量生成一个新的,更小的可视化图像。

下一个清单显示了如何使用TensorGroupDependency。您首先调用add方法来注册张量。然后,调用generate_dot_representation方法为您提供可视化效果。此可视化仅显示已注册的张量及其相关性。

代码语言:javascript
复制
d = TensorGroupDependency()
d.add(prediction, 'prediction')
d.add(target, 'target')
d.add(bootstrapped_q, 'bootstrapped_q')
d.add(loss, 'loss')

# Generate tensor dependency graph in DOT notation.
dot = d.generate_dot_representation()
print(dot)

# Generate assertions describing the above dependency graph.
assertions = d.generate_assertions(target_exp='d')
print(assertions)

第1至5行创建一个张量依赖图对象,并注册要可视化其关系的张量。第8行和第9行以DOT语言生成并打印那些张量依赖关系,这些依赖关系可以以图形方式呈现:

让我们了解以上依赖关系图:

  • 图中的节点表示张量或张量集(例如神经网络中的所有变量)。
  • 如果B中的至少一个张量取决于A中的一个张量,则从节点B到节点A会有一个有向边。在我们的示例中,损耗张量取决于预测和目标张量。因此,从预测节点和目标节点到损失节点有两个方向性边缘。
  • 在每个节点中,您可以看到其种类,例如[Tensor],[Placeholder],[Variable]。
  • 在每个节点中,您还会看到张量形状,例如(None,1),表示二维张量,其中第一维为动态长度None,第二维为长度1。损耗张量具有形状(),因为它 是标量。

要检查图结构的正确性,您需要解释为什么每个边都存在。这意味着解释这些张量之间的依赖关系。如果您无法解释某些边的存在,则您脑海中的想法与您实际构建的图形之间会有差异。这通常表示一个错误。

解释完所有边缘之后,您可以通过调用generate_assertions方法来生成描述图的断言,如上面片段中的第12行所示。以下清单显示了生成的断言。它们描述了相同的依赖图。它们成为您代码的一部分,并将在以后的所有执行中进行检查:

代码语言:javascript
复制
d.assert_immediate_ancestors('target', {'bootstrapped_q', 'reward'})
d.assert_immediate_ancestors('prediction', set())
d.assert_immediate_ancestors('bootstrapped_q', set())
d.assert_immediate_ancestors('loss', {'prediction', 'target'})
d.assert_immediate_ancestors('reward', set())

顺便说一句,如果您在Tensorflow代码中精心设计了名称范围,并且在TensorBoard可视化文件中进行了认真的折叠,您将获得与上述库相同的功能。但我认为库很不错,因为:

  • 您很可能没有仔细设计名称范围-是吗?
  • 使用该库,您可以生成那些张量依赖断言,这将帮助您在以后的所有执行中进行调试。

技术3:张量方程评估

到目前为止,您已经验证了定义的张量之间的依赖关系。最后一步是检查张量是否执行正确的数值计算。例如,方程B = A +1和B = A -1都引入了从B到A的依存关系,因此它们的依存关系图是相同的。但是您需要指定B = A + 1是正确的实现。使用张量方程评估对算法中的每个方程执行以下操作:

  • 在每个优化步骤中,通过在session.run中添加它们来评估所涉及的张量。
  • 用这些张量求值以numpy编写相同的方程式,以计算所需的值。然后断言期望值与实际值相同。

接下来的清单显示了损失张量的张量方程评估。session.run会评估parameter_update_operations,这是您常用的东西,例如渐变下降步骤。除了这项常规工作之外,session.run现在还评估预测,目标和损失张量。您可以从这三个张量评估中计算出所需的损失。最后,您断言实际损失等于第4行和第5行的期望损失。请注意,第4行和第5行在Python世界中。在Python世界中,您可以使用循环,调用任意函数;它比Tensorflow世界中的方法容易得多。

代码语言:javascript
复制
prediction_, target_, loss_, _ = session.run(
    [prediction, target, loss, parameter_update_opeations],
    feed_dict={...})
desired_loss = np.mean(np.power(target_ - prediction_, 2))
np.testing.assert_almost_equal(actual=loss_, desired=desired_loss, decimal=3)

这些技术有效且实用吗?

我们已将这些技术应用于所有Tensorflow学习者。下表报告了我们花在验证五个模型上的时间以及发现的错误数量。

Table 1. Bugs detected with assertion techniques

"学习模块"列列出了机器学习模型的名称。这些是深度强化学习中的Actor-Critic算法。

"编码时间"列报告了我们花费在编写这些学习者代码上的时间(以小时为单位)。总共我们花了24个小时。

"验证时间"列报告了我们在验证上花费的时间。这包括编写断言,运行代码,观察断言冲突并修复检测到的错误。总共我们花了5个小时。换句话说,验证需要20%的工作量。

"检测到的错误"列是每种断言技术的细分。它显示了花费在每种技术上的时间百分比以及检测到的错误数量。总共,我们仅在5小时内检测到23个错误。更重要的是,应用这些技术后,我们知道我们的代码是正确的。

我们可以清楚地看到VeriTensor在检测错误方面很有效。

为什么VeriTensor对检测错误有效?

首先,它们要求您通过断言定义代码的正确性。编写规范并不是一个新主意,但VeriTensor使其实用:

  • 形状断言要求您写下所引入的张量的形状-简单!
  • 张量依赖性仅要求您关注引入的张量。在此阶段无需检查数值运算。这样可以将图形从数百个节点减少到十二个左右,从而使人类研究变得切实可行。自动断言生成减少了写下断言所需的时间。
  • 在张量方程评估中,您将检查Python世界中的每个方程。Python世界比Tensorflow世界更容易。

其次,在Tensorflow中发现错误的来源令人生畏。人们花费大部分时间来定位错误的来源。一旦知道了来源,通常即可轻松修复该错误。按顺序应用时,VeriTensor技术可帮助您定位故障。在张量依赖阶段有问题时,您会知道所有涉及的张量都具有正确的形状。当张量方程式有问题时,您就会知道依赖关系结构是正确的。简而言之,您可以更好地关注和定位每个问题。

第三,VeriTensor将Tensorflow代码调试从一门艺术变成了一个软件工程过程。如果遵循简单的任务清单,该过程将确保代码正确:

  • 为您引入的所有张量编写一个形状断言。
  • 解释这些张量之间的所有依赖关系边,并自动生成结构性断言。
  • 编写一个断言以检查算法中的每个方程。

验证和/或测试代码时的常见问题是知道如何进行和何时停止。您从代码的哪一部分开始?您应该检查哪些方面?经过足够的测试,您怎么知道?

我们的三种技术消除了这些疑虑。您一一应用它们,每种技术都有有限且可管理的步骤。步骤的数量受您在代码中引入的张量和方程式的数量限制(通常约为12个)。最后,您知道您的代码是正确的。这是一个工程过程,而不是一门艺术。不要忽略这种工程过程的力量。人们知道确切的步骤后,他们的效率就会大大提高。

不要忽略这种工程过程的力量。人们知道确切的步骤后,他们的效率就会大大提高。

验证代码正确性,而不是性能

我需要明确说明VeriTensor会验证代码的正确性。它不会评估代码的性能。正确性意味着您实现的代码符合您的想法。绩效是学习有意义模型的能力。通常通过绘制损失,交叉验证和测试数据来衡量性能。

您必须先确定代码的正确性,然后再查看其性能。我称这是性能原则之前的正确性。否则,您需要担心性能不好是因为学习算法不够好,还是代码中存在一些错误。显然,您需要后者,但是很难证明您的代码没有错误。

性能先于原则:只有在确定正确性之后,才能查看代码的性能。

可悲的是,我看到很多人都采用的模式是使用性能指标来进行调试。当他们的代码不学习时,他们将通过绘制损失函数来开始调试。这违反了性能原则之前的正确性,因此无法有效地发现错误。这是因为:

  • 性能指标是渐近定向的,而不是单调的。例如,损失函数应随时间减少。但是在任何时间点,包括调试时,这些数字都可以上升或下降。没有正确的值使您很难识别出是否有问题。将此与断言进行比较:您知道发生断言冲突时情况不对。
  • 即使您发现性能指标显然是错误的,它们也不会告诉您错误的来源。将此与VeriTensor的故障定位支持进行比较。您可以在阶段中找到错误-张量成形阶段,张量依赖阶段和张量值阶段。您可以在每个阶段集中精力。
  • 修复错误后,很难为该错误编写回归测试。这是因为基于性能指标的错误和症状的根源很远。将此与使用断言的测试用例编写经验进行比较。您只需要将主学习循环变成具有较小学习时间步长的单元测试,以使测试尽快终止。您可以使用真实输入,也可以使用随机输入。

影片介绍

一年半以前,我在Tensorflow Deep Dive活动中介绍了VeriTensor。演讲受到好评。这是演示。

在那之后的20个月中,我将VeriTensor应用于所有的机器学习代码,并且一次又一次地起作用。希望对您有帮助。

(本文翻译自Wei Yi的文章《Reducing Tensorflow Debugging Time by 90 Percent》,参考:https://towardsdatascience.com/reducing-tensorflow-debugging-time-by-90-percent-41e8d60f9494)

来源:

https://www.toutiao.com/a6784055060306330124/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

来都来了,走啥走,留个言呗~

IT大咖说 | 关于版权

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT大咖说 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 通过断言进行规范
  • VeriTensor方法
  • 技术1:张量形状断言
  • 技术2:张量间的依赖
  • 技术3:张量方程评估
  • 这些技术有效且实用吗?
  • 为什么VeriTensor对检测错误有效?
  • 验证代码正确性,而不是性能
  • 影片介绍
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档