方法#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樹),速度會更快。 –