簡體   English   中英

如何避免使用numpy for-loops?

[英]How to avoid using for-loops with numpy?

我已經編寫了下面這段代碼,它完全符合我的要求,但速度太慢了。 我確信有一種方法可以讓它更快,但我似乎無法找到它應該如何完成。 代碼的第一部分只是為了顯示哪種形狀。

兩個測量圖像( VV1HH1
預計算值, VV模擬和HH模擬,均取決於3個參數(預先計算為(101, 31, 11) 101,31,11 (101, 31, 11)值)
索引2只是將VVHH圖像放在同一個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循環並行?

這可以替換內部循環, jjj

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.ndim2 ,因為它將添加第三個軸,並沿其求和。 這會減慢它的速度,因為你必須構建列表,堆棧它,然后求它,但這些都是並行或numpy操作。

我還將zip更改為numpy操作np.column_stack ,因為對於長數組, zip 慢,假設它們已經是1d數組(它們在你的例子中)。

我不能輕易測試這個,所以如果有問題,請隨時告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM