我已经编写了以下代码,它完全符合我的要求,但速度太慢了。我确信有一种方法可以让它更快,但我似乎无法找到它应该如何完成。代码的第一部分只是为了显示哪种形状。
两张测量图像(VV1
和HH1
)
预先计算的值,VV
模拟值和HH
模拟值,均取决于3个参数(预计算为(101, 31, 11)
值)
索引2只是将VV
和HH
图像放在同一个ndarray中,而不是制作两个3darrays
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,但我已经搜索了很长时间,但我找不到写东西的方法像matlab中的“parfor”而不是“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循环并行?
答案 0 :(得分:5)
这可以替换内部循环,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)
答案 1 :(得分:0)
在计算机科学中,有Big O notation的概念,用于获得做某事需要做多少工作的近似值。为了使程序快速,尽量少做。
这就是为什么Janne的答案要快得多,你的计算量也会减少。更进一步,我们可以应用memoization的概念,因为你是CPU绑定而不是RAM绑定。如果需要比以下示例更复杂,可以使用memory library。
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
很慢,也要记住它。
答案 2 :(得分:0)
并行化循环的一种方法是以使用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数组(他们在你的例子中。)
我无法轻易测试,所以如果有问题,请随时告诉我。