在TensorFlow中创建“滚动窗口”类型分组的有效方法

lokaqttq  于 2023-02-05  发布在  其他
关注(0)|答案(1)|浏览(125)

假设你有一个n维Tensor,其中一个维对应于时间。
我想做的是:给定某个整数window_size,我想用两个新的维度[..., n_groups, window_size]替换我的时间维度。其中n_groups代表时间维度上所有可能的大小为window_size的分组。因此,如果我们从大小为n_periods的时间维度开始,那么n_groups最终应该是n_periods - window_size
使用传统的“Python”循环和切片可以很容易地完成所有这些操作,例如:

stacked = tf.stack([inputs[i:i+window_size] for i in range(len(inputs) - window_size + 1)], axis=0)

但是,如果时间维度非常长,则会产生数量惊人的图形操作。我想知道是否有内置的TensorFlow函数可以帮助我更高效地完成这项相对简单的任务...
“滚动窗口分组”的概念非常普遍,以至于Pandas项目有一个非常复杂的sizeable API来处理这种特殊情况。我本以为TensorFlow也会包含这样的实用程序。

omvjsjqw

omvjsjqw1#

考虑关于map_fn的tf文档:

"map_fn会将fn使用的运算应用于elems的每个元素,从而导致O(elems. shape [0])个总运算。由于map_fn可以并行处理元素,这在一定程度上有所缓解。但是,使用map_fn表示的转换通常仍低于使用矢量化运算表示的等效转换。"

在给定输入Tensor的情况下,可以尝试以下方法:

input_tensor = tf.range([10])

# <tf.Tensor: shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int32)>

转换为方阵:

res = tf.repeat(tf.expand_dims(input_tensor, 0), input_tensor.shape[0], axis = 0)

  # array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], dtype=int32)>

然后将map_fn应用于该Tensor,包括输入中的具有负值的范围向量:

elements = tf.range(10, dtype=tf.int32) * -1
w,_ = tf.map_fn(lambda x: (tf.roll(x[0], x[1], axis=0), x[1]), (res, elements), dtype=(tf.int32, tf.int32))

这会将元素排列为(左侧):

#<tf.Tensor: shape=(10, 10), dtype=int32, numpy=
#array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
#       [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
#       [2, 3, 4, 5, 6, 7, 8, 9, 0, 1],
#       [3, 4, 5, 6, 7, 8, 9, 0, 1, 2],
#       [4, 5, 6, 7, 8, 9, 0, 1, 2, 3],
#       [5, 6, 7, 8, 9, 0, 1, 2, 3, 4],
#       [6, 7, 8, 9, 0, 1, 2, 3, 4, 5],
#       [7, 8, 9, 0, 1, 2, 3, 4, 5, 6],
#       [8, 9, 0, 1, 2, 3, 4, 5, 6, 7],
#       [9, 0, 1, 2, 3, 4, 5, 6, 7, 8]], dtype=int32)>

最后,使用Tensor切片获取所需的元素,如下所示:

window = 8
 tf.slice(w, [0, 0], [(w.shape[0] - window) + 1, window])

给出:

#<tf.Tensor: shape=(3, 8), dtype=int32, numpy=
#array([[0, 1, 2, 3, 4, 5, 6, 7],
#       [1, 2, 3, 4, 5, 6, 7, 8],
#       [2, 3, 4, 5, 6, 7, 8, 9]], dtype=int32)>

对于窗口= 4

window = 4
tf.slice(w, [0, 0], [(w.shape[0] - window) + 1, window])

给出:

#array([[0, 1, 2, 3],
#   [1, 2, 3, 4],
#   [2, 3, 4, 5],
#   [3, 4, 5, 6],
#   [4, 5, 6, 7],
#   [5, 6, 7, 8],
#   [6, 7, 8, 9]], dtype=int32)>

尝试将其转换为一个tf图,看看它是否比普通的python循环有更好的性能。

相关问题