我不认为性能在这里很重要,但我无法抵制。zip()函数完全复制两个向量(实际上更像是矩阵转置),只是为了按“Pythonic”顺序获取数据。测试一下底层实现的运行时间可能会很有趣:
import math
def cosine_similarity(v1,v2):
"compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
sumxx, sumxy, sumyy = 0, 0, 0
for i in range(len(v1)):
x = v1[i]; y = v2[i]
sumxx += x*x
sumyy += y*y
sumxy += x*y
return sumxy/math.sqrt(sumxx*sumyy)
v1,v2 = [3, 45, 7, 2], [2, 54, 13, 15]
print(v1, v2, cosine_similarity(v1,v2))
Output: [3, 45, 7, 2] [2, 54, 13, 15] 0.972284251712
这种方法通过像C语言一样逐个提取元素的方式来消除噪音,但不进行批量数组复制,所有重要操作都可以在一个循环中完成,并且只使用一个平方根。
注:更新了打印调用为函数。该代码原本是Python 2.7,现在使用带有from __future__ import print_function
语句的Python 2.7运行。无论哪种方式输出结果都相同。
在3.0 GHz Core 2 Duo上运行的CPython 2.7.3:
>>> timeit.timeit("cosine_similarity(v1,v2)",setup="from __main__ import cosine_similarity, v1, v2")
2.4261788514654654
>>> timeit.timeit("cosine_measure(v1,v2)",setup="from __main__ import cosine_measure, v1, v2")
8.794677709375264
因此,在这种情况下,不使用 Pythonic 的方法大约快了3.6倍。
scipy.spatial.distance.cosine
的速度要快得多。 - Ozzahcos_sim = a @ b.T / norm(a) / norm(b)
。其中@
表示矩阵乘法,norm()
表示向量的 L2 范数(也称欧几里得范数)。整个式子计算两个向量的余弦相似度。 - Union find