更快的Python Cosine Scipy CSR“向量”之间的差异

时间:2016-04-03 20:52:09

标签: python cython sparse-matrix jit numba

这是一个经典问题,但我相信很多人仍在寻找答案。 这个问题与this one不同,因为我的问题是两个稀疏向量(不是矩阵)之间的操作。

我写了一篇关于Cosine Scipy空间距离(SSD)在数据维度越来越高时变得越来越慢的blog post(因为它适用于密集向量)。帖子是印度尼西亚语,但代码,我的实验设置&无论语言如何,结果都应该易于理解(在帖子的底部)。

目前,对于高维数据(与SSD相比),此解决方案的速度提高了70多倍。内存效率更高:

    import numpy as np

    def fCosine(u,v): # u,v CSR vectors, Cosine Dissimilarity
        uData = u.data; vData = v.data
        denominator = np.sqrt(np.sum(uData**2)) * np.sqrt(np.sum(vData**2))
        if denominator>0:
            uCol = u.indices; vCol = v.indices # np array
            intersection = set(np.intersect1d(uCol,vCol))
            uI = np.array([u1 for i,u1 in enumerate(uData) if uCol[i] in intersection])
            vI = np.array([v2 for j,v2 in enumerate(vData) if vCol[j] in intersection])             
            return 1-np.dot(uI,vI)/denominator
        else:
            return float("inf")

是否有可能进一步改善该功能(Pythonic或通过JIT / Cython ???)。

1 个答案:

答案 0 :(得分:1)

这是一个替代方案alt_fCosine,对于大小为10**510**4非零元素的CSR向量,它(在我的机器上)的速度提高约3倍:

import scipy.sparse as sparse
import numpy as np
import math

def fCosine(u,v): # u,v CSR vectors, Cosine Dissimilarity
    uData = u.data; vData = v.data
    denominator = np.sqrt(np.sum(uData**2)) * np.sqrt(np.sum(vData**2))
    if denominator>0:
        uCol = u.indices; vCol = v.indices # np array
        intersection = set(np.intersect1d(uCol,vCol))
        uI = np.array([u1 for i,u1 in enumerate(uData) if uCol[i] in intersection])
        vI = np.array([v2 for j,v2 in enumerate(vData) if vCol[j] in intersection])             
        return 1-np.dot(uI,vI)/denominator
    else:
        return float("inf")

def alt_fCosine(u,v): 
    uData, vData = u.data, v.data
    denominator = math.sqrt(np.sum(uData**2) * np.sum(vData**2))
    if denominator>0:
        uCol, vCol = u.indices, v.indices 
        uI = uData[np.in1d(uCol, vCol)]
        vI = vData[np.in1d(vCol, uCol)]
        return 1-np.dot(uI,vI)/denominator
    else:
        return float("inf")

# Check that they return the same result
N = 10**5
u = np.round(10*sparse.random(1, N, density=0.1, format='csr'))
v = np.round(10*sparse.random(1, N, density=0.1, format='csr'))
assert np.allclose(fCosine(u, v), alt_fCosine(u, v))

alt_fCosine取代了两个列表推导,即对np.intersection1d的调用 以及两次调用np.in1d和高级的Python集的形成 索引。

N = 10**5

In [322]: %timeit fCosine(u, v)
100 loops, best of 3: 5.73 ms per loop

In [323]: %timeit alt_fCosine(u, v)
1000 loops, best of 3: 1.62 ms per loop

In [324]: 5.73/1.62
Out[324]: 3.537037037037037