我试图写一大块可重复使用的代码来读取一个张量的形状,然后使用生成的对象来定义其他张量的形状。我可以选择使用tf.shape(tensor)
读取张量的动态形状,或者使用tensor.get_shape()
读取张量的静态形状。玩具示例看起来像这样(使用两种不同的策略):
def my_function_strategy_1(x, y):
x_shape = tf.shape(x)
a = tf.reshape(y, x_shape)
b = tf.zeros(x_shape)
num_x_values = x_shape[0]
c = tf.reshape(y, [num_x_values, 4])
d = tf.zeros([num_x_values, 4])
return a, b, c, d
def my_function_strategy_2(x, y):
x_shape = x.get_shape()
a = tf.reshape(y, x_shape)
b = tf.zeros(x_shape)
num_x_values = x_shape[0]
c = tf.reshape(y, [num_x_values, 4])
d = tf.zeros([num_x_values, 4])
return a, b, c, d
我想在不同的图表中使用这一块代码。有时输入张量的形状将是已知的,有时它们将是未知的:
graph_A = tf.Graph()
with graph_A.as_default():
x = tf.placeholder(tf.float32, [2, 4])
y = tf.placeholder(tf.float32, [8])
a, b, c, d = my_function(x, y)
with graph_B.as_default():
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
a, b, c, d = my_function(x, y)
我想要的行为是:(A)当输入张量的形状已知时(如graph_A
),我希望TensorFlow在图形创建时计算图形中的所有形状(所以它可以有效地分配资源等),以及(B)当输入张量的形状未知时(如graph_B
),我希望TensorFlow等到运行时计算图中的所有形状。
函数的strategy_1
版本几乎执行此操作。它实现了(B),但它并没有完全实现(A),因为TensorFlow留下了一些未知的张量形状。例如,在上面的玩具示例中,a
,b
和c
的形状是在图形创建时计算的,但d
的形状未知(即使d
使用非常相似的操作)。您可以打印a.get_shape()
,b.get_shape()
等
相反,函数的strategy_2
版本实现了图中所有张量的(A),但没有达到(B),因为TensorFlow(可以理解)在尝试使用时抛出异常(未知)输入张量的静态形状,以塑造其他张量。
有没有办法在单一功能中同时实现(A)和(B)? strategy_1
版本如何/为什么适用于图表中的大多数张量,但不是全部?
答案 0 :(得分:2)
你可以仔细挑选你知道的形状元素,并拥有两个世界中最好的元素"结果:
def my_get_shape(tensor):
if tensor.shape.ndims is None:
# Fully dynamic
return tf.shape(tensor)
if tensor.shape.is_fully_defined():
# Fully static
return tensor.shape
# Partially static
dyn_shape = tf.shape(tensor)
shape = []
for i, d in enumerate(tensor.shape):
shape.append(d.value if d.value is not None else dyn_shape[i])
return shape
def my_function(x, y):
x_shape = my_get_shape(x) # Or just tf.shape(x)! - see edit
a = tf.reshape(y, x_shape)
b = tf.zeros(x_shape)
num_x_values = x_shape[0]
c = tf.reshape(y, [num_x_values, 4])
d = tf.zeros([num_x_values, 4])
return a, b, c, d
# Fully static
with tf.Graph().as_default():
x = tf.placeholder(tf.float32, [2, 4])
y = tf.placeholder(tf.float32, [8])
a, b, c, d = my_function(x, y)
print('a:', a.shape, ', b:', b.shape, ', c:', c.shape, ', d:', d.shape)
# a: (2, 4) , b: (2, 4) , c: (2, 4) , d: (2, 4)
# Fully dynamic
with tf.Graph().as_default():
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
a, b, c, d = my_function(x, y)
print('a:', a.shape, ', b:', b.shape, ', c:', c.shape, ', d:', d.shape)
# a: <unknown> , b: <unknown> , c: (?, 4) , d: (?, 4)
# Partially static
with tf.Graph().as_default():
x = tf.placeholder(tf.float32, [None, 4])
y = tf.placeholder(tf.float32)
a, b, c, d = my_function(x, y)
print('a:', a.shape, ', b:', b.shape, ', c:', c.shape, ', d:', d.shape)
# a: (?, 4) , b: (?, 4) , c: (?, 4) , d: (?, 4)
编辑:
实际上,在前一个代码段中将my_get_shape
替换为tf.shape
非常有效。似乎tf.shape
应该是默认值(小心不要用它填充图表),除非你明确想要保持维度未定义。
我已经调查了一下,我无法完全解决所有问题。我不知道这是否有用,但这里有一些我发现的东西。显然TensorFlow在C ++级别(以前似乎曾经在Python中,但现在不再是),一个&#34;形状推断&#34;机制。例如,如果您在link中查看),您会看到每个操作声明在末尾都包含.SetShapeFn
,这是一个使用feature request来尝试猜测输出形状的函数的操作。除了其他方面,此类还可以检查张量中的值是否已知,例如,当给定张量为静态时,tf.shape
或tf.fill
(和tf.ones
相关时,{0} })具有已知值。形状推理算法的分辨率是在Python中设置为张量形状的,它可以通过tensorflow/core/ops/array_ops.cc
直接调用(虽然我不知道它是如何有用的):
from tensorflow.python.framework.common_shapes import call_cpp_shape_fn
with tf.Graph().as_default():
print(call_cpp_shape_fn(tf.reshape(tf.placeholder(tf.float32), tf.fill([2], 3)).op))
# Shows this:
# {
# 'shapes': [dim { size: 3 } dim { size: 3 }],
# 'handle_data': [None],
# 'inputs_needed': b'\x12\x01\x01'
# }
print(call_cpp_shape_fn(tf.reshape(tf.placeholder(tf.float32), (2 * tf.fill([2], 3))).op))
# Shows this:
# {
# 'shapes': [dim { size: -1 } dim { size: -1 }],
# 'handle_data': [None],
# 'inputs_needed': b'\x12\x01\x01'
# }
您可以看到,在tf.fill([2], 3)
被正确检查的同时,TensorFlow没有确定2 * tf.fill([2], 3)
是[6, 6]
,大概是因为静态跟踪乘法等操作,甚至如果操作数是已知的常数,则被认为太贵了。
我还没有发现,操作系统声明可以静态地知道它们的值,或者确切地检索这些值的位置/方式。例如,对于tf.shape
,它似乎能够专门选择已知值并将其余值保留为未定义。