向量检索:余弦相似度与内积
同一批向量,改一下检索指标,第一名就可能变了。原因未必是索引坏了:内积同时受方向和长度影响,余弦相似度把长度除掉。只有明确归一化条件,才能说它们等价。
这是RAG 混合检索之前的一项基础配置检查。English version
一个二维例子就能看出区别
设查询 q=(1,0),候选 A=(1,0),B=(80,60)。它们是人为构造的教学向量,没有对应真实文本。A与查询完全同向;B的长度为100,方向则偏离查询。
内积 q·A=1,q·B=80,所以按内积降序时B在前。余弦为 cos(q,d)=(q·d)/(‖q‖‖d‖):A得到1,B得到0.8,顺序变成A在前。这个例子只说明两个目标不同,不能证明哪个更适合实际语义检索。
归一化后怎样联系起来
对非零向量,定义 x̂=x/‖x‖。把两个向量都变成单位长度以后,x̂·ŷ就是原向量的余弦相似度。Faiss官方文档给出的余弦检索方法,是归一化入库向量和查询向量,再做最大内积搜索。
这里还有一个容易忽略的细节:针对固定的非零查询,仅改变查询长度不会改变原始内积排序,因为所有分数乘了同一个正数。但它会改变分数值和阈值含义。候选长度各不相同,不能用同样理由忽略它们;如果要让返回数值也是余弦,就应两端一致归一化。
L2距离的等价也有前提
展开平方距离可得 ‖x−y‖²=‖x‖²+‖y‖²−2x·y。只有两端都是单位向量时,才化成 2−2x·y。因此,在单位向量上最大内积与最小平方L2的精确排序一致;对未归一化向量,不能直接套用这个结论。
| 量 | 选择方向 | 单位向量关系 |
|---|---|---|
| 内积 | 越大越近 | 等于余弦 |
| 余弦相似度 | 越大越近 | 范围−1到1 |
| 平方L2距离 | 越小越近 | 2−2×内积 |
Faiss的L2返回平方欧氏距离。排序时开不开平方不改变先后,但把返回值用于距离阈值或展示时,要确认自己用的是哪一个量。
不用模型也能复算
以下标准库Python示例已经执行,验证了排序变化、单位向量恒等式和零向量处理。没有调用嵌入模型,也没有运行Faiss索引,不能把它称为真实召回测试。
from math import sqrt, isclose
def dot(a, b):
if len(a) != len(b):
raise ValueError('dimension mismatch')
return sum(x*y for x,y in zip(a,b))
def unit(v):
norm = sqrt(dot(v,v))
if norm == 0:
raise ValueError('zero vector has no cosine direction')
return [x/norm for x in v]
q = [1., 0.]
docs = {'A': [1., 0.], 'B': [80., 60.]}
raw = {k: dot(q,v) for k,v in docs.items()}
cos = {k: dot(unit(q),unit(v)) for k,v in docs.items()}
assert raw == {'A': 1., 'B': 80.}
assert isclose(cos['A'], 1.) and isclose(cos['B'], .8)
for v in docs.values():
x,y = unit(q),unit(v)
squared_l2 = sum((a-b)**2 for a,b in zip(x,y))
assert isclose(squared_l2, 2-2*dot(x,y), abs_tol=1e-12)
try:
unit([0.,0.])
except ValueError:
pass
else:
raise AssertionError('zero vector must be rejected')
print('inner product:', raw)
print('cosine:', cos)
print('normalized squared L2:', {k:2-2*s for k,s in cos.items()})
内积输出A=1、B=80;余弦输出A=1、B=0.8;单位向量平方L2约为0与0.4。末尾的浮点数0.3999999999999999是表示误差,因此恒等式用容差比较。零向量没有可定义的余弦方向,代码显式拒绝;真实系统也应确定异常策略。
改指标之前先查三件事
第一,查嵌入模型要求什么打分方式,输出是否已经归一化。向量长度可能包含模型设计中的信号,归一化并不是对所有模型都合适的无损处理。
第二,查入库和查询是否使用相同版本、维度和预处理。只把新查询归一化,却保留旧的非单位文档向量,并不会得到一般的余弦分数。需要变更时,应同步考虑已有索引与线上阈值。
第三,用精确计算建立一小份基准,再比较实际近似索引。数学上的排序等价,不保证不同近似搜索配置、量化误差与并列值处理返回完全相同的列表。把分数含义和搜索误差分开检查,才能判断是配置问题还是近似召回损失。
参考资料
Faiss: MetricType and distances(2024-12-03)
补发说明:本文于北京时间2026年10月9日补发,文章日期保留原计划09:00。示例为原创数学验证,不包含模型性能结论。
支持
如果这篇文章对你有帮助,欢迎支持本站。
二维码可点击放大。更多支持方式见支持页面。


