提升Numpy性能

19 投票
5 回答
7926 浏览
提问于 2025-04-15 18:52

我想提高用Python进行卷积运算的性能,希望能得到一些建议,看看怎么能更好地提升速度。

目前我在用scipy来进行卷积,代码大概像下面这个片段:

import numpy
import scipy
import scipy.signal
import timeit

a=numpy.array ( [ range(1000000) ] )
a.reshape(1000,1000)
filt=numpy.array( [ [ 1, 1, 1 ], [1, -8, 1], [1,1,1] ] )

def convolve():
  global a, filt
  scipy.signal.convolve2d ( a, filt, mode="same" )

t=timeit.Timer("convolve()", "from __main__ import convolve")
print "%.2f sec/pass" % (10 * t.timeit(number=10)/100)

我正在处理图像数据,使用的是灰度图(数值在0到255之间),现在每次卷积大约需要四分之一秒。我在想可以尝试以下几种方法:

使用corepy,最好能加一些优化。

用icc和ikml重新编译numpy。

使用python-cuda。

我想知道有没有人对这些方法有经验(通常能提高多少性能,值得花时间去做吗),或者有没有人知道更好的库来用Numpy进行卷积。

谢谢!

补充:

通过用C语言重写Python循环,速度提升了大约10倍,比使用Numpy要快。

5 个回答

1

在讨论用 ctypes 调用 C 之前,我建议先在 C 里运行一个独立的卷积程序,这样可以看看性能的极限在哪里。
同样的,CUDA、cython、scipy.weave 等也可以这样做。

补充一下,7月7日的更新:在我的 Mac G4 PCC 上,用 gcc 4.2 处理 8 位数据的卷积,带有裁剪,每个点大约需要 20 个时钟周期,内存访问大约需要 2 个时钟周期。你的情况可能会有所不同。

有几点需要注意:

  • 你在意裁剪到 0 到 255 之间的正确性吗?np.clip() 的速度比较慢,cython 等工具可能不太了解。
  • Numpy/scipy 可能需要和 A 一样大小的临时内存(所以要确保 2 * sizeof(A) 小于缓存大小)。
    不过,如果你的 C 代码是就地更新的,那内存需求就减半,但算法会有所不同。

顺便提一下,可以在谷歌上搜索一下 theano 的卷积功能 => “一个应该模仿 scipy.signal.convolve2d 的卷积操作,但更快!目前正在开发中。”

2

对于这个3x3的例子,我注意到

1  1  1
1 -8  1
1  1  1

  1  1  1     0  0  0
= 1  1  1  +  0 -9  0
  1  1  1     0  0  0

而且其中第一个是可以分解的——你可以先对每一行用(1 1 1)进行卷积,然后再对每一列进行卷积。最后再减去原始数据的九倍。这种方法的速度可能会更快,也可能不会,这取决于scipy的程序员是否聪明到能自动处理这个问题。(我有一段时间没检查了。)

你可能想要做一些更有趣的卷积操作,而这些操作的分解可能是可行的,也可能不可行。

10

在scipy中,处理二维卷积的代码有点乱,而且没有经过优化。如果你想看看scipy底层是怎么运作的,可以去看看这个链接:http://svn.scipy.org/svn/scipy/trunk/scipy/signal/firfilter.c

如果你只是想用一个小的、固定的卷积核(就像你展示的那样)来处理数据,下面这个函数可能会有用:

def specialconvolve(a):
    # sorry, you must pad the input yourself
    rowconvol = a[1:-1,:] + a[:-2,:] + a[2:,:]
    colconvol = rowconvol[:,1:-1] + rowconvol[:,:-2] + rowconvol[:,2:] - 9*a[1:-1,1:-1]
    return colconvol

这个函数利用了卷积核的可分离性,正如DarenW上面提到的那样,同时也利用了更优化的numpy算术运算。根据我的测量,它的速度比convolve2d函数快了超过1000倍。

撰写回答