PQ:把高维向量编码成短码
把向量拆成多组子向量,用各自码本中的编号表示每组子向量。
相关概念
工程问题#
向量库规模增长后,原始浮点数据难以全部装入内存,扫描时的数据搬运也昂贵。
实际实现#
ProductQuantizer 将 d 维分成 M 组,为各组训练码本并编码;查询预计算子向量到码字的距离表,再查表累加近似距离。
处理流程#
- 按子空间训练多个码本
- 将每段向量编码成码字编号
- 为查询计算子空间距离表
- 累加查表距离并挑选候选
源码片段#
固定版本 · 原文摘录faiss/impl/ProductQuantizer.cppv1.9.0 · L429–L440
429void ProductQuantizer::compute_distance_table(const float* x, float* dis_table)430 const {431 if (transposed_centroids.empty()) {432 // use regular version433 for (size_t m = 0; m < M; m++) {434 fvec_L2sqr_ny(435 dis_table + m * ksub,436 x + m * dsub,437 get_centroids(m, 0),438 dsub,439 ksub);440 }ProductQuantizer::compute_distance_table
遍历 M 个子空间,将查询子向量与该子空间的 ksub 个码字计算平方 L2 距离,写入各自的查表区段。后续按数据库向量保存的码字编号取表项并累加。本片段是未启用转置码本的分支,不是逐浮点分量做标量量化,也不是完整检索流程。
CPPMIT
收益与取舍#
工程分析带来的收益
- 每向量编码约占 M×nbits/8 字节
- 查表计算降低扫描原始浮点数据的需求
付出的代价
- 有损量化会改变距离与结果排序
- 码本训练和查询距离表也消耗时间与内存
可比较的选择
保留原始向量做重排可改善候选排序精度,但增加原始数据存储与读取;不能找回未进入候选集的结果。
版本与适用范围#
字节公式不含码本、ID 和外层索引;此实现要求 d 能被 M 整除。PQ 本身是编码方法,可与 IVF 等检索结构组合。
参考来源#
源码、官方文档与原始论文01
ProductQuantizer 训练、编码与查表
源码v1.9.0faiss/impl/ProductQuantizer.cpp · ProductQuantizer::train / compute_distance_table / search