方法#1
我们正在与大型数据集和内存的工作是一个问题,所以我会尽量在循环中优化计算。现在,我们可以使用np.einsum
与np.argsort
更换到位实际排序的np.linalg.norm
一部分np.argpartition
,像这样 -
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's cdist
为一个完全量化的解决方案,像这样 -
from scipy.spatial.distance import cdist
out = data_coarse[np.argpartition(cdist(p_fine,p),k,axis=1)[:,:k]].mean(1)
但是,由于我们这里的内存限制,我们可以执行这些化经营ns大块。基本上,我们将从具有数百万行的高排列p_fine
中得到块的行,并使用cdist
,并且因此在每次迭代中获得输出元素的块而不是仅一个标量。有了这个,我们会减少该块的长度。
所以,最后我们将有像这样的实现 -
out = np.empty((m,))
L = 10 # Length of chunk (to be used as a param)
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)
运行测试
设置 -
# Setup inputs
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
所以,有大约2x
改进与第一提出的方法和20x
在与甜蜜点与len_per_iter
参数组在1000
第二个原来的做法。希望这会使您的25分钟运行时间稍微减少一分钟。不错,我猜!
有没有你不能使用[最近邻居回归]的原因(http://scikit-learn.org/stable/modules/generated/sklearn.nei ghbors.KNeighborsRegressor.html)在sklearn中?可能比手工更有效率。 – benten
我认为numpy不是做这种事情的好模块,因为精细网格点上的循环不能被矢量化。如果您需要手动编写代码,我建议使用Cython并使用显式for循环。 –
如果我理解正确,'p'和'p_fine'是网格。由于网格通常是结构化的,如果切换到其中搜索空间数据的速度很快的不同数据结构(例如kD树),速度会更快。 –