【问题标题】:Numpy summation with sliding window is really slow带有滑动窗口的 Numpy 求和真的很慢
【发布时间】:2019-01-22 18:28:16
【问题描述】:

代码:

shape = np.array([6, 6])
grid = np.array([x.ravel() for x in np.meshgrid(*[np.arange(x) for i, x in enumerate(shape)], indexing='ij')]).T
slices = [tuple(slice(box[i], box[i] + 2) for i in range(len(box))) for box in grid]
score = np.zeros((7,7,3))
column = np.random.randn(36, 12) #just for example

column
>> array([[  0,   1,   2,   3, ... 425, 426, 427, 428, 429, 430, 431]])

column = column.reshape((16, 3, 3, 3))
for i, window in enumerate(slices):
        score[window] += column[i]
score
>> array([[[0.000e+00, 1.000e+00, 2.000e+00],
    [3.000e+01, 3.200e+01, 3.400e+01],
    [9.000e+01, 9.300e+01, 9.600e+01], ... 
    [8.280e+02, 8.300e+02, 8.320e+02],
    [4.290e+02, 4.300e+02, 4.310e+02]]])

它可以工作,但最后两行需要很长时间,因为它们将处于循环状态。问题是“网格”变量包含一个窗口数组。我现在不知道如何加快这个过程。

【问题讨论】:

  • 添加示例数据?
  • 请查看minimal reproducible example 并包含所需的输出示例。
  • 最后两行似乎很容易矢量化...纯 python for 循环对于迭代和求和真的很慢..矢量化操作正好解决了这个问题
  • 你的意思是我可以用 np.vectorize() 向量化它们吗?
  • 经过一些调整以使代码正常工作,我在两个部分上尝试了 `%%timeit。设置平均为 5 毫秒,for 循环为 6 毫秒。没有太大的区别。超过 600 次的迭代需要时间。

标签: numpy sliding-window numpy-slicing


【解决方案1】:

让我们稍微简化一下问题 - 减少尺寸,并删除最终尺寸 3 尺寸:

In [265]: shape = np.array([4,4])
In [266]: grid = np.array([x.ravel() for x in np.meshgrid(*[np.arange(x) for i
     ...: , x in enumerate(shape)], indexing='ij')]).T
     ...: grid = [tuple(slice(box[i], box[i] + 3) for i in range(len(box))) fo
     ...: r box in grid]
     ...: 
     ...: 
In [267]: len(grid)
Out[267]: 16
In [268]: score = np.arange(36).reshape(6,6)
In [269]: X = np.array([score[x] for x in grid]).reshape(4,4,3,3)
In [270]: X
Out[270]: 
array([[[[ 0,  1,  2],
         [ 6,  7,  8],
         [12, 13, 14]],

        [[ 1,  2,  3],
         [ 7,  8,  9],
         [13, 14, 15]],

        [[ 2,  3,  4],
         [ 8,  9, 10],
         [14, 15, 16]],

        ....
        [[21, 22, 23],
         [27, 28, 29],
         [33, 34, 35]]]])

这是一个移动窗口 - 一个 (3,3) 数组,上移 1,...,下移 1,等等

使用as_strided 可以构造数组视图,该视图由所有这些窗口组成,但无需实际复制值。与as_strided 合作之后,我能够构建等效的步幅:

In [271]: score.shape
Out[271]: (6, 6)
In [272]: score.strides
Out[272]: (48, 8)
In [273]: ast = np.lib.stride_tricks.as_strided
In [274]: x=ast(score, shape=(4,4,3,3), strides=(48,8,48,8))
In [275]: np.allclose(X,x)
Out[275]: True

这可以扩展到您的 (28,28,3) 维度,并变成总和。

之前的 SO 问题中已经介绍了生成此类移动窗口。它也在其中一个图像处理包中实现。


适应3通道图像,

In [45]: arr.shape
Out[45]: (6, 6, 3)
In [46]: arr.strides
Out[46]: (144, 24, 8)
In [47]: arr[:3,:3,0]
Out[47]: 
array([[ 0.,  1.,  2.],
       [ 6.,  7.,  8.],
       [12., 13., 14.]])

In [48]: x = ast(arr, shape=(4,4,3,3,3), strides=(144,24,144,24,8))
In [49]: x[0,0,:,:,0]
Out[49]: 
array([[ 0.,  1.,  2.],
       [ 6.,  7.,  8.],
       [12., 13., 14.]])

由于我们一次将窗口移动一个元素,x 的步幅很容易从源步幅推导出来。

对于 4x4 窗口,只需更改形状

x = ast(arr, shape=(3,3,4,4,3), strides=(144,24,144,24,8))

Efficiently Using Multiple Numpy Slices for Random Image Cropping

@Divikar 建议使用skimage

使用默认step=1,结果兼容:

In [55]: from skimage.util.shape import view_as_windows
In [63]: y = view_as_windows(arr,(4,4,3))
In [64]: y.shape
Out[64]: (3, 3, 1, 4, 4, 3)
In [69]: np.allclose(x,y[:,:,0])
Out[69]: True

【讨论】:

  • 好的。我还不熟悉 as_strided 函数。 'strides' 参数是什么意思?
  • strides 是 numpy 数组的基本属性(与 shapedtype 一起)。这是使这种多维代码如此强大的部分原因。但它在as_strided 中的使用是一个相当高级的话题。
  • 我已经稍微更新了我的代码。我知道您的解决方案可以转化为求和,但在我的特殊情况下,我希望结果为“分数”形状,因为我想迭代地将“列”添加到“分数”变量。
  • 我会接受一个答案,但我有一点其他问题
猜你喜欢
  • 1970-01-01
  • 2016-11-25
  • 1970-01-01
  • 2022-10-16
  • 2016-08-16
  • 2015-12-16
  • 2017-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多