隆重推出 TF-Coder,一个为您编写棘手的 TensorFlow 表达式的工具!
2020 年 8 月 26 日
作者:Google 研究员 Kensen Shi

在操作张量时,必须跟踪多个维度、张量形状和 DType 兼容性,当然还有数学正确性。此外,TensorFlow 有数百种操作,找到要使用的正确操作可能是一个挑战。

与其直接编写张量操作代码,不如通过说明性示例进行演示,并自动获得相应的代码?TensorFlow Coder (TF-Coder) 使这成为可能!

TF-Coder 是一种程序合成工具,可帮助您编写 TensorFlow 代码。首先,该工具要求您提供所需张量转换的输入-输出示例。然后,它运行组合搜索以查找执行该转换的 TensorFlow 表达式。TF-Coder 的输出是您可以包含在项目中的真实 TensorFlow 代码。

以下一分钟的视频介绍了 TF-Coder,此 Colab 笔记本 允许您将 TF-Coder 工具用于自己的张量操作问题。



在本博文中,我们将说明 TF-Coder 可以帮助您编写 TensorFlow 代码的各种场景。

通过示例在 TensorFlow 中编程

假设您希望以广播方式将一个M元素向量与一个N元素向量“相加”,以生成一个包含所有成对和的M x N矩阵。与其翻阅 TensorFlow 文档以找出如何执行此操作,不如提供一个输入-输出示例(使用M = 3 和N = 4)

输入张量,作为将输入变量名称映射到示例张量值的字典
inputs = {
    'rows': [10, 20, 30],
    'cols': [1, 2, 3, 4],
}
所需的输出张量,对应于提供的输入张量
output = [[11, 12, 13, 14],
          [21, 22, 23, 24],
          [31, 32, 33, 34]]
有了这些信息(默认情况下已输入 TF-Coder Colab 中),TF-Coder 工具将在不到一秒的时间内自动找到合适的 TensorFlow 代码。
tf.add(cols, tf.expand_dims(rows, 1))
上述问题非常简单,只是为了说明通过示例编程的概念。正如我们在下面将看到的,TF-Coder 也可以用于更难的问题。

TF-Coder 帮助您找到要使用的正确函数

假设您正在使用数值特征,例如项目的價格。您数据集中的价格范围很广,例如,从不到 10 美元到超过 1000 美元。如果这些价格直接用作特征,您的模型可能会过度拟合训练数据中的特定价格,并且在评估期间也可能难以处理异常价格。

为了解决这些问题,您可能希望使用分桶将数值价格转换为分类特征。例如,使用 [10, 50, 100, 1000] 的分桶边界意味着小于 10 美元的价格应落在分桶 0 中,10 美元到 50 美元之间的价格应落在分桶 1 中,依此类推。

在选择分桶边界后,您如何实际使用 TensorFlow 将数值价格映射到分桶索引?例如,给定以下分桶边界和项目价格
# Input tensors
boundaries = [10, 50, 100, 1000]
prices = [15, 3, 50, 90, 100, 1001]
您希望计算每个项目的桶号
# Output tensor
bucketed_prices = [1, 0, 2, 2, 3, 4]
尽管 TensorFlow 带有各种分桶操作,但弄清楚哪个特定操作执行这种精确的分桶可能很棘手。由于 TF-Coder 可以通过行为识别数百种张量操作,因此您可以通过提供输入-输出示例来查找正确的操作。
# Input-output example
inputs = {
    'boundaries': [10, 50, 100, 1000],
    'prices': [15, 3, 50, 90, 100, 1001],
}
output = [1, 0, 2, 2, 3, 4]
在几秒钟内,TF-Coder 输出以下解决方案
tf.searchsorted(boundaries, prices, side='right')
这给了我们一个有用的提示,并且 tf.searchsorted 的文档确认此代码确实按预期执行了分桶。

TF-Coder 帮助您以巧妙的方式组合函数

现在让我们考虑另一个问题:计算一个 0-1 张量,该张量标识输入张量的每一行的最大元素。
# Input tensor
scores = [[0.7, 0.2, 0.1],
          [0.4, 0.5, 0.1],
          [0.4, 0.4, 0.2],
          [0.3, 0.4, 0.3],
          [0.0, 0.0, 1.0]]
 
# Output tensor
top_scores = [[1, 0, 0],
              [0, 1, 0],
              [1, 0, 0],
              [0, 1, 0],
              [0, 0, 1]]
请注意,如果相同的最大切面在同一行中出现多次,例如在 scores 的第三行中,则应仅标记第一个这样的最大切面,以便 top_scores 的每一行都只有一个值为 1 的条目。

与上一个问题不同,没有一个 TensorFlow 函数可以执行此计算。如果您在文档中搜索“max”,您可能会发现 tf.reduce_maxtf.argmaxtf.maximum 相关,但应该使用哪一个?tf.reduce_max 生成 [0.7, 0.5, 0.4, 0.4, 1.0]tf.argmax 生成 [0, 1, 0, 1, 2],而 tf.maximum 不正确,因为它接受两个参数。这些都没有接近我们想要的输出。

TF-Coder 可以帮助解决此类棘手问题。您可以以输入-输出示例的形式编写问题。
# Input-output example
inputs = {
    'scores': [[0.7, 0.2, 0.1],
               [0.4, 0.5, 0.1],
               [0.4, 0.4, 0.2],
               [0.3, 0.4, 0.3],
               [0.0, 0.0, 1.0]],
}
output = [[1, 0, 0],
          [0, 1, 0],
          [1, 0, 0],
          [0, 1, 0],
          [0, 0, 1]]
TF-Coder 使用 tf.one_hottf.argmax 的组合,为这个问题提供了一个简短的解决方案。
tf.cast(tf.one_hot(tf.argmax(scores, axis=1), 3), tf.int32)
通过对 TensorFlow 操作组合进行详细搜索,TF-Coder 通常会找到这样的优雅解决方案,这可能会简化和加速您的 TensorFlow 程序。

TF-Coder 帮助您编写正确的代码,减少调试工作量

考虑将整数计数列表归一化为概率分布,方法是将每一行除以该行的总和。例如
# Input tensor
counts = [[0, 1, 0, 0],
          [0, 1, 1, 0],
          [1, 1, 1, 1]]
 
# Output tensor
normalized = [[0.0, 1.0, 0.0, 0.0],
              [0.0, 0.5, 0.5, 0.0],
              [0.25, 0.25, 0.25, 0.25]]
即使您知道要使用的相关函数(tf.reduce_sum 后跟 tf.divide),编写正确的代码仍然非同小可。第一次尝试可能看起来像这样
# First attempt
normalized = tf.divide(counts, tf.reduce_sum(counts, axis=1))
这样对吗?有很多潜在的陷阱需要考虑
  • 求和轴是否正确,还是应该为 axis=0
  • countstf.reduce_sum(counts, axis=1) 的形状是否兼容除法,或者您是否需要重新整形或转置这两个形状?
  • countstf.reduce_sum(counts, axis=1) 都是 tf.int32 张量。可以对 tf.int32 张量进行除法,还是需要先将其转换为浮点 DType?
  • 两个参数的顺序是否正确,还是应该互换?
  • 输出的类型是 tf.int32tf.float32 还是其他类型?
  • 有没有没有考虑到的更简单或更好的方法?
您可以使用以下输入-输出示例将此任务交给 TF-Coder
# Input-output example
inputs = {
    'counts': [[0, 1, 0, 0],
               [0, 1, 1, 0],
               [1, 1, 1, 1]],
}
output = [[0.0, 1.0, 0.0, 0.0],
          [0.0, 0.5, 0.5, 0.0],
          [0.25, 0.25, 0.25, 0.25]]
TF-Coder 的解决方案是
tf.cast(tf.divide(counts, tf.expand_dims(tf.reduce_sum(counts, axis=1), axis=1)), tf.float32)
通过使用 TF-Coder 解决此问题,可以减轻练习的心理负担。当 TF-Coder 生成上述解决方案时,它保证代码在对示例输入运行时会正确生成示例输出。TF-Coder 的解决方案还将避免任何不必要的步骤。因此,您可以快速推断出上述大多数问题的答案:需要一个额外的 tf.expand_dims 步骤才能使形状兼容除法,并且 tf.divide 的结果必须转换为 tf.float32(事实上,当对两个 tf.int32 张量进行除法时,tf.divide 返回一个 tf.float64 张量)。这样一来,TF-Coder 帮助您编写简洁正确的代码,而无需进行痛苦的调试循环。

注意事项

TF-Coder 存在一些限制。它目前可以在一分钟的搜索时间内找到涉及 3-4 个操作的解决方案,但涉及 6 个或更多个操作的解决方案过于复杂,无法在合理的时间内找到。此外,TF-Coder 目前不支持复杂或字符串张量,也不支持 RaggedTensors。支持操作的完整列表可以在 Colab 笔记本 中找到。

此外,TF-Coder 只能保证其解决方案对给定的输入-输出示例有效。该工具搜索一个匹配提供的输入-输出示例的简单 TensorFlow 表达式,但有时此解决方案过于简单,无法以预期的形式进行推广。使示例尽可能明确可能会有所帮助,这通常可以通过在输入和输出张量中添加更多数字来实现。请查看 TF-Coder 的解决方案,以确保它们正确地实现了预期的行为。

亲自试试 TF-Coder!

务必尝试使用 TF-Coder!即使是 Google 的经验丰富的 TensorFlow 用户也通过 TF-Coder 学到了新东西。

您可以使用此 Colab 笔记本 访问该工具——无需下载或安装。按照此 教程 进行详细演练。您也可以查看我们在 GitHub 上的代码和文档,以及我们的 研究论文

注意:在 Colab 工具中,我们希望记录提供给 TF-Coder 的问题以及生成的解决方案,以便我们可以改进该工具并构建一个数据集,该数据集将加速总体程序合成研究,但这项数据收集完全是可选的。
下一篇文章
Introducing TF-Coder, a tool that writes tricky TensorFlow expressions for you!

作者:Google 研究员 Kensen Shi

在操作张量时,必须跟踪多个维度、张量形状和 DType 兼容性,当然还有数学正确性。此外,TensorFlow 有数百种操作,找到要使用的正确操作可能是一个挑战。

与其直接编写张量操作代码,不如通过……