向量检索的相似度计算方式详解
一、为什么要算"相似度"?
向量检索的核心就是:把文本/图像等内容变成向量(embedding),然后用"向量之间的相似度"来判断两段内容有多相关。
用户提问 → 向量化 → 与向量库中所有 chunk 的向量算相似度 → 取 Top-K 最相似的。
所以相似度计算方式直接决定了"什么算相关、排序怎么排",是检索系统的核心算法。
相似度计算方式的本质
不同度量方法衡量的是向量之间不同的几何关系:
| 度量 | 本质衡量 | 关注点 |
|---|---|---|
| 余弦相似度(Cosine) | 向量夹角 | 方向是否一致(语义是否相关) |
| 内积(Dot Product) | 向量方向 + 长度 | 方向一致 + 匹配强度 |
| 欧氏距离(L2) | 向量空间距离 | 绝对距离远近 |
二、常见相似度计算方式有哪些?
1. 余弦相似度(Cosine Similarity)
公式:
cosine(A, B) = (A · B) / (|A| × |B|)
- 分子是内积,分母是两个向量模长的乘积。
- 结果范围:[-1, 1](对非负 embedding 通常为 [0, 1])。
- 只关心方向(夹角),不看长度。
几何意义:向量 A、B 指向的方向是否一致。夹角越小越相似;即使 A 是 B 的 10 倍长,只要方向一致,余弦相似度 = 1。
特点:
- 对向量长度不敏感 → 天然不受文本长度影响(短句和长段只要语义相同,得分就高)。
- 最直观、最常用,绝大多数 embedding 模型的默认相似度。
2. 内积(Dot Product / Dot)
公式:
dot(A, B) = Σ(aᵢ × bᵢ) = a₁b₁ + a₂b₂ + ... + aₙbₙ
- 结果是标量,范围无上下限(可为正、负、任意大)。
- 既看方向,又看长度:方向一致时值大;同时向量越长,值越大。
几何意义:A 在 B 方向上的投影长度 × B 的长度。等价于 |A| × |B| × cosθ。
特点:
- 对向量长度敏感 → 长向量(通常对应长文本或高频词)天然得分更高。
- 计算比余弦快(少一步归一化/除法),常用于大规模索引加速。
3. 欧氏距离(Euclidean Distance / L2)
公式:
dist(A, B) = √(Σ(aᵢ − bᵢ)²)
- 结果是距离,越小越相似(注意:是"距离"不是"相似度",用法相反)。
- 范围 [0, +∞)。
特点:
- 衡量绝对空间距离,对向量长度高度敏感。
- 在 embedding 检索中用得较少,但在某些聚类、NN 搜索场景使用。
4. 曼哈顿距离 / 其他
- 曼哈顿距离(L1):坐标差的绝对值之和,对异常值更鲁棒。
- 汉明距离:用于二进制向量(哈希向量),按位不同个数。
- 杰卡德相似度:集合相似度,用于稀疏/布尔向量(如 BOW)。
工程上主流的向量检索(FAISS、Milvus、Qdrant、pgvector 等)最常配置的就是 内积 和 余弦相似度 两种。下面重点讲这两个。
三、余弦相似度 vs 内积:详细对比
| 维度 | 余弦相似度(Cosine) | 内积(Dot Product) |
|---|---|---|
| 公式 | `A·B / ( | A |
| 是否归一化 | 是(除模长) | 否 |
| 取值范围 | [-1, 1](非负 embedding 为 [0,1]) | 无上限(可为负/任意大) |
| 关心什么 | 只关心方向 | 既关心方向也关心长度 |
| 对向量长度敏感 | 不敏感 | 敏感 |
| 对文本长度影响 | 短句长段同语义 → 得分接近 | 长文本 → 得分天然偏高 |
| 计算开销 | 稍高(多一次除法/归一化) | 低(直接点积) |
| 典型用途 | 通用语义相似度 | 已归一化向量的相似度 / 大规模检索加速 |
关键区别的本质
余弦 = 归一化后的内积。
如果先把所有向量做 L2 归一化(v = v / |v|,使每个向量模长=1),那么:
归一化后:dot(A', B') = A'·B' = cos(A, B)
也就是说:在向量都已归一化的前提下,内积 ≡ 余弦相似度,两者结果完全一致。
这正是两个概念最容易混淆的地方,也是工程上的核心结论:
- 余弦相似度 = "先归一化,再内积"
- 内积 = "不归一化,直接点积"
所以到底用哪个,取决于你的向量库里的向量是否被归一化。
四、何时选择哪种?(决策指南)
4.1 核心决策规则
| 情况 | 建议 |
|---|---|
| 向量已做 L2 归一化(模长=1) | 用内积(Dot),等价于余弦且更快 |
| 向量未归一化,且希望不受文本长度影响 | 用余弦相似度(Cosine) |
| 需要体现"匹配强度/频次"(长文本更相关) | 用内积(不归一化时) |
| 追求大规模检索速度 | 用内积(向量归一化后配合内积,兼得正确性与速度) |
4.2 按实际场景细分
① 文本 Embedding(RAG 最常见场景)
- 强烈建议:对 embedding 向量做 L2 归一化 + 使用内积(Dot)。
- 原因:
- 归一化后消除了文本长度干扰,内积=余弦,语义相关性一致;
- 同时享受内积的计算速度(向量检索库普遍对 dot 优化更好)。
- 主流实践(OpenAI Embedding、Milvus、Qdrant、FAISS 官方教程)都推荐这种组合。
② 向量库默认配置的坑
- Milvus、Qdrant、pgvector 等默认常用 L2/欧氏距离 或 内积,且假设输入已归一化。
- 如果你直接塞未归一化的文本向量还用 dot,长度会影响结果 → 结果不准。
- 对策:要么喂之前先归一化,要么明确选 Cosine。
③ 什么时候真的该用"纯余弦"而非"归一化+内积"?
- 当向量无法归一化或不想改动向量时(例如使用已有向量、跨系统迁移)。
- 实现简单直观,便于调试(得分范围固定 [0,1],容易设阈值)。
- 缺点:每次查询都要算模长(或在索引端归一化),略慢。
④ 什么时候该用"纯内积"(不归一化)?
- 你故意希望长度参与:如词频加权、匹配强度加权、推荐系统中希望"更热门的项"更相关。
- 输入向量本身已归一化时(归一化内积=余弦,直接用 dot)。
⑤ 欧氏距离/L2 什么时候用?
- 几何任务(聚类、最近邻几何分析)而非语义相似度。
- 对"绝对位置"敏感的任务(如坐标、图像特征)。
4.3 一张决策流程图
embedding 向量是否已经 L2 归一化(模长=1)?
├─ 是 → 用内积 Dot(=余弦,更快)✅
└─ 否
├─ 希望检索结果不受文本长度影响 → 归一化后内积 或 直接用余弦 ✅
├─ 希望长文本/高频词天然更相关 → 直接用内积(不归一化)
└─ 需要固定得分范围做阈值过滤 → 直接用余弦([0,1])
五、工程实践细节与常见坑
5.1 常用向量库的相似度配置
| 库 | 支持的度量 | 推荐配置(文本 RAG) |
|---|---|---|
| FAISS | L2, InnerProduct(内积) | IndexFlatIP + 预先归一化向量 |
| Milvus | IP(内积), COSINE, L2 | IP + 归一化;或 COSINE |
| Qdrant | Dot, Cosine, Euclid | Dot(假设归一化)或 Cosine |
| pgvector | inner_product, cosine, l2 | cosine,或用 normalized dot |
| Chroma | l2, ip, cosine | cosine(默认即余弦) |
| Weaviate | dot, cosine, l2 | dot(已归一化)或 cosine |
⚠️ 不同库对
dot/ip的前提假设不同(有的默认向量已归一化,有的不检查),务必看文档确认,否则容易出现"配置了 IP 但结果像余弦又像内积"的迷惑行为。
5.2 归一化在哪里做?
- 写入时归一化:入库前统一
v = v / |v|,索引里存的都是单位向量(推荐,检索时零开销)。 - 查询时归一化:query 向量也做同样归一化,保证与库中向量可比。
- 一致性要求:入库和查询必须用同一套归一化规则,否则相似度计算错乱。
5.3 常见坑
- 库里同时有归一化和未归一化的向量 → 相似度计算结果完全失真。
- 换了 Embedding 模型没重新归一化 → 不同模型的向量尺度不同,混用必错。
- 阈值设错:
- 余弦阈值取 0.7~0.8 往往合理;
- 内积阈值无固定范围,需按实际分布统计(如取 Top-K 分位数),不能用余弦的直觉设内积阈值。
- 内积得分可能为负:不归一化 + 向量含负值时,dot 可能为负,别用负数直接当"不相似=0"处理,要看排序而非绝对分。
- 归一化损失信息:归一化丢掉长度信息。如果任务中文本长度本身是重要特征(如长文档重要性更高),归一化会损失这部分信号(对 RAG 语义检索通常无妨)。
5.4 计算量对比(实际影响)
- 余弦:
O(n)除法 + 内积,每次查询需对 query 算模长(但 query 只有 1 个,可忽略),库端若未预归一化则每次比较都要除法。 - 内积(归一化向量):
O(n)纯乘法累加,无除法 → 在 HNSW/量化索引里通常更快。 - 结论:归一化 + 内积在保证正确性(=余弦)的同时更快,是推荐组合。
六、与 RAG 其他环节的关系
6.1 相似度计算 vs 混合检索
- 向量检索用相似度(余弦/内积)算语义相似度;
- BM25 用 TF-IDF 算关键词匹配分;
- 两者分数量纲不同(向量 0~1 vs BM25 无上限),融合时需要归一化(MinMax/Z-score)或用 RRF 排名融合,否则权重失衡。
6.2 相似度计算 vs Rerank
- 向量相似度(余弦/内积)衡量的是"语义相近";
- Rerank 模型衡量的是"能否回答这个问题";
- 两者目标不同 → 先向量召回大池子,再 Rerank 精排,是标准做法。
6.3 归一化后的余弦阈值经验
- 语义相关(强相关):余弦 > 0.8
- 一般相关:0.6~0.8
- 弱相关/不相关:< 0.6
- (不同 Embedding 模型分布不同,需实测统计,以上仅为经验参考)
七、总结速查
| 结论 | 一句话 |
|---|---|
| 余弦 vs 内积的本质 | 余弦 =归一化后的内积;归一化时两者完全等价 |
| RAG 文本检索推荐 | L2 归一化向量 + 内积(Dot),正确且快 |
| 什么时候选纯余弦 | 向量无法归一化 / 想要固定 [0,1] 得分做阈值 |
| 什么时候选纯内积 | 想要长度/强度参与排序(非语义检索场景) |
| 最大坑 | 库中向量混用归一化与未归一化,或混用不同模型的向量 |
| 评估方式 | 用 Recall@K / MRR 对比不同度量的检索效果 |
一句话总结:做 RAG 向量检索,默认组合是"embedding 归一化 + 内积";它既等价于余弦相似度的语义效果,又享受内积的计算速度。理解"归一化是连接两者的桥梁",就不会选错。
评论