Faiss 1.15.0:RaBitQ 向量检索量化,1024 维向量只占 128 字节
Faiss 1.15.0 是 Meta 开源的高效稠密向量相似性搜索与聚类库。这一版把 RaBitQ 检索路径打磨成生产可用状态:每个维度只用 1 个比特存码,1024 维向量每条约 128 字节,FastScan 查询路径 QPS 再提 80%(出处见 CHANGELOG.md 中 "Optimize RaBitQ fastscan query setup + 80% qps (#5396)")。
这个版本到底改了什么
RaBitQ 从"新特性"变成了"快特性",这一版的改动集中在检索提速、硬件覆盖和加载方式三个层面,每条都对应你关心的业务指标。
- RaBitQ FastScan 全链路提速:查询建表优化使 QPS 提升 80%(#5396),并融合了 AND-dot 与 popcount 扫描(#5412)、AVX512 查表量化(#5397)、SIMD 位平面运算(#5392)。直接影响:同等硬件下查询吞吐量。
- 可平滑迁移的转换构造器:实现了 IVFRaBitQ → FastScan 变体的转换构造(#5422)。直接影响:旧 IVFRaBitQ 索引无需重建即可切到更快的扫描路径。
- RISC-V 支持:新增 RVV 向量距离内核(#5354)与 RaBitQ 内核(#5369)。直接影响:非 x86 平台的可用性。
- 内存映射 I/O:Flat 与静态 Vamana(SVS)索引支持 mmap(#5271),配合
faiss/impl/mapped_io.cpp。直接影响:索引加载时间与大内存机器上的可服务规模。 - HNSW 热点路径:visited 表跨查询复用不再逐次重分配(#5448),哈希表阈值从 50 万提升到 1000 万(#5446)。直接影响:HNSW 高并发下延迟方差。
一句话总结:这一版让 RaBitQ 从"能用的新索引"变成"值得上线的快索引"。
核心技术,一次讲透
✨ 关键点:RaBitQ 的本质是"随机旋转 + 符号位编码",把浮点距离比较变成了比特运算。
它是什么。 编码时分三步:先用一个随机旋转矩阵把向量坐标打散(让能量均匀分布在所有维度上);再减去全局中心点;然后逐维判断"旋转后坐标的正负",每维记 1 个比特,d 维向量的主码就是 d 个比特 = d/8 字节(faiss/impl/RaBitQuantizer.h)。生活化类比:像把一团乱麻先抖匀(随机旋转),再用"每根线头朝上还是朝下"记一份清单(符号位)——清单很小,但抖匀过之后它能可靠地反推出毛线团的大致形状。
它不是什么。 它不是无损压缩,reconstruct 出来的是近似向量而非原值;也不是降维——维度数不变,变的是每维的表示精度(1 比特)。
为什么更好。 搜索端把查询同样量化成少量比特(默认 qb=4,见 faiss/IndexRaBitQ.h 中的 RaBitQSearchParameters),距离计算就退化成"按位与 + 数 1 的个数"(AND + popcount),这类运算在 SIMD 里一条 512 位指令就能处理 512 个维度,而传统 PQ 需要查码本表加浮点累加。benchs/bench_rabitq.py 开头注释解释了这一点:d=1024 时每比特平面只需 2 轮 512 位指令。
一句话总结:旧做法是"子块查码本",新做法是"整体符号位 + 比特运算",编码更便宜、搜索更快,代价是精度略低于 PQ。
用数据验证
基准脚本 benchs/bench_rabitq.py 在合成数据(10 万训练、20 万库、1000 查询)上横扫 d ∈ {256, 512, 768, 1024},对 RaBitQ、IVFRaBitQ 与 PQFastScan、HNSWFlat、SQ 做 recall/QPS/内存三维对比。表内为可直接核验的量化事实,跑分数字以你自己环境的输出为准:
| 方案 | 每向量主码大小(d=1024) | 距离计算方式 | 关键参数出处 |
|---|---|---|---|
| 全精度 Flat | 4 KB(1024 × 4B fp32) | 浮点 L2 | — |
| RaBitQ(nb_bits=1,默认) | 128 B(1 bit/维)+ 少量统计量 | 比特 AND + popcount,SIMD | faiss/IndexRaBitQ.h |
| IVFRaBitQ + FastScan | 128 B/条,按簇组织 | 同左 + nprobe 粗筛 | faiss/IndexIVFRaBitQ.h |
如何自己复现:
# 在仓库根目录运行官方 RaBitQ 基准,输出 recall/QPS/内存
python benchs/bench_rabitq.py
# 正确性对照(RaBitQ 估计距离 vs 暴力精确距离):
python -m pytest tests/test_rabitq.py
📊 数据来源:上表代码尺寸按 nb_bits=1 直接计算(d 比特 = d/8 字节);吞吐对比由脚本运行时打印,本仓库不内置固定跑分数字,引用第三方数字请以自测为准。
一句话总结:128 字节 vs 4 KB 是 32 倍码长压缩,QPS 数字跑一遍 bench_rabitq.py 即得,无需引用二手数据。
三步跑通最小闭环
环境准备
装预编译 wheel 最快,源码编译用于吃满 AVX512/新硬件:
pip install faiss-cpu
# 或源码编译(AVX512 优化在 faiss/utils/simd_impl/ 下按 FAISS_OPT_LEVEL 选择)
git clone https://gitcode.com/GitHub_Trending/fa/faiss
cd faiss
cmake -B build -DFAISS_OPT_LEVEL=avx512
cmake --build build -j$(nproc)
最小可运行示例
下面用工厂字符串一行创建"IVF 粗量化 + RaBitQ 编码"索引,训练后检索:
import faiss, numpy as np
d, n, nlist = 1024, 100_000, 1000
x = np.random.randn(n, d).astype("float32")
index = faiss.index_factory(d, "IVF1024,RaBitQ") # 128B/条码
index.train(x)
index.add(x)
# 检索(k=10)
faiss.vector_to_array(index.search(np.random.randn(4, d).astype("float32"), 10))
# 注意:先 train 再 add;查询向量同样走 RaBitQ 量化(qb=4)
生产配置模板
生产建议显式控制 nprobe 与查询比特数 qb,二者共同决定延迟/召回曲线:
import faiss
index = faiss.index_factory(1024, "IVF4096,RaBitQ") # 4096 簇,1bit/维
index.train(xtrain); index.add(xdb)
p = faiss.IVFRaBitQSearchParameters() # bench_rabitq.py 同款参数对象
p.qb = 4 # 查询量化比特数:4=默认速度/精度折中
# 检索时: faiss.search_with_parameters(index, xq, k, p)
# p.nprobe 越大召回越高、延迟越高,按 SLO 调
💡 提示:训练点数要有下限——benchs/bench_rabitq.py 注释给出的经验值是 39 × nlist,即 nlist=1000 时至少约 3.9 万条。
一句话总结:工厂字符串 + IVFRaBitQSearchParameters 两个对象,就是 RaBitQ 的生产配置面。
按业务场景选方案
按"数据规模 × 硬件约束"对号入座,不用背参数:
| 场景 | 数据规模 | 硬件约束 | 推荐方案 |
|---|---|---|---|
| 精确基线 / 小规模 | < 100 万 | 任意 | IndexFlatL2(fp32,召回=1.0) |
| 通用实时检索 | 100 万 ~ 亿 | CPU(AVX2/AVX512) | IVFnlist,RaBitQ,nlist 取千级,调 nprobe |
| 内存受限的大库 | 千万 ~ 亿 | 内存紧张 | RaBitQ 码(128B/条@1024 维)+ Flat 索引 mmap(#5271) |
| 极高召回、低延迟 | 百万级 | 有 GPU | HNSW / CAGRA(见 tutorial/cpp/6-HNSW.cpp) |
| 精度要求 > 99% | 百万级 | 内存充足 | IVFPQ 或 IVFFlat 全精度码 |
| RISC-V / 非 x86 平台 | 任意 | RVV 支持 | RaBitQ(RVV 内核 #5369) |
这些坑,先知道再动手
- 症状:FastScan 相关路径报错。原因:码按 8 维一组打包,维度必须整除 8。解法:确认 d % 8 == 0,不满足就退回非 FastScan 变体或补齐维度。
- 症状:设
qb=0后速度断崖。原因:qb=0 意味着查询不做量化、直接走 fp32 距离计算机,SIMD 查表路径失效(faiss/IndexRaBitQ.h 第 28 行注释明确写了 FastScan 不支持 qb=0)。解法:追求速度保持默认 qb=4,只对"必须精确复核"的少量查询临时用 fp32。 - 症状:召回率不达预期。原因:nprobe 太小,粗量化漏检邻近簇。解法:用 benchs/bench_rabitq.py 的 nprobe 4/16/32 扫描法(脚本内
handle_ivf_index就这么做)画召回-延迟曲线,按 SLO 选点。 - 症状:
reconstruct结果与原向量差距可见。原因:RaBitQ 是有损距离估计器,重建靠 1 比特符号 + 范数统计量反推,不是原始向量。解法:需要原值时把原始向量单独落盘,索引只用于定位 ID。 - 症状:升级后 QPS 与预期不符。原因:SIMD 动态分发依赖 CPU 能力,AVX512 优化在
faiss/utils/simd_impl/下,老 CPU 会自动回退到更窄的指令集。解法:升级前先跑tests/test_simd_levels.cpp(配套 tests/test_simd_dispatch.py)确认当前生效的 SIMD 档位。
一句话总结:先确认 d 整除 8 与 qb 默认值,再谈调优。
下一步:① 用 IVF1024,RaBitQ 替换你现在的 IVFPQ 建一条对照索引;② 跑 python benchs/bench_rabitq.py 拿到本机 QPS/召回数字;③ 用 tests/test_rabitq.py 核对估计距离与精确距离的偏差。 社区资源:README.md、tutorial/、benchs/
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



