已关闭
[RFC]: A3支持IVFPQ #19
xiangjie10创建于 7月23日关闭于 27 天前
7月23日 添加了label:rfc
7月23日 添加了label:triaged
7月23日 修改了issue 的描述
7月23日 修改了issue 的描述
7月24日 修改了issue 的描述
7月24日 修改了issue 的描述
7月24日 修改了issue 的描述
7月24日 修改了issue 的描述
7月24日 修改了issue 的描述
7月27日 关联了里程碑:MindSDK 26.1.0
7月27日 移除了里程碑
7月27日 关联了里程碑:MindSDK 26.2.0
27 天前 关闭了 issue
27 天前 添加了label:resolved
4 天前 issue状态由 TODO 改变为 DONE


IVFPQ 算法 A3 支持技术方案设计 (RFC)
状态 (Status): Draft
作者 (Authors): @NPU_Adaptation_Team
创建日期 (Created): 2026-07-21
更新日期 (Updated): 2026-07-22
1. 概述
1.1 简介
本提案旨在在 Ascend A3 NPU 平台上开发 IVFPQ 与 OPQ-IVFPQ 两种近似检索算法,覆盖训练、索引构建、入库、检索的完整生命周期。
主要工作包括:
1.2 动机
背景
业务规模扩展至 2 亿条向量,需要将开源 Faiss 的检索能力适配至 Ascend A3 NPU 平台以获得硬件加速能力。需同时支持两种量化方案:
dsub=4)dsub=32)痛点
价值
1.3 目标
目标
nlist = 262144与nlist = 524288两种聚类粒度METRIC_INNER_PRODUCT)NpuIndexIVFPQ对标GpuIndexIVFPQ,OPQ 由用户在 CPU 端手动管理)2. 用例分析
2.1 功能需求
核心功能
参数规格
basedimnlistm(M)nbitsksub=256dsubnprobeMETRIC_INNER_PRODUCT(IP)功能要求
OPQMatrix::apply手动完成),与 faiss GPU 方案一致2.2 性能需求
精度验收标准
精度测试方法(对齐 faiss 标准 benchmark ):
recall@1 / recall@10 / recall@100IndexIVFPQ基线对比验收指标:
性能验收标准
性能测试方法:
不同batch size下(1,2,4,8,16,32,64),测试A3 16die 与 H100 2die 的检索耗时对比
验收指标:
验收场景矩阵:
多卡支持:系统设计支持多卡扩展
2.3 DFX 要求
可靠性
可测试性
faiss/npu/test/TestNpuIVFPQ.cpp单元测试faiss/npu/test/scripts/test_ivfpq_accuracy.pyCPU vs NPU 精度对比测试兼容性
NpuIndexIVFPQ对标GpuIndexIVFPQ)3. 方案设计
3.1 总体方案
设计思路
参考 faiss GPU
GpuIndexIVFPQ的分层架构,在 NPU 平台上开发NpuIndexIVFPQ,实现 IVFPQ 算法的完整生命周期(训练、构建、入库、检索)。核心设计要点:三阶段检索 Pipeline:L1 Coarse Quantization → L2 Subspace Distance → L3 PQ Scan + TopK。
OPQ 集成:与 faiss GPU 方案一致,OPQ 旋转由用户在 CPU 端手动管理(训练
OPQMatrix+apply旋转),NPU IVFPQ 只接收旋转后的向量,无需感知 OPQ 的存在。NPU 侧无需额外开发 OPQ 相关算子或 Cloner。技术架构
核心流程
训练阶段
训练分为粗量化训练和 PQ 码本训练两步,均通过
trainKMeansOnNpu在 NPU 上执行 K-Means:OPQ-IVFPQ 方案(1024 维)在训练前需先在 CPU 训练 OPQ 旋转矩阵,再通过
OPQMatrix::apply在 CPU 应用旋转,之后执行上述两步训练。graph TD A[输入训练数据] --> B{是否启用 OPQ?} B -->|是| C[CPU 训练 OPQMatrix<br/>faiss::OPQMatrix::train] B -->|否| D[粗量化训练<br/>NPU K-Means<br/>生成 nlist 个聚类中心] C --> E[CPU apply 应用旋转<br/>x_rot = x · R] E --> D D --> F[PQ 码本训练<br/>NPU K-Means × M 个子空间<br/>ksub=256] F --> G[保存聚类中心 + PQ 码本] style C fill:#fff3e0 style E fill:#e3f2fd style D fill:#e8f5e9 style F fill:#e8f5e9入库阶段
入库采用分页策略(
addPaged_),大批量添加按 256 MiB 分页,每页执行三步:addL1_):计算每个向量所属聚类(NPU 或 CPU)addL2_):按子空间量化为 M 字节编码(CPU,使用训练好的码本查表)copyVectorToDevice_):将 PQ codes + id 上传到 NPU 设备OPQ-IVFPQ 方案在 L1 之前需先通过
OPQMatrix::apply在 CPU 应用旋转。graph TD A[输入向量] --> B{是否启用 OPQ?} B -->|是| C[CPU apply 旋转] B -->|否| D[L1: 计算所属聚类<br/>addL1_] C --> D D --> E[L2: PQ 编码<br/>addL2_ CPU 查表] E --> F[写入倒排列表<br/>copyVectorToDevice_]检索阶段
检索执行 L1 → L2 → L3 三阶段 Pipeline:
OPQ-IVFPQ 方案在 L1 之前需先通过
OPQMatrix::apply在 CPU 应用旋转。3.2 技术选型
不涉及
3.3 功能与性能设计
功能实现方案
OPQ 旋转矩阵集成
与 faiss GPU 方案一致,NPU 不单独实现 OPQ,也不通过
IndexPreTransform包装。OPQ 旋转由用户在 CPU 端手动管理,NPU IVFPQ 只接收旋转后的向量,无需感知 OPQ 的存在。流程与 GPU 一致:
OPQMatrix→ CPUapply旋转训练数据 → 训练NpuIndexIVFPQ(使用旋转后的数据)apply旋转入库向量 →NpuIndexIVFPQ::add(旋转后的向量)apply旋转查询向量 →NpuIndexIVFPQ::search(旋转后的向量)// 1024 维 OPQ-IVFPQ 方案 faiss::OPQMatrix opq(1024, 32, 1024); opq.train(nt, trainData); // CPU 训练 // 对训练数据应用旋转 std::vector<float> trainDataRot(nt * 1024); opq.apply(nt, trainData, trainDataRot.data()); // 训练 NPU IVFPQ(使用旋转后的数据) faiss::npu::NpuIndexIVFPQConfig config; faiss::npu::NpuIndexIVFPQ npuIvfpq( provider, 1024, 524288, 32, 8, faiss::METRIC_INNER_PRODUCT, config); npuIvfpq.train(nt, trainDataRot.data()); // 入库:先旋转再添加 std::vector<float> xbRot(nb * 1024); opq.apply(nb, xb, xbRot.data()); npuIvfpq.add(nb, xbRot.data()); // 检索:先旋转再搜索 std::vector<float> xqRot(nq * 1024); opq.apply(nq, xq, xqRot.data()); npuIvfpq.search(nq, xqRot.data(), k, distances, labels);影响范围
faiss/npu/NpuIndexIVFPQ.hfaiss/npu/impl/IVFPQ.hfaiss/npu/impl/IVFPQ.cppfaiss/npu/ops/faiss/npu/test/TestNpuIVFPQ.cppfaiss/npu/test/scripts/test_ivfpq_accuracy.py3.4 安全隐私与DFX设计
安全隐私
兼容性
NpuIndexIVFPQ对标GpuIndexIVFPQ)可维护性
可测试性
可靠性
FaissException3.5 编程与调用设计
3.5.1 编程模型基本设计
开发环境
FAISS_ENABLE_NPUoption)开发约束
OPQMatrix)可验收设计
3.5.2 接口定义与设计
3.5.2.1 NpuIndexIVFPQConfig
NpuIndexIVFConfig。struct NpuIndexIVFPQConfig : public NpuIndexIVFConfig { bool useFloat16LookupTables = false; bool usePrecomputedTables = false; bool interleavedLayout = false; bool useMMCodeDistance = false; bool useNpuTrain = false; ClusteringParameters cp; std::vector<int32_t> trainingDevices; bool useDistributedCoarse = false; int trainSamplesPerList = 40; int maxTrainSamples = 10000000; };3.5.2.2 NpuIndexIVFPQ 构造与训练
// 构造函数 1:从已训练的 CPU IndexIVFPQ 拷贝 NpuIndexIVFPQ( NpuResourcesProvider* provider, const faiss::IndexIVFPQ* index, NpuIndexIVFPQConfig config = NpuIndexIVFPQConfig()); // 构造函数 2:创建空索引,使用默认 Flat 量化器 NpuIndexIVFPQ( NpuResourcesProvider* provider, int dims, idx_t nlist, idx_t subQuantizers, idx_t bitsPerCode, faiss::MetricType metric = faiss::METRIC_L2, NpuIndexIVFPQConfig config = NpuIndexIVFPQConfig()); // 构造函数 3:创建空索引,使用用户提供的粗量化器 NpuIndexIVFPQ( NpuResourcesProvider* provider, Index* coarseQuantizer, int dims, idx_t nlist, idx_t subQuantizers, idx_t bitsPerCode, faiss::MetricType metric = faiss::METRIC_L2, NpuIndexIVFPQConfig config = NpuIndexIVFPQConfig()); void train(idx_t n, const float* x) override;异常处理:
FaissExceptionFaissException调用参考代码:
// 128 维 IVFPQ 方案 faiss::npu::NpuIndexIVFPQConfig config; config.useNpuTrain = true; auto provider = std::make_shared<faiss::npu::StandardNpuResources>(); faiss::npu::NpuIndexIVFPQ index( provider.get(), 128, 524288, 32, 8, faiss::METRIC_INNER_PRODUCT, config); index.train(trainNum, trainData); index.add_with_ids(ntotal, baseData, ids); index.nprobe = 128; index.search(nq, queryData, 100, distances, labels);// CPU/NPU 数据拷贝 void copyFrom(const faiss::IndexIVFPQ* index); void copyTo(faiss::IndexIVFPQ* index) const; // 属性查询 int getNumSubQuantizers() const; int getBitsPerCode() const; int getCentroidsPerSubQuantizer() const; size_t getCodeSize() const; // 预分配粗量化结果的检索 void search_preassigned( idx_t n, const float* x, idx_t k, const idx_t* assign, const float* centroid_dis, float* distances, idx_t* labels, bool store_pairs, const SearchParametersIVF* params = nullptr, IndexIVFStats* stats = nullptr) const override;3.5.2.3 OPQ-IVFPQ(1024 维)
// 1024 维 OPQ-IVFPQ 方案 int dim = 1024; int nlist = 524288; int M = 32; // Step 1: CPU 训练 OPQ 旋转矩阵 faiss::OPQMatrix opq(dim, M, dim); opq.train(trainNum, trainData); // Step 2: 对训练数据应用旋转 std::vector<float> trainDataRot(trainNum * dim); opq.apply(trainNum, trainData, trainDataRot.data()); // Step 3: 构造并训练 NPU IVFPQ 索引(使用旋转后数据) auto provider = std::make_shared<faiss::npu::StandardNpuResources>(); faiss::npu::NpuIndexIVFPQConfig config; config.useNpuTrain = true; faiss::npu::NpuIndexIVFPQ npuIvfpq( provider.get(), dim, nlist, M, 8, faiss::METRIC_INNER_PRODUCT, config); npuIvfpq.train(trainNum, trainDataRot.data()); // Step 4: 入库(先旋转再添加) std::vector<float> baseDataRot(ntotal * dim); opq.apply(ntotal, baseData, baseDataRot.data()); npuIvfpq.add_with_ids(ntotal, baseDataRot.data(), ids); // Step 5: 检索(先旋转再搜索) npuIvfpq.nprobe = 128; std::vector<float> queryDataRot(nq * dim); opq.apply(nq, queryData, queryDataRot.data()); npuIvfpq.search(nq, queryDataRot.data(), 100, distances, labels);3.5.3 编程手册设计
需要在《faiss-npu 用户指南》中新增以下章节:
IVFPQ 大规模库使用指南(128 维)
OPQ-IVFPQ 使用指南(1024 维)
常见问题与解决方案
4. 缺点和风险
4.1 潜在风险
性能风险
复杂度提升
4.2 负面影响
对 faiss 主干的影响
faiss/npu/目录下独立开发4.3 实现成本
维护成本
4.4 应对措施
dsub=32的子空间距离算子性能,若不达标则新增专用算子(列入未解决问题)5. 现有技术
faiss 开源实现
IndexIVFPQ:本提案 NPU 实现的精度基线,复用其OPQMatrix、ProductQuantizer、Clustering等核心组件GpuIndexIVFPQ:架构参考,本提案 NPU 实现参照其分层设计(用户层 → 实现层 → 算子层 → 资源层)benchs/bench_all_ivf/bench_all_ivf.py提供标准的精度/性能评估方法与公开 baseline(Indexing 1G vectors wiki)可复用的 faiss 组件
OPQMatrix::trainProductQuantizerClusteringOPQMatrixGpuIndexIVFPQ架构借鉴与差异
OPQMatrix的训练和应用方式(与 GPU 一致,用户手动管理)、faiss GPU 的分层架构设计、faiss benchmark 的ms_per_query+recall@k评估方法附录
参考资料
benchs/bench_all_ivf/bench_all_ivf.pyfaiss/VectorTransform.hfaiss/gpu/GpuIndexIVFPQ.h术语表
ksub = 2^nbitsdsub = dim / M文档更新计划