优化numpy中的内存使用

8 投票
2 回答
4807 浏览
提问于 2025-04-16 00:33

下面这个程序使用PyGame加载了两张图片,把它们转换成Numpy数组,然后进行一些其他的Numpy操作(比如快速傅里叶变换FFT),最后输出一些结果(就是几个数字)。输入的图片可以很大,但在任何时候,程序中只会有一到两个大对象在运行。

测试用的图片大约有1000万像素,转换成灰度图后大约是10MB。它被转换成一种叫做uint8的数据类型的Numpy数组,经过一些处理(比如应用汉明窗),变成了float64类型的数组。这样加载的两张图片,后面的FFT步骤会得到一种叫做complex128的数据类型的数组。在添加了过多的gc.collect调用之前,程序的内存使用量在每一步都会增加。此外,大多数Numpy操作的结果会以最高精度返回。

在我的1GB的Linux机器上运行测试(不包括gc.collect调用)时,出现了长时间的内存抖动,我没有等到最后。我还没有详细的内存使用统计——我尝试了一些Python模块和time命令,但都没用;现在我在研究valgrind。观察进程状态(PS)并处理测试后期机器无响应的情况,显示最大内存使用量大约是800MB。

一个包含1000万个单元的complex128数组应该占用160MB的内存。理想情况下,最多同时有两个这样的数组,加上不小的Python和Numpy库以及其他一些东西,可能需要预留500MB的内存。

我想到了解决这个问题的两个方向:

  • 尽快丢弃中间数组。这就是gc.collect调用的目的——它们似乎改善了情况,现在程序只需几分钟的内存抖动就能完成;-)。我认为在像Python这样的语言中进行内存密集型编程,可能需要一些手动干预。

  • 在每一步使用精度较低的Numpy数组。不幸的是,返回数组的操作,比如fft2,似乎不允许指定数据类型。

所以我主要想问的是:在Numpy数组操作中,有没有办法指定输出的精度?

更一般来说,使用Numpy时还有其他常见的节省内存的技巧吗?

另外,Numpy有没有更符合习惯的方式来释放数组内存?(我想这可能会让数组对象在Python中仍然存在,但处于不可用状态。)显式删除后立即进行垃圾回收感觉有点不太好。

import sys
import numpy
import pygame
import gc


def get_image_data(filename):
    im = pygame.image.load(filename)
    im2 = im.convert(8)
    a = pygame.surfarray.array2d(im2)
    hw1 = numpy.hamming(a.shape[0])
    hw2 = numpy.hamming(a.shape[1])
    a = a.transpose()
    a = a*hw1
    a = a.transpose()
    a = a*hw2
    return a


def check():
    gc.collect()
    print 'check'


def main(args):
    pygame.init()

    pygame.sndarray.use_arraytype('numpy')

    filename1 = args[1]
    filename2 = args[2]
    im1 = get_image_data(filename1)
    im2 = get_image_data(filename2)
    check()
    out1 = numpy.fft.fft2(im1)
    del im1
    check()
    out2 = numpy.fft.fft2(im2)
    del im2
    check()
    out3 = out1.conjugate() * out2
    del out1, out2
    check()
    correl = numpy.fft.ifft2(out3)
    del out3
    check()
    maxs = correl.argmax()
    maxpt = maxs % correl.shape[0], maxs / correl.shape[0]
    print correl[maxpt], maxpt, (correl.shape[0] - maxpt[0], correl.shape[1] - maxpt[1])


if __name__ == '__main__':
    args = sys.argv
    exit(main(args))

2 个回答

1

如果我理解得没错,你是在计算两张图片之间的卷积。Scipy这个工具包里有一个专门的模块可以做到这一点(ndimage),这个方法可能比通过傅里叶变换的“手动”方式更节省内存。建议你试试这个方法,而不是使用Numpy。

1

在这个链接中,有人提到“Scipy 0.8版本将会支持几乎所有fft代码的单精度计算”,而Scipy 0.8.0的第一个测试版刚刚发布。
(我自己还没试过,真是胆小。)

撰写回答