【问题标题】:How to explicitly broadcast a tensor to match another's shape in tensorflow?如何在张量流中显式广播张量以匹配另一个形状?
【发布时间】:2016-03-25 13:13:12
【问题描述】:

我有三个张量,张量流中的A, B and CAB 的形状都是(m, n, r)C 是形状为(m, n, 1) 的二元张量。

我想根据C 的值从A 或B 中选择元素。最明显的工具是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)

这里的正确方法是什么?

【问题讨论】:

  • 一个可行的技巧:我可以使用 multiply 的广播语义并乘以一个张量:Expander = tf.ones_like(B),然后是 C = Expander*C

标签: tensorflow


【解决方案1】:

在最新的tensorflow版本(2.0)中,您可以使用tf.broadcast_to如下:

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.broadcast_to(C, A.shape)

D = tf.where(C,A,B)

【讨论】:

    【解决方案2】:

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


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

    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() 采用张量列表,因此它将转换其输入中的每个元素(@ 987654329@、1tf.shape(C)[2]) 转换为张量。由于每个元素都是标量,因此结果将是向量。)

    【讨论】:

    • 我认为您有一个额外的[ 并且缺少一个),但是当我运行 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 }
    • 好点,我更新了答案——另外,tf.shape() 的参数应该是A(或B)。这对我有用 - 你看到了什么错误?
    • 是的,现在已修复 :) 没有注意到 tf.shape() 的参数不正确。谢谢!
    • 现在是tf.stack,是吗?
    • 是的,但不再需要使用tf.stack() 来解决这个问题(见编辑)。早在将tf.pack() 重命名为tf.stack() 之前就已经解决了根本问题,因此为了历史准确性,我将其保留为tf.pack()
    【解决方案3】:
    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)
    

    【讨论】:

      【解决方案4】:

      这是一个肮脏的黑客:

      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)
      

      【讨论】:

        猜你喜欢
        • 2018-12-13
        • 2018-06-10
        • 2020-07-22
        • 1970-01-01
        • 2017-08-17
        • 1970-01-01
        • 2018-02-26
        • 2016-12-12
        • 1970-01-01
        相关资源
        最近更新 更多