今天在 GitHub Trending 上看到一个有意思的项目:turbovec,这是一个基于 Google Research TurboQuant 算法的高性能向量搜索引擎,能够将 10M 文档的向量数据从 31GB 压缩到 4GB,而且搜索速度比 FAISS 更快。

一、项目概述

turbovec 是一个用 Rust 编写的向量索引库,提供 Python 绑定,基于 Google Research 的 TurboQuant 算法实现。该算法的核心特点是数据无关量化器,能够达到失真度的香农下界,而且不需要训练码本,不需要单独的训练阶段。

核心特性:

  • 在线 ingestion:添加向量即可索引,无需训练步骤、参数调优,随着语料增长无需重建索引
  • 比 FAISS 更快:手写 NEON (ARM) 和 AVX-512BW (x86) 内核,在 ARM 上比 FAISS IndexPQFastScan 快 12-20%,在 x86 上匹配或超越
  • 搜索时过滤:向 search() 传递 ID 允许列表(或 slot 位掩码),内核直接遵守,总是从允许集合中返回最多 k 个结果,不会对选择性过滤器造成召回率损失
  • 纯本地:无需托管服务,数据不会离开你的机器或 VPC,可与任何开源嵌入模型配对,构建完全气隙隔离的 RAG 栈

二、技术原理

2.1 TurboQuant 算法核心

TurboQuant 的核心洞察是:在应用随机旋转后,每个坐标都遵循已知分布——与输入数据无关。

算法流程:

  1. 归一化:从每个向量中剥离长度(范数)并存储为单个浮点数。现在每个向量都是超球面上的单位方向。

  2. 随机旋转:将所有向量乘以相同的随机正交矩阵。旋转后,每个坐标独立遵循 Beta 分布,在高维度上收敛到高斯 N(0, 1/d)。这适用于任何输入数据——旋转使坐标分布可预测。

  3. 逐坐标校准(TQ+):步骤 2 中的 Beta 分布是渐近的——在有限维度下,单个坐标会偏离规范形状(尤其是低比特和词向量风格的嵌入)。TQ+ 在第一次添加时拟合每个坐标的两个标量(平移和缩放),将每个坐标的经验 5/95% 分位数映射到规范 Beta 边际。Lloyd-Max 码本然后对目标分布进行量化。校准在第一次添加后冻结,后续添加重复使用——无需重新训练、重建或单独的训练阶段。召回增益:在漂移最严重的单元格上,@1 最多 +1.4pp(例如 2 位时的 GloVe)。

  4. Lloyd-Max 标量量化:由于分布已知,我们可以预计算对每个坐标进行分桶的最优方式。对于 2 位,是 4 个桶;对于 4 位,是 16 个桶。Lloyd-Max 算法 找到使均方误差最小化的桶边界和质心。这些是一次从数学计算出来的,而不是从数据中计算出来的。

  5. 比特打包:每个坐标现在是一个小整数(2 位为 0-3,4 位为 0-15)。将这些紧密打包成字节。一个 1536 维向量从 6,144 字节(FP32)变为 384 字节(2 位)。这是 16 倍的压缩。

  6. 长度重归一化评分:标量量化系统地低估了内积——重建的单位方向比原始方向稍短。我们在编码时计算每个向量一个标量——旋转后的单位向量与其自己的质心重建的内积——并将 ||v|| / ⟨u, x̂⟩ 存储在每个压缩向量旁边。搜索内核在堆插入之前将此标量乘以每个候选分数,将以向下偏差的内积估计器在零搜索时间成本和零额外存储的情况下变为无偏。召回增益在量化收缩最大的低比特宽度时最为明显。

编码成本:每个向量在编码时额外付出一个 d 维点积来计算 ⟨u, x̂⟩。在 d=1536 的 1M 向量上,这是亚秒级的额外编码时间——这是在 ingestion 时支付的一次性价格,而不是在查询时。

2.2 SIMD 优化

搜索时,不是解压缩每个数据库向量,而是将查询一次性旋转到同一域中,并直接针对码本值进行评分。评分内核使用 SIMD intrinsic(ARM 上的 NEON,支持 AVX-512BW 的现代 x86 上有 AVX2 回退)和半字节拆分查找表来实现最大吞吐量。

Lloyd-Max 码本实现的失真在信息论下界(香农失真率限制)的 2.7 倍因子内;长度重归一化步骤消除了 Lloyd-Max 码本在内积估计器本身引入的残差偏差。

2.3 架构设计

项目采用 Rust 核心 + Python 绑定的架构:

  • Rust 核心turbovec crate 实现核心算法和 SIMD 优化
  • Python 绑定:通过 maturin 构建 Python wheel,提供 turbovec Python 包
  • 框架集成:提供与 LangChain、LlamaIndex、Haystack、Agno 的无缝集成

三、安装与快速开始

3.1 Python 安装

pip install turbovec

基础使用示例:

from turbovec import TurboQuantIndex

index = TurboQuantIndex(dim=1536, bit_width=4)
index.add(vectors)
index.add(more_vectors)

scores, indices = index.search(query, k=10)

index.write("my_index.tq")
loaded = TurboQuantIndex.load("my_index.tq")

需要稳定 ID survives deletes?使用 IdMapIndex

import numpy as np
from turbovec import IdMapIndex

index = IdMapIndex(dim=1536, bit_width=4)
index.add_with_ids(vectors, np.array([1001, 1002, 1003], dtype=np.uint64))

scores, ids = index.search(query, k=10)   # ids are your uint64 external ids
index.remove(1002)                         # O(1) by id

index.write("my_index.tvim")
loaded = IdMapIndex.load("my_index.tvim")

3.2 Rust 安装

cargo add turbovec

Rust 使用示例:

use turbovec::TurboQuantIndex;

let mut index = TurboQuantIndex::new(1536, 4);
index.add(&vectors);
let results = index.search(&queries, 10);
index.write("index.tv").unwrap();
let loaded = TurboQuantIndex::load("index.tv").unwrap();

3.3 框架集成

turbovec 提供与主流 RAG 框架的 drop-in 替换:

  • LangChainpip install turbovec[langchain] · 替换 langchain_core.vectorstores.InMemoryVectorStore
  • LlamaIndexpip install turbovec[llama-index] · 替换 llama_index.core.vector_stores.SimpleVectorStore
  • Haystackpip install turbovec[haystack] · 替换 haystack.document_stores.in_memory.InMemoryDocumentStore
  • Agnopip install turbovec[agno] · 替换 agno.vectordb.lancedb.LanceDb

四、使用方法与实战

4.1 混合检索(过滤搜索)

turbovec 支持限制由另一个系统(SQL、BM25、ACL、时间窗口等)产生的候选集:

import numpy as np
from turbovec import IdMapIndex

idx = IdMapIndex(dim=1536, bit_width=4)
idx.add_with_ids(vectors, ids)

# Stage 1: 外部系统缩小到候选 ids
allowed = np.array(db.execute("SELECT id FROM docs WHERE tenant=?", (t,)).fetchall(),
                   dtype=np.uint64)

# Stage 2: 在候选集内进行密集重排序
scores, ids = idx.search(query, k=10, allowlist=allowed)

过滤发生在 SIMD 内核内部,以 32 向量块粒度进行:在 any LUT 查找或评分工作之前,没有允许 slot 的块会被短路,而在评分块内的单个不允许 slot 会在堆插入时被丢弃。因此,选择性允许列表(索引的小部分允许)避免了大部分 SIMD 成本,而不是先付出成本再丢弃结果。

输出长度为 min(k, len(allowed))——当允许列表小于 k 时,你得到正好 len(allowed) 个结果,而不是填充的回退。

4.2 实际 RAG 应用场景

场景 1:多租户 SaaS RAG

# 每个租户有独立的 allowlist
tenant_allowed_ids = get_tenant_doc_ids(tenant_id)
scores, ids = index.search(query, k=10, allowlist=tenant_allowed_ids)

场景 2:时间窗口过滤

# 只搜索最近 30 天的文档
recent_doc_ids = get_docs_in_time_range(last_30_days)
scores, ids = index.search(query, k=10, allowlist=recent_doc_ids)

场景 3:混合检索架构

# Stage 1: BM25 稀疏检索得到候选集
bm25_candidates = bm25_search(query, top_k=100)

# Stage 2: turbovec 密集重排序
scores, ids = turbovec_index.search(query, k=10, allowlist=bm25_candidates)

五、性能基准

5.1 召回率

TurboQuant vs FAISS IndexPQ(LUT256, nbits=8)——论文第 4.4 节的基线。100K 向量,k=64。FAISS PQ 子量化器计数调整为匹配 TurboQuant 的比特率(m=d/4 在 2 位,m=d/2 在 4 位)。

结果摘要:

  • 在 OpenAI d=1536 和 d=3072 上,TurboQuant 在 2 位和 4 位的 R@1 上比 FAISS 高 0.4-3.4 个点,并且两者都在 k=4 时收敛到 1.0
  • GloVe d=200 是更难的 regime——在低维时,渐近 Beta 假设更松散。TurboQuant 在 4 位时比 FAISS 高 0.3 个点,在 2 位 R@1 时低 1.2 个点,两者都在 k≈16 时关闭与 FAISS 的差距

5.2 搜索速度

所有基准:100K 向量,1K 查询,k=64,5 次运行的中位数。

ARM(Apple M3 Max):

  • 单线程:TurboQuant 在每个配置上都比 FAISS FastScan 快 12-20%
  • 多线程:同样的优势

x86(Intel Xeon Platinum 8481C / Sapphire Rapids, 8 vCPUs):

  • 单线程:TurboQuant 在每个 4 位配置上都赢 1-6%,在 2 位 ST 上运行在 FAISS 的 ~1% 内
  • 多线程:2 位 MT 行(d=1536 和 d=3072)是唯一稍微落后于 FAISS 的配置(2-4%),其中内部累加循环太短,无法匹配 FAISS 的 AVX-512 VBMI 路径的展开摊销

5.3 压缩率

turbovec 实现显著压缩:

  • FP32(1536 维):6,144 字节/向量
  • 2-bit TurboQuant:384 字节/向量(16x 压缩)
  • 4-bit TurboQuant:768 字节/向量(8x 压缩)

一个 10M 文档的语料库在 FP32 下需要 31GB RAM,在 2-bit TurboQuant 下只需要 4GB。

六、常见问题与解决方案

6.1 安装失败

问题pip install turbovec 失败,提示 Rust 编译器未找到

解决方案

# 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 重新安装 turbovec
pip install turbovec --no-cache-dir

6.2 维度不匹配

问题dimension mismatch 错误

解决方案:确保 dim 参数与你的嵌入模型输出维度匹配:

# OpenAI embeddings: dim=1536
index = TurboQuantIndex(dim=1536, bit_width=4)

# Sentence transformers: dim=384
index = TurboQuantIndex(dim=384, bit_width=4)

6.3 内存使用

问题:索引大型数据集时内存不足

解决方案

  1. 使用 IdMapIndex 分批添加向量
  2. 增加 bit_width 以减少压缩率,从而降低内存使用
  3. 确保系统有足够 RAM(10M 向量在 2-bit 需要 ~4GB)

6.4 召回率调优

问题:召回率不满意

解决方案

  1. 增加 bit_width 从 2 到 4(或更高)
  2. 确保向量已正确归一化
  3. 检查是否有足够候选(k 参数)

6.5 Git 认证问题(对于博客自动提交)

问题:自动 Git push 失败

解决方案

  1. 配置 SSH key:ssh-keygen -t ed25519 -C "[email protected]"
  2. 将公钥添加到 GitHub
  3. 使用 SSH URL 而非 HTTPS:
    git remote set-url origin [email protected]:username/hugo-blog.git
    

七、总结

turbovec 是一款卓越的向量搜索引擎,它将 Google Research 的 TurboQuant 算法与精心优化的 SIMD 实现相结合,提供了以下核心优势:

  1. 卓越的压缩率:将向量数据压缩 8-16 倍,显著降低内存需求
  2. 领先的搜索速度:在 ARM 上比 FAISS 快 12-20%,在 x86 上匹配或超越
  3. 零训练开销:无需码本训练,在线 ingestion,随数据增长无需重建
  4. 强大的过滤支持:搜索时过滤,SIMD 内核内高效执行
  5. 无缝框架集成:与 LangChain、LlamaIndex、Haystack、Agno 等主流框架即插即用

对于构建隐私敏感、低延迟、可扩展的 RAG 系统的团队来说,turbovec 是一个值得认真考虑的选择。其纯本地、无托管服务的特性,加上与任何开源嵌入模型的兼容性,使其成为企业级 RAG 部署的理想基础组件。

项目资源:

  • GitHub:https://github.com/RyanCodrai/turbovec
  • Paper:https://arxiv.org/abs/2504.19874
  • PyPI:https://pypi.org/project/turbovec/
  • crates.io:https://crates.io/crates/turbovec

参考论文:

  • TurboQuant: Online Vector Quantization with Near-optimal Distortion Rate (ICLR 2026)
  • RaBitQ: Quantizing High-Dimensional Vectors with a Theoretical Error Bound for Approximate Nearest Neighbor Search (SIGMOD 2024)
  • FAISS Fast accumulation of PQ and AQ codes (FastScan)