ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

faiss benchs 目录实战指南:复现 Polysemous Codes 与十亿级 GPU 相似度搜索基准

faiss benchs 目录实战指南:复现 Polysemous Codes 与十亿级 GPU 相似度搜索基准 faiss benchs 目录实战指南复现 Polysemous Codes 与十亿级 GPU 相似度搜索基准【免费下载链接】faissA library for efficient similarity search and clustering of dense vectors.项目地址: https://gitcode.com/GitHub_Trending/fa/faissbenchs/是 faiss 仓库中用于复现论文实验数据的基准测试脚本目录它既包含 SIFT1M 上 Polysemous 量化码的召回-速度曲线也包含 SIFT1B / Deep1B 十亿级数据集的 CPU 与多 GPU 搜索基准。读完本篇你可以掌握这些脚本的完整参数体系数据集名、index_factory 键、搜索参数网格、中间结果的断点续跑机制训练/索引缓存目录、以及 GPU 侧内存控制选项-tempmem、-altadd、-noptables的使用方法并理解每个脚本背后的调用链与数据来源。一、目录定位复现两篇论文的实验数据benchs/README.md 开宗明义该目录中的脚本用于复现以下两篇论文中报告的数据Polysemous codesECCV 2016Douze / Jégou / Perronnininproceedings{DJP16, Author {Douze, Matthijs and J{\e}gou, Herv{\e} and Perronnin, Florent}, Booktitle ECCV, Organization {Springer}, Title {Polysemous codes}, Year {2016} }Billion-scale similarity search with GPUsarXiv:1702.08734Johnson / Douze / Jégouinproceedings{JDJ17, Author {Jeff Johnson and Matthijs Douze and Herv{\e} J{\e}gou}, journal {arXiv:1702.08734}, Title {Billion-scale similarity search with GPUs}, Year {2017}, }README 特别提示由于实现演进、机器差异等因素复现出的数值尤其是耗时会与论文略有出入。所有脚本都是自包含的只依赖 Faiss 本身和存放在子目录中的外部训练/查询数据不需要其他基础设施。整个目录的脚本可分为三类论文复现脚本polysemous、GPU、通用工具benchs/datasets.py、以及 README 末尾列出的附加基准见本文第五节。二、公共工具datasets.py 中的加载与评测函数所有 SIFT1M 脚本共用 benchs/datasets.py值得先了解三个函数fvecs_read(fname)/ivecs_read(fname)分别读取 fvecsfloat32 向量与 ivecsint32 向量格式文件load_sift1M()固定读取四个文件——sift1M/sift_learn.fvecs训练集、sift1M/sift_base.fvecs库向量、sift1M/sift_query.fvecs查询、sift1M/sift_groundtruth.ivecs真值返回(xb, xq, xt, gt)evaluate(index, xq, gt, k)执行一次index.search(xq, k)计算每查询平均耗时ms以及 R1、R10、R100 召回返回(t, recalls)。这意味着运行任何 SIFT1M 脚本前工作目录下必须存在sift1M/子目录。README 给出的数据集来源是 corpus-texmexIRISA 实验室的 ANN_SIFT1M 数据集README 原文附有下载链接解压到sift1M/子目录即可。三、SIFT1M 上的 Polysemous 实验3.1 脚本做了什么benchs/bench_polysemous_sift1m.py 复现 Polysemous 论文 Figure 3 的数据。其核心逻辑从源码看非常简洁构建faiss.IndexPQ(d, 16, 8)16 子空间、每子空间 8 位码字共 8 字节码长并设置index.do_polysemous_training True即启用 polysemous 训练脚本在 10 万训练点上训练 PQ重复训练 5 次NUM_TRAIN_RUNS 5输出训练耗时的中位数/均值/标准差/最小/最大值将全部库向量加入索引后先用faiss.omp_set_num_threads(1)把 OpenMP 线程数固定为 1保证搜索计时可复现基线index.search_type faiss.IndexPQ.ST_PQ测一次扫一遍 Hamming 阈值for ht in 64, 62, 58, 54, 50, 46, 42, 38, 34, 30将search_type切换为ST_polysemous并设置index.polysemous_ht ht后逐个评测。Polysemous 检索的机制是对 PQ 码先做廉价的 Hamming 距离初筛只有通过阈值ht的候选才计算精确 ADCasymmetric distance因此ht越小、初筛越激进、速度越快但召回随之下降——这正是输出表中速度从 7.5ms 单调降到 1.2ms、R1 从 0.447 跌到 0.18 的内在原因。3.2 参考输出README 给出的输出样例PQ training on 100000 points, remains 0 points: training polysemous on centroids add vectors to index PQ baseline 7.517 ms per query, R1 0.4474 Polysemous 64 9.875 ms per query, R1 0.4474 Polysemous 62 8.358 ms per query, R1 0.4474 Polysemous 58 5.531 ms per query, R1 0.4474 Polysemous 54 3.420 ms per query, R1 0.4478 Polysemous 50 2.182 ms per query, R1 0.4475 Polysemous 46 1.621 ms per query, R1 0.4408 Polysemous 42 1.448 ms per query, R1 0.4174 Polysemous 38 1.331 ms per query, R1 0.3563 Polysemous 34 1.334 ms per query, R1 0.2661 Polysemous 30 1.272 ms per query, R1 0.1794可以观察到一个细节ht64等于码长即不做任何初筛过滤时 polysemous 比 PQ 基线更慢——这是 Hamming 检查本身的额外开销只有当ht降到 58 以下、初筛开始真正省掉 ADC 计算后时间才开始显著下降。四、十亿级数据集实验SIFT1B / Deep1B4.1 数据准备benchs/bench_polysemous_1bn.py 复现 Polysemous codes 论文中两个 10 亿规模数据集的实验。脚本对数据目录的约定源码中的硬编码路径BIGANN / SIFT1B从 corpus-texmex 下载 ANN_SIFT1B 的四个文件到bigann/子目录脚本会以内存映射方式读取bigann_base.bvecs、bigann_query.bvecs、bigann_learn.bvecs真值读bigann/gnd/idx_{M}M.ivecsDeep1B查询与真值文件按 README 指引下载库向量与训练向量按 README 指引借助 GNOIMI 项目的下载脚本下载到deep1b/然后拼接为base.fvecs与learn.fvecs。脚本读取deep1b/base.fvecs、deep1b/deep1B_queries.fvecs、deep1b/learn.fvecs、deep1b/deep1B_groundtruth.ivecs并且由于 Deep1B 训练集过大源码里会截断xt xt[:10_000_000]只用前 1000 万条。十亿向量的文件不可能全部放进内存脚本用np.memmap做只读内存映射并在matrix_slice_iterator/dataset_iterator中按 10 万条一个 block 流式喂给index.add()。4.2 命令行参数体系脚本的取参方式是位置参数python bench_polysemous_1bn.py 数据集名 index_factory 键 [搜索参数 ...]数据集名SIFT1000M即 SIFT1B/BIGANN、Deep1B也支持SIFT1M、SIFT2M等子集名以便小规模调试。README 特别提示作为 SIFT1B 子集的SIFT1M与第三节的独立 SIFT1M 数据集不是同一份数据索引类型任意合法的 index_factory 键对应源码中faiss.index_factory(d, index_key)的调用剩余参数逐个被解析为搜索期参数通过faiss.ParameterSpace的ps.set_index_parameters(index, param)应用到索引上。4.3 断点续跑训练与索引缓存这些实验动辄数小时脚本把中间结果落到临时目录/tmp/bench_polysemous/源码中tmpdir /tmp/bench_polysemous实现两级缓存训练缓存{数据集}_{索引键}_trained.index——训练一次后faiss.write_index落盘下次直接faiss.read_index加载填充缓存{数据集}_{索引键}_populated.index——10 亿向量全部 add 完之后落盘。训练向量的数量由choose_train_size(index_key)按索引键自动决定源码可见规则基础量n_train 256 * 1000供 PQ 子码本与 PCA 使用若键中含IVF{n}n_train max(n_train, 100 * ncentroids)保证 kmeans 训练点数约为质心数的 100 倍若键中含IMI2x{b}n_train max(n_train, 256 * (1 b))按 IMI 子簇总量给足训练点。此外rate_limited_imap用单线程池做生产者-消费者限流让 mmap 块的解码与 add 重叠执行避免内存峰值。4.4 复现论文 Table 2IMIPolyDADC16 字节版本的 Table 2 结果README 原文命令python bench_polysemous_1bn.py SIFT1000M IMI2x12,PQ16 nprobe16,max_codes{10000,30000},ht{44..54}注意这里借助了bash 的花括号展开生成参数网格max_codes与ht的笛卡尔积会展开成 2×1122 组参数每组单独跑一次搜索。README 报告的耗时量级训练约 2 分钟、向索引添加 10 亿向量约 3.1 小时均多线程搜索是单线程的源码中faiss.omp_set_num_threads(1)。输出样例节选自 READMER1 R10 R100 time %pass nprobe16,max_codes10000,ht44 0.1779 0.2994 0.3139 0.194 12.45 nprobe16,max_codes10000,ht48 0.2033 0.3694 0.3917 0.640 20.77 nprobe16,max_codes10000,ht54 0.2170 0.4240 0.4546 0.256 39.66 nprobe16,max_codes30000,ht44 0.1882 0.3327 0.3555 0.226 11.29 nprobe16,max_codes30000,ht54 0.2278 0.4601 0.5031 0.303 39.20%pass列是脚本从 C 静态统计对象算出的源码中为ivfpq_stats.n_hamming_pass * 100.0 / ivf_stats.ndis即 IVFPQ 中通过 Hamming 初筛的码比较占全部距离计算的比例。README 指出 Table 2 报告的工作点是%pass约 20% 的那一行对应ht48上表 ht48 行 %pass20.77 与之吻合。8 字节版本只需把 factory 键换成IMI2x12,PQ8。4.5 复现论文附录 Table 3 与 Deep1B 自动调参附录实验仅在 ArXiv 版中有用 OPQ 预处理README 原文命令python bench_polysemous_1bn.py SIFT1000M OPQ8_64,IMI2x13,PQ8 nprobe{1,2,4,8,16,32,64,128},ht{20,24,26,28,30}同样依赖 bash 花括号展开{1,2,4,...}是枚举列表、{44..54}是范围序列二者语法不同。README 说明原论文数据对应nprobe32,ht28的运行点。Deep1B 则展示了 faiss 的运行时自动调参接口传一个autotune关键字即可脚本内部构建faiss.OneRecallAtRCriterion(nq, 1)作为准则函数优化 1-R1然后调用ps.explore(index, xq, crit)让 Faiss 自己扫操作点python bench_polysemous_1bn.py Deep1B OPQ20_80,IMI2x14,PQ20 autotune ... Done in 4067.555 s, available OPs: Parameters 1-R1 time nprobe1,ht22,max_codes256 0.0215 3.115 nprobe512,ht68,max_codes524288 0.4478 36.903 nprobe1024,ht80,max_codes131072 0.4557 46.363 nprobe1024,ht78,max_codes262144 0.4616 61.939README 说明论文原结果取自nprobe1024,ht66,max_codes262144。源码中还有autotuneMT变体不调omp_set_num_threads(1)允许多线程搜索时调参。五、GPU 实验以下基准在原论文中跑在 1 或 4 块 Titan X 上同时它们也是学习 GPU Faiss 用法的良好起点。适用前提当前 faiss 构建需带 GPU 支持faiss.get_num_gpus()返回大于 0且数据文件已按前述方式就位。5.1 SIFT1M GPU 搜索bench_gpu_sift1m.pybenchs/bench_gpu_sift1m.py 复现 GPU 论文中exact k-NN time曲线分两段实验精确搜索faiss.GpuIndexFlatL2(res, d, flat_config)建 GPU Flat 索引GpuIndexFlatConfig指定device 0add 全部库向量、warmup 一次后对k 1, 2, 4, ..., 1024源码中for lk in range(11): k 1 lk分别评测输出如下README 样例k1 0.715 s, R1 0.9914 k128 0.761 s, R1 0.9935 k1024 1.424 s, R1 0.9935近似搜索用faiss.index_factory(d, IVF4096,PQ64)在 CPU 侧建模再faiss.index_cpu_to_gpu(res, 0, index, co)克隆到 GPU随后train → add → warmup扫nprobe 1..512输出召回曲线nprobe 1 0.043 s recalls 0.3909 0.4312 0.4312 nprobe 32 0.134 s recalls 0.7957 0.9549 0.9550 nprobe 512 1.348 s recalls 0.8228 0.9999 1.0000源码中有两处值得注意的实现细节co.useFloat16 TrueREADME 与源码注释都解释了原因——64 字节长的 PQ 需要 16 位浮点查找表才能放进有限的临时显存temp memoryREADME 指出对小数据集更高效的做法是把 factory 串改成IVF16384,Flat即GpuIVFFlat倒排列表存全向量而非 PQ 码脚本中保留了这行注释掉的备选代码。样例输出nprobe 1 0.025 s recalls 0.4084 0.4105 0.4105 nprobe 256 0.299 s recalls 0.9866 0.9944 0.9944README 同时解释了输出中两条警告的含义kmeans 训练点不足 4096×39 会告警10 万训练点不足以支撑 4096 质心属预期现象add()的临时内存分配告警只在频繁触发时才值得关注且基准测的并非 add 速度。5.2 MNIST8m 上的 k-means 聚类kmeans_mnist.pybenchs/kmeans_mnist.py 复现 GPU 论文中的聚类实验。它使用 Léon Bottou 的无限 MNIST数据集README 指引按其网站说明获取脚本约定mnist8m-patterns-idx3-ubyte文件位于mnist8m子目录注意源码中basedir /path/to/mnist/data是一个需要指向实际下载位置的变量完整读取路径为basedir mnist8m/mnist8m-patterns-idx3-ubyte。命令行两个位置参数k质心数与ngpuGPU 数例如python kmeans_mnist.py 1 256。从源码看其结构faiss.Clustering(d, k)niter 20并设clus.max_points_per_centroid 10000000以禁止 kmeans 对训练集降采样每块 GPU 建一个GpuIndexFlatL2useFloat16 False保持 float32 精度多 GPU 时用IndexReplicas包装多个 GPU Flat 索引kmeans 的最近邻查找在副本间自动分片输出最终目标函数与总耗时。README 样例输出Clustering 8100000 points in 784D to 256 clusters, redo 1 times, 20 iterations Preprocessing in 7.94526 s Iteration 19 (131.697 s, search 114.78 s): objective1.44881e13 imbalance1.05963 nsplit0 final objective: 1.449e13 total runtime: 140.615 s5.3 SIFT1B / Deep1B 的 GPU 搜索与 knn-graphbench_gpu_1bn.pybenchs/bench_gpu_1bn.py 是本目录中最复杂的脚本它把数据集构建流程在 Python 里拆解成预处理 → 粗量化器训练 → PQ/Flat 训练 → 分片 add多阶段流水线以最大化 CPU/GPU 并行度与 GPU 间分布。README 提醒即使多卡构建 10 亿索引也可能耗时数小时建议先用 SIFT1M/SIFT2M 等小数据集验证流程。用法python bench_gpu_1bn.py 数据集 index_factory 键 [选项]。脚本usage()中列出的完整选项集与源码逐一对应选项含义默认值-ngpu N使用的 GPU 数全部可见 GPU-tempmem N每块 GPU 的临时显存字节数系统默认-nocache不读写中间缓存文件使用缓存-float16GPU 侧使用 16 位浮点关闭-abs Nadd 按不超过 N 个向量分块32768-max_add N每 add N 个向量把分片刷回 CPU防几何扩容溢出-1关闭-altadd替代 add 路径add 期间索引不常驻 GPU对慢速 GPU 上的大数据集略快关闭-R R数据集副本数数据复制到 ngpu/R 块 GPU 上1-noptablesIVFPQ 不使用预计算查找表使用-qbs N查询按不超过 N 个向量分块16384-nnn N每个查询搜索的邻居数10-nprobe a,b,c尝试这些 nprobe 值1,2,...,256-knngraph改为构建 k-nn 图模式关闭-oI xx%d.npy/-oD xx%d.npy把搜索结果索引/距离存为 numpy 文件不存中间产物同样缓存在临时目录源码cacheroot /tmp/bench_gpu_1bn按数据集预处理IVF量化器命名 preproc/质心/索引三类缓存文件-knngraph模式的缓存文件带BK_前缀以免与普通实验冲突。SIFT1B 搜索README 原文命令复现 GPU 论文数据python bench_gpu_1bn.py SIFT1000M OPQ8_32,IVF262144,PQ8 -nnn 10 -ngpu 1 -tempmem $[1536*1024*1024] ... 0/10000 (0.024 s) probe1 : 0.161 s 1-R1: 0.0752 1-R10: 0.1924 0/10000 (0.005 s) probe64 : 0.353 s 1-R1: 0.1332 1-R10: 0.4461 0/10000 (0.006 s) probe256: 1.160 s 1-R1: 0.1342 1-R10: 0.4511-tempmem $[1536*1024*1024]是 bash 的算术展开$[...]语法把临时显存压到 1.5G否则索引放不下。Deep1B 搜索4 卡、带副本python bench_gpu_1bn.py Deep1B OPQ20_80,IVF262144,PQ20 -nnn 10 -R 2 -ngpu 4 -altadd -noptables -tempmem $[1024*1024*1024] ... 0/10000 (0.006 s) probe64 : 0.238 s 1-R1: 0.4841 1-R10: 0.8649README 解释了这里显存吃紧的对策-noptables关掉预计算表、限制临时显存、-altadd避免 add 期间显存溢出源码中-R 2会把 CPU 索引手动复制成两个分片 GPU 索引组源码注释称之为 replicas are made manually。Deep1B knn-graphpython bench_gpu_1bn.py Deep1B OPQ20_80,IVF262144,PQ20 -nnn 10 -altadd -knngraph -R 2 -noptables -tempmem $[130] -ngpu 4knngraph 模式下源码逻辑查询集换成库向量本身xq xb先用多卡 GPU Flat 索引对 1 万个节点nq_gt 10000计算 100 邻的精确真值分块 add float_maxheap_array_t归并再以rank-10 交集率faiss.ranklist_intersection_size作为质量指标随 nprobe 递增999997440/1000000000 (9741.095 s, 0.4722) probe4 : 9741.128 s rank-10 intersection results: 0.4722 999997440/1000000000 (70616.392 s, 0.6047) probe256: 70616.581 s rank-10 intersection results: 0.6047注意 README 的提醒knngraph 不会复用前面搜索实验的倒排文件因为两者的训练集不同knngraph 用全库做训练源码中也因此给缓存文件加BK_前缀。六、附加基准脚本一览README 还列出了一批附加基准它们更多是 Faiss 各组件的用法示例并声明某些测试/基准可能已过时。完整清单继承自 benchs/README.mdbench_6bit_codec.cpp—— 在合成数据集上测试 SQ6 向量编码bench_cppcontrib_sa_decode.cpp—— 对 PQ / IVFPQ / ResidualPQ 的专用解码 kernel 做基准bench_for_interrupt.py—— 评估可由 Python 代码触发的中断回调处理器的开销bench_hamming_computer.cpp—— Hamming 距离计算的各专门实现bench_heap_replace.cpp—— Heap 数据结构不同实现调用的基准bench_hnsw.py—— SIFT1M 上 HNSW 与其他索引的组合基准bench_hnsw_prune_headroom.py—— HNSWprune_headroom的召回与构建时间影响bench_index_flat.py—— 合成数据集上的IndexFlatL2bench_index_pq.py—— SIFT1M 上的 PQbench_ivf_fastscan_single_query.py—— BIGANN 上不同 nprobe 的单查询 IVF{nlist},PQ{M}x4fs 基准bench_ivf_fastscan.py—— SIFT1M 上 IVF{nlist},PQ{M}x4fs 与其他索引对比bench_ivf_selector.cpp——faiss::IDSelectorAll接口可能的开销bench_pairwise_distances.py—— 两个合成数据集间的成对距离计算bench_partition.py—— 分区partitioning函数基准bench_pq_tables.py——ProductQuantizer.compute_inner_prod_tables()与compute_distance_tables()调用基准bench_quantizer.py—— SIFT1M / Deep1B / BigANN 上多种量化器对比bench_scalar_quantizer.py—— Sift1M 上的 IVFSQ除上述清单外当前目录中还存在若干 README 未收录的较新脚本从目录结构看如 benchs/bench_rabitq.py、benchs/bench_eden.py、benchs/bench_hybrid_cpu_gpu.py以及 benchs/bench_all_ivf/、benchs/distributed_ondisk/ 等带独立说明的子目录可作为新量化器与分布式/磁盘场景的额外参考C 基准的构建入口见 benchs/CMakeLists.txt。七、运行要点小结路径约定脚本按相对路径读取数据子目录sift1M/、bigann/、deep1b/运行时工作目录须为benchs/或数据子目录的父目录缓存目录CPU 实验用/tmp/bench_polysemous/GPU 实验用/tmp/bench_gpu_1bn/两者都会把训练好的索引/填充好的索引/质心/预处理器落盘删除对应缓存文件即可强制重训这也是复跑单一参数组而不必重建 10 亿索引的关键计时口径搜索前显式faiss.omp_set_num_threads(1)CPU 脚本保证单线程可复现GPU 脚本先做 warmup 搜索再进入计时循环数值差异README 明确提示复现数值尤其耗时与论文会有出入比较时应以同一硬件上的相对趋势为准。【免费下载链接】faissA library for efficient similarity search and clustering of dense vectors.项目地址: https://gitcode.com/GitHub_Trending/fa/faiss创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表