faiss IVF 系列索引基准测试实战:bench_all_ivf 的构建、集群运行与精度-速度-压缩权衡分析

faiss IVF 系列索引基准测试实战:bench_all_ivf 的构建、集群运行与精度-速度-压缩权衡分析 faiss IVF 系列索引基准测试实战:bench_all_ivf 的构建、集群运行与精度-速度-压缩权衡分析【免费下载链接】faissA library for efficient similarity search and clustering of dense vectors.项目地址: https://gitcode.com/GitHub_Trending/fa/faiss本篇围绕 faiss 仓库中 benchs/bench_all_ivf 基准测试套件展开,完整讲解如何在该框架下评测各类 IVF 倒排索引变体的「压缩率 vs 检索速度 vs 召回精度」权衡:从数据集接入、index_factory建索引与训练、精度-速度评测协议,到 Slurm 集群批量编排与 Pareto 前沿绘图。读完后,你可以独立复跑该基准、修改实验矩阵(数据集、粗量化器、残差量化器、Refine 策略),并理解每个命令行参数在源码中的实际作用。一、基准测试的目标:压缩、速度与精度的三角权衡benchs/bench_all_ivf/README.md 开篇即点明该套件的目的:对 IVF(inverted file,倒排文件)系列索引的各种变体做基准测试,考察三者的权衡关系——压缩(compression):每个向量的编码字节数(code_size),决定了索引在磁盘/内存中的体积;速度(speed):单位查询耗时(ms/query),在多核 CPU 上通常以 QPS 呈现;精度(accuracy):1-recallR(前 R 个结果中命中真实最近邻的比例)或 intersectionR(结果集与真值集合的交集比)。该套件于 2018 年 10 月运行过一轮完整实验(结果发布在 faiss 官方 wiki 的 Indexing 1G vectors 章节),其实验规模从 100 万向量一直覆盖到 10 亿向量(deep1B / bigann1B),涵盖 SIFT、BigANN、Deep、GloVe、Music-100 等多个公开数据集。README 中列出的模块分工如下(后文逐一展开):文件职责datasets.py数据文件访问、真值(ground-truth)计算与精度报告bench_all_ivf.py评测一种索引类型:训练 → 添加向量 → 搜索评测run_on_cluster_generic.bash对所有待测索引类型批量调用bench_all_ivf.py,并适配集群调度parse_bench_all_ivf.py汇总所有结果,绘制精度-速度权衡(tradeoff)曲线需要说明一点实现细节:README 中写的datasets.py,在当前仓库中对应的实际文件是 benchs/bench_all_ivf/datasets_oss.py。bench_all_ivf.py 的导入逻辑是先尝试import datasets_fb(内部版本),失败后回退到开源版的datasets_oss:try: import datasets_fb as datasets except ModuleNotFoundError: import datasets_oss as datasets两者都暴露sanitize、load_dataset等函数,开源版直接复用faiss.contrib.datasets中的数据访问类。二、数据集接入层:datasets_oss.py 与 faiss.contrib.datasetsdatasets_oss.py 的load_dataset()支持的--db取值如下(见 benchs/bench_all_ivf/datasets_oss.py#L67-L109):--db取值数据集对象维度度量sift1MDatasetSIFT1M128L2bigann1M…bigann1BDatasetBigANN(nb_M...)128L2deep100k/deep1M/deep10M/deep100M/deep1BDatasetDeep1B(nb...)96L2gloveDatasetGlove100IP(内积,向量已 L2 归一化)music-100DatasetMusic100100IPdeep_centroids_NDatasetCentroids同 deep用预聚类质心代替库向量这些数据集类定义在 contrib/datasets.py 中,统一继承Dataset抽象类,提供get_queries()、get_train(maxtrain)、get_database()、database_iterator(bs, split)、get_groundtruth(k)等接口。几个对大模型评测很关键的实现:分片迭代:database_iterator(bs..., split(nsplit, rank))支持把数据库切成nsplit片并只遍历第rank片(见 contrib/datasets.py#L46-L58),配合--split参数可实现多机分布式建库;真值限制:get_groundtruth(k)断言k 100,即各数据集官方真值文件最多存 100 个最近邻;对 1B 级库,真值由 benchs/bench_all_ivf/make_groundtruth.py 调用faiss.contrib.exhaustive_search.knn_ground_truth分块(65536 一批)暴力计算得到;数据根目录:contrib.datasets.dataset_basedir会依次探测若干内部路径,找不到时回退到相对路径data/(见 contrib/datasets.py#L136-L147),开源用户只需把数据集放到./data/下并按各数据集类注释的文件名组织好。此外 datasets_oss.py 还内置了一个DatasetCentroids类:从已训好的IVF{N},SQ8索引文件中提取质心表,把在大库上建索引的问题转变成在质心集上建索引——这用于研究超大规模粗量化器(1024×1024 级别质心)本身该用什么索引来加速近邻查找(对应集群脚本中deep_centroids_前缀的实验)。三、bench_all_ivf.py:一次完整的训练-建库-评测流程bench_all_ivf.py 是单实验执行器,一个进程完成索引训练、向量添加、搜索评测三阶段,结果输出到 stdout(可加--json以 JSON 落盘)。其核心是把索引类型写成index_factory字符串,例如:IVF65536_HNSW32,PQ64x4fs—— 质心表 65536、用 HNSW32 加速质心近邻查找,PQ 残差编码 FastScan;OPQ64_128,IVF262144(IVF512,PQ16x4fs,RFlat),PQ64x4fs—— 外层 OPQ 旋转 嵌套 IMI 式粗量化 RFlat 重排;HNSW32—— 纯图索引作对照基线。3.1 工作流程:check_files 自动推断待办--todo默认值为check_files,其语义是根据索引文件是否存在自动决定执行哪些阶段(见 bench_all_ivf.py#L599-L606):if args.todo [check_files]: if os.path.exists(args.indexfile): args.todo [search] elif os.path.exists(args.trained_indexfile): args.todo [add, search] else: args.todo [train, add, search]即:已有完整索引 → 只搜索;只有训练好的索引 → 追加向量再搜索;什么都没有 → 训练、建库、搜索全做。这个断点续跑设计是集群大规模实验的基础——同一任务重跑时不重复昂贵阶段。3.2 训练阶段:训练量估算、GPU 训练与预置质心run_train()通过faiss.index_factory(d, indexkey, METRIC_L2/INNER_PRODUCT)建索引,并用unwind_index_ivf()剥开IndexPreTransform/IndexRefine外壳拿到真正的IndexIVF对象(见 bench_all_ivf.py#L23-L39),以便直接设置by_residual、cp.niter、clustering_index等 IVF 专属字段。几个值得注意的训练策略(全部源自 bench_all_ivf.py#L106-L237):训练集规模自动估算(--maxtrain 0时):含IMI的 key:maxtrain 256 × 2^(log2(nlist)/2),即按子质心数的平方根折算;普通 IVF:maxtrain 50 × nlist;其余:退化为256 × 100。GPU 训练粗聚类(--train_on_gpu):把IndexFlatL2(d)用faiss.index_cpu_to_all_gpus复制到全部 GPU 上,挂到index_ivf.clustering_index,k-means 的最近邻分配阶段被 GPU 加速——这就是 README 所说可使用 1 到 8 个 GPU 做大词汇量 k-means的实现(1B 级实验用IVF1048576、IVF4194304质心数时尤为关键)。预置质心复用(--get_centroids_from indexfile):从另一个已训练索引读出质心表src_quant.reconstruct_n(),若当前索引还有向量变换(如 OPQ)则先vec_transform.train(xt2)再对质心做变换,然后quantizer.add(centroids)跳过 k-means,把训练时间从小时级降到分钟级。集群脚本 run_on_cluster_generic.bash 中的add_precomputed_quantizer()正是按IVF65536/262144/1048576/4194304命名约定去precomputed_clusters/目录匹配质心文件(见 run_on_cluster_generic.bash#L89-L124)。OPQ FastScan 的特殊处理:当外层是OPQMatrix且内层是IndexIVFPQFastScan时,会临时给 OPQ 挂一个普通ProductQuantizer完成训练,规避 PQ4 训练在 FastScan 路径上的限制。粗量化器自身的 add 参数校准:若 quantizer 是IndexRefine,脚本会把k_factor提到 32/64;若是IndexHNSW,把efSearch设为 40/64、efConstruction设为 200。原因是:粗量化器训练时,每个向量被分配到最近质心,这一步本身也是一次近邻搜索,默认参数在大质心数下分配会不准确。3.3 建库阶段:分块与分片run_add()支持两种模式:--add_bs -1(默认)一次性index.add(整库),或按--add_bs分块迭代添加(大库实验普遍用--add_bs 1000000),同时配合--split N R只在第 R/N 片上 add,实现多机并行建库。每次 add 后打印进度与 RSS 内存占用。3.4 搜索评测:ms/query、1-recall 与 ndiseval_setting()(见 bench_all_ivf.py#L65-L98)的评测协议是:循环调用index.search(xq, k),直到累计耗时超过--min_test_duration(默认 3 秒)才停止,用总耗时/查询数/轮数得到稳定的ms_per_query;然后按--k(默认 100)输出:默认:R1、R10、R100,均为 1-recall 口径(第 1 个真值是否落在前 rank 个返回结果中);加--inter时:输出interk,用faiss.eval_intersection(gt[:, :k], I[:, :k]) / (nq * k)计算交集比——GloVe 这类 IP 数据集上,1-recall 对距离接近的并列结果过于敏感,intersection 更稳健;同时从faiss.cvar.indexIVF_stats.ndis读取每次搜索平均计算的距离数(ndis),这是 IVF 索引最核心的工作量指标,直接对应nprobe带来的扫描量(该统计字段定义见 faiss/IndexIVF.h 中InvertedListScannerStats的ndis)。3.5 搜索参数:手动列表或 autotune--searchparams有两种形态:显式参数串列表:逐个通过faiss.ParameterSpace.set_index_parameters(index, param)应用,例如nprobe1,nprobe10,nprobe100;autotune(默认):调用 faiss 的自动调参框架。脚本按--inter选择准则对象——faiss.IntersectionCriterion(nq, k)或faiss.OneRecallAtRCriterion(nq, 1)(二者定义于 faiss/AutoTune.h#L56-L73),再执行ps.explore(index, xq, crit),由ParameterSpace在参数空间中搜索最优工作点,最后对选出的 operating points 逐一复测。范围可用--autotune_max var:val(截断上界,10M/100M/1B 实验都用nprobe:2000或nprobe:3000防止搜索过深)与--autotune_range var:v1,v2,...(固定取值列表)约束;--n_autotune(默认 500)限制最大实验次数。3.6 完整命令行参数一览bench_all_ivf.py 的全部命令行选项按argparse分组整理如下(默认值来自源码):general options(通用)参数默认说明--nthreads-1训练与 add 阶段线程数(-1 表示沿用 OpenMP 默认)--jsonFalse结束时以 JSON 输出全部统计--todocheck_files执行阶段:train/add/search/check_files的组合dataset options(数据集)参数默认说明--dbdeep1M数据集名,见第二节表格--compute_gtFalse现场计算并保存真值--force_IPFalse强制用 IP 度量而非 L2--accept_short_gtFalse兼容 Deep1B 只存单个最近邻真值的特例index construction(建索引)参数默认说明--indexkeyHNSW32index_factory索引串--trained_indexfile空训练好索引的读写文件--maxtrain256×256训练点上限,0 表示按 3.2 节规则自动估算--indexfile空完整索引的读写文件--split N R1 0数据库分片,只处理第 R 片(共 N 片)--add_bs-1分块 add 的块大小,-1 为整库一次 addIVF options(IVF 专属)参数默认说明--by_residual-1强制开/关残差编码(-1 不改动)--no_precomputed_tablesFalse关闭 PQ 预计算距离表,省内存--get_centroids_from空从该索引文件取质心,跳过 k-means--clustering_niter-1k-means 迭代轮数(-1 保持默认)--train_on_gpuFalse粗聚类的最近邻分配放到全部 GPU 上index-specific options(索引专属)参数默认说明--M0-1HNSW 底层层数--RQ_train_defaultFalseRQ 训练关闭渐进降维--RQ_beam_size-1RQ 编码 beam 宽度--LSQ_encode_ils_iters-1LSQ 编码的 ILS 迭代数--RQ_use_beam_LUT-1RQ add 时是否使用 beam 查找表searching(搜索)参数默认说明--k100最近邻个数--interFalse用 intersection 而非 1-recall 度量--searchthreads-1搜索阶段线程数--searchparamsautotune参数串列表或autotune--n_autotune500autotune 最大实验次数--autotune_max空var:val形式,截断变量上界--autotune_range空var:v1,v2,...形式,固定取值--min_test_duration3.0单参数评测的最短秒数,消除抖动--indexes_to_merge空加载多个分片索引用merge_from合并后搜索apply_AQ_options()还允许针对 Additive Quantizer(RQ/LSQ)变体在脚本内改写rq.train_type、rq.max_beam_size、lsq.encode_ils_iters、rq.use_beam_LUT等字段,便于在同一套框架下评测 AQ 家族。一个可复现的最小化调用示例(对 SIFT1M 评测 IVF1024 PQ64x4 的 FastScan 变体):python -u benchs/bench_all_ivf/bench_all_ivf.py \ --db sift1M \ --indexkey IVF1024,PQ64x4fs \ --maxtrain 0 \ --indexfile /tmp/sift1M_ivf1024_pq64x4fs.faissindex \ --searchthreads 32 \ --min_test_duration 3四、集群编排:run_on_cluster_generic.bashrun_on_cluster_generic.bash 把实验数量很大这件事工程化:由于索引类型 × 数据集规模 × 参数组合可达数百个任务,脚本按 Slurm 集群假设(sbatch/srun)组织提交,并要求外部提供两个封装函数:run_on_1machine name command:80 核、无 GPU、500G 内存、70h 时限;run_on_8gpu name command:80 核 8 GPU、100G 内存,用于大质心数 k-means。每个任务名name决定输出文件$logdir/name.stdout;脚本先写一个srun command包装脚本,再以--partitionpriority --output$logdir/name.stdout提交。若同名脚本已存在则跳过,天然具备幂等性。实验矩阵按规模分层组织(以 deep/bigann 为例):规模数据集粗量化器(coarse)典型 index key 特征1Mglove、music-100、sift1M、deep1MIVF1024、IVF4096_HNSW32全谱系:PCAR/OPQ 旋转 × SQ4/SQ8/SQfp16/PQ{M}x4fs(PQFastScan)/RFlat 重排/Refine 精排10Mdeep10M、bigann10MIVF65536(IVF256,PQ16x4fs,RFlat)、IVF16384_HNSW32、IVF262144(IVF512,...)嵌套 IVF(子 IVF 编码质心) 外层 OPQ,--autotune_max nprobe:2000100Mdeep100M、bigann100MIVF65536_HNSW32、IVF1048576(IVF1024,...)同上,--add_bs 10000001Bdeep1B、bigann1BIVF1048576_HNSW32、IVF4194304(IVF1024,PQ48x4fs,RFlat)--autotune_max nprobe:3000、分块 add脚本中有两个很实用的辅助函数:replace_coarse_PQHD:把 key 中的占位符PQHD替换成按数据集维度算出的子 PQ 参数(HD dim/2;另派生coarse16/32/64/128/256等变体),使同一套 key 模板可同时适配 128 维(bigann)与 96 维(deep)数据;add_precomputed_quantizer:按 3.2 节第 3 条的约定,为 bigann/deep 系数据集自动补上--get_centroids_from $centdir/clustering.db{rname}1M.IVF{N}.faissindex。另一个被if false屏蔽但仍在仓库中的实验块是质心索引研究:先对 deep1M/bigann1M 用IVF{262144,65536,1048576,4194304},SQ8--train_on_gpu预训练质心表,再在deep_centroids_{N}数据集上比较IVF{sqrt(N)}、HNSW32、裸PQ48x4fs等方案在不同 k(4/8/16/64/256)下的 inter 指标——这正是回答当 IVF 有 400 万质心时,质心查找本身该用什么索引这一工程问题的实验设计。五、结果解析与权衡图:parse_bench_all_ivf.pyparse_bench_all_ivf.py 把散落的.stdout日志重建成精度-速度权衡图,流程如下:版本管理:日志命名约定为key.v.stdout,v是单字母版本号(a 到 z)。keep_latest_stdout()只保留每个 key 的最新版本;collect_results_for(db, prefix)再按数据集名与任务前缀(如autotune.、cent_index.)过滤。若解析中途遇到srun:开头的崩溃行会抛RuntimeError(instant crash)并被记为缺失任务。字段抽取:parse_result_file()逐行匹配固定格式的输出行,抽取d/nq/nb/nt(来自数据集的__str__行)、index_size(磁盘字节数)、RSS 内存、PQ 预计算表大小、搜索线程数、add 耗时、code_size,以及结果表头(R1/R10/R100 还是 interk)和每条参数串的数值行。Pareto 前沿:extract_pareto_optimal()把所有方法的 (recall, time) 点拼成一张大表,按精度排序后做np.minimum.accumulate,筛出同精度下更快的支配点,即每个方法的 Pareto 最优工作点;未上前沿的方法以灰色细线绘制作背景。绘图:横轴 1-recallR(或 interR),纵轴 QPS(1000 / ms_per_query),按 code_size 区间分组出图——plot_tradeoffs(db, allres, allstats, code_sizecs_range, recall_rank1)会用 contrib/factory_tools.py 的get_code_size()把 index key 解析成实际每向量字节数做过滤,图线样式还编码了方法类别(Refine/RFlat 用虚线、SQ 用点划、FastScan 带圆点标记),并在标签上附上 code_size 与相对紧编码(代码8 字节 ID)的存储开销倍数。构建时间报告:plot_subset()还会把 add 耗时(分钟/小时)与overhead xx%(索引实际体积 / 理论紧凑体积)写进图例,使权衡分析同时回答建库要多久、多占多少内存。六、配套工具:独立 k-means 评测与 SCANN 对照目录内还有三个辅助脚本,分别覆盖基准的两个盲区:benchs/bench_all_ivf/bench_kmeans.py:独立于索引的 k-means 质量评测。对给定数据集跑faiss.Clustering(d, k)(可选 GPU 加速的IndexFlatL2最近邻索引),以eval_freq为步长分多段执行 k-means 迭代,每段结束后把当前质心加进 Flat 索引、对验证集做最近邻分配并报告量化误差Σ‖xb − centroid[argmin]‖²。这为回答聚类要不要多迭代几轮PCA 降到多少维训练更好提供了与索引无关的度量;benchs/bench_all_ivf/cmp_with_scann.py:与 SCANN 库的受控对比。faiss 侧固定用IVF2000,PQ{d/2}x4fs(见faiss_make_index),另挂IndexRefineFlat模拟 SCANN 的 reorder 机制;SCANN 侧用tree(num_leaves2000) score_ah并可选reorder(100)。两侧扫描同一组nprobe(对应 SCANN 的leaves_to_search)与pre_reorder_k,输出 R1/10/100/1000 与耗时,度量可选 1-recall 或 intersection;benchs/bench_all_ivf/make_groundtruth.py:对 deep1B 全库用faiss.contrib.exhaustive_search.knn_ground_truth分块计算 100-NN 真值并以 ivecs 格式落盘——这是 1B 级实验评测精度的前提。七、从源码结构看:这套基准依赖的 faiss 核心机制基准脚本的每个关键动作都映射到 faiss 的核心 API,顺藤摸瓜可以深入理解 IVF 的实现:索引类型即字符串:faiss.index_factory的 key 语法(粗量化器、残差量化器、RFlat/Refine(...)组合)决定了实验矩阵的表达力,可参考 faiss/index_factory.cpp 的实现;IVF 统计:faiss.cvar.indexIVF_stats.ndis的底层是InvertedListScannerStats结构体(faiss/IndexIVF.h),其中ndis累计被计算距离的向量数,是解释为什么 nprobe 翻倍则 ndis 近似翻倍的直接依据;自动调参准则:OneRecallAtRCriterion与IntersectionCriterion均继承AutoTuneCriterion(定义见 faiss/AutoTune.h#L56-L73),ParameterSpace.explore()负责在参数网格上采样并维护最优工作点列表,这正是--searchparams autotune的驱动机制;GPU 训练路径:faiss.index_cpu_to_all_gpus(IndexFlatL2(d))把 Flat 索引克隆到每块 GPU 并做副本(IndexReplicas 语义),k-means 迭代中的最近邻分配因此跨 8 卡并行——对应 README可借助 1 到 8 个 GPU 做大词汇量 k-means的表述,前提是编译了 GPU 版 faiss。八、小结:如何复用这套基准单机复跑:准备数据集(放到data/或自行设置faiss.contrib.datasets.dataset_basedir)后,用 3.6 节的命令对单个--indexkey执行train/add/search,--todo check_files会保证重跑幂等;批量实验:模仿 run_on_cluster_generic.bash 的 key 命名规范(autotune.db数据集.indexkey 中逗号换下划线),把sbatch换成你的调度方式即可;--add_bs 1000000与--get_centroids_from是控制 100M 以上实验时长与磁盘占用的两个关键旋钮;分析产出:用parse_bench_all_ivf.py的collect_results_forplot_tradeoffs生成精度 vs QPS、按 code_size 分档的权衡图,并结合图例中的 add 耗时与存储 overhead 做选型。这套基准的价值在于把 IVF 选型中所有可调维度——粗量化器结构(Flat/HNSW/嵌套 IVF)、向量变换(无/PCAR/OPQ)、残差编码(SQ/PQ/FastScan)、重排(RFlat/Refine)与搜索参数(nprobe)——统一收敛到一条可复现的命令行协议里,其设计(断点续跑、预置质心、Pareto 汇总)对任何大规模 ANN 评测任务都有直接的参考价值。【免费下载链接】faissA library for efficient similarity search and clustering of dense vectors.项目地址: https://gitcode.com/GitHub_Trending/fa/faiss创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考