处理大型 Numpy 数组的技术? [复制]

Posted

技术标签:

【中文标题】处理大型 Numpy 数组的技术? [复制]【英文标题】:Techniques for working with large Numpy arrays? [duplicate] 【发布时间】:2012-12-30 08:23:28 【问题描述】:

有时您必须对一个或多个大型Numpy 数组执行许多中间操作。这会很快导致MemoryErrors。到目前为止,在我的研究中,我发现 Pickling(Pickle、CPickle、Pytables 等)和gc.collect() 是缓解这种情况的方法。我想知道有经验的程序员在处理大量数据时是否还有其他技术(当然,除了删除策略/代码中的冗余)。

另外,如果有一点我可以肯定,那就是没有什么是免费的。使用其中一些技术,有哪些权衡(即速度、鲁棒性等)?

【问题讨论】:

这能回答你的问题吗? Very large matrices using Python and NumPy 【参考方案1】:

除了其他答案中所说的所有内容之外,如果我们想存储计算的所有中间结果(因为我们并不总是需要将中间结果保存在内存中),我们还可以使用来自 @987654322 的 accumulate @各种聚合后:

聚合

对于二进制 ufunc,有一些有趣的聚合可以直接从对象中计算出来。例如,如果我们想通过特定操作减少数组,我们可以使用任何 ufunc 的 reduce 方法。 reduce 重复地对数组的元素应用给定的操作,直到只剩下一个结果。

例如,在 add ufunc 上调用 reduce 会返回数组中所有元素的总和:

x = np.arange(1, 6)
np.add.reduce(x) # Outputs 15

同样,在 multiply ufunc 上调用 reduce 会得到所有数组元素的乘积:

np.multiply.reduce(x) # Outputs 120

积累

如果我们想存储计算的所有中间结果,我们可以改为使用累积:

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 数组执行许多中间操作,可以在不使用任何其他库的情况下为您提供出色的结果。

【讨论】:

【参考方案2】:

如果可能,请使用numexpr。对于像 a**2 + b**2 + 2*a*b 这样的数值计算(对于 ab 是数组)它

    将编译执行速度快且内存开销最小的机器代码,如果同一数组在您的表达式中出现多次,则会处理内存局部性问题(以及缓存优化),

    使用双核或四核 CPU 的所有内核,

    是 numpy 的扩展,不是替代品。

对于中型和大型数组,单独使用numpy更快。

看看上面给出的网页,有一些例子可以帮助你理解 numexpr 是否适合你。

【讨论】:

【参考方案3】:

dask.array 库提供了一个 numpy 接口,该接口使用阻塞算法来处理具有多个内核的大于内存的数组。

您还可以查看Spartan、Distarray 和Biggus。

【讨论】:

【参考方案4】:

第一个最重要的技巧:分配一些大数组,并使用和回收其中的一部分,而不是让大量临时数组变得生机勃勃并丢弃/垃圾收集。听起来有点过时,但是通过仔细的编程加速可以令人印象深刻。 (您可以更好地控制对齐和数据局部性,因此可以使数字代码更高效。)

第二:使用numpy.memmap,希望操作系统缓存访问磁盘的效率足够高。

第三:正如@Jaime 所指出的,如果整个矩阵太大,则对子矩阵进行处理。

编辑:

避免不必要的列表理解,正如 SE 中的 answer 中所指出的那样。

【讨论】:

【参考方案5】:

我感受到了你的痛苦......你有时最终会存储数倍于数组大小的值,这些值稍后将被丢弃。一次处理数组中的一项时,这无关紧要,但在矢量化时可能会杀死您。

我将使用工作中的一个示例来进行说明。我最近使用 numpy 编写了描述 here 的算法。它是一种颜色映射算法,它采用 RGB 图像,并将其转换为 CMYK 图像。对每个像素重复的过程如下:

    使用每个 RGB 值的最高有效 4 位作为 3 维查找表的索引。这将确定 LUT 中立方体的 8 个顶点的 CMYK 值。 根据上一步的顶点值,使用每个 RGB 值的最低有效 4 位在该立方体内进行插值。最有效的方法需要计算 16 个 uint8 数组,其大小与正在处理的图像的大小相同。对于 24 位 RGB 图像,相当于需要存储图像的 6 倍来处理它。

你可以做几件事来处理这个问题:

1。分而治之

也许您无法一次性处理 1,000x1,000 数组。但是,如果您可以使用 python for 循环遍历 10 个 100x1,000 的数组,它仍然会以非常大的优势击败超过 1,000,000 个项目的 python 迭代器!它会变慢,是的,但不会那么慢。

2。缓存昂贵的计算

这与我上面的插值示例直接相关,虽然值得留意,但更难发现。因为我在一个每个维度有 4 位的三维立方体上进行插值,所以只有 16x16x16 个可能的结果,可以存储在 16 个 16x16x16 字节的数组中。所以我可以预先计算它们并使用 64KB 的内存存储它们,并为整个图像逐个查找值,而不是以巨大的内存成本为每个像素重做相同的操作。这已经为小至 64x64 像素的图像带来了回报,并且基本上允许处理像素数量为 x6 倍的图像,而无需细分阵列。

3。明智地使用你的dtypes

如果您的中间值可以放入单个 uint8,请不要使用 int32s 的数组!由于静默溢出,这可能会变成一场神秘错误的噩梦,但如果您小心,它可以节省大量资源。

【讨论】:

@NoobSaibot 你能解释一下你对“变量数组”有什么想法吗? 对不起。可变大小的数组。在您的 缓存昂贵的计算 部分中,您给出了一个天才示例,说明您的值何时存在于有限集中。如果您的示例要求 x,y,z 任何大小的可能结果,您将如何应用 #2?

以上是关于处理大型 Numpy 数组的技术? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

对于小型/大型 numpy 数组,释放的处理方式是不是不同?

在多处理进程之间共享大型只读 Numpy 数组

Python - 数组复制/分配,numpy的意外'=array [:]'行为

将numpy结构化数组子集转换为numpy数组而不复制

NumPy 数组和 python 列表有啥区别? [复制]

将张量变量(非常数)转换为 numpy 数组? [复制]