在操作张量的时候,需要同时考虑到多个维度、张量形状和 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 = 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 还可以解决更棘手的问题:
用 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_max
、 tf.argmax
和 tf.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_hot
和 tf.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
? counts
和tf.reduce_sum(counts, axis=1)
的形状是否兼容除法?需不需要重塑或转置?counts
和tf.reduce_sum(counts, axis=1)
都是tf.int32
张量。tf.int32
张量可以被除吗?还是需要先将它们转换成浮点 DType?- 这两个参数的顺序是否正确?应该互换吗?
- 输出的类型是
tf.int32
、tf.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 收到的问题以及产生的解决方案,由此改进工具并建立数据集,在总体上加速程序合成研究,但此为可选项,并非强制。
原文:Introducing TF-Coder, a tool that writes tricky TensorFlow expressions for you!
中文:TensorFlow 微信公众号