nprobe 管什么
IVF 类索引先把向量聚成 nlist 个簇,查询时只扫描与查询向量最近的 nprobe 个簇。nprobe 越大,被检查的候选越多、召回越高,代价是延迟几乎线性上升;设成 1 只扫一个簇,延迟最低但相关文档大量漏掉。调参方法是先在小规模数据上算出精确检索基线,再用网格搜索沿召回与延迟曲线选工作点。
基本信息
| 所属结构 | IVF 系列索引,如 FAISS IndexIVFFlat、Milvus IVF_FLAT |
|---|---|
| 相关参数 | nlist 决定簇数量,nprobe 决定每次查询扫描的簇数 |
| 取值方向 | nprobe 越大,召回越高、延迟越高 |
| 调参依据 | 精确检索基线,加上召回与延迟的对照曲线 |
典型使用场景
- 索引选型验证:确认 IVF 相对精确检索丢了多少召回,判断是否可接受
- 延迟优化:在不跌破召回下限的前提下把 nprobe 调到尽量小
- 数据增长后复核:向量总量翻倍后原参数覆盖比例变化,需要重新校准
- 分片策略评估:多分片下每片都要扫描,nprobe 与分片数需一起看
- 上线前压测:用真实查询分布跑一遍,确定参数对应的 P95 延迟
nprobe 网格搜索
nprobe 网格搜索
import faiss, numpy as np
d = 256
index = faiss.IndexIVFFlat(faiss.IndexFlatL2(d), d, 128) # 128 个簇
index.train(np.random.rand(100000, d).astype("float32")) # 先训练簇中心
index.add(np.random.rand(500000, d).astype("float32")) # 再写数据
q = np.random.rand(100, d).astype("float32")
for nprobe in (1, 4, 8, 16, 32, 64):
index.nprobe = nprobe
D, I = index.search(q, 10) # 记录每档的 recall@10 与 p95 延迟
参数影响与方向
| 参数 | 作用 | 调大后的影响 |
|---|---|---|
| nlist | 簇的数量 | 划分更细,同样 nprobe 覆盖比例下降 |
| nprobe | 查询扫描的簇数 | 召回上升,延迟上升 |
| 训练样本量 | 学习簇中心 | 样本过少时部分簇失去意义 |
| efSearch(HNSW) | 图搜索广度 | 同类权衡,参数名不同但道理一致 |
nprobe 与 nlist 要配对比曲线看:nlist 变大后同一个 nprobe 覆盖的簇比例变小,召回会跟着掉,只调一个参数容易得出错误结论。
召回评估必须用精确检索结果当参照:只比较不同 nprobe 之间的相对高低,无法知道离理论上限还差多少。
参数值不通用:128 个簇上的合适 nprobe 换到 4096 个簇就不适用,写入运维文档时要连同 nlist 与数据规模一起记录,避免换环境后照搬。