簡體   English   中英

Python中的矢量化余弦相似度計算

[英]Vectorized cosine similarity calculation in Python

我有兩組大的矢量, AB A每個元素都是長度為400的1維向量,浮點值在-10到10之間。對於A每個向量,我試圖計算B中所有向量的余弦相似度,以便找到前5個B中最符合給定A向量的向量。 現在我循環遍歷所有A ,並循環遍歷所有B ,用SciPy的spatial.distance.cosine(a, b)逐個計算余弦相似度。 有更快的方法嗎? 也許有矩陣?

您可以首先在其單位向量中轉換每個向量(通過將其除以其長度)。 然后距離公式簡化為

 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')

這是一個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]: 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM