中间numpy数组太大而无法存储

时间:2020-10-23 17:38:00

标签: python-3.x numpy

我有两个向量(一维数组)ab,我想计算c使得c [i] = sum_j(1 /(a [i] + b [j])。我的第一个猜测是:

import numpy as np

a = np.arange(1,int(1e4))
b = np.arange(1, int(1e4))

intermediate = 1/(a[:,None] + b)
c = intermediate.sum(-1)

不幸的是,a很大,因此创建intermediate数组会引发内存错误。我唯一能想到的解决方案是拆分a并执行for循环:

a_split = np.array_split(a, 100)
c_split = []
for array in a_split :
    intermediate = 1/(array[:,None] + b)
    c_split.append(intermediate.sum(-1))
c = np.concatenate(c_split)

但是我被告知不要在python中使用循环(此步骤将重复很多次,因此性能是一个问题),并且这样绕过numpy看起来并不好。有没有更优雅/更有效的方法来避免这种庞大的中间数组?

1 个答案:

答案 0 :(得分:4)

Python中的循环很慢,但是在numpy中添加1e8元素也是如此。

简而言之,我认为python循环不会成为您的瓶颈,并且中间矩阵约为400GB,因此您肯定需要做一些事情。设置好之后,您正在尝试在numpy和100个循环python中进行100,000,000个求和,尽管python相对较慢,但 并不慢。

总体而言,在进行这些大型计算时,您需要了解所有各种速度才能实现更好的优化。

要了解一下,我进行了以下测试。

a = np.arange(1,int(1e4))
x = np.arange(10)

%%timeit
(1./(a+a)).sum()
# 20.2 µs ± 339 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%%timeit
for i in x:
    pass
# 1.07 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

请注意,最后一个是len(x)= 10,所以它更像是0.1µs /循环。

也就是说,即使您逐行对矩阵求和(即循环遍历a),循环的时间也比进行求和的时间少200倍。逐行处理也可能会更容易,因此您不需要将复杂的流程拆分为多个部分,而拆分为较大的部分所获得的速度增益可能不会很明显。

我想证明这一点并不难:

这是您的原始照片:

%%timeit
a_split = np.array_split(a, 100)
c_split = []
for array in a_split :
    intermediate = 1/(array[:,None] + b)
    c_split.append(intermediate.sum(-1))
c = np.concatenate(c_split)
# 321 ms ± 7.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

逐项遍历a(但保留完全相同的结构):

%%timeit
a_split = np.array_split(a, len(a))
c_split = []
for array in a_split :
    intermediate = 1/(array[:,None] + b)
    c_split.append(intermediate.sum(-1))
c = np.concatenate(c_split)
# 252 ms ± 5.95 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此循环遍历a实际上更快,而且如果您是从头开始编写的,那也将容易得多。实际上,出于兴趣我也会这样做:

%%timeit
c = []
for x in a :
    intermediate = 1/(x + b)
    c.append(intermediate.sum())
c = np.array(c)
# 224 ms ± 6.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

所以....是最快的(但是这些时间差通常太小而无法考虑,这里的结论是分块会增加复杂性,而速度却没有有意义的差别)。

主要要点是,请确保您的内部循环在numpy中(此处为10000个元素的总和,我将不显示它,但在numpy中速度要快10000倍)。除此之外,进行优化并非总是值得的。