【发布时间】:2014-07-01 20:39:10
【问题描述】:
我需要快速计算一个矩阵,其条目是通过将过滤器与每行的向量进行卷积得到的,对结果向量的条目进行二次采样,然后将结果与另一个向量进行点积。具体来说,我想计算
M = [conv(e_j, f)*P_i*v_i ]_{i,j},
其中 i 从 1 到 n 变化,j 从 1 到 m 变化。这里 e_j 是大小为 n 的指示符(行)向量,仅在 j 列中具有一个,f 是长度为 s 的过滤器,P_i 是一个 (n+s-1)-by-k 矩阵,它从卷积,v_i 是长度为 k 的列向量。
计算 M 的每个条目需要 O(n*s) 次操作,因此计算 M 总共需要 O(n*s*n*m)。对于 n=6,m=7,s=3,一个核心我的计算机(8GLOPs)应该能够在大约 0.094 微秒内计算 M。然而,我遵循example given in the Cython documentation 的非常简单的 cython 实现需要超过 2 毫秒来计算具有这些参数的示例。这大约是 4 个数量级的差异!
这是一个包含 Cython 实现和测试代码的 shar 文件。将其复制并粘贴到文件中并在干净的目录中运行“bash
cat > fastcalcM.pyx <<'EOF'
import numpy as np
cimport numpy as np
cimport cython
from scipy.signal import convolve
DTYPE=np.float32
ctypedef np.float32_t DTYPE_t
@cython.boundscheck(False)
def calcM(np.ndarray[DTYPE_t, ndim=1, negative_indices=False] filtertaps, int
n, int m, np.ndarray[np.int_t, ndim=2, negative_indices=False]
keep_indices, np.ndarray[DTYPE_t, ndim=2, negative_indices=False] V):
""" Computes a numrows-by-k matrix M whose entries satisfy
M_{i,k} = [conv(e_j, f)^T * P_i * v_i],
where v_i^T is the i-th row of V, and P_i samples the entries from
conv(e_j, f)^T indicated by the ith row of the keep_indices matrix """
cdef int k = keep_indices.shape[1]
cdef np.ndarray M = np.zeros((n, m), dtype=DTYPE)
cdef np.ndarray ej = np.zeros((m,), dtype=DTYPE)
cdef np.ndarray convolution
cdef int rowidx, colidx, kidx
for rowidx in range(n):
for colidx in range(m):
ej[colidx] = 1
convolution = convolve(ej, filtertaps, mode='full')
for kidx in range(k):
M[rowidx, colidx] += convolution[keep_indices[rowidx, kidx]] * V[rowidx, kidx]
ej[colidx] = 0
return M
EOF
#-----------------------------------------------------------------------------
cat > test_calcM.py << 'EOF'
import numpy as np
from fastcalcM import calcM
filtertaps = np.array([-1, 2, -1]).astype(np.float32)
n, m = 6, 7
keep_indices = np.array([[1, 3],
[4, 5],
[2, 2],
[5, 5],
[3, 4],
[4, 5]]).astype(np.int)
V = np.random.random_integers(-5, 5, size=(6, 2)).astype(np.float32)
print calcM(filtertaps, n, m, keep_indices, V)
EOF
#-----------------------------------------------------------------------------
cat > test.sh << 'EOF'
python setup.py build_ext --inplace
echo -e "%run test_calcM\n%timeit calcM(filtertaps, n, m, keep_indices, V)" > script.ipy
ipython script.ipy
EOF
#-----------------------------------------------------------------------------
cat > setup.py << 'EOF'
from distutils.core import setup
from Cython.Build import cythonize
import numpy
setup(
name="Fast convolutions",
include_dirs = [numpy.get_include()],
ext_modules = cythonize("fastcalcM.pyx")
)
EOF
我认为对 scipy 的 convolve 的调用可能是罪魁祸首(我不确定 cython 和 scipy 可以很好地配合使用),所以我实现了自己的卷积代码,在 Cython 文档中使用了相同的示例,但这导致了整体代码大约慢了 10 倍。
关于如何接近理论上可能的速度的任何想法,或差异如此之大的原因?
【问题讨论】:
标签: python performance cython convolution