近邻检索(ANN)

ANN - Approximate Nearest Neighbor

ANN(近似最近邻检索)是向量数据库的核心检索范式:通过聚类、哈希、图索引等方式放弃一点点召回精度,换取比暴力精确检索快 100 到 1000 倍的查询速度。

详细解释

ANN(Approximate Nearest Neighbor,近似最近邻) 是所有向量检索 / RAG 系统里最”隐身”但最关键的基础设施。假设你有 N 条文档 embedding(N 通常是 100 万到 1 亿),每条向量维度 d=1536。暴力检索(Exact NN)是拿 query 向量和这 1 亿条每一条都算一次余弦距离或 L2 距离 → O(N×d) 运算量,哪怕是 A100 也要几十秒,不可能用在在线 RAG。ANN 的思路非常反人类:“我放弃一点精度,允许返回结果里混入 3% 到 10% 不是真正 Top-K 的邻居,但你能在 10 毫秒内出答案——对 RAG 够用吗?” 实际业务里 99% 的情况答案都是”完全够用,反正 RAG 重排时再把这些假邻居刷掉就行”。

ANN 三大主流索引家族(选型 2025)

索引算法代表库原理召回 95% 时的相对 QPS内存/磁盘开销构建索引速度最佳场景
HNSW(层级小世界图)Faiss IndexHNSWFlat、HNSWlib、Weaviate、Qdrant、Milvus、pgvector HNSW构建多层邻近图(Skip List 的图版),查询时从顶层粗找 → 下层精找,每步只看相邻 k 个节点(ef_search)。约 7000 QPS/单 A100高(每向量额外存 M 邻居指针,~80% 向量体积)慢(大索引 1 亿条要数小时)在线查询密集型(90% 业务首选)
IVF / IVF-PQ(倒排聚类 + 乘积量化)Faiss IVFPQ、ScaNN先把向量库用 K-Means 分成 K 个聚类中心(典型 K=1024 至 16384),查询时只去 query 最近的 nprobe 个聚类里做暴力;再加 PQ 把高维向量压成字节码子空间,内存缩 8 到 16 倍。约 4000 QPS低(IVF-PQ 可缩到原尺寸 1/16)海量低成本、查询 QPS 不极端高(1 亿向量只给一台机器)
DiskANN(SSD 友好)DiskANN / Microsoft SPTAG、FAISS DiskANN用压缩向量直接存在 SSD 上,内存只保留小图导航层;查询用顺序读 SSD,吞吐接近内存 HNSW,成本低 10 倍。约 3000 QPS极低(1% 内存 + SSD)PB 级超大规模(千万到十亿向量)

99% 的业务都在用的 HNSW 调参三参数

HNSW 是 2016 年 Yury Malkov 提出的论文算法,至今仍是向量数据库默认选项,因为不用调就能跑出 95% 召回率 + 毫秒级。三个关键超参:

参数构建时生效查询时生效推荐值调参口诀
M(每层邻居数)✅ 是❌ 否M=16(128维)~ M=32(1536维)低维(文本 embedding1536 以内)M=16/24;高维(多模态 2048+)M=32/48。越大越准但越吃内存
ef_construction(构建时候选扩展数)✅ 是❌ 否200 起步,土豪上 400构建索引慢准程度:100 省钱,200 常用,400 接近精度天花板再涨意义不大
ef_search(查询时候选扩展数)❌ 否✅ 是64(低 latency)~ 256(高精度)在线服务默认 100;召回不够的 case 就加到 256;低延迟场景缩到 64

经验:ef_search >= K(你要找多少条就至少扩多少条)。典型 RAG 取 top_k=20,ef_search 取 100 就够。

召回率 vs 延迟之间的业务折衷

很多同学会卡在”我要 99% 召回 + 1ms 延迟”,这在 ANN 里几乎是自相矛盾的——因为 ANN 的本质就是”你愿意给我多少算力预算,我换给你多少精度”。建议按业务链条分层:

  1. RAG 粗检索(第一阶段):召回率 ≥ 95%,延迟 ≤ 50ms,top_k=50 → HNSW ef_search=100 即可。
  2. Rerank 精排(第二阶段):拿 Rerank 模型 把粗检索来的 50 条重排成 3 条给 LLM——只要粗召回真邻居前 3 条都在这 50 条里(概率 ≥ 99.8%),Rerank 一定会把它们提上来。
  3. 结论:粗检索不用抠 ANN 到 99%,把剩下那 4% 交给 Rerank 去补,整体成本最低、效果最好。

唯元智创 的 RAG SDK 默认就是这样的 Pipeline:Query → HyDE 改写 → Embedding → ANN Top-50(HNSW ef_search=120,默认 97% 召回)→ Rerank Cross-Encoder Top-3 → LLM 生成答案,用户只需要在控制台上开开关即可,不用写任何 ANN 调参代码。

常见问题

pgvector 里用 HNSW 和 ivfflat 怎么选?
2024 年后 pgvector 已支持 HNSW,新建项目一律选 HNSW 就对了。ivfflat(2023 年时唯一选项)的缺点:(1) 需要你手动先跑聚类 lists=K,这个 K 不选对就不准;(2) 新增文档后要反复 re-index 才准;(3) 低维还可以,维度到 1536 以后相比 HNSW 慢一个量级。现在 pgvector HNSW WITH (m=16, ef_construction=200) 直接建索引,线上 SET hnsw.ef_search = 100;,几乎没有理由再碰 ivfflat。
余弦相似度(Cosine)和内积(Inner Product / dot)选哪个?
99% 文本 embedding 场景:选 Cosine 就行。原因:主流 embedding 模型(text-embedding-3-small / Qwen-Embedding / BGE M3 等)训练时都做了 L2 归一化(也就是输出向量模长 = 1)——在模长都等于 1 的向量集上,Cosine 相似度 = Inner Product 完全等价,选谁都一样。有两类情况你该选内积:(1) 你的 embedding 明确没做归一化且”模长大的代表更有信息量”(如旧版某些 sentence-transformers);(2) 做推荐系统,向量模长本身带权重信号。其余一律 Cosine。
我能不能用 brute force 精确检索当 ANN 的兜底?
能,而且这其实是 RAG 在线冷启动阶段最稳的方案之一。做法:你的向量总数 < 10 万条时,完全可以用 FAISS IndexFlatL2 或 pgvector 里的 +> 操作符(精确)跑,延迟也才 5 到 20ms——完全能接受。等向量超过 50 万条以后再换 HNSW,不用上来就上 ANN。Semantic Chunking 把文档切得越精细,向量条数涨得越快——通常 1 万页 PDF 切出来会是 50 万到 200 万条 chunk,这时候你就必须上 ANN 了。