【问题标题】:Improving Numpy Performance提高 Numpy 性能
【发布时间】:2011-01-12 21:25:42
【问题描述】:

我想使用 python 提高卷积的性能,并希望对如何最好地提高性能有所了解。

我目前正在使用 scipy 执行卷积,使用的代码有点像下面的 sn-p:

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 执行卷积。

谢谢!

编辑:

通过使用 Numpy 在 C 中重写 python 循环,速度提高了大约 10 倍。

【问题讨论】:

    标签: python math numpy scipy convolution


    【解决方案1】:

    截至 2018 年,SciPy/Numpy 组合似乎已经加快了很多。这是我在笔记本电脑(Dell Inspiron 13、i5)上看到的。 OpenCV 做得最好,但您无法控制模式。

    >>> img= np.random.rand(1000,1000)
    >>> kernel = np.ones((3,3), dtype=np.float)/9.0
    >>> t1= time.time();dst1 = cv2.filter2D(img,-1,kernel);print(time.time()-t1)
    0.0235188007355
    >>> t1= time.time();dst2 = signal.correlate(img,kernel,mode='valid',method='fft');print(time.time()-t1)
    0.140458106995
    >>> t1= time.time();dst3 = signal.convolve2d(img,kernel,mode='valid');print(time.time()-t1)
    0.0548939704895
    >>> t1= time.time();dst4 = signal.correlate2d(img,kernel,mode='valid');print(time.time()-t1)
    0.0518119335175
    >>> t1= time.time();dst5 = signal.fftconvolve(img,kernel,mode='valid');print(time.time()-t1)
    0.13204407692
    

    【讨论】:

      【解决方案2】:

      scipy 中用于进行 2d 卷积的代码有点混乱且未优化。如果您想了解 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 倍以上。

      【讨论】:

      • 感谢您指出这一点,我没想到 scipy convolve 可能会那么低效。看起来,虽然我没有仔细检查,但 scipy convolve 正在执行相当多的内存操作操作,并且有许多 if 语句会减慢速度。我会发回结果,感谢大家的cmets。
      • 是的,convolve2d 效率很低,因为它处理一般情况(它处理任意对象 - 例如,您应该能够与 Decimal 对象数组进行卷积)。我认为通过在常见情况下使用特殊的代码路径可以大大加快速度(特别是为了避免三重循环内的函数指针调用,这很可能是主机之一。
      【解决方案3】:

      在说 C 和 ctypes 之前,我建议在 C 中运行一个独立的卷积,看看限制在哪里。
      同样对于 CUDA、cython、scipy.weave ...

      添加 7feb:convolve33 8 位数据与剪辑需要约 20 个时钟周期每点, 在我的带有 gcc 4.2 的 mac g4 pcc 上,每个 mem 访问需要 2 个时钟周期。您的里程会有所不同。

      一些微妙之处:

      • 您是否关心正确剪辑到 0..255 ? np.clip() 很慢, cython 等不知道。
      • Numpy/scipy 可能需要 A 大小的临时内存(因此请保持 2*sizeof(A) 但是,如果您的 C 代码执行就地运行更新,那么这只是内存的一半,但算法不同。

      顺便说一句,谷歌theano convolve => “应该模仿 scipy.signal.convolve2d 的卷积运算,但速度更快!正在开发中”

      【讨论】:

        【解决方案4】:

        对于特定示例 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 程序员是否足够聪明地自动执行此操作。 (我有一段时间没有检查了。)

        您可能想要做更多有趣的卷积,其中可能会或可能不会因式分解。

        【讨论】:

          【解决方案5】:

          卷积的典型优化是使用信号的 FFT。原因是:实空间中的卷积是FFT空间中的乘积。计算 FFT、乘积和结果的 iFFT 通常比用通常的方式进行卷积要快。

          【讨论】:

          • 然后用 cuda 来做这个,它会非常快。如果 cuda 在目标环境中工作,它可能会获得最大的性能...... GPU 确实非常快。 cuda 不会获胜的唯一方法是,如果数据传输到 GPU 并返回开始占主导地位。
          • 我希望显卡之间来回传输数据不会有问题!对现有库有什么建议吗?
          • 傅里叶技巧适用于大型卷积核,但对于所示示例,它只有 3x3。简单的方法可能更快 - 但如果 FFT 使用 CUDA 而简单的方法不使用,则无需测量。
          猜你喜欢
          • 1970-01-01
          • 2018-01-08
          • 1970-01-01
          • 2016-01-14
          • 2021-06-11
          • 2014-01-31
          • 1970-01-01
          • 1970-01-01
          • 2021-05-17
          相关资源
          最近更新 更多