news 2026/9/15 15:15:57

LEANN 归一化嵌入支持:自动距离度量检测与 Cosine/MIPS 正确选择实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LEANN 归一化嵌入支持:自动距离度量检测与 Cosine/MIPS 正确选择实战指南

LEANN 归一化嵌入支持:自动距离度量检测与 Cosine/MIPS 正确选择实战指南

【免费下载链接】LEANN[MLsys2026 Best Paper]: https://arxiv.org/abs/2506.08276. RAG on Everything with LEANN. Enjoy 97% storage savings while running a fast, accurate, and 100% private RAG application on your personal device.项目地址: https://gitcode.com/GitHub_Trending/le/LEANN

LEANN 在构建索引时会自动识别 OpenAI、Voyage AI、Cohere 等厂商的归一化嵌入模型(L2 范数为 1 的单位向量),并自动为其选择最优的distance_metric="cosine",无需手动干预。本文以 docs/normalized_embeddings.md 为骨架,结合leann-coreleann-backend-hnsw的源码实现,深入讲解归一化嵌入的原理、自动检测的判定逻辑、支持的模型清单、完整的构建与搜索用法,以及错误度量导致 HNSW 提前终止等问题的底层原因,帮助你为自己的 RAG 应用选对距离度量。

什么是归一化嵌入(Normalized Embeddings)

归一化嵌入是指L2 范数恰好等于 1的向量,即单位向量。这类向量经过 L2 归一化(向量除以自身范数)后,任意两个向量之间的余弦相似度(Cosine Similarity)就等于它们的内积(点积),因此它们天然是为余弦相似度优化,而非为最大内积搜索(MIPS,Maximum Inner Product Search)设计。

在 LEANN 中,归一化嵌入的识别结果直接决定索引使用的距离度量,进而影响整个检索链路的质量。这一点在 api.py 的LeannBuilder.__init__(L469-L536)中有着完整的实现。

自动检测机制:三步帮你做对度量选择

当你用归一化嵌入模型创建LeannBuilder实例时,LEANN 会自动完成三件事(见 api.py):

  1. 未指定度量时,自动设置distance_metric="cosine":检测到归一化模型且用户没有显式传入distance_metric时,代码会直接写入backend_kwargs["distance_metric"] = "cosine",并发出UserWarning告知用户已自动切换;
  2. 手动指定了其他度量时给出警告:若用户显式传入非 cosine 度量(如mips),LEANN 不会静默覆盖,而是发出警告提示该组合可能导致次优的检索结果;
  3. 以正确度量获得最优检索性能:构建端与查询端都会依据cosine对向量做 L2 归一化,使 HNSW 图的度量与向量空间严格匹配。

检测逻辑的源码实现细节

从源码看,检测分为精确匹配模式匹配两级(api.py):

  • 精确匹配:维护一个(embedding_mode, model_name)元组集合normalized_embeddings_models,包含 OpenAI、Voyage、Cohere 的全部已知归一化模型;匹配时先将 mode 与 model 转小写,再做全等或子串包含判断;
  • 模式匹配(兜底):未命中精确集合时,按厂商规则推断:
    • OpenAI:mode 或 model 名含"openai",且模型名含"text-embedding""ada""3-small""3-large"之一;
    • Voyage:mode 或 model 名含"voyage"即视为归一化(Voyage 全系模型均归一化);
    • Cohere:mode 或 model 名含"cohere",且模型名含"embed"

这意味着即使你使用的具体 OpenAI/Cohere 版本号不在精确清单中(例如未来的text-embedding-4-*),只要命名符合上述模式,同样会被正确识别。

支持的归一化嵌入模型清单

根据 docs/normalized_embeddings.md 及 api.py 中的精确集合,以下模型会被自动识别为归一化嵌入:

厂商模型名说明
OpenAItext-embedding-ada-002全系 OpenAI 文本嵌入模型均归一化
OpenAItext-embedding-3-small归一化
OpenAItext-embedding-3-large归一化
Voyage AIvoyage-2归一化
Voyage AIvoyage-3归一化
Voyage AIvoyage-large-2归一化
Voyage AIvoyage-multilingual-2归一化
Voyage AIvoyage-code-2归一化
Cohereembed-english-v3.0归一化
Cohereembed-multilingual-v3.0归一化
Cohereembed-english-light-v3.0归一化
Cohereembed-multilingual-light-v3.0归一化

注意:模式匹配规则(尤其是 Voyage 全系、OpenAItext-embedding系列)会覆盖精确清单之外的同系列新版本,因此上表是"确认清单"而非"封闭清单"。

示例用法:自动检测与手动覆盖

以下代码继承自 docs/normalized_embeddings.md 的示例,并补充了源码层面的行为说明。

方式一:自动检测(推荐)

from leann.api import LeannBuilder # 自动检测 - 将使用 cosine 距离 builder = LeannBuilder( backend_name="hnsw", embedding_model="text-embedding-3-small", embedding_mode="openai" ) # Warning: Detected normalized embeddings model 'text-embedding-3-small'... # Automatically setting distance_metric='cosine'

当未传入distance_metric时,api.py 会将distance_metric写入backend_kwargs并随索引一起持久化到<index>.meta.jsonbackend_kwargs字段中。之后无论是构建端(HNSWBuilder)还是查询端(HNSWSearcherBaseSearcher),都会从该字段读取度量,保证索引构建与在线检索使用同一套度量约定(参见 searcher_base.py 中搜索时从 meta 读取distance_metric、缺省回退mips的逻辑)。

方式二:手动覆盖(不推荐)

builder = LeannBuilder( backend_name="hnsw", embedding_model="text-embedding-3-small", embedding_mode="openai", distance_metric="mips" # 将显示警告 ) # Warning: Using 'mips' distance metric with normalized embeddings...

此时检测到is_normalized=True且用户指定了非 cosine 度量,api.py 会保留用户的设置并发出UserWarning。度量本身仍然生效,但代价是检索质量下降——具体原因见下文"为什么这很重要"一节。

支持的度量取值与底层映射

在 HNSW 后端中,度量字符串会被映射为 Faiss 的度量枚举(hnsw_backend.py):

distance_metric取值Faiss 枚举说明
"mips"faiss.METRIC_INNER_PRODUCT最大内积搜索,非归一化模型的默认最优度量
"l2"faiss.METRIC_L2欧氏距离平方
"cosine"faiss.METRIC_INNER_PRODUCT余弦相似度(向量归一化后内积即余弦)

值得注意的是,cosine在 Faiss 层实际映射为METRIC_INNER_PRODUCT——因为归一化后的单位向量内积等价于余弦相似度,无需 Faiss 内部再做归一化。真正完成归一化的是 LEANN 自己的normalize_l2()函数(hnsw_backend.py):

def normalize_l2(data: np.ndarray) -> np.ndarray: norms = np.linalg.norm(data, axis=1, keepdims=True) norms[norms == 0] = 1 # 避免除零 return data / norms

该函数对零向量做了除零保护。它的调用发生在两端

  • 构建端HNSWBuilder.build()distance_metric == "cosine"时对全部入库向量执行normalize_l2(data)(hnsw_backend.py);
  • 查询端HNSWSearcher.search()对查询向量同样执行normalize_l2(query)(hnsw_backend.py)。

只有两端都归一化,内积度量才能严格等价于余弦相似度。

非归一化嵌入:继续使用 MIPS

facebook/contriever以及其他未经归一化的 sentence-transformers 模型,LEANN不会把它们判定为归一化模型,因此默认仍使用mips度量——这对它们才是最优的(docs/normalized_embeddings.md)。从源码看,这类模型在精确集合与模式匹配两级检测中均不命中(api.py),backend_kwargs中不会出现distance_metric,各后端因此回退到各自默认值(HNSW 为mips,见 hnsw_backend.py)。

这印证了 LEANN 的设计原则:度量与向量空间必须匹配。归一化模型用 cosine,非归一化模型用 MIPS,各取所长。

为什么这很重要:错误度量的三大危害

原文档明确指出,对归一化嵌入使用错误度量会导致三类问题,而源码给出了更精确的机理:

  1. HNSW 提前终止导致检索质量下降:归一化向量全部落在单位球面上,所有内积分数都挤压在很窄的区间内。HNSW 搜索默认启用相对距离检查check_relative_distance),当候选分数区间过窄时会被误判为"已收敛"而过早终止搜索,错过真正的高质量邻居。源码中的针对性处理非常直观(hnsw_backend.py):

    # 对 OpenAI embeddings + cosine 距离,禁用相对距离检查 # 防止所有分数都处于狭窄区间时提前终止 if self.distance_metric == "cosine" and any( openai_model in embedding_model for openai_model in ["text-embedding", "openai"] ): params.check_relative_distance = False else: params.check_relative_distance = True

    可以看到,LEANN 对"OpenAI 归一化嵌入 + cosine"这一典型组合显式关闭了相对距离检查,从机制上规避提前终止问题;反之若使用 MIPS 度量,则无法触发这一保护,正是文档所述"poor search quality"的根源。

  2. 结果排序不正确:MIPS 搜索的是内积最大,而语义相关性更接近余弦相似度。向量模长与方向信息混杂在内积中,会使排序偏离真实的语义距离。

  3. 性能次优:度量与向量空间不匹配时,图结构、剪枝(pruning)与距离重算逻辑都基于错误的几何假设,整体检索效果与正确度量相比有明显差距。

此外,重算模式下的距离计算也在嵌入服务端遵循同样的度量约定:l2用平方欧氏距离,其他(含 cosine 与 mips)用负点积(hnsw_embedding_server.py),保证重算得分与索引度量一致。

实践要点小结

  • 使用 OpenAI / Voyage / Cohere 等归一化嵌入模型构建LeannBuilder时,无需手动指定distance_metric,LEANN 会自动设置cosine
  • 若坚持手动覆盖为非 cosine 度量,请接受UserWarning并知晓检索质量风险;
  • 自定义 embedding 流程时,若你的模型输出已经是单位向量,可参考 api.py 的检测清单自行确认;若输出未归一化,则保持默认 MIPS 即可;
  • 度量选择会影响构建与查询两端(归一化、Faiss 度量枚举、相对距离检查、重算距离),任何一环不一致都会破坏检索一致性。

更多相关细节可继续阅读 docs/normalized_embeddings.md 原文,以及索引构建入口 api.py、HNSW 后端实现 hnsw_backend.py 与检索基类 searcher_base.py。

【免费下载链接】LEANN[MLsys2026 Best Paper]: https://arxiv.org/abs/2506.08276. RAG on Everything with LEANN. Enjoy 97% storage savings while running a fast, accurate, and 100% private RAG application on your personal device.项目地址: https://gitcode.com/GitHub_Trending/le/LEANN

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 15:14:31

用 Trae AI 编程实战:从零开发 Flutter Web 2048 小游戏

最近我把主力开发工具换成了 Trae&#xff0c;起因是看到别人用 AI 对话就把一个完整的游戏做出来了&#xff0c;几乎没怎么手写代码。我决定亲自验证一下这件事到底靠谱不靠谱&#xff0c;于是给自己定了个目标&#xff1a;用 Trae 从 0 到 1 开发一个 Flutter Web 小游戏 204…

作者头像 李华
网站建设 2026/9/15 15:13:41

2021国赛Wireshark流量分析实战:从pcap到攻击链还原

2021年那场国赛&#xff0c;不知道有多少人跟我一样&#xff0c;在Wireshark这道题上栽了跟头。事后复盘才发现&#xff0c;这题压根不是在考你记了多少协议&#xff0c;而是考你在乱糟糟的流量包里&#xff0c;能不能快速定位到关键的那几秒。中职网络安全赛项的流量分析部分&…

作者头像 李华
网站建设 2026/9/15 15:10:05

协同过滤电影推荐系统:从算法原理到前后端分离工程实践

简介&#xff1a;运用Python与协同过滤算法构建的电影推荐系统&#xff0c;采用Vue实现前后端分离&#xff0c;并集成Django与MySQL&#xff0c;是一套面向计算机相关专业学生、适用于毕业设计与推荐算法入门实践的完整可运行项目。压缩包共688个文件&#xff0c;约13.01MB&…

作者头像 李华
网站建设 2026/9/15 15:09:16

Python实现Shamir密钥共享:从拉格朗日插值到工程落地

简介&#xff1a;Shamir(t,n)秘密分享方案是信息安全领域中经典的密钥管理技术&#xff0c;由Adi Shamir于1979年提出&#xff0c;允许将秘密拆分为n份、任意t份即可恢复。资源包用Python实现了这一门限方案&#xff0c;主要面向信安专业学习者、密码学初学者以及需要构建安全分…

作者头像 李华
网站建设 2026/9/15 15:06:37

RuboCop 1.30.1 版本解析:六项 Bug 修复与源码级原理解读

RuboCop 1.30.1 版本解析&#xff1a;六项 Bug 修复与源码级原理解读 【免费下载链接】rubocop A Ruby static code analyzer and formatter, based on the community Ruby style guide. 项目地址: https://gitcode.com/GitHub_Trending/rub/rubocop RuboCop 1.30.1 是 …

作者头像 李华