介绍新工具 TF-Coder,帮你更好写出 TensorFlow 表达式

在操作张量的时候,需要同时考虑到多个维度、张量形状和 DType 兼容性,同时也不能落下数学正确性。此外,还需从数百个 TensorFlow 算子中做出选择,要找到合适的运算也是一项挑战。

与其对张量操作直接编码,能不能用一个说明性示例进行演示并自动获取相应代码呢?TensorFlow Coder (TF-Coder) 告诉您:能!

TF-Coder 是一款协助编写 TensorFlow 代码的 程序合成 工具。首先需要给出所需张量变换的输入 - 输出示例。然后,TF-Coder 通过运行组合搜索,查找执行该变换的 TensorFlow 表达式。并最终输出可以在您的项目中直接使用的实际的 TensorFlow 代码。

有个一分钟左右的 视频 介绍了 TF-Coder,此 Colab notebook 可以让您使用 TF-Coder 工具来解决张量操作问题。

那么,TF-Coder 可以在哪些场景中起到作用呢?

通过示例在 TensorFlow 中编程

假设您想通过广播的方式将一个 M 元素向量与一个 N 元素向量“相加”,生成一个包含所有成对和的 M x N 矩阵。不必在 TensorFlow 文档中挖空心思地寻找,只需提供一个输入 - 输出示例(使用 M = 3N = 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 还可以解决更棘手的问题:

用 TF-Coder 找到合适的函数

假设您正在处理一个数字特征,比方说某物品的价格,数据集中的价格范围很广,例如从少于 10 美元到超过 1000 美元。如果直接使用这些价格作为特征,则模型可能会出现过拟合,并且在评估过程中也可能难以处理离群价格。

为了解决上述问题,可能需要通过 分桶 (Bucketing) 将数字价格转换为分类特征。例如,使用 [10, 50, 100, 1000] 的 bucket 边界表示低于 10 美元的价格应该落入 bucket0,10 美元到 50 美元之间的价格落入 bucket1,以此类推。

确定 bucket 边界之后,如何使用 TensorFlow 将数字价格实际映射到 bucket 索引呢?例如,给定以下 bucket 边界和物品价格:

# Input tensors
boundaries = [10, 50, 100, 1000]
prices = [15, 3, 50, 90, 100, 1001]

您要计算每个物品的 bucket 编号:

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

TF-Coder 详细搜索 TensorFlow 运算组合后,经常能够找到这样的优雅解决方案,让您的 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.divide 在除以两个 tf.int32 张量时会返回一个 tf.float64 张量)。通过这种方式,TF-Coder 可以帮助您写出简洁又正确的代码,免去调试周期的痛苦。

注意事项

TF-Coder 也有一定局限性。目前,它可以在一分钟的搜索时间内找到涉及 3-4 个运算的解决方案,但涉及 6 个或更多复杂运算的解决方案时,无法在合理的时间内找到。此外,TF-Coder 尚不支持复数、字符串张量或者不规则张量。支持的完整算子列表见 Colab notebook

此外,TF-Coder 只保证其解决方案适用于给定的输入 - 输出示例。工具会搜索一个简单的 TensorFlow 表达式来匹配所提供的输入 - 输出示例,但有时这个解决方案过于简单,不能按预期方式进行泛化。让示例尽可能的明确可能会有所帮助,这可通过在输入和输出张量上添加更多数字来实现。请查看 TF-Coder 的解决方案,以确保它们实现预期行为。

亲身体验一下 TF-Coder 吧!

记得试一试 TF-Coder!即使是 Google 的 TensorFlow 老用户也可以在 TF-Coder 的帮助下不断了解新鲜事物。

您可以使用这个 Colab notebook 访问工具:无需下载或安装。按照 此教程 进行详细的演练。您也可以在 GitHub 上查看我们的代码和文档,还可以阅读我们的 研究论文

注:在 Colab 工具中,我们希望记录 TF-Coder 收到的问题以及产生的解决方案,由此改进工具并建立数据集,在总体上加速程序合成研究,但此为可选项,并非强制。

image

原文:Introducing TF-Coder, a tool that writes tricky TensorFlow expressions for you!
中文:TensorFlow 微信公众号