【问题标题】:Vectorized cosine similarity calculation in PythonPython中的向量化余弦相似度计算
【发布时间】:2016-03-08 07:40:26
【问题描述】:

我有两组向量,ABA 的每个元素都是长度为 400 的一维向量,浮点值介于 -10 和 10 之间。对于 A 中的每个向量,我正在尝试计算与 B 中的所有向量的余弦相似度为了在B 中找到最匹配给定A 向量的前5 个向量。现在我循环遍历所有A,循环遍历所有B,用SciPy 的spatial.distance.cosine(a, b) 逐一计算余弦相似度。有没有更快的方法来做到这一点?也许有矩阵?

【问题讨论】:

    标签: python matrix cosine-similarity


    【解决方案1】:

    您可以首先在其单位向量中变换每个向量(将其除以 通过它的长度)。然后距离公式简化为

     d = 1 - e_v * e_w
    
     with e_v = v / ||v||_2 , e_w = w / ||v||_2 
    

    计算速度更快。

    使用scipy.spatial.distance.cdist(XA, XB, 'cosine') 可能更快。 您需要从向量集(伪代码)构建一个矩阵:

    XA=np.array([vecA1,vecA2,...,vecA400])
    XB=np.array([vecB1,vecB2,...,vecB400])
    distances = scipy.spatial.distance.cdist(XA, XB, 'cosine')
    

    【讨论】:

    【解决方案2】:

    这是一个 NAIVE 无循环,没有开销(?)您需要的实现......

    from np.linalg import norm
    res = 1 - np.dot(A/norm(A, axis=1)[...,None],(B/norm(B,axis=1)[...,None]).T)
    

    您能否在您的数据子集上对其进行基准测试,并让我们知道它是否比 scipy 的余弦距离更快?


    ps,axis=1 以上是基于您的向量按行存储的假设,

    print A
    # [[1 2 3 4 5 6 7 8 ... 400]
    #  [2 3 4 5 6 7 8 9 ... 401]
    


    评论

    In [79]: A = np.random.random((2,5))
    
    In [80]: A
    Out[80]: 
    array([[ 0.2917865 ,  0.89617367,  0.27118045,  0.58596817,  0.05154168],
           [ 0.61131638,  0.2859271 ,  0.09411264,  0.57995386,  0.09829525]])
    
    In [81]: norm(A,axis=1)
    Out[81]: array([ 1.14359988,  0.90018201])
    
    In [82]: norm(A,axis=1)[...,None]
    Out[82]: 
    array([[ 1.14359988],
           [ 0.90018201]])
    
    In [83]: A/norm(A,axis=1)
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-83-707fa10dc673> in <module>()
    ----> 1 A/norm(A,axis=1)
    
    ValueError: operands could not be broadcast together with shapes (2,5) (2,) 
    
    In [84]: A/norm(A,axis=1)[...,None]
    Out[84]: 
    array([[ 0.25514737,  0.78364267,  0.23712878,  0.51238915,  0.04506968],
           [ 0.67910309,  0.31763254,  0.10454846,  0.64426289,  0.10919486]])
    
    In [85]: norm(A/norm(A,axis=1)[...,None], axis=1)
    Out[85]: array([ 1.,  1.])
    
    In [86]: 
    

    上面的会话用于解释规范化过程, 当我们有归一化矩阵 A' 和 B' 时,我们取点积(当然我们必须转置 B' 矩阵),结果是一个矩阵,其元素 j, j 是NORMALIZED向量A_i和B_j的点积,我们从1中减去这个矩阵,我们得到一个余弦距离矩阵。或者我希望...

    测试和基准测试

    In [1]: import numpy as np                                              
    
    In [2]: from numpy.linalg import norm as n
    
    In [3]: from scipy.spatial.distance import cosine
    
    In [4]: A = np.random.random((100,400))
    
    In [5]: B = np.random.random((100,400))
    
    In [6]: C = np.array([[cosine(a,b) for b in B] for a in A])
    
    In [7]: c = 1.0 - np.dot(A/n(A,axis=1)[:,None],(B/n(B,axis=1)[:,None]).T)
    
    In [8]: np.max(C-c)
    Out[8]: 8.8817841970012523e-16
    
    In [9]: np.min(C-c)
    Out[9]: -8.8817841970012523e-16
    
    In [10]: %timeit [[cosine(a,b) for b in B] for a in A];
    1 loops, best of 3: 1.3 s per loop
    
    In [11]: %timeit 1.0 - np.dot(A/n(A,axis=1)[:,None],(B/n(B,axis=1)[:,None]).T)
    100 loops, best of 3: 9.28 ms per loop
    
    In [12]: 
    

    【讨论】:

    • 您能解释一下这个解决方案的作用吗?我不清楚这如何让我得到 AB 中所有向量之间的余弦相似性。
    • @alavin89 余弦距离cosine similarity的1的补码。反过来,余弦相似度是两个向量之间的角度 a 的余弦,我们从关系 dot(v1, v2) = mod(v1) mod(v2) cos(a )。 --- 在您的 Q 中,您引用了 scipy.spatial.distance.cosine: here it is its documentationthe source code
    • 而不是使用norm(A,axis=1)[...,None]更清楚使用norm(A,axis=1, keepdims=True)
    猜你喜欢
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 2017-09-27
    • 2010-10-05
    • 2016-10-28
    • 2017-07-07
    • 1970-01-01
    相关资源
    最近更新 更多