Numpy 中的内存使用

cjm*_*671 5 python numpy scipy python-2.7

我有一个在 Mac 上编写的程序,但由于 RAM 不足(MemoryError)无法在我的 Raspberry Pi 上运行。

该程序的本质是一些图像处理,它将 640x480 uint8 与两倍大小的 complex128 进行卷积。

我认为内存使用情况是:初始图像:

640 x 480 x 8 bits / 8 bits / 1024 bytes = 300 kb
Run Code Online (Sandbox Code Playgroud)

复矩阵:

640 x 480 x 2^2 x 128 bits / 8 bits / 1024^2 = 18.75 MB
Run Code Online (Sandbox Code Playgroud)

假设它必须在内存中保存这些不同矩阵的两到三个副本——这应该是一个相当小的占用空间——也许 < 100 MB。不幸的是,它似乎耗尽了全部 330MB 可用空间(Python 运行时也必须加载到这个空间中)。

  1. 我的分析正确吗?
  2. 关于如何在 Python 中更好地管理内存的任何提示?

更新:

正如下面所建议的,我已经做了一些内存分析,它确实是 fftconvolve 使 RAM 使用量激增,如下所示:

Line # Mem 使用增量行内容

65   86.121 MiB    0.000 MiB     @profile
66                               def iriscode(self):
67   86.121 MiB    0.000 MiB       img = self.polar
68
69   86.379 MiB    0.258 MiB       pupil_curve = find_max(img[0:40])
70   86.379 MiB    0.000 MiB       blur = cv2.GaussianBlur(self.polar, (9, 9), 0)
71   76.137 MiB  -10.242 MiB       iris_fft = fit_iris_fft(radial_diff(blur[50:,:])) + 50
72
73   76.160 MiB    0.023 MiB       img = warp(img, iris_fft, pupil_curve)
74                                 # cv2.imshow("mask",np.uint8(ma.getmaskarray(img))*255)
75
76                                 global GABOR_FILTER
77  262.898 MiB  186.738 MiB       output = signal.fftconvolve(GABOR_FILTER, img, mode="valid")
Run Code Online (Sandbox Code Playgroud)

尽管如此,这种增长的幅度还是让我感到惊讶。任何想法我可以做些什么来减少它?我尝试使用complex64而不是,complex128但内存使用情况是一样的。

Jai*_*ime 4

要了解发生了什么,您可以查看fftconvolve 此处的源代码。

傅里叶变换卷积背后的整个思想是,时域中的卷积只是频域中的元素乘法。但因为您使用的是 FFT,它会将您的函数视为周期性函数,即,就好像卷积核缠绕在边缘上。因此,为了获得正确的结果,数组用零填充到一个常见的形状,在您的情况下,该形状将是(1280+640-1, 960+480-1) = (1919, 1439). 为了加快计算速度,这个形状会进一步扩展到下一个更大的数字,该数字只有 2、3 或 5 作为质因数,在您的情况下会产生一个(1920, 1440)形状。对于复杂的数组,需要占用1920 * 1440 * 16 / 2**20 = 42 MiB.

您将有 2 个这样的数组,每个输入一个,再加上两个(当您计算它们各自的 FFT 时),加上另一个(当您将它们相乘时),再加上另一个(当您计算它们的逆 FFT 以获得卷积时)。

目前尚不清楚所有这些数组是否会同时共存,因为有些数组可能会一路上被垃圾收集,但在某个时刻至少会有 3 个,可能是 4 个。加上 FFT 计算的一些开销,你就得到了你186 MiB解释了。

您可能想尝试非 FFT 卷积,它不需要所有的填充。您还可以尝试稍微优化scipy.signal.fftconvolve. 将此else替换为:

else:
    ret = fftn(in1, fshape)
    ret *= fftn(in2, fshape)
    ret = ifftn(ret)[fslice].copy()
Run Code Online (Sandbox Code Playgroud)

应该去掉其中一个中间副本,并给你 40 个额外的 MiB,这可能对你的情况有用。