【问题标题】:In Tensorflow, how to use tf.gather() for the last dimension?在 Tensorflow 中,如何将 tf.gather() 用于最后一个维度?
【发布时间】:2016-08-14 08:54:02
【问题描述】:

我正在尝试根据最后一个维度收集张量的切片,以用于层之间的部分连接。因为输出张量的shape是[batch_size, h, w, depth],所以我想根据最后一个维度来选择切片,比如

# L is intermediate tensor
partL = L[:, :, :, [0,2,3,8]]

但是,tf.gather(L, [0, 2,3,8]) 似乎只适用于第一个维度(对吗?)谁能告诉我该怎么做?

【问题讨论】:

    标签: python tensorflow deep-learning


    【解决方案1】:

    这里有一个跟踪错误来支持这个用例:https://github.com/tensorflow/tensorflow/issues/206

    现在你可以:

    1. 转置矩阵,以便首先收集维度(转置成本很高)

    2. 将您的张量整形为 1d(整形很便宜)并将您的收集列索引转换为线性索引处的单个元素索引列表,然后重新整形

    3. 使用gather_nd。仍然需要将您的列索引转换为单个元素索引的列表。

    【讨论】:

    • 请注意,从 TensorFlow 1.3 开始,tf.gather 有一个轴参数。
    【解决方案2】:

    实施 2. 来自@Yaroslav Bulatov 的:

    #Your indices
    indices = [0, 2, 3, 8]
    
    #Remember for final reshaping
    n_indices = tf.shape(indices)[0]
    
    flattened_L = tf.reshape(L, [-1])
    
    #Walk strided over the flattened array
    offset = tf.expand_dims(tf.range(0, tf.reduce_prod(tf.shape(L)), tf.shape(L)[-1]), 1)
    flattened_indices = tf.reshape(tf.reshape(indices, [-1])+offset, [-1])
    selected_rows = tf.gather(flattened_L, flattened_indices)
    
    #Final reshape
    partL = tf.reshape(selected_rows, tf.concat(0, [tf.shape(L)[:-1], [n_indices]]))
    

    感谢How to select rows from a 3-D Tensor in TensorFlow?

    【讨论】:

      【解决方案3】:

      使用gather_nd,您现在可以执行以下操作:

      cat_idx = tf.concat([tf.range(0, tf.shape(x)[0]), indices_for_dim1], axis=0)
      result = tf.gather_nd(matrix, cat_idx)
      

      另外,正如用户 Nova 在@Yaroslav Bulatov 引用的线程中所报告的那样:

      x = tf.constant([[1, 2, 3],
                       [4, 5, 6],
                       [7, 8, 9]])
      idx = tf.constant([1, 0, 2])
      idx_flattened = tf.range(0, x.shape[0]) * x.shape[1] + idx
      y = tf.gather(tf.reshape(x, [-1]),  # flatten input
                    idx_flattened)  # use flattened indices
      
      with tf.Session(''):
        print y.eval()  # [2 4 9]
      

      要点是展平张量并通过 tf.gather(...) 使用跨步 1D 寻址。

      【讨论】:

      • 我不确定您的第一个示例是否有效。假设tf.shape(x)[0] 是1,那么cat_idx 将是[0, 0, 2, 3, 8],这不是您想要与tf.gather_nd 一起使用的。事实上,在这种情况下,它会抛出一个错误,因为indicesgather_nd 的第二个参数)的最内层维度的长度不能大于paramsgather_nd 的第一个参数)的等级。
      • 我在下面发布了一个更正的版本(使用tf.stack)。
      【解决方案4】:

      Tensor 没有属性 shape,但有 get_shape() 方法。以下可由 Python 2.7 运行

      import tensorflow as tf
      import numpy as np
      x = tf.constant([[1, 2, 3],
                       [4, 5, 6],
                       [7, 8, 9]])
      idx = tf.constant([1, 0, 2])
      idx_flattened = tf.range(0, x.get_shape()[0]) * x.get_shape()[1] + idx
      y = tf.gather(tf.reshape(x, [-1]),  # flatten input
                    idx_flattened)  # use flattened indices
      
      with tf.Session(''):
        print y.eval()  # [2 4 9]
      

      【讨论】:

        【解决方案5】:

        另一个使用 tf.unstack(...)、tf.gather(...) 和 tf.stack(..) 的解决方案

        代码:

        import tensorflow as tf
        import numpy as np
        
        shape = [2, 2, 2, 10] 
        L = np.arange(np.prod(shape))
        L = np.reshape(L, shape)
        
        indices = [0, 2, 3, 8]
        axis = -1 # last dimension
        
        def gather_axis(params, indices, axis=0):
            return tf.stack(tf.unstack(tf.gather(tf.unstack(params, axis=axis), indices)), axis=axis)
        
        print(L)
        with tf.Session() as sess:
            partL = sess.run(gather_axis(L, indices, axis))
            print(partL)
        

        结果:

        L = 
        [[[[ 0  1  2  3  4  5  6  7  8  9]
           [10 11 12 13 14 15 16 17 18 19]]
        
          [[20 21 22 23 24 25 26 27 28 29]
           [30 31 32 33 34 35 36 37 38 39]]]
        
        
         [[[40 41 42 43 44 45 46 47 48 49]
           [50 51 52 53 54 55 56 57 58 59]]
        
          [[60 61 62 63 64 65 66 67 68 69]
           [70 71 72 73 74 75 76 77 78 79]]]]
        
        partL = 
        [[[[ 0  2  3  8]
           [10 12 13 18]]
        
          [[20 22 23 28]
           [30 32 33 38]]]
        
        
         [[[40 42 43 48]
           [50 52 53 58]]
        
          [[60 62 63 68]
           [70 72 73 78]]]]
        

        【讨论】:

          【解决方案6】:

          @Andrei 答案的正确版本应该是

          cat_idx = tf.stack([tf.range(0, tf.shape(x)[0]), indices_for_dim1], axis=1)
          result = tf.gather_nd(matrix, cat_idx)
          

          【讨论】:

            【解决方案7】:

            你可以尝试这种方式,例如(在大多数情况下至少在 NLP 中),

            参数的形状为[batch_size, depth],索引为[i, j, k, n, m],长度为batch_size。那么gather_nd 会有所帮助。

            parameters = tf.constant([
                                      [11, 12, 13], 
                                      [21, 22, 23], 
                                      [31, 32, 33], 
                                      [41, 42, 43]])    
            targets = tf.constant([2, 1, 0, 1])    
            batch_nums = tf.range(0, limit=parameters.get_shape().as_list()[0])     
            indices = tf.stack((batch_nums, targets), axis=1) # the axis is the dimension number   
            items = tf.gather_nd(parameters, indices)  
            # which is what we want: [13, 22, 31, 42]
            

            这个sn-p首先通过batch_num找到第一个维度,然后通过目标编号沿着那个维度获取item。

            【讨论】:

              【解决方案8】:

              从 TensorFlow 1.3 开始,tf.gather 具有 axis 参数,因此不再需要此处的各种解决方法。

              https://www.tensorflow.org/versions/r1.3/api_docs/python/tf/gather https://github.com/tensorflow/tensorflow/issues/11223

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-02-21
                • 2019-07-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-08-23
                相关资源
                最近更新 更多