先记住这个答案
不同度量有不同值域:余弦相似度范围是 [−1, 1],但若向量未经 L2 归一化,实际计算的是点积,可能超出此范围;内积无固定范围,可为任意实数;欧氏距离(L2)非负且无上界。负分常源于余弦相似度中方向相反的向量,或点积中负方向分量;大于 1 的分数通常不是纯余弦,而是点积或未归一化内积。需先确认数据库所用度量及是否自动归一化。
- 余弦相似度理论在[-1,1]内,负分代表方向相反
- 内积和L2距离无上界,可大于1
- 若返回>1常因未归一化的点积
度量定义与分数范围来源
余弦相似度定义为 cos(θ) = (A·B) / (||A||·||B||),对任意非零向量,结果必然落在 [−1, 1]。负值表示两向量夹角大于 90° 甚至相反,并非错误。如果数据库在计算前会对向量做 L2 归一化,则内积就等于余弦,范围仍是 [−1, 1];但并非所有数据库都默认归一化,需查阅具体库的文档。若未归一化而使用内积计算“相似度”,范围就失去上界,可能得到大于 1 或小于 −1 的值。
欧氏距离(L2 距离)计算的是坐标差的平方和再开方,结果总是 ≥0,且无上界。距离越大越不相似,但不是相似度分数。有些数据库返回距离而非相似度,因此看到“0.8”并不代表相似度高。相反,距离为 0 表示完全一致。判断时先确认返回字段是 similarity 还是 distance。
具体场景:未归一化的点积查询
假设一个检索系统用 text-embedding-ada-002 生成向量,其模长约在 0.9 到 1.2 之间。如果向量库配置为“内积”度量且未启用归一化,用户查询“猫的饲养”得到返回分数 1.6,而另一条文档“狗的护理”得分 0.7。用户疑惑为何大于 1。查看库配置发现该库默认不做归一化,实际计算的是 A·B。
处理方式:要么在写入和查询前都执行 L2 归一化,使内积等价于余弦,分数回到 [−1, 1];要么明确将度量改为“余弦”,让库内部计算时自动归一化。如果不改变配置,则不能拿分数绝对值直接当相似度,只能做相对排序,且分数下限可能为负。
适用边界与失效条件
即便使用余弦度量,如果向量包含零向量,余弦定义失效(分母为 0),许多实现会返回 0 或报错。另外,某些库对余弦的实现是先归一化再做点积,若浮点误差累积,结果可能略微超出 [−1, 1],如 1.0000001,这种可以视为数值噪声,不影响排序。
更隐蔽的情况是不同库对“余弦相似度”的返回定义不同:有的返回 1 - cos(θ)(即余弦距离),范围 [0, 2],分数越小越相似。误把这种距离当相似度就可能看到大于 1 的分数,从而误解其含义。因此必须查阅所用向量数据库的 API 文档,确认返回字段是 similarity(越大越相似)还是 distance(越小越相似)。
容易答错的地方
- 把负数直接视为坏数据
- 余弦相似度为负表示向量方向相反,在文本检索中可能意味着语义相反或不相关。如果整体分数普遍偏低但仍有相对排列,负分是正常现象,不应直接丢弃,而应检查度量定义和归一化状态。
- 认为分数必须归一化到0-1
- 只有余弦相似度才有自然上界1,且负下界。内积、L2距离均无此性质。要求所有数据库返回0-1分数反而是误用。若业务需要0-1,可对距离做变换,如
1 / (1 + distance),但会改变分布特征。
面试官还会怎么问?
为什么有的库返回余弦相似度却是负数?
只要向量夹角超过90度,余弦值就是负数,说明语义上不相关甚至相反。如果库做了归一化,负号是正确信息。若数据库返回的“相似度”是负数但向量方向相同,可能是实现用了余弦距离。
内积得分大于1一定是坏事吗?
不一定。内积受向量模长影响,模长大的文档更容易得分高。在未归一化时,得分可能整体偏移。关键是看库是否统一对查询和文档归一化,否则无法跨文档比较绝对值。
如何把L2距离转换为0-1相似度?
常用高斯核 exp(-distance^2 / (2*σ^2)) 或 1/(1+distance)。但没有万能σ,需根据数据分布调试。更可靠的做法是直接用距离排序,而非转换。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。