方法一
我们正在处理大型数据集,并且内存是一个问题,因此我将尝试优化循环中的计算。现在,我们可以使用np.einsum
来替换np.linalg.norm
部分,并使用np.argpartition
替代实际的np.argsort
排序,代码如下 -
out = np.empty((m,))
for i, ps in enumerate(p_fine):
subs = ps-p
sq_dists = np.einsum('ij,ij->i',subs,subs)
out[i] = data_coarse[np.argpartition(sq_dists,k)[:k]].sum()
out = out/k
方法 #2
现在,我们还可以采用另一种方法,使用Scipy的cdist函数
来实现完全向量化的解决方案,代码如下 -
from scipy.spatial.distance import cdist
out = data_coarse[np.argpartition(cdist(p_fine,p),k,axis=1)[:,:k]].mean(1)
但是,由于我们在这里受到内存限制,因此我们可以分块执行这些操作。基本上,我们将从具有数百万行的高数组 p_fine
中获取行块,并使用 cdist
,因此在每次迭代中获取输出元素的块而不仅仅是一个标量。通过这种方式,我们将通过该块的长度减少循环计数。
因此,最终我们将拥有以下实现 -
out = np.empty((m,))
L = 10
num_iter = m//L
for j in range(num_iter):
p_fine_slice = p_fine[L*j:L*j+L]
out[L*j:L*j+L] = data_coarse[np.argpartition(cdist\
(p_fine_slice,p),k,axis=1)[:,:k]].mean(1)
运行时测试
安装 -
m,n = 20000,100
p_fine = np.random.rand(m,3)
p = np.random.rand(n,3)
data_coarse = np.random.rand(n)
k = 5
def original_approach(p,p_fine,m,n,k):
data_fine = np.empty((m,))
for i, ps in enumerate(p_fine):
data_fine[i] = np.mean(data_coarse[np.argsort(np.linalg.norm\
(ps-p,axis=1))[:k]])
return data_fine
def proposed_approach(p,p_fine,m,n,k):
out = np.empty((m,))
for i, ps in enumerate(p_fine):
subs = ps-p
sq_dists = np.einsum('ij,ij->i',subs,subs)
out[i] = data_coarse[np.argpartition(sq_dists,k)[:k]].sum()
return out/k
def proposed_approach_v2(p,p_fine,m,n,k,len_per_iter):
L = len_per_iter
out = np.empty((m,))
num_iter = m//L
for j in range(num_iter):
p_fine_slice = p_fine[L*j:L*j+L]
out[L*j:L*j+L] = data_coarse[np.argpartition(cdist\
(p_fine_slice,p),k,axis=1)[:,:k]].sum(1)
return out/k
时间 -
In [134]: %timeit original_approach(p,p_fine,m,n,k)
1 loops, best of 3: 1.1 s per loop
In [135]: %timeit proposed_approach(p,p_fine,m,n,k)
1 loops, best of 3: 539 ms per loop
In [136]: %timeit proposed_approach_v2(p,p_fine,m,n,k,len_per_iter=100)
10 loops, best of 3: 63.2 ms per loop
In [137]: %timeit proposed_approach_v2(p,p_fine,m,n,k,len_per_iter=1000)
10 loops, best of 3: 53.1 ms per loop
In [138]: %timeit proposed_approach_v2(p,p_fine,m,n,k,len_per_iter=2000)
10 loops, best of 3: 63.8 ms per loop
因此,第一种提出的方法有约2倍
的改进,第二种方法在len_per_iter
参数设置为1000
的最佳位置上比原始方法快20倍
。希望这将把您的25分钟运行时间缩短到一分钟左右。我想这还不错!
p
和p_fine
是网格。由于网格通常是有结构的,如果您切换到不同的数据结构(例如 kD 树),在其中搜索空间数据会更快。 - Hannes Ovrén