2020 年 8 月 26 日 — 作者:Google 研究员 Kensen Shi
在操作张量时,必须跟踪多个维度、张量形状和 DType 兼容性,当然还有数学正确性。此外,TensorFlow 有数百种操作,找到要使用的正确操作可能是一个挑战。
与其直接编写张量操作代码,不如通过……
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 也可以用于更难的问题。[10, 50, 100, 1000]
的分桶边界意味着小于 10 美元的价格应落在分桶 0 中,10 美元到 50 美元之间的价格应落在分桶 1 中,依此类推。# 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
的文档确认此代码确实按预期执行了分桶。# 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
的条目。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
不正确,因为它接受两个参数。这些都没有接近我们想要的输出。# 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)
通过对 TensorFlow 操作组合进行详细搜索,TF-Coder 通常会找到这样的优雅解决方案,这可能会简化和加速您的 TensorFlow 程序。# 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
还是其他类型?# 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 帮助您编写简洁正确的代码,而无需进行痛苦的调试循环。