
LEANN 基线基准测试指南BM25 与 DiskANN 检索延迟对比【免费下载链接】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/LEANNLEANNbenchmarks/bm25_diskann_baselines/提供了一套独立的检索基线基准一个是基于 Pyserini/Lucene 的 BM25 稀疏检索另一个是基于 leann-backend-diskann 的 DiskANN 图索引检索。本文将以 benchmarks/bm25_diskann_baselines/README.md 为主线完整讲解两个基准的索引数据获取、运行命令、参数含义与实测结果并结合 run_bm25.py、run_diskann.py 与 diskann_backend.py 的源码深入拆解每个开关背后对延迟与吞吐的影响。读完本文你将掌握如何在本地复现这套稀疏基线 vs 图索引基线的延迟对比并理解 DiskANN 搜索参数complexity、beam_width、cache_mechanism 等的底层语义。基准定位为什么要同时测 BM25 与 DiskANNRAG 系统的检索环节存在两类主流方案以 BM25 为代表的词法稀疏检索与以 DiskANN 为代表的向量近似最近邻ANN图索引检索。二者在索引结构、查询路径、硬件依赖上差异极大单独报告任何一方的延迟都缺乏参照系。LEANN 在benchmarks/bm25_diskann_baselines/下同时提供两个独立脚本用于在同一台机器、同一份查询集上分别测量两种基线的纯检索延迟作为评估 LEANN 自身检索性能的外部参照。两个脚本的设计原则见 README.md 的 Notes 部分DiskANN 只统计search-only 延迟查询的 embedding 事先计算好并从计时中剔除BM25 直接对文本查询做词法检索天然不涉及 embedding两个基准使用同一份真实查询集Natural QuestionsNQ结果明确标注为machine-specific机器相关仅代表在当前仓库 当前机器上测得的本地数据。数据准备拉取索引与查询集README 给出的第一步是从 AWS S3 同步两个预构建索引到本地aws s3 sync s3://powerrag-diskann-rpj-wiki-20250824-224037-194d640c/bm25_rpj_wiki/index_en_only/ benchmarks/data/indices/bm25_index/ aws s3 sync s3://powerrag-diskann-rpj-wiki-20250824-224037-194d640c/diskann_rpj_wiki/ benchmarks/data/indices/diskann_rpj_wiki/两条命令分别把bm25_rpj_wiki/index_en_only/同步到benchmarks/data/indices/bm25_index/Pyserini 的 Lucene BM25 索引目录diskann_rpj_wiki/同步到benchmarks/data/indices/diskann_rpj_wiki/DiskANN 磁盘图索引目录。从 bucket 命名中的rpj_wiki可以推断两个索引均构建自同一份 Wiki 语料推测为 RedPajama 系列 Wiki 语料英文子集且索引前缀默认为ann对应脚本参数--index-prefix的默认值磁盘上形如ann_disk.index及相关 PQ 文件。查询集固定为benchmarks/data/queries/nq_open.jsonlNatural Questions 的 open-domain 抽取版两个脚本默认都指向它。注意S3 bucket 中的索引文件不在仓库内运行基准前需自行执行同步并确保awsCLI 已安装配置。DiskANN 基线run_diskann.py运行命令与结果uv run --script benchmarks/bm25_diskann_baselines/run_diskann.pyREADME 记录的本机实测结果指标数值平均延迟0.011093 s/queryQPS90.15p500.010731 sp950.015000 s对应设置与脚本默认值一致recompute_embeddingsFalse关闭重算重排只走 PQ 近似搜索路径embeddings 预计算use_serverFalse不计时batching off逐条查询每次只送 1 个向量embs[i : i 1]缓存关闭cache_mechanism2、num_nodes_to_cache0。脚本机制逐段拆解run_diskann.py使用 PEP 723 内联元数据声明依赖leann-backend-diskannrun_diskann.py由uv run --script自动解析执行。1. 参数面默认值即 README 记录的基准设置参数默认值说明--index-dirbenchmarks/data/indices/diskann_rpj_wikiDiskANN 索引文件目录--index-prefixann索引文件前缀C 层会拼出ann_disk.index等文件--queries-filebenchmarks/data/queries/nq_open.jsonlNQ 查询集--num-queries200参与计时的查询条数--top-k10返回的近邻数--complexity62搜索候选列表规模L62越大越准越慢--threads1搜索线程数--beam-width1每轮并发的 I/O 请求数--cache-mechanism2缓存机制见下文源码语义--num-nodes-to-cache0缓存节点数0 即不缓存2. embedding 预计算run_diskann.pyembs _compute( queries, model_namefacebook/contriever-msmarco, modesentence-transformers, use_serverFalse, ).astype(np.float32)关键点模型固定为facebook/contriever-msmarco与索引构建时的向量空间必须一致use_serverFalse表示不走 ZMQ embedding server直接本地计算。这正是 api.py 中compute_embeddings的分支语义use_serverTrue走端口转发给 server适合查询场景use_serverFalse走embedding_compute的直连计算适合 build 场景。预计算完成后向量在计时循环之外从而保证测到的是纯索引搜索延迟。3. 构造 Searcherrun_diskann.pysearcher _DiskannSearcher( index_prefix_path, num_threadsint(args.threads), cache_mechanismint(args.cache_mechanism), num_nodes_to_cacheint(args.num_nodes_to_cache), )底层对应 DiskannSearcher。注意index_prefix_path传的是基础路径不含_disk.index后缀C 层会自动拼出_disk.index若目录下检测到*_disk_graph.index与*_partition.bin两个分区文件还会自动启用分区前缀走图分区索引。4. 计时循环run_diskann.py先跑 1 次 warmup不计时然后对每条查询执行searcher.search( embs[i : i 1], # 单条查询batching off top_kargs.top_k, complexityargs.complexity, beam_widthargs.beam_width, prune_ratio0.0, # 不做近似剪枝 recompute_embeddingsFalse, batch_recomputeFalse, dedup_node_disFalse, )这些开关直接对应 diskann_backend.py 的search签名complexity候选列表大小决定 PQ 距离遍历的广度是准确率—延迟的核心旋钮beam_width每轮并行 I/O 请求数影响磁盘读取的并发度prune_ratio用近似距离剪枝邻居的比例0.0–1.0基准关闭0.0recompute_embeddingsFalse时走use_deferred_fetchFalse的纯 PQ 路径C 层遍历始终用 PQ 距离不触发重排见 diskann_backend.py 的策略注释cache_mechanism的语义在源码中明确注释1 用 sample data 初始化缓存2 就绪缓存但不初始化其他值 禁用缓存diskann_backend.py。基准取2配合num_nodes_to_cache0等价于彻底关闭缓存测的是冷缓存的原始索引访问能力。结果按 p50/p95 输出p50 即排序后len(times)//2处的值QPS 用1.0 / avg计算。BM25 基线run_bm25.py运行命令与结果uv run --script benchmarks/bm25_diskann_baselines/run_bm25.pyREADME 记录的本机实测结果指标数值平均延迟0.028589 s/queryQPS34.97p500.026060 sp900.043695 sp950.053260 sp990.055257 s对应设置k10、k10.9、b0.4、queries100。环境准备JDK 21 与 Pyserinirun_bm25.py依赖pyseriniLucene 之上的 Python 检索库而 Pyserini 需要 JDK。脚本头部注释给出了 Arch Linux 下的完整配置流程run_bm25.pysudo pacman -S jdk21-openjdk export JAVA_HOME/usr/lib/jvm/java-21-openjdk sudo archlinux-java status sudo archlinux-java set java-21-openjdk # fish shell 下持久化 set -Ux JAVA_HOME /usr/lib/jvm/java-21-openjdk fish_add_path --global $JAVA_HOME/bin set -Ux LD_LIBRARY_PATH $JAVA_HOME/lib/server $LD_LIBRARY_PATH which javac # 应输出 /usr/lib/jvm/java-21-openjdk/bin/javac其他发行版只需保证JAVA_HOME指向可用的 JDK21 及以上即可脚本会在缺失时提示pip install pyserini。参数面参数默认值说明--bm25-indexbenchmarks/data/indices/bm25_indexPyserini Lucene 索引目录--queriesbenchmarks/data/queries/nq_open.jsonl查询文件--k10Top-k 检索数--k10.9BM25 词频饱和参数--b0.4BM25 文档长度归一化参数--limit100最多执行的查询条数README 结果即 100--warmup5预热查询数不计时--fetch-docsoff额外抓取命中文档内容更慢默认关--report无可选输出 JSON 报告路径脚本机制要点查询加载run_bm25.py优先解析 JSONL依次尝试query/text/question字段解析失败的行按纯文本处理非 JSONL 文件按每行一条查询读取。BM25 参数注入run_bm25.pyLuceneSearcher打开索引后调用searcher.set_bm25(k1args.k1, bargs.b)部分 pyserini 构建版本不要求显式设置因此用 try/except 兜底。计时先跑warmup条预热再逐条searcher.search(q, kargs.k)计时若开--fetch-docs还会对每个命中调用searcher.doc(h.docid)把文档读取 I/O 计入。报告--report可把 queries、k、k1、b、avg、p50/p90/p95/p99、total_time、qps、索引绝对路径等结构化写入 JSONrun_bm25.py便于后续对比分析。结果对比与解读把两个基线的 README 结果放到一起基线avgQPSp50p95DiskANNNQsearch-only0.011093 s90.150.010731 s0.015000 sBM25k10, k10.9, b0.40.028589 s34.970.026060 s0.053260 s在 README 记录的这台机器上DiskANN 的纯搜索延迟约为 BM25 的 1/2.6吞吐约为 2.6 倍。解读时需注意三点口径不同DiskANN 侧 embedding 已预计算并排除在计时外BM25 侧则是纯词法打分两者都是纯检索但 DiskANN 的实际端到端延迟还需加上 embedding 计算成本关闭了缓存与重排DiskANN 的cache_mechanism2、num_nodes_to_cache0意味着没有利用节点缓存加速若开启缓存cache_mechanism1或更大的num_nodes_to_cache延迟通常还会进一步下降machine-specificREADME 明确声明结果是在当前仓库、当前机器上测得的本地数据不代表通用结论换机器、换索引、换查询集后数字会变化复现时应以自身环境为准。源码纵深LEANN 内置 BM25 与外部基线的区别值得说明的是run_bm25.py使用的 Pyserini/Lucene 是外部独立基线与 LEANN 自身代码无关。而 LEANN 核心也内置了一个基于 SQLite FTS5 的 BM25 索引Fts5BM25Index见 api.pyBM25Index抽象基类定义契约fit(documents)建索引、查询时命中bm25()打分Fts5BM25Index用CREATE VIRTUAL TABLE bm25_passages USING fts5(...)持久化词项/倒排数据并在建表时按_cjk_ngrams开关决定是否对中日韩文本做 unigram/bigram 切分api.py由于 SQLite FTS5 的bm25()返回值是越小越好LEANN 内部取负号统一成越大越好的分数语义方便与向量分数做混合融合见 api.py 的注释。也就是说bm25_diskann_baselines目录下的 BM25 基准用于横向对标外部检索实现如果要在 LEANN 内部做混合检索sparse dense则应使用Fts5BM25Index这条内置路径。二者得分口径、索引后端均不同做对比实验时不要混用。复现注意事项索引不在仓库内必须先用aws s3 sync拉取benchmarks/data/indices/下的两个索引目录脚本才会通过目录存在性检查run_bm25.py检查os.path.isdirrun_diskann.py检查index_dir.is_dir()否则直接退出查询集固定默认都指向benchmarks/data/queries/nq_open.jsonl若换用自定义查询文件需保证向量空间与索引构建时一致DiskANN 侧为facebook/contriever-msmarco环境差异Pyserini 需要 JDK 21 且JAVA_HOME正确DiskANN 侧leann-backend-diskann当前版本 0.3.8见 pyproject.toml需随uv run --script自动安装或预装结果可复现性两个脚本都提供 warmup 机制排除冷启动且run_bm25.py支持--report输出 JSON 报告建议复现时固定同一批参数并多次运行取稳定值。通过这套双基线基准你可以快速在同一环境下回答一个关键问题给定你的语料与查询分布词法稀疏检索与图索引向量检索各自的纯检索延迟处在什么水平从而为 LEANN 的检索链路选型或混合检索比例提供可量化的参照。【免费下载链接】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),仅供参考