关于在 Numpy 中向量化块操作的建议

sha*_*kde 6 python performance numpy scientific-computing scipy

我正在尝试实现一系列统计操作,我需要帮助向量化我的代码。

这个想法是NxN从两个图像中提取补丁,计算这两个补丁之间的距离度量。

为此,首先我使用以下循环构建补丁:

params = []
for i in range(0,patch1.shape[0],1):
    for j in range(0,patch1.shape[1],1):
        window1 = np.copy(imga[i:i+N,j:j+N]).flatten()
        window2 = np.copy(imgb[i:i+N,j:j+N]).flatten()
        params.append((window1, window2))
print(f"We took {time()- t0:2.2f} seconds to prepare {len(params)/1e6} million patches.")
Run Code Online (Sandbox Code Playgroud)

这大约需要 10 秒才能完成,而且我并不太关心预处理时间。下面的步骤是我想要优化的步骤。

在此之后,为了加快处理速度,我使用了 multipool 来计算实际结果。包含实际计算的函数如下:

@njit
def cauchy_schwartz(imga, imgb):
    p, _ = np.histogram(imga, bins=10)
    p = p/np.sum(p)
    q, _ = np.histogram(imgb, bins=10)
    q = q/np.sum(q)

    n_d = np.array(np.sum(p * q)) 
    d_d = np.array(np.sum(np.power(p, 2) * np.power(q, 2)))
    return -1.0 * np.log10( n_d, d_d)
Run Code Online (Sandbox Code Playgroud)

我使用这个结构来处理所有的补丁:

def f(param):
    return cauchy_schwartz(*param)

with Pool(4) as p:
    r = list(tqdm.tqdm(p.imap(f,params), total=len(params)))
Run Code Online (Sandbox Code Playgroud)

我确信必须有更优雅的方法来做到这一点,因为如果我将整个 10Kpx x 10Kpx 图像发送到该cauchy_schwartz函数,它会在一秒钟内处理所有内容,但使用我的方法,即使在 4 个内核上也需要很长时间。

我的心智模型是blockproc在 matlab 中是如何工作的——我最终以这种模式编写了这段代码。我将不胜感激有关提高此代码性能的任何建议。

meT*_*sky 3

通过使用apply_along_axis,您可以摆脱cauchy_schwartz。由于您不太关心预处理时间,假设您已经获得了params包含展平补丁的数组

params = np.random.rand(3,2,100)

params正如您所看到的的形状(3,2,100),随机选择三个数字 3、2 和 100 来创建一个辅助数组来演示使用 的逻辑apply_along_axis。3 对应于您拥有的面片数量(由面片形状和图像大小决定),2 对应于两个图像,100 对应于展平的面片。params因此, is的轴,这与您的代码创建的(idx of patches, idx of images, idx of entries of a flattened patch)列表完全匹配params

params = []
for i in range(0,patch1.shape[0],1):
    for j in range(0,patch1.shape[1],1):
        window1 = np.copy(imga[i:i+N,j:j+N]).flatten()
        window2 = np.copy(imgb[i:i+N,j:j+N]).flatten()
        params.append((window1, window2))
Run Code Online (Sandbox Code Playgroud)

使用辅助数组params,这是我的解决方案:

hist = np.apply_along_axis(lambda x: np.histogram(x,bins=11)[0],2,params)
hist = hist / np.sum(hist,axis=2)[...,None]

n_d = np.sum(np.product(hist,axis=1),axis=1)
d_d = np.sum(np.product(np.power(hist,2),axis=1),axis=1)
res = -1.0 * np.log10(n_d, d_d)
Run Code Online (Sandbox Code Playgroud)