这有点让人头疼,但是,一旦我们了解 tf.function 正在将 python 操作和控制流映射到 tf 图,而裸函数只是急切地执行,我们可以挑选它,它会生成一个更有意义。
我已经调整了你的例子来说明发生了什么。考虑下面的test1 和test2:
@tf.function
def test1(a):
print_op = tf.print(tf.size(a))
print("python print size: {}".format(tf.size(a)))
if tf.math.not_equal(tf.size(a),0):
print('fail')
with tf.control_dependencies([print_op]):
return None
def test2(a):
print_op = tf.print(tf.size(a))
print("python print size: {}".format(tf.size(a)))
if tf.math.not_equal(tf.size(a),0):
print('fail')
with tf.control_dependencies([print_op]):
return None
除了 @tf.function 装饰器之外,它们彼此相同。
现在执行test2(tf.Variable([[]])) 给我们:
0
python print size: 0
这是我认为你所期望的行为。而test1(tf.Variable([[]])) 给出:
python print size: Tensor("Size_1:0", shape=(), dtype=int32)
fail
0
关于这个输出,有几件事(除了fail)你可能会感到惊讶:
-
print() 语句打印出(尚未评估的)张量而不是零
-
print() 和 tf.print() 的顺序已经颠倒了
这是因为通过添加@tf.function,我们不再有 python 函数,而是使用 autograph 从函数代码映射的 tf 图。这意味着,在评估 if 条件时,我们还没有执行 tf.math.not_equal(tf.size(a),0) 并且只有一个对象(Tensor 对象的实例),它在 python 中是真实的:
class MyClass:
pass
my_obj = MyClass()
if (my_obj):
print ("my_obj evaluates to true") ## outputs "my_obj evaluates to true"
这意味着我们在评估 tf.math.not_equal(tf.size(a),0) 之前到达 test1 中的 print('fail') 语句。
那么解决方法是什么?
好吧,如果我们在 if 块中删除对仅 python 的 print() 函数的调用并将其替换为对签名友好的 tf.print() 语句,那么签名将无缝地将我们的 if ... else ... 逻辑转换为图形友好的tf.cond 声明,确保一切都以正确的顺序发生:
定义测试3(a):
print_op = tf.print(tf.size(a))
print("python 打印尺寸:{}".format(tf.size(a)))
如果 tf.math.not_equal(tf.size(a),0):
tf.print('失败')
使用 tf.control_dependencies([print_op]):
返回无
test3(tf.Variable([[]]))
0
python print size: 0