Cython 1D归一化滑动互相关优化

时间:2014-05-16 21:51:40

标签: numpy cython

我有以下代码执行规范化的互相关,寻找python中两个信号的相似性:

def normcorr(template,srchspace):
template=(template-np.mean(template))/(np.std(template)*len(template)) # Normalize template
CCnorm=srchspace.copy()
CCnorm=CCnorm[np.shape(template)[0]:] # trim CC matrix
for a in range(len(CCnorm)):
    s=srchspace[a:a+np.shape(template)[0]]
    sp=(s-np.mean(s))/np.std(s)
    CCnorm[a]=numpy.sum(numpy.multiply(template,sp))
return CCnorm

但你可以想象它太慢了。查看cython文档,在原始python中执行循环时,可以承诺大幅提高速度。所以我尝试编写一些cython代码,其中包含如下变量的数据类型:

from __future__ import division
import numpy as np
import math as m
cimport numpy as np
cimport cython
def normcorr(np.ndarray[np.float32_t, ndim=1] template,np.ndarray[np.float32_t, ndim=1]  srchspace):
    cdef int a
    cdef np.ndarray[np.float32_t, ndim=1] s
    cdef np.ndarray[np.float32_t, ndim=1] sp
    cdef np.ndarray[np.float32_t, ndim=1] CCnorm
    template=(template-np.mean(template))/(np.std(template)*len(template))
    CCnorm=srchspace.copy()
    CCnorm=CCnorm[len(template):]
    for a in range(len(CCnorm)):
        s=srchspace[a:a+len(template)]
        sp=(s-np.mean(s))/np.std(s)
        CCnorm[a]=np.sum(np.multiply(template,sp))
    return CCnorm

但是一旦我编译它,代码实际上比纯python代码运行得慢。我发现这里(How to call numpy/scipy C functions from Cython directly, without Python call overhead?)从cython调用numpy可能会显着减慢代码速度,这是我的代码的问题,在这种情况下我必须定义内联函数来替换所有对np的调用,或者是否有东西否则我错了,我错过了?

2 个答案:

答案 0 :(得分:3)

因为你在cython循环中调用numpy函数,所以不会有速度提升。

如果您使用pandas,可以在numpy中使用roll_mean()roll_std()以及convolve()来快速进行计算,以下是代码:

import numpy as np
import pandas as pd

np.random.seed()

def normcorr(template,srchspace):
    template=(template-np.mean(template))/(np.std(template)*len(template)) # Normalize template
    CCnorm=srchspace.copy()
    CCnorm=CCnorm[np.shape(template)[0]:] # trim CC matrix
    for a in range(len(CCnorm)):
        s=srchspace[a:a+np.shape(template)[0]]
        sp=(s-np.mean(s))/np.std(s)
        CCnorm[a]=np.sum(np.multiply(template,sp))
    return CCnorm

def fast_normcorr(t, s):
    n = len(t)
    nt = (t-np.mean(t))/(np.std(t)*n)
    sum_nt = nt.sum()
    a = pd.rolling_mean(s, n)[n-1:-1]
    b = pd.rolling_std(s, n)[n-1:-1]
    b *= np.sqrt((n-1.0) / n)
    c = np.convolve(nt[::-1], s, mode="valid")[:-1]
    result = (c - sum_nt * a) / b    
    return result

n = 100
m = 1000
t = np.random.rand(n)
s = np.random.rand(m)

r1 = normcorr(t, s)
r2 = fast_normcorr(t, s)
assert np.allclose(r1, r2)

您可以检查结果r1r2是否相同。这是timeit测试:

%timeit normcorr(t, s)
%timeit fast_normcorr(t, s)

输出:

10 loops, best of 3: 59 ms per loop
1000 loops, best of 3: 273 µs per loop

它快了200倍。

答案 1 :(得分:1)

如果使用cython -a编译代码并查看HTML输出,您将看到有很多Python开销。

@cython.boundscheck(False)
@cython.cdivision(True)   # Don't check for divisions by 0
def normcorr(np.ndarray[np.float32_t, ndim=1] template,np.ndarray[np.float32_t, ndim=1]  srchspace):
    cdef int a
    cdef int N = template.shape[0]
    cdef NCC = srchspace.shape[0] - N
    cdef np.ndarray[np.float32_t, ndim=1] s
    cdef np.ndarray[np.float32_t, ndim=1] sp
    cdef np.ndarray[np.float32_t, ndim=1] CCnorm
    template=(template - template.mean()) / (template.std() * N)
    CCnorm=srchspace[N:].copy()    # You don't need to copy the whole array
    for a in xrange(NCC):  # Use xrange in Python2
        s=srchspace[a:a+N]
        sp=(s-np.mean(s)) / np.std(s)
        CCnorm[a]= (template * sp).sum()
    return CCnorm

为了提高性能,您可以优化最后两行:

@cython.boundscheck(False)
@cython.cdivision(True)
cdef multiply_by_normalised(np.ndarray[np.float32_t, ndim=1] template, np.ndarray[np.float32_t, ndim=1] s):
    cdef int i
    cdef int N = template.shape[0]
    cdef float_32_t mean, std, out = 0

    mean = s.mean()
    std = s.std()

    for i in xrange(N):
        out += (s[i] - mean) / std * template[i]
    return out

如果您仍需要挤出更多时间,可以使用比Numpy更快的瓶颈meanstd函数。