首页/AI/RAG 与向量库速查/向量归一化与度量等价
Embedding 与检索50 条

向量归一化与度量等价

向量归一化为单位长度后,内积与余弦相似度等价;部分数据库在 COSINE 度量下内部自动归一化。归一化处理不一致会造成分数漂移与排序错乱。

RAG 与向量库速查AI更新于 2026-10-13

向量归一化是什么

归一化(L2 归一化)是把向量按自身欧氏范数缩放成单位长度,只保留方向信息、丢掉模长。做完这一步,两个向量的内积恒等于它们的余弦相似度,分数落在 -1 到 1 之间,排序与余弦完全一致,于是可以把索引库的度量设成内积(IP)换取更省算的检索。多数句向量模型(BGE、E5、text-embedding 系列)在模型卡里直接建议开启 normalize_embeddings,本质就是把这一步封装进编码函数。

基本信息

计算方式v ← v / ‖v‖,按欧氏范数缩放
效果内积等价余弦,分数范围 -1~1
Python 封装SentenceTransformer encode(normalize_embeddings=True)
数组级调用faiss.normalize_L2(x),原地修改需先 copy
一致性范围入库向量、查询向量、索引度量三处对齐

典型使用场景

  • 用 FAISS IndexFlatIP 做余弦检索:必须先自行归一化,库不管这件事
  • 把零散来源的向量灌进同一个集合:统一尺度后不同模型产出的分数才有可比性
  • 校验上游管道:入库前抽查范数是否为 1,能提前发现漏跑归一化
  • 从 COSINE 度量切到 IP 度量做压测:归一化后两者结果应完全一致,可当回归手段
  • 排查“同样的文本分数忽高忽低”:先确认两侧范数是否都被缩放

归一化与验证

归一化与验证
import numpy as np

v = np.array([0.3, 0.4, 0.5], dtype='float32')
q = np.array([0.1, 0.9, 0.2], dtype='float32')
vn = v / np.linalg.norm(v)          # 单位化:范数变成 1
qn = q / np.linalg.norm(q)
print(float(np.linalg.norm(vn)))    # 应输出 1.0 附近
print(float(qn @ vn))               # 归一化后的内积
print(float(q @ v / (np.linalg.norm(q) * np.linalg.norm(v))))  # 余弦相似度,两者相等

各库归一化要求

库与度量是否需自行归一化说明
FAISS IndexFlatIP需要库不做缩放,模长会直接主导排序
FAISS IndexFlatL2不需要欧氏距离本身含模长信息
pgvector <=>不需要余弦距离按夹角计算,内部处理范数
pgvector <#>需要负内积,两侧统一归一化后才等价余弦
Milvus / Qdrant按度量配置选余弦类度量时保持两侧处理一致,换度量要重灌
切换度量或更换编码方式后,库里已有的未归一化向量会造成分数漂移:要么整体重灌,要么批量补做归一化并校验范数,不能只改新写入的数据。
归一化只解决模长问题,不解决模型是否匹配:把两个不同模型产出的向量混在一个集合里,归一化之后依然不可比。

易错点

  • 部分数据归一化、部分没有,距离排序会错乱,且错误表现为“偶尔答非所问”极难定位
  • 只归一化查询侧、入库侧漏掉,长文档向量模长大,排序被模长带偏
  • 用 faiss.normalize_L2 时未先复制数组,原始向量被原地改写,后续统计口径全变
  • 切换 COSINE 度量但库里仍有未归一化存量向量,分数与评测基线对不上,需要重灌或统一预处理

数据来源与参考

  • FAISS wiki(相似度与归一化)
  • Sentence-Transformers 官方文档(normalize_embeddings)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌