有时您必须在一个或多个大型Numpy阵列上执行许多中间操作。这可以很快导致MemoryError
s。在我迄今为止的研究中,你发现Pickling(Pickle,CPickle,Pytables等)和gc.collect()
是减轻这种情况的方法。我想知道程序员在处理大量数据时是否还有其他技术(当然,除了删除策略/代码中的冗余)。
另外,如果有一点我确定没有什么是免费的。使用其中一些技术,有什么权衡(即速度,稳健性等)?
答案 0 :(得分:21)
我感觉到你的痛苦......你有时最终会以你后来丢弃的值存储几倍大小的数组。在一次处理数组中的一个项目时,这是无关紧要的,但在向量化时会杀死你。
我将使用工作中的示例进行说明。我最近使用numpy对here描述的算法进行了编码。它是一种彩色图算法,它采用RGB图像,并将其转换为CMYK图像。对每个像素重复的过程如下:
您可以采取以下措施来解决这个问题:
也许你不能在一次通过中处理1,000x1,000阵列。但是如果你可以用python for循环迭代10个100x1,000的数组,那么它仍然会超过1,000,000个项目的python迭代器!它会变慢,是的,但不是那么多。
这与我上面的插值示例直接相关,并且更难以发现,尽管值得关注它。因为我在每个维度上有4位的三维立方体进行插值,所以只有16x16x16可能的结果,可以存储在16个16x16x16字节的数组中。所以我可以预先计算它们并使用64KB内存来存储它们,并逐个查找整个图像的值,而不是以巨大的内存成本为每个像素重做相同的操作。这已经为小到64x64像素的图像付出了代价,并且基本上允许处理图像数量为x6倍的图像而无需细分数组。
dtypes
如果您的中间值可以放在单个uint8
中,请不要使用int32
的数组!由于无声溢出,这可能会变成神秘错误的噩梦,但如果你小心,它可以节省大量资源。
答案 1 :(得分:9)
第一个最重要的技巧:分配几个大数组,并使用和回收它们的一部分,而不是带来生命和丢弃/垃圾收集大量的临时数组。听起来有点过时,但仔细编程加速可能会令人印象深刻。 (您可以更好地控制对齐和数据位置,因此可以提高数字代码的效率。)
第二:使用numpy.memmap
并希望操作系统对磁盘的访问缓存足够高效。
第三:正如@Jaime所指出的那样,如果整个矩阵变大,那么就可以阻止子矩阵。
编辑:
避免不必要的列表理解,正如SE中的answer所指出的那样。
答案 2 :(得分:5)
dask.array库提供了一个numpy接口,它使用阻塞算法来处理具有多个内核的大于内存的数组。
答案 3 :(得分:3)
如果可以,请使用numexpr。对于a**2 + b**2 + 2*a*b
(a
和b
为数组)等数值计算,
将编译将快速执行且内存开销最小的机器代码,如果在表达式中多次出现相同的数组,则会处理内存局部性(以及缓存优化),
使用双核或四核CPU的所有核心,
是numpy的扩展,不是替代。
对于中型和大型阵列,单独使用numpy会更快。
看看上面给出的网页,有一些例子可以帮助您了解numexpr是否适合您。
答案 4 :(得分:0)
如果要存储所有中间结果(因为我们并不总是需要将中间结果保存在内存中),那么在其他答案中,我们还可以使用accumulate
经过各种类型的聚合numpy
:
聚合
对于二进制函数,有一些有趣的聚合可以直接从对象中计算出来。例如,如果我们想通过特定操作来简化数组,则可以使用任何ufunc的reduce方法。约简将给定操作重复应用于数组的元素,直到仅保留单个结果。
例如,在add ufunc上调用reduce会返回数组中所有元素的总和:
x = np.arange(1, 6)
np.add.reduce(x) # Outputs 15
类似地,在乘法ufunc上调用reduce会得到所有数组元素的乘积:
np.multiply.reduce(x) # Outputs 120
积累
如果我们要存储计算的所有中间结果,可以改为使用accumulate:
np.add.accumulate(x) # Outputs array([ 1, 3, 6, 10, 15], dtype=int32)
np.multiply.accumulate(x) # Outputs array([ 1, 2, 6, 24, 120], dtype=int32)
在一个或多个大型Numpy数组上执行许多中间操作时,明智地使用这些numpy操作可以在不使用任何其他库的情况下获得出色的结果。