Python 中子数组的快速求和

Fast summing of subarrays in Python

我有一个半径为 w 的数据立方体 a,对于该立方体的每个元素,我想在半径为 r 的立方体中添加元素和所有周围的值,其中 r < w。结果应以相同形状的数组 b.

返回

举个简单的例子,假设:

a = numpy.ones(shape=(2*w,2*w,2*w),dtype='float32')
kernel = numpy.ones(shape=(2*r,2*r,2*r),dtype='float32')
b = convolve(a,kernel,mode='constant',cval=0)

那么对于所有不在边上的索引,b 的值为 (2r)(2r)(2r)。

目前我正在使用循环来执行此操作并且它非常慢,尤其是对于较大的 w 和 r。我尝试了 scipy 卷积,但在循环中几乎没有加速。我现在正在查看 numba 的并行计算功能,但无法弄清楚如何重写代码以使用 numba。我有 Nvidia RTX 卡,所以 CUDA GPU 计算也是可能的。

欢迎提出建议。

这是我当前的代码:

for x in range(0,w*2):
    print(x)
    for y in range(0,w*2):
        for z in range(0,w*2):
            if x >= r:
                x1 = x - r
            else:
                x1 = 0
            if x < w*2-r:
                x2 = x + r
            else:
                x2 = w*2 - 1
            
            if y >= r:
                y1 = y - r
            else:
                y1 = 0
            if y < w*2-r:
                y2 = y + r
            else:
                y2 = w*2 - 1

            if z >= r:
                z1 = z - r
            else:
                z1 = 0
            if z < w*2-r:
                z2 = z + r
            else:
                z2 = w*2 - 1

            b[x][y][z] = numpy.sum(a[x1:x2,y1:y2,z1:z2])

return b

这是您的代码的一个非常简单的版本,因此它可以与 numba 一起使用。我发现 speed-ups 是纯 numpy 代码的 10 倍。但是,您应该能够使用 FFT 卷积算法(例如 scipy 的 fftconvolve)获得更大的 speed-ups。您能否分享一下让卷积发挥作用的尝试?

from numba import njit

@njit
def sum_cubes(a,b,w,r):
    for x in range(0,w*2):
        #print(x)
        for y in range(0,w*2):
            for z in range(0,w*2):
                if x >= r:
                    x1 = x - r
                else:
                    x1 = 0
                if x < w*2-r:
                    x2 = x + r
                else:
                    x2 = w*2 - 1

                if y >= r:
                    y1 = y - r
                else:
                    y1 = 0
                if y < w*2-r:
                    y2 = y + r
                else:
                    y2 = w*2 - 1

                if z >= r:
                    z1 = z - r
                else:
                    z1 = 0
                if z < w*2-r:
                    z2 = z + r
                else:
                    z2 = w*2 - 1

                b[x,y,z] = np.sum(a[x1:x2,y1:y2,z1:z2])
    return b

编辑:您的原始代码中有一个小错误。 numpy 索引的工作方式,最后一行应该是

b[x,y,z] = np.sum(a[x1:x2+1,y1:y2+1,z1:z2+1])

除非你希望立方体是 off-centre。

假设您确实希望立方体居中,那么进行此计算的一种更快的方法是使用 scipy 的统一过滤器:

from scipy.ndimage import uniform_filter
def sum_cubes_quickly(a,b,w,r):
    b = uniform_filter(a,mode='constant',cval=0,size=2*r+1)*(2*r+1)**3
    return b

w = 50,r = 10 的随机生成数据的一些快速运行时比较:

  • 原始原始 numpy 代码 - 15.1 秒
  • Numba 的 numpy 代码 - 8.1 秒
  • uniform_filter - 13.1 毫秒