TensorFlow:numpy.repeat() 备选方案

TensorFlow: numpy.repeat() alternative

我想以成对的方式比较来自我的神经网络的预测值 yp,所以我使用了(回到我的旧 numpy 实现中):

idx = np.repeat(np.arange(len(yp)), len(yp))
jdx = np.tile(np.arange(len(yp)), len(yp))
s = yp[[idx]] - yp[[jdx]]

这基本上创建了一个我随后使用的索引网格。 idx=[0,0,0,1,1,1,...]jdx=[0,1,2,0,1,2...]。不知道有没有更简单的方法...

总之,TensorFlow有一个tf.tile(),但好像缺少一个tf.repeat()

idx = np.repeat(np.arange(n), n)
v2 = v[idx]

我得到错误:

TypeError: Bad slice index [  0   0   0 ..., 215 215 215] of type <type 'numpy.ndarray'>

使用 TensorFlow 常量进行索引也不起作用:

idx = tf.constant(np.repeat(np.arange(n), n))
v2 = v[idx]

-

TypeError: Bad slice index Tensor("Const:0", shape=TensorShape([Dimension(46656)]), dtype=int64) of type <class 'tensorflow.python.framework.ops.Tensor'>

我的想法是将我的 RankNet 实现转换为 TensorFlow。

您可以使用 tf.tile() and tf.reshape():

的组合来实现 np.repeat() 的效果
idx = tf.range(len(yp))
idx = tf.reshape(idx, [-1, 1])    # Convert to a len(yp) x 1 matrix.
idx = tf.tile(idx, [1, len(yp)])  # Create multiple columns.
idx = tf.reshape(idx, [-1])       # Convert back to a vector.

您可以使用 tf.tile():

简单地计算 jdx
jdx = tf.range(len(yp))
jdx = tf.tile(jdx, [len(yp)])

对于索引,您可以尝试使用 tf.gather()yp 张量中提取不连续的切片:

s = tf.gather(yp, idx) - tf.gather(yp, jdx)

看起来你的问题很受欢迎以至于人们 refer it on TF tracker。遗憾的是,TF 中仍未实现相同的功能。

你可以结合tf.tile, tf.reshape, tf.squeeze. Here is a way to convert examples from np.repeat:

来实现
import numpy as np
import tensorflow as tf

x = [[1,2],[3,4]]
print np.repeat(3, 4)
print np.repeat(x, 2)
print np.repeat(x, 3, axis=1)

x = tf.constant([[1,2],[3,4]])
with tf.Session() as sess:
    print sess.run(tf.tile([3], [4]))
    print sess.run(tf.squeeze(tf.reshape(tf.tile(tf.reshape(x, (-1, 1)), (1, 2)), (1, -1))))
    print sess.run(tf.reshape(tf.tile(tf.reshape(x, (-1, 1)), (1, 3)), (2, -1)))

在最后一种情况下,每个元素的重复次数不同,您很可能需要 loops

以防万一有人对复制矩阵的二维方法感兴趣。我认为这可行:

TF_obj = tf.zeros([128, 128])
tf.tile(tf.expand_dims(TF_obj, 2), [1, 1, 2])

您可以通过 tf.stack 将值与自身相结合来模拟缺失 tf.repeat:

value = np.arange(len(yp))  # what to repeat
repeat_count = len(yp)      # how many times
repeated = tf.stack ([value for i in range(repeat_count)], axis=1)

我建议只在小的重复次数上使用它。

import numpy as np
import tensorflow as tf
import itertools    

x = np.arange(6).reshape(3,2)
x = tf.convert_to_tensor(x)
N = 3 # number of repetition
K = x.shape[0] # for here 3

order = list(range(0, N*K, K))
order = [[x+i for x in order] for i in range(K)]
order = list(itertools.chain.from_iterable(order))
x_rep = tf.gather(tf.tile(x, [N, 1]), order)

结果来自:

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

收件人:

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

如果你想:

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

只需使用tf.tile(x, [N, 1])

虽然已经给出了许多干净且有效的解决方案,但它们似乎都是基于每次迭代从头开始生成一组索引。

虽然在训练期间生成这些节点的成本通常不高,但如果使用您的模型进行推理,成本可能会很高。

重复tf.range(就像你的例子)已经出现了几次,所以我构建了以下函数创建器。给定某些事物将被重复的最大次数以及需要重复的事物的最大数量,它 returns 一个产生与 np.repeat(np.arange(len(multiples)), multiples).

相同值的函数
import tensorflow as tf
import numpy as np


def numpy_style_repeat_1d_creator(max_multiple=100, max_to_repeat=10000):
    board_num_lookup_ary = np.repeat(
        np.arange(max_to_repeat),
        np.full([max_to_repeat], max_multiple))
    board_num_lookup_ary = board_num_lookup_ary.reshape(max_to_repeat, max_multiple)

    def fn_to_return(multiples):
        board_num_lookup_tensor = tf.constant(board_num_lookup_ary, dtype=tf.int32)
        casted_multiples = tf.cast(multiples, dtype=tf.int32)
        padded_multiples = tf.pad(
            casted_multiples,
            [[0, max_to_repeat - tf.shape(multiples)[0]]])

        return tf.boolean_mask(
            board_num_lookup_tensor,
            tf.sequence_mask(padded_multiples, maxlen=max_multiple))

    return fn_to_return

#Here's an example of how it can be used
with tf.Session() as sess:
    repeater = numpy_style_repeat_1d_creator(5,4)
    multiples = tf.constant([4,1,3])

    repeated_values = repeater(multiples)
    print(sess.run(repeated_values))

一般的想法是存储一个重复的张量,然后将其屏蔽,但它可能有助于直观地看到它(这是针对上面给出的示例):

In the example above the following Tensor is produced:
[[0,0,0,0,0],
 [1,1,1,1,1],
 [2,2,2,2,2],
 [3,3,3,3,3]]

For multiples [4,1,3] it will collect the non-X values:
[[0,0,0,0,X],
 [1,X,X,X,X],
 [2,2,2,X,X],
 [X,X,X,X,X]]

resulting in:
[0,0,0,0,1,2,2,2]

tl;dr:为避免每次都生成索引(可能代价高昂),预先重复所有内容,然后每次都屏蔽该张量

只是为了一维张量,我做了这个函数

def tf_repeat(y,repeat_num):   
        return tf.reshape(tf.tile(tf.expand_dims(y,axis=-1),[1,repeat_num]),[-1]) 

最近从 1.13 开始使用 RaggedTensor 实用程序添加了 relatively fast 实现,但它不是正式导出的 API 的一部分。您仍然可以使用它,但它可能会消失。

from tensorflow.python.ops.ragged.ragged_util import repeat

来自源代码:

# This op is intended to exactly match the semantics of numpy.repeat, with
# one exception: numpy.repeat has special (and somewhat non-intuitive) behavior
# when axis is not specified.  Rather than implement that special behavior, we
# simply make `axis` be a required argument.

根据 tf api documenttf.keras.backend.repeat_elements()np.repeat() 做同样的工作。例如,

x = tf.constant([1, 3, 3, 1], dtype=tf.float32)
rep_x = tf.keras.backend.repeat_elements(x, 5, axis=0)
# result: [1. 1. 1. 1. 1. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 1. 1. 1. 1. 1.]

所以我发现tensorflow有一种重复数组元素的方法。 tf.keras.backend.repeat_elements 方法就是您要查找的方法。晚点来的人,可以省去不少力气。这个 link 提供了对方法的解释,具体说

Repeats the elements of a tensor along an axis, like np.repeat

我提供了一个非常简短的示例,它证明元素的复制方式与 np.repeat 完全相同。

import numpy as np
import tensorflow as tf

x = np.random.rand(2,2)
# print(x)  # uncomment this line to see the array's elements

y = tf.convert_to_tensor(x)
y = tf.keras.backend.repeat_elements(x, rep=3, axis=0)
# print(y)  # uncomment this line to see the results