Python不同维度的卷积

5
我正在尝试在Python中实现卷积神经网络。
然而,当我使用signal.convolve或np.convolve时,它不能在X,Y上执行卷积(其中X是3D,Y是2D)。 X是训练的小批量,Y是过滤器。
我不想为每个训练向量进行循环,如下所示:
for i in xrange(X.shape[2]):
    result = signal.convolve(X[:,:,i], Y, 'valid')
    ....

那么,有没有可以高效地执行卷积的函数?
1个回答

5

Scipy实现了标准的N维卷积,因此要进行卷积的矩阵和核都是N维的。

一个快速的解决方法是给Y添加一个额外的维度,使得Y成为3维的:

result = signal.convolve(X, Y[..., None], 'valid')

假设在您的示例中,最后一个轴对应于图像索引,如[width,height,image_idx](或[height,width,image_idx])。如果情况相反,并且图像在第一个轴上进行索引(如C顺序数组中更常见),则应将Y [..., None] 替换为Y [None,...]Y [...,None]会向Y添加一个额外的轴,使其成为3维[kernel_width,kernel_height,1],从而将其转换为有效的3维卷积核。
注意:这假定所有输入小批次都具有相同的width x height,这在CNN中是标准的。
编辑:按照@Divakar的建议进行一些计时。
测试框架设置如下:
def test(S, N, K):
    """ S: image size, N: num images, K: kernel size"""
    a = np.random.randn(S, S, N)
    b = np.random.randn(K, K)
    valid = [slice(K//2, -K//2+1), slice(K//2, -K//2+1)]

    %timeit signal.convolve(a, b[..., None], 'valid')
    %timeit signal.fftconvolve(a, b[..., None], 'valid')
    %timeit ndimage.convolve(a, b[..., None])[valid]

以下是不同配置的测试内容:
  • Varying image size S:

    >>> test(100, 50, 11) # 100x100 images
    1 loop, best of 3: 909 ms per loop
    10 loops, best of 3: 116 ms per loop
    10 loops, best of 3: 54.9 ms per loop
    
    >>> test(1000, 50, 11) # 1000x1000 images
    1 loop, best of 3: 1min 51s per loop
    1 loop, best of 3: 16.5 s per loop
    1 loop, best of 3: 5.66 s per loop
    
  • Varying number of images N:

    >>> test(100, 5, 11) # 5 images
    10 loops, best of 3: 90.7 ms per loop
    10 loops, best of 3: 26.7 ms per loop
    100 loops, best of 3: 5.7 ms per loop
    
    >>> test(100, 500, 11) # 500 images
    1 loop, best of 3: 9.75 s per loop
    1 loop, best of 3: 888 ms per loop
    1 loop, best of 3: 727 ms per loop
    
  • Varying kernel size K:

    >>> test(100, 50, 5) # 5x5 kernels
    1 loop, best of 3: 217 ms per loop
    10 loops, best of 3: 100 ms per loop
    100 loops, best of 3: 11.4 ms per loop
    
    >>> test(100, 50, 31) # 31x31 kernels
    1 loop, best of 3: 4.39 s per loop
    1 loop, best of 3: 220 ms per loop
    1 loop, best of 3: 560 ms per loop
    
因此,简而言之,ndimage.convolve 总是更快的,除非卷积核的大小非常大(例如,在最后一个测试中,K = 31)。

不错的发现!为了更快的版本,可以使用 ndimage.filters.convolve(X,Y[...,None]),也许需要进行一些切片操作? - Divakar
@Divakar 如果有时间的话,计时会很好(如果我有时间的话我会做),但是我认为对于大内核大小,ndimage,filters.convolve 应该会更慢(因为它在空间域中进行卷积,而 signal.convolve 在傅里叶域中进行卷积)。ndimage 滤波器的优点是支持不同的边界条件(与傅里叶不同,我认为根据定义等价于用 0 填充图像)。我会尽快进行计时! - Imanol Luengo
@Divakar 测试完成!我感觉这有点像是某种既视感。正如你所建议的那样,ndimage.convolve 通常更快。我记得在另一个线程中也有过类似的对话哈哈。 - Imanol Luengo
你知道那是好的记忆,而不仅仅是似曾相识! ;) 这是我认为你所指的帖子:https://dev59.com/CJjga4cB1Zd3GeqPN7JD#38232755 - Divakar
@Divakar 哈哈,就是那个!那时候我提议使用uniform_filter而不是signal.convolve。虽然由于内核的可分离性,uniform_filter更快,但上述结果似乎证实了我们在那个线程中得出的结论:对于大内核大小,傅里叶变换(fftconvolve)>空间卷积,否则空间卷积>傅里叶变换。 - Imanol Luengo
耶!这里确实占据了主导地位的理论!:D - Divakar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接