余弦相似度为什么常用于 Embedding 检索

举报
yd_248795310 发表于 2026/11/15 01:07:24 2026/11/15
【摘要】 做 Embedding 检索时,经常会遇到“余弦相似度”这个词。把问题转换成向量,再和知识库里的向量算一下余弦相似度,挑出分数最高的几条,检索就完成了。流程看着不复杂,但这里有个问题:比较两个向量,直接算它们之间的距离不就行了吗?为什么偏偏要算夹角?要说清楚这件事,先得看看,我们到底想比较向量的什么。 一、都是向量,“接近”却有不同的含义假设用一个特别简化的二维向量表示文本:A = [1, ...

做 Embedding 检索时,经常会遇到“余弦相似度”这个词。

把问题转换成向量,再和知识库里的向量算一下余弦相似度,挑出分数最高的几条,检索就完成了。

流程看着不复杂,但这里有个问题:比较两个向量,直接算它们之间的距离不就行了吗?为什么偏偏要算夹角?

要说清楚这件事,先得看看,我们到底想比较向量的什么。

一、都是向量,“接近”却有不同的含义

假设用一个特别简化的二维向量表示文本:

A = [1, 2]
B = [2, 4]
C = [2, 1]

暂时不用管每个数字代表什么,只把它们当成从原点出发的箭头。

B 的两个数都是 A 的两倍,所以它们指向同一个方向,只是 B 更长。

C 的长度和 A 相同,但方向不同。

这时候,如果问“谁和 A 更像”,答案取决于你怎么定义“像”。

按坐标之间的直线距离算:

  • A 到 B 的距离约为 2.24。
  • A 到 C 的距离约为 1.41。

所以 C 更近。

但如果只看方向,显然是 B 更接近 A,因为它们根本就指向同一个方向。

欧氏距离关心两个点相隔多远,余弦相似度关心两个向量指向是否一致。

这两种比较都有道理,只是关注的东西不同。

二、为什么文本检索经常更关心方向

Embedding 模型会把文本映射成一串数字,也就是一个高维向量。训练得合适的模型,会让语义相关的文本在向量空间里呈现出某种接近关系。

比如:

怎么修改服务器的 SSH 端口?

和:

Linux 如何更改远程登录端口?

用词不完全一样,但讨论的事情接近。我们希望它们的向量也足够相似,检索时能互相找到。

问题在于,向量除了方向,还有长度,也叫模长。

不同模型中,模长的含义可能不同:它可能携带有用信息,也可能受到训练方式等因素影响。不能简单地认为“文本越长,向量就越长”,更不能默认模长越大,文本就越相关。

当我们希望比较语义关系,同时减少模长差异的影响时,余弦相似度就很方便。

它会把长度因素除掉,主要比较方向。

不过,这里有个前提:模型得把我们需要的语义关系学进这种几何关系里。 余弦相似度只是计算工具,不会自己理解句子。

拿一个不适合文本检索的模型生成向量,再认真计算夹角,也未必能检索出好结果。

三、余弦相似度到底怎么算

两个非零向量的余弦相似度,公式是:

[
\operatorname{sim}(A,B)=\frac{A\cdot B}{|A||B|}
]

公式看着有点抽象,拆开其实就是:

先把对应位置的数字相乘后求和,再除以两个向量的长度之积。

其中,分子里的 (A\cdot B) 叫点积。

还是用前面的例子:

A = [1, 2]
B = [2, 4]

它们的点积是:

[
1\times2+2\times4=10
]

两个向量的长度分别为:

[
|A|=\sqrt{1^2+2^2}=\sqrt{5}
]

[
|B|=\sqrt{2^2+4^2}=\sqrt{20}
]

所以:

[
\operatorname{sim}(A,B)
=\frac{10}{\sqrt{5}\times\sqrt{20}}
=1
]

只要两个非零向量方向相同,即使长度不同,余弦相似度也是 1。

再看 A 和 C:

A = [1, 2]
C = [2, 1]

它们的余弦相似度是:

[
\operatorname{sim}(A,C)
=\frac{1\times2+2\times1}{\sqrt{5}\times\sqrt{5}}
=0.8
]

这样,B 就比 C 更像 A。

余弦相似度的理论范围是 ([-1,1]):同向时为 1,垂直时为 0,反向时为 -1。但映射到文本之后,不能直接把负数理解成“语义相反”,也不能把 0 理解成绝对无关。具体分数的含义,还要看模型的训练和实际数据。

四、为什么不直接用点积

点积也能比较向量,而且计算更简单。

还是上面的数据:

A = [1, 2]
B = [2, 4]

点积为 10。

如果把 B 放大十倍:

B₂ = [20, 40]

点积就变成了 100。

方向完全没变,分数却涨了十倍。这说明点积同时受到方向和模长的影响。

可以把它的关系写成:

[
A\cdot B=|A||B|\cos\theta
]

因此,用点积检索时,一个向量可能因为模长较大而获得更高分。

这并不意味着点积不好。如果模型在训练时就让模长承载了有用信息,使用点积可能更合适。反过来,如果这些长度差异会干扰检索,余弦相似度更容易满足需求。

选择哪一种,应该看模型的训练目标和使用说明。

五、归一化之后,事情就简单了

实际项目里,还有一个经常和余弦相似度一起出现的操作:归一化。

这里通常指 L2 归一化,也就是把向量除以自己的长度:

[
\hat A=\frac{A}{|A|}
]

处理后,向量的方向不变,长度变成 1。

想象一下,把长短不一的箭头全部缩放成相同长度,就可以更直接地比较它们的方向。

两个向量都归一化之后:

[
|\hat A|=|\hat B|=1
]

余弦相似度就变成了:

[
\operatorname{sim}(\hat A,\hat B)=\hat A\cdot\hat B
]

也就是说,归一化后的向量,直接计算点积,就能得到余弦相似度。

这也是为什么有些检索代码明明在做“余弦检索”,配置里却出现了 inner product,也就是内积。如果入库向量和查询向量都已经归一化,这样做是说得通的。

归一化还有一个有意思的结果:

[
|\hat A-\hat B|^2=2-2(\hat A\cdot\hat B)
]

右边的点积就是余弦相似度。于是,对单位向量来说,余弦相似度越高,欧氏距离就越小。

所以,在所有向量都经过 L2 归一化的前提下,用余弦相似度、点积或欧氏距离进行精确排序,得到的先后顺序是一致的。 如果使用近似索引,实际返回结果仍可能受索引配置等因素影响。

看到这里就能发现,这几种计算方式并没有想象中那么割裂。向量有没有归一化,是一个很关键的条件。

六、分数 0.8,等于有八成相似吗

不等于。

余弦相似度是一个几何计算结果,不是概率,也不是人类对两句话“相似程度”的百分比打分。

某个模型算出 0.8,可能已经是很相关的结果;换一个模型,0.8 也许只是比较普通的分数。

因此,不能随手规定:

大于 0.8 就相关,小于 0.8 就无关。

更靠谱的做法,是准备一些真实查询,观察相关文档和不相关文档各自的得分,再根据业务需求确定阈值。

还有个容易忽略的问题:排在第一,不代表值得使用。

假如知识库里根本没有答案,检索仍然可以找出分数最高的几条。这就像让一群没复习的人参加考试,总会有人拿第一,但第一名也可能不及格。

所以,检索系统既要考虑排名,也要考虑结果是否足够相关。

七、算得很准,也可能找错内容

余弦相似度只能比较已有向量。向量没表达好的信息,它也没办法补回来。

比如:

如何开启远程访问?

和:

如何关闭远程访问?

两句话用词高度重合,但操作方向相反。模型如果没有充分区分这种差异,算出的相似度仍可能很高。

型号、版本号、错误码也有类似问题。用户查的是某个具体错误,语义检索可能找到几篇“讨论类似故障”的文章,却漏掉真正包含那个错误码的文档。

因此,实际检索经常会把几种方法一起用:语义检索负责找意思接近的内容,关键词检索负责照顾精确词项,再通过重排进一步判断候选内容是否能回答问题。

余弦相似度很适合做其中的一步,但不能包办整个检索过程。

八、为什么它这么常用

余弦相似度常用于 Embedding 检索,主要是因为它提供了一种直接的比较方式:在模型适合这种度量的前提下,通过向量方向衡量相关程度,同时减少模长差异的影响。

计算也不复杂。向量提前归一化后,比较时只需要做点积,很方便接入向量检索系统。

不过,真正决定效果的,仍然是模型、文本切分、数据质量和检索流程。实际使用时,先确认模型推荐什么度量、输出是否已经归一化,再选择索引配置,通常比纠结“哪种公式天生更好”更有用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。