使用大型Numpy阵列的技术?

时间:2013-01-16 04:12:57

标签: python arrays optimization memory-management numpy

有时您必须在一个或多个大型Numpy阵列上执行许多中间操作。这可以很快导致MemoryError s。在我迄今为止的研究中,你发现Pickling(Pickle,CPickle,Pytables等)和gc.collect()是减轻这种情况的方法。我想知道程序员在处理大量数据时是否还有其他技术(当然,除了删除策略/代码中的冗余)。

另外,如果有一点我确定没有什么是免费的。使用其中一些技术,有什么权衡(即速度,稳健性等)?

5 个答案:

答案 0 :(得分:21)

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

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

  1. 使用每个RGB值的最重要的4位作为三维查找表的索引。这确定了LUT中多维数据集的8个顶点的CMYK值。
  2. 根据上一步骤的顶点值,使用每个RGB值的最低4位在该多维数据集内插值。最有效的方法是计算16个uint8数组,其大小与正在处理的图像大小相同。对于24位RGB图像,相当于需要存储x6倍的图像来处理它。
  3. 您可以采取以下措施来解决这个问题:

    1。划分和征服

    也许你不能在一次通过中处理1,000x1,000阵列。但是如果你可以用python for循环迭代10个100x1,000的数组,那么它仍然会超过1,000,000个项目的python迭代器!它会变慢,是的,但不是那么多。

    2。缓存昂贵的计算

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

    3。明智地使用dtypes

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

答案 1 :(得分:9)

第一个最重要的技巧:分配几个大数组,并使用和回收它们的一部分,而不是带来生命和丢弃/垃圾收集大量的临时数组。听起来有点过时,但仔细编程加速可能会令人印象深刻。 (您可以更好地控制对齐和数据位置,因此可以提高数字代码的效率。)

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

第三:正如@Jaime所指出的那样,如果整个矩阵变大,那么就可以阻止子矩阵。

编辑:

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

答案 2 :(得分:5)

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

您还可以查看SpartanDistarrayBiggus

答案 3 :(得分:3)

如果可以,请使用numexpr。对于a**2 + b**2 + 2*a*bab为数组)等数值计算,

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

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

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

  4. 对于中型和大型阵列,单独使用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操作可以在不使用任何其他库的情况下获得出色的结果。