Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

IVF

IVF:基于 k-means 中心的 Voronoi 分桶,仅扫描距离 q 最近的 scan_buckets_count 个桶,并支持可选的精排

IVF(Inverted File,倒排索引)是 VSAG 的 分桶式 索引。它在构建时将语料聚类成若干桶, 查询时只扫描与查询距离最近的若干个桶的中心对应的倒排列表,把 O(N) 的线性扫描降为 O(N · scan_buckets_count / buckets_count),并通过这两个参数在召回与延迟之间进行权衡。

与图索引相比,IVF 在召回上略有损失,但换来了更低的内存开销、更高的批量吞吐以及更简单的 切片方式——因此在语料非常大(数亿及以上)、内存紧张、或查询可天然并行化的场景中, IVF 通常是一个更合适的默认选择。

工作原理

  1. 聚类。 在数据集的采样上运行 k-means(或随机采样,ivf_train_type: "random") 得到 buckets_count 个中心(centroid)。
  2. 分配。 每条向量被写入距离最近的中心对应的倒排列表,以配置的粗量化 (base_quantization_type)存储;可选地再保留一份高精度副本(use_reorder: true) 用于精排。
  3. 检索。 查询时先计算查询向量与所有中心的距离,选出最近的 scan_buckets_count 个桶;然后只在这些桶内对向量打分。启用精排时,factor 控制从粗排阶段多取多少候选 再送入精排器重打分。

此外还有一种 GNO-IMI 策略(partition_strategy_type: "gno_imi"),它把空间按两组 正交中心划分(first_order_buckets_count × second_order_buckets_count),在超大规模 语料上能得到更精细的分区。

快速开始

#include <vsag/vsag.h>

std::string params = R"({
    "dtype": "float32",
    "metric_type": "l2",
    "dim": 128,
    "index_param": {
        "buckets_count": 256,
        "base_quantization_type": "sq8",
        "partition_strategy_type": "ivf",
        "ivf_train_type": "kmeans"
    }
})";
auto index = vsag::Factory::CreateIndex("ivf", params).value();

// 构建索引。
auto base = vsag::Dataset::Make();
base->NumElements(n)->Dim(128)->Ids(ids)->Float32Vectors(data)->Owner(false);
index->Build(base);

// 执行检索。
auto query = vsag::Dataset::Make();
query->NumElements(1)->Dim(128)->Float32Vectors(q)->Owner(false);
auto result = index->KnnSearch(
    query, /*topk=*/10,
    R"({"ivf": {"scan_buckets_count": 16}})").value();

构建参数

构建参数放在 index_param 下。完整列表请见 索引参数 及仓库中的 docs/ivf.md

参数类型默认值说明
partition_strategy_typestring"ivf"分桶策略:ivf(单层)或 gno_imi(双层正交)
buckets_countint10倒排列表数量(ivf 策略下生效)
first_order_buckets_countint10第一级桶数(gno_imi 策略下生效)
second_order_buckets_countint10第二级桶数(gno_imi 策略下生效)
ivf_train_typestring"kmeans"中心训练方式:kmeansrandom
base_quantization_typestring"fp32"fp32fp16bf16sq8sq4sq8_uniformsq4_uniformpqpqfsrabitq —— 各量化器细节见量化章节
base_pq_dimint1PQ 子空间数(pq / pqfs 时必填)
use_reorderboolfalse是否保留高精度副本用于精排
precise_quantization_typestring"fp32"精排量化类型(use_reorder: true 时使用)
base_io_typestring"memory_io"粗排向量的存储后端
precise_io_typestring"block_memory_io"精排向量的存储后端(memory_ioblock_memory_iommap_iobuffer_ioasync_ioreader_io
precise_file_pathstring""当精排 IO 为磁盘后端时的文件路径

buckets_count 的经验值一般为 sqrt(N) ~ 4 * sqrt(N),其中 N 是语料规模。

检索参数

检索参数放在 ivf 子对象下:

参数类型默认值说明
scan_buckets_countint—(必填)每次查询扫描的桶数,须 ≤ buckets_count
factorfloat2.0启用精排时,粗排阶段会预取 factor * topk 个候选再重打分
enable_reorderbooltrue即使索引构建时启用了 reorder,也可以在单次请求里设为 false 跳过最终精排
parallelismint1单次查询内扫描桶时使用的线程数
timeout_msdouble+∞单次查询最长耗时(毫秒),超时会返回当前的部分结果
auto result = index->KnnSearch(
    query, topk,
    R"({"ivf": {"scan_buckets_count": 32, "factor": 2.0, "parallelism": 4}})").value();
auto fast_result = index->KnnSearch(
    query, topk,
    R"({"ivf": {"scan_buckets_count": 32, "factor": 2.0, "enable_reorder": false}})").value();

何时选择 IVF

  • 超大规模语料(数亿及以上),工作集无法完全放入内存。
  • 对每秒查询数(QPS)敏感、对单次延迟相对宽松的批量或高吞吐场景。
  • 内存紧张的部署,可使用激进的量化方案(sq8sq4_uniformpqpqfs)配合 use_reorder 恢复召回。
  • 对切片友好的部署:桶天然映射到分片或磁盘块。

对于延迟敏感、要求高召回的稠密 embedding 场景,请优先比较 HGraph

相关文档