问题 如何明确地广播张量以匹配张量流中的另一个形状?


我有三个张量, A, B and C 在张量流中, A 和 B 两者都是形状 (m, n, r)C 是二元张量的形状 (m, n, 1)

我想根据值选择A或B中的元素 C。显而易见的工具是 tf.select但是,它没有广播语义,所以我需要先明确广播 C 形状与A和B相同

这将是我第一次尝试如何做到这一点,但它不喜欢我混合张量(tf.shape(A)[2])进入形状列表。

import tensorflow as tf
A = tf.random_normal([20, 100, 10])
B = tf.random_normal([20, 100, 10])
C = tf.random_normal([20, 100, 1])
C = tf.greater_equal(C, tf.zeros_like(C))

C = tf.tile(C, [1,1,tf.shape(A)[2]])
D = tf.select(C, A, B)

这里的正确方法是什么?


8263
2017-12-18 18:53


起源

一个有效的黑客:我可以使用广播语义 乘 然后乘以一个张量: Expander = tf.ones_like(B), 然后 C = Expander*C - wxs


答案:


编辑: 在自0.12rc0以来的所有版本的TensorFlow中,问题中的代码直接起作用。 TensorFlow会自动将张量和Python数字叠加到张量参数中。以下解决方案使用 tf.pack() 仅在0.12rc0之前的版本中需要。注意 tf.pack() 被重命名为 tf.stack() 在TensorFlow 1.0中。


您的解决方案非常接近工作。您应该替换该行:

C = tf.tile(C, [1,1,tf.shape(C)[2]])

......具有以下内容:

C = tf.tile(C, tf.pack([1, 1, tf.shape(A)[2]]))

(问题的原因是TensorFlow不会隐式地将张量和Python文字列表转换为张量。 tf.pack() 获取张量列表,因此它将转换其输入中的每个元素(11,和 tf.shape(C)[2])到一个张量。由于每个元素都是标量,因此结果将是一个向量。)


12
2017-12-18 19:00



我想你有额外的 [ 并且错过了 )但是当我发现时,我得到了一些有些神秘的错误 跑 tf会话: InvalidArgumentError: Inputs to operation Select_13 of type Select must have the same size and shape. Input 0: dim { size: 20 } dim { size: 100 } dim { size: 1 } != input 1: dim { size: 20 } dim { size: 100 } dim { size: 10 } - wxs
好点,我更新了答案 - 也是,论证 tf.shape() 本来应该 A (要么 B)。这对我有用 - 你看到了什么错误? - mrry
是的它现在已修复:)没有注意到错误的参数 tf.shape()。谢谢! - wxs
这是 tf.stack 现在,是吗? - wchargin
是的,但不再需要使用了 tf.stack() 解决这个问题(见编辑)。在重命名之前很久就解决了潜在的问题 tf.pack() 至 tf.stack()所以我要保持原样 tf.pack() 为了历史准确性。 - mrry


这是一个肮脏的黑客:

import tensorflow as tf

def broadcast(tensor, shape):
    return tensor + tf.zeros(shape, dtype=tensor.dtype)

A = tf.random_normal([20, 100, 10])
B = tf.random_normal([20, 100, 10])
C = tf.random_normal([20, 100, 1])

C = broadcast(C, A.shape)
D = tf.select(C, A, B)

3
2018-06-21 14:56





import tensorflow as tf

def broadcast(tensor, shape):
     """Broadcasts ``x`` to have shape ``shape``.
                                                                   |
     Uses ``tf.Assert`` statements to ensure that the broadcast is
     valid.

     First calculates the number of missing dimensions in 
     ``tf.shape(x)`` and left-pads the shape of ``x`` with that many 
     ones. Then identifies the dimensions of ``x`` that require
     tiling and tiles those dimensions appropriately.

     Args:
         x (tf.Tensor): The tensor to broadcast.
         shape (Union[tf.TensorShape, tf.Tensor, Sequence[int]]): 
             The shape to broadcast to.

     Returns:
         tf.Tensor: ``x``, reshaped and tiled to have shape ``shape``.

     """
     with tf.name_scope('broadcast') as scope:
         shape_x = tf.shape(x)
         rank_x = tf.shape(shape0)[0]
         shape_t = tf.convert_to_tensor(shape, preferred_dtype=tf.int32)
         rank_t = tf.shape(shape1)[0]

         with tf.control_dependencies([tf.Assert(
             rank_t >= rank_x,
             ['len(shape) must be >= tf.rank(x)', shape_x, shape_t],
             summarize=255
         )]):
             missing_dims = tf.ones(tf.stack([rank_t - rank_x], 0), tf.int32)

         shape_x_ = tf.concat([missing_dims, shape_x], 0)
         should_tile = tf.equal(shape_x_, 1)

         with tf.control_dependencies([tf.Assert(
             tf.reduce_all(tf.logical_or(tf.equal(shape_x_, shape_t), should_tile),
             ['cannot broadcast shapes', shape_x, shape_t],
             summarize=255
         )]):
             multiples = tf.where(should_tile, shape_t, tf.ones_like(shape_t))
             out = tf.tile(tf.reshape(x, shape_x_), multiples, name=scope)

         try:
             out.set_shape(shape)
         except:
             pass

         return out

A = tf.random_normal([20, 100, 10])
B = tf.random_normal([20, 100, 10])
C = tf.random_normal([20, 100, 1])

C = broadcast(C, A.shape)
D = tf.select(C, A, B)

0
2017-09-19 12:49