25

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

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

4

5 回答 5

28

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

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

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

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

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 数组!由于静默溢出,这可能会变成一场神秘错误的噩梦,但如果你小心的话,它可以节省大量资源。

于 2013-01-16T05:47:34.567 回答
10

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

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

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

编辑:

正如SE中的这个答案所指出的,避免不必要的列表理解。

于 2013-01-16T16:54:55.243 回答
6

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

您还可以查看SpartanDistarrayBiggus

于 2015-05-03T16:57:26.370 回答
3

如果可能,请使用numexpra**2 + b**2 + 2*a*b对于像(对于ab作为数组)这样的数字计算,它

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

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

  3. 是 numpy 的扩展,而不是替代方案。

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

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

于 2013-01-17T16:12:48.720 回答
0

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

骨料

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

于 2019-10-29T04:16:00.557 回答