如何使用 python 加快最近邻搜索?

How can I speed up nearest neighbor search with python?

我有一个代码,它计算最近的体素(未分配)到体素(已分配)。那就是我有一个体素数组,很少有体素已经分配了标量(1,2,3,4 ....等)值,并且很少有体素是空的(假设值为“0”)。下面的代码找到最接近未分配体素的分配体素,并为该体素分配相同的标量。因此,具有标量“0”的体素将根据最近的体素分配一个值(1 或 2 或 3,...)。下面的代码有效,但它需要太多时间。 还有其他选择吗?或者如果您对如何进一步改进它有任何反馈?

""" #self.voxels 是一个 3D numpy 数组"""

def fill_empty_voxel1(self,argx, argy, argz):
""" where # argx, argy, argz are the voxel location where the voxel is zero"""
    argx1, argy1, argz1 = np.where(self.voxels!=0)   # find the non zero voxels
    a = np.column_stack((argx1, argy1, argz1)) 
    b = np.column_stack((argx, argy, argz))
    tree = cKDTree(a, leafsize=a.shape[0]+1)
    distances, ndx = tree.query(b, k=1, distance_upper_bound= self.mean) # self.mean is a mean radius search value
    argx2, argy2, argz2 = a[ndx][:][:,0],a[ndx][:][:,1],a[ndx][:][:,2]
    self.voxels[argx,argy,argz] = self.voxels[argx2,argy2,argz2] # update the voxel array

例子

""" 这是一个小数据集的小例子:"""

import numpy as np
from scipy.spatial import cKDTree
import timeit

voxels = np.zeros((10,10,5), dtype=np.uint8)
voxels[1:2,:,:] = 5.
voxels[5:6,:,:] = 2.
voxels[:,3:4,:] = 1.
voxels[:,8:9,:] = 4.
argx, argy, argz = np.where(voxels==0)

tic=timeit.default_timer()
argx1, argy1, argz1 = np.where(voxels!=0)   # non zero voxels
a = np.column_stack((argx1, argy1, argz1)) 
b = np.column_stack((argx, argy, argz))
tree = cKDTree(a, leafsize=a.shape[0]+1)
distances, ndx = tree.query(b, k=1, distance_upper_bound= 5.)
argx2, argy2, argz2 = a[ndx][:][:,0],a[ndx][:][:,1],a[ndx][:][:,2]
voxels[argx,argy,argz] = voxels[argx2,argy2,argz2]
toc=timeit.default_timer()
timetaken = toc - tic #elapsed time in seconds
print '\nTime to fill empty voxels', timetaken

可视化:

from mayavi import mlab
data = voxels.astype('float')
scalar_field = mlab.pipeline.scalar_field(data)
iso_surf = mlab.pipeline.iso_surface(scalar_field)
surf = mlab.pipeline.surface(scalar_field)  
vol = mlab.pipeline.volume(scalar_field,vmin=0,vmax=data.max())  
mlab.outline()    
mlab.show()    

现在,如果我将体素数组的维度设置为 (500,500,500),那么计算最近搜索所花费的时间就不再有效了。我怎样才能克服这个?并行计算可以减少时间吗(我不知道我是否可以将代码并行化,如果可以,请告诉我)?

潜在修复:

我可以通过在 cKDTree 查询中添加 n_jobs = -1 参数来显着缩短计算时间。

distances, ndx = tree.query(b, k=1, distance_upper_bound= 5., n_jobs=-1)

我能够在不到一个小时的时间内计算出 13 核 CPU 上的 (400,100,100) 数组的距离。我尝试使用 1 个处理器,完成相同的阵列大约需要 18 个小时。 感谢@gsamaras 的回答!

尝试 sklearn.neighbors.NearestNeighbors 会很有趣,它提供 n_jobs 参数:

The number of parallel jobs to run for neighbors search.

这个包还提供了 Ball Tree 算法,你可以对比 kd-tree 算法进行测试,但我的直觉是 kd-tree 会更好(但这又取决于你的数据,所以研究一下!).


您可能还想使用降维,这很简单。这个想法是你减少你的维度,因此你的数据包含更少的信息,这样可以更快地解决最近邻问题。当然,这里有个取舍,准确性!

你 might/will 通过降维获得较低的准确性,但它可能值得一试。然而,这通常适用于高维space,而你只是在3D中。所以我不知道 对于您的具体情况 使用 sklearn.decomposition.PCA.

是否有意义

备注:

如果您真的想要高性能,例如 , you could switch to , and use CGAL 就达不到。

您可以切换到近似最近邻 (ANN) 算法,这些算法通常利用复杂的散列或邻近图技术来快速索引您的数据并执行更快的查询。一个例子是 Spotify 的 Annoy. Annoy's README includes a plot which shows precision-performance tradeoff comparison of various ANN algorithms published in recent years. The top-performing algorithm (at the time this comment was posted), hnsw, has a Python implementation under Non-Metric Space Library (NMSLIB).