Skip to main content

余弦相似度

什么是余弦相似度​

向量的数字序列,共同决定了向量在高维空间中的方向和长度。而余弦相似度主要就是撤除长度的影响,得到方向的夹角。夹角越小越相似,即方向相同。

向量方向的直观理解​

如何体现向量的方向和长度呢?以一维向量为例:

向量:[-0.5]、[0.5]、[1] 的方向和长度。

1770297993797

向量: [0.5,0.5]、[0.7,0.7]、[0.7,0.5]、[0.5,-0.5]、[-0.5,-0.5]、[-0.6,0.5]的方向和长度

1770298050710

余弦相似度主要匹配的就是:同向(无所谓长度)

我们能直接发现[0.5,0.5] 和 [0.7,0.7] 是同向不同长

那计算机如何判定就依赖余弦相似度算法了。

余弦相似度的定义​

在文本向量语义匹配中,余弦相似度是衡量两个向量方向相似程度的核心算法,即判断两段文本语义是否相近。

余弦相似度 → 两个向量的点积 ÷ 两个向量模长的乘积

cosine similarity(A⃗,B⃗)=A⃗⋅B⃗∣∣A⃗∣∣×∣∣B⃗∣∣\text{cosine similarity}(\vec{A}, \vec{B}) = \frac{\vec{A} \cdot \vec{B}}{||\vec{A}|| \times ||\vec{B}||}

计算示例​

以向量 A[0.5,0.5]A[0.5, 0.5]、B[0.7,0.7]B[0.7, 0.7]、C[0.7,0.5]C[0.7, 0.5]、D[−0.6,−0.5]D[-0.6, -0.5] 为例:

点积​

点积:两个向量同维度数值相乘后求和

a⃗⋅b⃗=a⃗[0]×b⃗[0]+a⃗[1]×b⃗[1]\vec{a} \cdot \vec{b} = \vec{a}[0] \times \vec{b}[0] + \vec{a}[1] \times \vec{b}[1] A⋅B=0.5×0.7+0.5×0.7=0.7A \cdot B = 0.5 \times 0.7 + 0.5 \times 0.7 = 0.7

模长​

模长:向量各维度数值平方和的平方根

∣∣v⃗∣∣=v⃗[0]2+v⃗[1]2||\vec{v}|| = \sqrt{\vec{v}[0]^2 + \vec{v}[1]^2} ∣∣A⃗∣∣=0.52+0.52||\vec{A}|| = \sqrt{0.5^2 + 0.5^2}

余弦相似度公式​

cosine_similarity(A⃗,B⃗)=AB点积A模长×B模长\text{cosine\_similarity}(\vec{A}, \vec{B}) = \frac{\text{AB点积}}{\text{A模长} \times \text{B模长}}

具体计算​

A与B余弦相似度:

cosine_similarity(A,B)=0.5×0.7+0.5×0.70.52+0.52×0.72+0.72=1.0\text{cosine\_similarity}(A, B) = \frac{0.5 \times 0.7 + 0.5 \times 0.7}{\sqrt{0.5^2+0.5^2} \times \sqrt{0.7^2+0.7^2}} = 1.0

A与C余弦相似度:

cosine_similarity(A,C)=0.5×0.7+0.5×0.50.52+0.52×0.72+0.52≈0.986\text{cosine\_similarity}(A, C) = \frac{0.5 \times 0.7 + 0.5 \times 0.5}{\sqrt{0.5^2+0.5^2} \times \sqrt{0.7^2+0.5^2}} \approx 0.986

A与D余弦相似度:

cosine_similarity(A,D)=0.5×(−0.6)+0.5×(−0.5)0.52+0.52×(−0.6)2+(−0.5)2≈−0.996\text{cosine\_similarity}(A, D) = \frac{0.5 \times (-0.6) + 0.5 \times (-0.5)}{\sqrt{0.5^2+0.5^2} \times \sqrt{(-0.6)^2+(-0.5)^2}} \approx -0.996

代码实现​

import numpy as np

def get_dot(vec_a,vec_b):
"""计算2个向量的点积,两个向量同维度数字乘积之和"""
if len(vec_a) != len(vec_b):
raise ValueError("2个向量必须维度数量相同")

dot_sum = 0
zipped = zip(vec_a, vec_b)
# n = 1
# try:
# while True:
# print(f"第{n}次:{next(zipped)}")
# n += 1
# except StopIteration:
# pass
for a,b in zip(vec_a,vec_b):
dot_sum += a * b

return dot_sum

def get_norm(vec):
"""获取单个向量的模长:对向量每个数字求平方再求和并开根号"""
sum_square = 0
for v in vec:
sum_square += v * v

return np.sqrt(sum_square)

def cosine_similarity(vec_a,vec_b):
"""余弦相似度:两个向量的点积 除以 两个向量的模长的乘积"""
result = get_dot(vec_a,vec_b) / (get_norm(vec_a) * get_norm(vec_b))
return result

if __name__=="__main__":
A = [0.5,0.5]
B = [0.7,0.7]
C = [0.7,0.5]
D = [-0.6,-0.5]
print("AB:",cosine_similarity(A,B))
print("AC:",cosine_similarity(A,C))
print("AD:",cosine_similarity(A,D))