【问题标题】:TensorFlow - Pad unknown size tensor to a specific size?TensorFlow - 将未知大小的张量填充到特定大小?
【发布时间】:2018-09-07 20:43:48
【问题描述】:

有没有办法用特定的填充值将可变大小的张量填充到给定的形状?例如给定张量:

[[1, 2],
 [3, 4]]

[[1, 2, 3],
 [4, 5, 6]]

有没有一种方法可以进行通用操作,该操作将采用任何一个并用一个值填充它们(例如,用值-1 塑造[2, 4])以产生:

[[1, 2, -1, -1],
 [3, 4, -1, -1]]

[[1, 2, 3, -1],
 [4, 5, 6, -1]]

分别?我的推理(如果有更好的解决方案)是我有来自 TFRecords 文件的示例,其中一部分具有可变长度。对于处理,静态长度使它们更易于使用。

【问题讨论】:

    标签: tensorflow


    【解决方案1】:

    是的。有。只要你不需要改变张量的秩,就很简单了。

    tf.pad() 接受带有张量的常规 python 列表。填充的格式是在该维度的每一侧填充多少对的列表。

    例如

    t = tf.constant([[1, 2], [3, 4]])
    paddings = [[0, 0], [0, 4-tf.shape(t)[0]]]
    out = tf.pad(t, paddings, 'CONSTANT', constant_values=-1)
    sess.run(out)
    # gives: 
    # array([[ 1,  2, -1, -1],
    #       [ 3,  4, -1, -1]], dtype=int32)
    

    如果您想将其概括为有用的功能,您可以执行以下操作:

    def pad_up_to(t, max_in_dims, constant_values):
        s = tf.shape(t)
        paddings = [[0, m-s[i]] for (i,m) in enumerate(max_in_dims)]
        return tf.pad(t, paddings, 'CONSTANT', constant_values=constant_values)
    

    max_in_dims 本质上是所需的输出形状。 注意:如果您提供的形状在任何维度上都严格小于t,则此功能将失败。

    你可以像这样使用它:

    t = tf.constant([[1, 2], [3, 4]]) # shape = [2, 2]
    t_padded = pad_up_to(t, [2, 4], -1) # shape = [2, 4], padded with -1s
    

    t = tf.placeholder(tf.float32, [None, None]) # shape = [?, ?]
    t_padded = pad_up_to(t, [5,5], -1) # shape = [5, 5], padded with -1s
    t_np = np.random.uniform(0, 1, [3,4]) # shape = [3,4], no padding
    t_padded_out = sess.run(t_padded, {t: t_np})
    t_np2 = np.random.uniform(0, 1, [2,1]) # shape = [2,1], no padding
    t_padded_out2 = sess.run(t_padded, {t: t_np2})
    

    虽然维度大小是动态计算的,但维度数量不是,所以要确保max_in_dims与t.shape的元素数量相同。

    【讨论】:

    • 如果 t 有一个动态大小(例如,它的大小只有在输入一些占位符后才确定)怎么办?
    • 在我提供的函数中,s 是一个张量,其形状为t,因此填充量是动态计算的。维数不是动态计算的,因此只需确保您的 max_in_dims 是一个向量,其元素数与您的 t 的维数相同。如果你这样做,它就会起作用(我写这个函数时考虑到了这个用例)。
    • 我没想到它可以使用动态尺寸,但令我惊讶的是,它确实如此!谢谢!
    • 很好的参考,不要浪费时间寻找更多现成的解决方案。
    • 这在具有动态大小的 TF 2.3 中对我不起作用,因为m 被评估为None,这会引发减法错误。但是,解决方法是简单地将行更改为[[0, m - s[i]] if m != None else [0,0] for (i, m) in enumerate(max_in_dims)]
    【解决方案2】:

    Multihunter's solution 的扩展,以便仅在必要时执行填充,并且不会对较长的输入产生错误:

    假设我们有一个名为 inp_seq 的顺序输入,它是一个等级为 4 的张量,应该进行填充以便在维度 1 中具有filter_size 的最小长度。

    def dynamic_padding(inp, min_size):
    
        pad_size = min_size - tf.shape(inp)[1]
        paddings = [[0, 0], [0, pad_size], [0, 0], [0, 0]] # assign here, during graph execution
        return tf.pad(inp, paddings)
    
    # Pad only if necessary
    padded = tf.cond(tf.less(tf.shape(inp_seq)[1], filter_size), true_fn=lambda: dynamic_padding(inp_seq, filter_size), false_fn=lambda: inp_seq)  
    

    【讨论】:

    • 创建 tf.Variable 的行是多余的,因为后续行会用 python 列表覆盖它。您可以删除该行,它的功能相同。 (另外,sequence 是由 python 基础库定义的类,而tensor 是由 tensorflow 定义的:我认为您应该澄清您的inp_seq 实际上是其中的哪一个;我想您在处理什么with 实际上是Tensors 的序列(或列表),例如inp_seq=[Tensor, Tensor, Tensor])
    • 我删除了多余的行,谢谢你的建议。输入只是一个张量;我使用的术语序列具有更广泛的含义(指的是沿一维顺序排列的高维数据,即要填充的数据),我不是指 Python 基础库。我在编辑中澄清了这一点。
    【解决方案3】:

    我遇到了类似的事情。不完全通用,但您可以执行以下操作:

    test_a = tf.constant([[1, 2], [3, 4]])
    test_b = tf.constant([[1, 2, 3], [4, 5, 6]])
    
    def pad_second_dim(input, desired_size):
        padding = tf.tile([[0]], tf.stack([tf.shape(input)[0], desired_size - tf.shape(input)[1]], 0))
        return tf.concat([input, padding], 1)
    
    with tf.Session() as sess:
        print sess.run(pad_second_dim(test_a, 4))
        # [[1 2 0 0] [3 4 0 0]]
        print sess.run(pad_second_dim(test_b, 4))
        # [[1 2 3 0] [4 5 6 0]]
    

    【讨论】:

      【解决方案4】:

      The accepted answer 对我也不起作用,我也不愿意使用assignments in the graph。在这里,我调整了 Multihunter 的答案,使其适用于可变大小。这对我有用。具体来说,我正在使用 tf.data.TFREcordDataset 使用数据,并希望在加载时应用填充,而不是写出预先填充的数据。

      MIN_SIZE = 100
      
      # v shape is undefined on the second dimension. 
      v = tf.get_variable(shape=(2, None), dtype=tf.int32)
      
      # Note: this will blow up if `padding_len` < 0
      padding_len = MIN_SIZE - tf.shape(v)[-1]
      
      # paddings = [ [0, 0], [0, padding_len] ]
      paddings = tf.concat( ([[0, 0]], [[0, padding_len ]]), axis=0)
      
      # padded.shape = (2, 100)
      padded = tf.pad(t, paddings, 'CONSTANT', constant_values=-1)
      

      【讨论】:

      • 我认为您所做的本质上是定义张量 paddings,而不是 python 列表,用作tf.pad 中的参数。在我原来的答案中,我正在做同样的事情,否则我会出错。事实证明,对于较新的 Tensorflow 版本(至少 1.10),Multihunter 的解决方案和我最新的解决方案都有效。
      猜你喜欢
      • 1970-01-01
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多