登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Embedding 向量归一化后应该用点积还是余弦相似度

来源:17golang原创

时间:2026-09-05 12:05:29 103浏览 收藏

做向量检索时,最容易被忽略的不是公式,而是“向量到底有没有被统一归一化”。如果查询向量和文档向量都经过 L2 归一化,那么点积与余弦相似度在数学上等价;在 Sentence Transformers 这类链路里,通常可以直接用点积,避免再次计算范数。只要有一侧没归一化、模型输出混用,或索引是旧数据,就不能只凭标题判断。

选择原则很简单:确认查询和文档向量都已按同一规则归一化,就用点积;输入状态不确定,或者需要在接口层自我保护,就用余弦并显式统一预处理。真正要审计的是归一化边界,而不是“点积”和“余弦”哪个听起来更高级。
要点速览
  • 单位向量满足 ||q||=||d||=1 时,q·d 与 cos(q,d) 相等。
  • 归一化后的批量检索优先点积,未归一化或混合输入优先余弦。
  • 模型、索引、查询、阈值必须共享同一预处理配置,不能只改计算函数。

归一化到底改变了什么?

Embedding 的方向通常承载语义关系,长度则可能受到文本长度、模型池化方式或业务输入差异影响。L2 归一化会把向量缩放到单位长度,方向不变,范数被固定为 1。于是相似度计算关注“夹角”,不会再因为某条向量数值整体偏大而改变排序。

这里有一个需要先划清的资产边界:查询侧与文档侧都必须使用同一模型、同一维度、同一归一化规则。只归一化查询向量,或者把旧索引里的原始向量与新生成的单位向量混在一起,都会让分数失去可比性。

Embedding 原始向量、L2 归一化、查询向量、文档向量与相似度接口的静态关系
图1:查看输入表示、归一化边界和检索接口之间的关系,先确认查询与文档是否共享同一预处理约束。

为什么归一化后点积等于余弦?

余弦相似度的公式是:

cos(q, d) = (q · d) / (||q|| × ||d||)

当 q 和 d 都已经做 L2 归一化时,两个范数都是 1,分母就变成 1,因此:

cos(q, d) = q · d

Sentence Transformers 的官方文档把 cosine、dot、euclidean 和 manhattan 列为可选相似度函数,并特别说明:如果模型末尾已有 Normalize 模块,选择 dot 是合理的,因为对归一化向量来说两者等价,而 cosine 还会再次归一化。这个结论是性能和一致性的工程提示,不代表所有 Embedding 都默认满足单位范数。

查询向量与文档向量在单位范数条件下通过点积和余弦得到等价分数
图2:对照单位向量条件、计算表达式和结果语义,定位点积可以替代余弦的前提。

检索链路中应该怎样选?

如果使用 Sentence Transformers,可以在编码阶段明确归一化,再用点积计算矩阵相似度:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
queries = model.encode(["如何给向量归一化"], normalize_embeddings=True)
documents = model.encode(
    ["使用 L2 范数把 Embedding 缩放到单位长度"],
    normalize_embeddings=True,
)
scores = queries @ documents.T

这段代码的关键不在运算符,而在两个 normalize_embeddings=True。如果文档向量已经提前入库,查询侧必须复用当时的模型和预处理约定。若无法保证这一点,则先统一输入,再使用框架提供的余弦函数:

import torch
import torch.nn.functional as F

q = torch.tensor([[0.8, 0.6]])
d = torch.tensor([[0.6, 0.8]])
score = F.cosine_similarity(q, d, dim=1)

不要把点积当作“总是更快的余弦”。只有在向量已归一化、矩阵乘法和阈值都按同一分数定义建立时,点积才是更直接的选择。

输入状态建议计算主要风险
查询、文档均已 L2 归一化点积索引重建后忘记沿用归一化
任一侧未归一化余弦把向量长度误当成语义相似度
输入状态未知或多来源混合先统一,再决定分数阈值与历史数据不可比

上线前怎样做一致性检查?

把它当成一次小型威胁建模:要保护的是召回排序和阈值判断,攻击路径则是预处理漂移。发布前至少留下以下记录:

  • 模型指纹:记录模型名称、版本或提交标识、向量维度和编码参数。
  • 归一化状态:明确索引构建、离线导入、在线查询三条路径是否都做 L2 归一化。
  • 分数抽样:对同一批 q、d 同时计算余弦和点积;只有两者都接近单位范数时,才预期分数一致。
  • 阈值复核:切换相似度函数后重新检查召回阈值,不要直接复用未经比较的旧阈值。

一个实用的审计结论是:分数不一致时,先查范数和数据来源,再查函数名。函数换对了但索引没重建,结果仍然可能错误。

相关问题

归一化后点积的分数范围是多少?

当两边都是单位向量时,点积等于余弦相似度,范围通常在 -1 到 1 之间。具体分布仍取决于模型和数据,不能把某个固定阈值套到所有模型上。

只对查询向量做归一化可以吗?

不建议。此时点积仍然会受到文档向量长度影响;除非业务明确需要长度参与排序,否则应让查询和文档共享同一预处理链路。

向量数据库里写 dot 还是 cosine?

先看入库向量和查询向量是否都已归一化,以及数据库索引对度量的要求。两边都是单位向量时可选 dot;状态不确定时,优先统一数据并重新确认阈值。

参考资料:Sentence Transformers Semantic Textual SimilaritySentenceTransformer APIPyTorch cosine_similarity

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>