Cython和numpy速度

时间:2009-07-29 12:39:53

标签: python numpy cython

我在我的python程序中使用cython进行相关计算。我有两个音频数据集,我需要知道它们之间的时差。基于开始时间切割第二组,然后滑过第一组。有两个for循环:一个滑动集合,内循环计算该点的相关性。这种方法效果很好,而且足够准确。

问题是纯python需要一分多钟。使用我的cython代码,大约需要17秒。这仍然太多了。您是否有任何提示如何加速此代码:

import numpy as np
cimport numpy as np

cimport cython

FTYPE = np.float
ctypedef np.float_t FTYPE_t

@cython.boundscheck(False)
def delay(np.ndarray[FTYPE_t, ndim=1] f, np.ndarray[FTYPE_t, ndim=1] g):
    cdef int size1 = f.shape[0]
    cdef int size2 = g.shape[0]
    cdef int max_correlation = 0
    cdef int delay = 0
    cdef int current_correlation, i, j

    # Move second data set frame by frame
    for i in range(0, size1 - size2):
        current_correlation = 0

        # Calculate correlation at that point
        for j in range(size2):
            current_correlation += f[<unsigned int>(i+j)] * g[j]

        # Check if current correlation is highest so far
        if current_correlation > max_correlation:
            max_correlation = current_correlation
            delay = i

    return delay

3 个答案:

答案 0 :(得分:37)

修改
现在有scipy.signal.fftconvolve这是我在下面描述的基于FFT的卷积方法的首选方法。我将保留原始答案来解释速度问题,但实际上使用scipy.signal.fftconvolve

原始答案:
使用 FFT convolution theorem 可以将问题从O(n ^ 2)转换为O(n log n),从而显着提高速度。这对于像您这样的长数据集特别有用,并且可以根据长度提供1000秒或更多的速度增益。它也很容易:只对产品进行FFT信号,乘法和逆FFT。 numpy.correlate在互相关例程中不使用FFT方法,最好用于非常小的内核。

这是一个例子

from timeit import Timer
from numpy import *

times = arange(0, 100, .001)

xdata = 1.*sin(2*pi*1.*times) + .5*sin(2*pi*1.1*times + 1.)
ydata = .5*sin(2*pi*1.1*times)

def xcorr(x, y):
    return correlate(x, y, mode='same')

def fftxcorr(x, y):
    fx, fy = fft.fft(x), fft.fft(y[::-1])
    fxfy = fx*fy
    xy = fft.ifft(fxfy)
    return xy

if __name__ == "__main__":
    N = 10
    t = Timer("xcorr(xdata, ydata)", "from __main__ import xcorr, xdata, ydata")
    print 'xcorr', t.timeit(number=N)/N
    t = Timer("fftxcorr(xdata, ydata)", "from __main__ import fftxcorr, xdata, ydata")
    print 'fftxcorr', t.timeit(number=N)/N

其中给出了每个周期的运行时间(以秒为单位,对于10,000个长波形)

xcorr 34.3761689901
fftxcorr 0.0768054962158

很明显fftxcorr方法要快得多。

如果您绘制结果,您会发现它们在零时移附近非常相似。但请注意,随着距离越来越远,xcorr将减少,fftxcorr将不会减少。这是因为当波形移位时,如何处理波形中不重叠的部分,这有点模棱两可。 xcorr将其视为零,并且FFT将波形视为周期性的,但如果它是一个问题,则可以通过零填充来修复。

答案 1 :(得分:2)

这种事情的诀窍是找到一种分裂和征服的方法。

目前,您正在滑向每个位置并检查每个位置的每个点 - 实际上是 O (n ^ 2)操作。

您需要减少对每个点的检查以及每个位置的比较,以确定不匹配的工作量。

例如,您可以缩短“这是否接近?”检查前几个位置的过滤器。如果相关性高于某个阈值,那么继续放弃,然后继续前进。

您可以将“检查每8个位置”乘以8。如果这个位置太低,请跳过它并继续前进。如果这个值足够高,则检查所有值以查看是否找到了最大值。

问题是进行所有这些乘法所需的时间 - (f[<unsigned int>(i+j)] * g[j])实际上,您正在填充所有这些产品的大矩阵并选择具有最大总和的行。您不想计算“所有”产品。足够的产品,以确保你找到了最大的总和。

找到最大值的问题是你必须总结所有以查看它是否最大。如果你可以把它变成一个最小化问题,一旦中间结果超过一个阈值,就更容易放弃计算产品和总和。

(我认为这可能有用。我没有尝试过。)

如果您使用max(g)-g[j]处理负数,那么您将寻找最小的,而不是最大的。您可以计算第一个位置的相关性。总结为更大值的任何东西都可以立即停止 - 不再为该偏移增加或增加,转移到另一个。

答案 2 :(得分:2)

  • 您可以从外部循环中提取范围(size2)
  • 您可以使用sum()代替循环来计算current_correlation
  • 您可以在列表中存储相关性和延迟,然后使用max()获取最大的