用Cython与numpy的速度(Cython and numpy speed)

2019-06-27 03:39发布

我使用的是用Cython在我的Python程序中的相关计算。 我有两个音频数据集,我需要知道它们之间的时间差。 第二组是基于起效时间切割,然后穿过第一组滑动。 有两个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

Answer 1:

编辑:
现在有scipy.signal.fftconvolve这将是做基于FFT的卷积方法,我下面介绍的首选方法。 我会离开原来的答案解释的速度问题,但在实践中使用scipy.signal.fftconvolve

原来的答案:
使用FFT和的卷积定理将由为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把波形为周期性的,但如果它是可以通过零填充固定的问题。



Answer 2:

有这样的事情,关键是要找到一种方法,分而治之。

目前,你滑动到每一个位置,在每个位置上检查每一个点-有效地为O(n ^ 2)操作。

你需要减少每个点的检查和每一个位置的东西,做更少的工作,以确定不匹配的比较。

例如,你可以有一个更短的“这是甚至接近?” 过滤器检查的前几个位置。 如果相关性高于某个阈值,然后继续前进,否则放弃,继续前进。

你可以有一个“检查每一个第8位”您通过8乘以如果太低,跳过它,继续前进。 如果这是足够高,然后检查所有值,看是否你已经找到了最大值。

问题是做所有这些乘法所需要的时间- ( f[<unsigned int>(i+j)] * g[j]实际上,你填充所有这些产品一个很大的矩阵和采摘行与最大总和。 你不想计算“全能”的产品。 只是足够的产品,以确保你已经找到了最大总和。

与发现最大的问题是,你必须要总结的一切 ,看它是否是最大的。 如果你可以把它变成一个最小化问题,它更容易放弃计算产品,一旦中间结果超过阈值总结。

(我认为这可能会奏效。我have't试了一下。)

如果您使用max(g)-g[j]负数的工作,你会寻找最小的,不是最大的。 你可以计算的第一位置的相关性。 凡是相加更大的价值能够立即停止 - 没有更多的乘法或增加了对偏移,转移到另一个。



Answer 3:

  • 可以提取从外部循环范围(size2个)
  • 你可以用SUM(),而不是一个循环计算current_correlation
  • 你可以存储的相关性和延误的列表,然后使用MAX()来获得最大的一个


文章来源: Cython and numpy speed