乘积量化(Product Quantization, PQ)把高维向量切成若干低维子段、各训一个小码本,用「笛卡尔积」以指数级组合码矢——存储从指数爆炸降到线性,是 矢量量化(VQ):从码本设计到率失真理论 逃出维度诅咒的关键一步,也是 FAISS、Milvus 等向量数据库的主力压缩方案。
向量数据库的场景把维度诅咒推到极致:亿级库向量、每条 512 维 float32 占 2KB,暴力检索不可行,连存都吃力。精确最近邻在高维没有免费午餐,工程答案只有一条——量化压缩 + 近似检索。PQ(Jegou et al., 2011)就是这条路线的基石。
乘积量化:笛卡尔积换指数码本
把 维向量均分为 段 维子矢量,每个子空间独立训一个 256 大小的码本。整向量码本是子码本的笛卡尔积,总码字数 ,但只需存储 个 维码矢:
数字很直观:128 维向量切成 8 段、每段 256 码矢,编码后只剩 8 字节(每段 1 字节索引)——对比 float32 的 512 字节压缩 64 倍。码本大小取 256 不是巧合:正好一个 uint8,硬件亲和。子空间维度低(8–32 维),每个子码本的 LBG 训练完全可行,维度诅咒被分解掉了。
import numpy as np
from sklearn.cluster import KMeans
class PQ:
def __init__(self, m=8, k=256):
self.m, self.k = m, k # m 个子空间,各 256 码矢 → 每向量 m 字节
def fit(self, X):
d = X.shape[1] // self.m
self.books = []
for i in range(self.m): # 子空间各自跑 LBG / K-means
sub = X[:, i*d:(i+1)*d]
self.books.append(
KMeans(n_clusters=self.k, n_init=1)
.fit(sub).cluster_centers_)
def encode(self, X):
d = X.shape[1] // self.m
codes = np.empty((len(X), self.m), dtype=np.uint8)
for i in range(self.m):
sub = X[:, i*d:(i+1)*d]
codes[:, i] = ((sub[:, None] - self.books[i][None]) ** 2
.sum(-1)).argmin(1) # 最近码矢索引即 1 字节编码
return codes
def decode(self, codes):
return np.concatenate(
[self.books[i][codes[:, i]] for i in range(self.m)], axis=1)检索用非对称距离计算(ADC):查询向量不量化,库向量只有 8 字节编码。离线对每个子空间预计算「查询子矢量 × 256 码矢」的距离表( 项),在线距离退化为 8 次查表求和——单向量距离计算从 浮点乘加变成 8 次内存访问。百万级候选的精排因此变得极便宜。
残差量化与加性量化
残差量化(RQ)走另一条分解路线:量化误差再量化。第一级 后,残差 交给第二级,逐级递归,最终 。每级码本可以很小,增加级数即可任意提升精度,还天然支持渐进式传输(先发粗码、再发残差)。RQ 在 2021 年后迎来意外回春:SoundStream、EnCodec 等音频 tokenizer 靠级联 RQ 把连续音频压成离散 token 序列,成为音乐生成模型的前端——一条 1980 年代的压缩技术喂饱了 2020 年代的生成模型。
加性量化(AQ)是 RQ 的推广:各级码本不再逐级贪心求解,而是联合优化「选哪 个码矢相加」。率失真性能优于 RQ,训练与编码复杂度更高,常用于极限压缩比的检索场景。
三者一脉相承:PQ 是「空间维度的笛卡尔分解」,RQ 是「精度的级联分解」,AQ 是统一两者的框架(PQ 可视为 AQ 各级限定在互斥子空间的特例)。
工程实践
码本质量的实践要点集中在数据分布上:训练集必须代表真实查询分布,分布漂移(语料更新、模型换代)后码本要重训;入库前归一化或白化对余弦检索场景常有立竿见影的收益。
实际系统几乎不裸用 PQ,而是 IVF-PQ 两级结构:粗量化器(少量码矢的 VQ)把空间切成 Voronoi 蜂窝,查询只扫最近几个蜂窝,蜂窝内再用 PQ 精排——粗排剪枝 + 精排查表,recall 与延迟才够用。GPU 化的查表、残差重排(rerank with original vectors)是常见的下一步优化。FAISS 的 IVF<PQ> 索引就是这套组合的直接实现。
选型经验一句话: 控制压缩比( 越小压得越狠、召回越伤),预算内优先保 recall,用 rerank 找回精度——压缩丢的排序信息,用少量原始向量重排补回来最划算。