[英]How to avoid using for-loops with numpy?
我已經編寫了下面這段代碼,它完全符合我的要求,但速度太慢了。 我確信有一種方法可以讓它更快,但我似乎無法找到它應該如何完成。 代碼的第一部分只是為了顯示哪種形狀。
兩個測量圖像( VV1
和HH1
)
預計算值, VV
模擬和HH
模擬,均取決於3個參數(預先計算為(101, 31, 11)
101,31,11 (101, 31, 11)
值)
索引2只是將VV
和HH
圖像放在同一個ndarray中,而不是制作兩個3darray
VV1 = numpy.ndarray((54, 43)).flatten()
HH1 = numpy.ndarray((54, 43)).flatten()
precomp = numpy.ndarray((101, 31, 11, 2))
我們讓三個參數中的兩個變化
comp = numpy.zeros((len(parameter1), len(parameter2)))
for i,(vv,hh) in enumerate(zip(VV1,HH1)):
comp0 = numpy.zeros((len(parameter1),len(parameter2)))
for j in range(len(parameter1)):
for jj in range(len(parameter2)):
comp0[j,jj] = numpy.min((vv-precomp[j,jj,:,0])**2+(hh-precomp[j,jj,:,1])**2)
comp+=comp0
我知道我應該做的顯而易見的事情是擺脫盡可能多的for循環,但我不知道如何使numpy.min
在處理更多維度時表現得正常。
第二件事(不太重要,如果它可以得到矢量化,但仍然很有趣)我注意到它主要占用CPU時間,而不是RAM,但我已經搜索了很長時間,但我找不到一種方法來寫“parfor “在matlab中代替”for“,(如果我只是將for循環放在一個單獨的方法中,是否有可能創建一個@parallel
裝飾器?)
編輯:回答Janne Karila:是的,肯定會改善它,
for (vv,hh) in zip(VV1,HH1):
comp+= numpy.min((vv-precomp[...,0])**2+(hh-precomp[...,1])**2, axis=2)
肯定是快得多,但有沒有可能刪除外部for循環? 有沒有辦法用@parallel
或其他東西制作一個for循環並行?
這可以替換內部循環, j
和jj
comp0 = numpy.min((vv-precomp[...,0])**2+(hh-precomp[...,1])**2, axis=2)
這可能是整個循環的替代品,盡管所有這些索引都讓我有點興奮。 (這會創建一個大的中間數組)
comp = numpy.sum(
numpy.min((VV1.reshape(-1,1,1,1) - precomp[numpy.newaxis,...,0])**2
+(HH1.reshape(-1,1,1,1) - precomp[numpy.newaxis,...,1])**2,
axis=2),
axis=0)
在計算機科學中,存在Big O符號的概念,用於獲得做某事需要多少工作的近似值。 為了使程序快速,盡可能少做。
這就是為什么Janne的答案要快得多,你做的計算更少。 更進一步,我們可以應用memoization的概念,因為你是CPU綁定而不是RAM綁定。 如果需要比以下示例更復雜,則可以使用內存庫 。
class AutoVivification(dict):
"""Implementation of perl's autovivification feature."""
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
memo = AutoVivification()
def memoize(n, arr, end):
if not memo[n][arr][end]:
memo[n][arr][end] = (n-arr[...,end])**2
return memo[n][arr][end]
for (vv,hh) in zip(VV1,HH1):
first = memoize(vv, precomp, 0)
second = memoize(hh, precomp, 1)
comp+= numpy.min(first+second, axis=2)
已經計算過的任何內容都會保存到字典中的內存中,我們可以稍后查找,而不是重新計算它。 您甚至可以將完成的數學分解為更小的步驟,如果需要,每個步驟都會被記憶。
AutoVivification字典只是為了更容易將結果保存在memoize中,因為我很懶。 同樣,你可以numpy.min
你做的任何數學,所以如果numpy.min
很慢, numpy.min
它。
並行化循環的一種方法是以使用map
的方式構造它。 在這種情況下,您可以使用multiprocessing.Pool
來使用並行映射。
我會改變這個:
for (vv,hh) in zip(VV1,HH1):
comp+= numpy.min((vv-precomp[...,0])**2+(hh-precomp[...,1])**2, axis=2)
對於這樣的事情:
def buildcomp(vvhh):
vv, hh = vvhh
return numpy.min((vv-precomp[...,0])**2+(hh-precomp[...,1])**2, axis=2)
if __name__=='__main__':
from multiprocessing import Pool
nthreads = 2
p = Pool(nthreads)
complist = p.map(buildcomp, np.column_stack((VV1,HH1)))
comp = np.dstack(complist).sum(-1)
請注意, dstack
假定每個comp.ndim
為2
,因為它將添加第三個軸,並沿其求和。 這會減慢它的速度,因為你必須構建列表,堆棧它,然后求它,但這些都是並行或numpy操作。
我還將zip
更改為numpy操作np.column_stack
,因為對於長數組, zip
更慢,假設它們已經是1d數組(它們在你的例子中)。
我不能輕易測試這個,所以如果有問題,請隨時告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.