LEANN 如何用 LAION 多模态基准通过 90% Recall@3 目标二分搜索出最优 search complexity?
【免费下载链接】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 的搜索质量由complexity(搜索复杂度/候选列表大小)参数控制:api.py 中的文档字符串将其解释为 "Search complexity/candidate list size, higher = more accuracy but slower",即值越大结果越准但越慢。问题随之而来:多大的complexity刚好满足业务需要的精度目标?LEANN 仓库自带的 LAION 多模态基准(benchmarks/laion/README.md)把这件事做成了 Stage 3——在 caption→图片的自召回检索上,对 complexity 做 1–128 的二分搜索,自动找出达到 90% Recall@3 的最小复杂度,并在最优值附近做验证。本文按 setup_laion.py 和 evaluate_laion.py 的真实实现,还原这条从建索引到拿到最优 complexity 的操作路径。
准备条件:环境与依赖
运行基准脚本前需要:
- Python >= 3.10(pyproject.toml 中
requires-python = ">=3.10"),以及uv; - 克隆 LEANN 仓库并进入仓库根目录,从源码同步工作区依赖。Linux (Ubuntu/Debian) 路径如下(README.md Installation 一节):
sudo apt-get update && sudo apt-get install -y \ libomp-dev libboost-all-dev protobuf-compiler libzmq3-dev \ pkg-config libabsl-dev libaio-dev libprotobuf-dev \ libmkl-full-dev uv sync --extra diskannLAION 基准使用 HNSW 后端,如果你只需要 HNSW,README 说明可以用libopenblas-dev替代 MKL,并在同步命令中去掉--extra diskann。
Stage 3 还有几个脚本自身会检查的前置条件:
- FAISS flat 基线文件必须存在于
baseline/目录(faiss_flat.index与metadata.pkl),否则 evaluate_laion.py 会打印 "FAISS baseline not found" 并提示先运行setup_laion.py,然后退出; - 评估查询文件
data/evaluation_queries.jsonl必须由 setup 生成; - 脚本会用
sentence-transformers加载clip-ViT-L-14模型(首次运行会下载模型权重,需要网络)。
第一步:准备数据、索引与基线
在benchmarks/laion目录下运行 setup 脚本(README.md Quick Start 的命令):
cd benchmarks/laion python setup_laion.py --num-samples 10000 --num-queries 200该脚本会依次完成(见 setup_laion.py):
- 从 HuggingFace
laion/laion400m流式读取样本,异步并行下载图片到data/laion_images/; - 用
clip-ViT-L-14(768 维、归一化向量)为每张图片生成嵌入,保存到data/clip_image_embeddings.npy; - 构建两个 LEANN 索引:紧凑索引
data/laion_index.leann(is_recompute=True, is_compact=True)和非紧凑索引data/laion_index_noncompact.leann(is_recompute=False, is_compact=False),均为 HNSW 后端、graph_degree=32、complexity=64、cosine距离; - 构建 FAISS flat 基线(
IndexFlatIP)到baseline/faiss_flat.index,并保存图片 id 的metadata.pkl——这是 Recall 计算的 ground truth 来源; - 从样本 caption 中抽样生成
data/evaluation_queries.jsonl(随机种子固定为 42)。
注意该步骤有网络下载副作用(LAION 子集 + CLIP 模型权重),文件全部写入benchmarks/laion/data/与benchmarks/laion/baseline/。
第二步:运行 Stage 3 二分搜索
setup 完成后,在同一目录运行:
python evaluate_laion.py --index data/laion_index.leann --stage 3--stage只接受2、3、4、5、all;3表示 complexity 分析,all会顺序执行所有阶段。Stage 3 的内部流程(evaluate_laion.py):
重建非紧凑索引用于加速搜索。脚本先以
is_recompute=False、is_compact=False基于现有 passages 和预计算嵌入重建非紧凑索引到data/laion_index_noncompact.leann。原因是二分搜索要反复跑 Recall 评估,非紧凑索引可直接用存储的嵌入、跳过 recompute,搜索更快。取前 50 条 caption 查询(
captions[:50])作为测试集。二分搜索。固定
target_recall = 0.9,搜索范围min_complexity, max_complexity = 1, 128,每轮取mid = (min + max) // 2:- 若该复杂度下 Recall@3 ≥ 90%:记录为当前最优,令
max_complexity = mid - 1,向更低复杂度搜索; - 否则:令
min_complexity = mid + 1,向更高复杂度搜索。
每轮评估都会打印当前 complexity 的 Recall@3 以及 "Target reached" / "Below target" 提示。
- 若该复杂度下 Recall@3 ≥ 90%:记录为当前最优,令
最优值附近验证。找到最优 complexity 后,脚本会额外测试
best-2到best+2共 5 个复杂度(上限 512),逐个打印 ✅(达到 90%)或 ❌(未达到),方便确认边界。紧凑索引对照。最后用紧凑索引(
recompute_embeddings=True)取前 10 条查询测试最优 complexity,打印 Recall 及与非紧凑索引结果的差值,让你确认 recompute 模式下精度没有明显漂移。
如何判断结果
成功时控制台会输出:
🎯 Optimal complexity found! Complexity: <找到的最小 complexity> Recall@3: <对应的 recall>以及验证块:
🔬 Verification test around optimal complexity: ✅/❌ Complexity <N>: <xx.x>%判断依据是"达到 90% 且复杂度尽可能小":脚本记录的是最后一个满足recall >= 0.9的mid,且每次命中后继续向更小值收缩。如果 1–128 全部低于目标,脚本会打印 "Could not find complexity achieving 90% recall / All tested complexities were below target"——此时需要扩大搜索范围或检查数据,文档没有给出进一步的自动处理。
benchmarks/laion/README.md 中的 Benchmark Results 给出了文档示例结果(示例数值,不是每次运行都能复现的固定值):
Target Recall: 90% Optimal Complexity: 85 Binary Search Range: 1-128 Non-compact Index (fast search, no recompute)限制与边界说明
- 文档内部有一处配置口径不一致:benchmarks/laion/README.md 的 "Dataset Configuration" 写的是 CLIP ViT-B/32(512 维),但 setup_laion.py 与 evaluate_laion.py 实际加载的是
clip-ViT-L-14(768 维),README 的 Benchmark Results 一节也写的是 ViT-L/14 768 维。按脚本实际行为为准。 - README Notes 提到 "当前实现使用 dummy 数据演示、CLIP 嵌入随机生成",而脚本源码已实现真实下载与真实 CLIP 编码;如果你换过脚本版本,建议以运行输出中的 embedding shape 确认为何值。
- Stage 3 会额外写出一个非紧凑索引(
*_noncompact.leann)并保留给 Stage 4 使用;如果直接跑--stage all,全部阶段结束后脚本会自动删除这个临时非紧凑索引(按其文件名前缀匹配删除文件),这是脚本内置的文件删除行为。 --stage all还会执行 Stage 5 多模态生成,需要transformers与Qwen/Qwen2.5-VL-7B-Instruct模型;只做 complexity 搜索时不需要,用--stage 3即可。- Recall 的 ground truth 来自 FAISS flat 全量近邻搜索(
IndexFlatIP,k=3),每条查询的 recall 按与 ground truth 三个 id 的交集大小除以 3 计算,平均后与 90% 目标比较。
下一步
拿到最优 complexity 后,脚本在--stage all结尾给出的建议是 "Use optimal complexity for best speed/accuracy balance"。实际搜索时把它传给LeannSearcher.search(caption, top_k=3, complexity=<最优值>, ...)即可(complexity的 API 默认值为 64)。如果需要进一步量化紧凑索引的存储节省与搜索速度比,可运行 Stage 4:
python evaluate_laion.py --index data/laion_index.leann --stage 4 --output results.jsonStage 4 会复用 Stage 3 留下的非紧凑索引,对比两类索引的.index大小、存储节省百分比与平均搜索时间,并把指标 JSON 写入--output指定的文件。
【免费下载链接】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),仅供参考