向量数据库的报价单背后,大头往往不是软件,是内存。算一笔最朴素的账:一条 768 维 float32 向量是 768 × 4 = 3072 字节,一千万条就是 30GB——还没算 HNSW 的边表(上一篇讲过,M=16 时边表还要再添三成)。而很多场景的全文语料,原始文档本身可能才几个 GB。存向量比存原文贵,这是向量检索时代的普遍现实,压缩因此不是可选优化,而是规模化前的必修课。这篇讲三条压缩路线各自的原理、代价,以及行业通用的补偿手法。
三条路线:降精度、降基数、降成一位
向量的信息冗余来自三个方向,对应三种压缩思路。
路线一:标量量化——每个分量用更少的比特。 float32 存每个维度用 32 bit,但嵌入分量的动态范围其实很小。float16 直接砍半(体积减半,几乎无损,是所有向量库都支持的无脑第一步);int8 标量量化把每个维度线性映射到 256 个整数档位(体积降到四分之一,召回损失通常在个位数百分比);更激进的 4 bit 量化(如 GGUF 生态里的 Q4 系列)能把体积压到八分之一,但召回损失开始明显,需要实测。标量量化的特点是简单、逐维度独立、与任何索引兼容,几乎是无风险的默认动作。
路线二:二值化——每个维度压到 1 bit。 把每个分量按符号取 0/1(或按阈值二值化),向量变成比特串,距离用 Hamming 距离(异或 + popcount)计算,CPU 有原生指令,快得离谱,体积降到 float32 的三十二分之一。代价是信息损失严重,单靠二值向量的召回率对很多模型来说不可接受。它几乎总是以「粗排」身份出现:用二值向量海选一个过采样的候选池,再用原始(或 int8)向量重打分精修——这个「两段式」手法下文还会反复出现。
路线三:PQ(乘积量化)——降「基数」而不是降比特。 前两条路线还是在给每个分量省比特,PQ(Jégou 等人 2011 年提出)的思路完全不同:把 d 维向量切成 m 段,每段单独做聚类。比如 768 维切成 96 段、每段 8 维,对每段的所有子向量跑 k-means 聚成 256 类,之后每个子向量只需记它的类别编号(一个字节)。原始的 96 × 8 × 4 = 3072 字节,压缩后是 96 × 1 = 96 字节——32 倍压缩。256 个聚类中心组成的「码本」是这段空间里的代表点集合,每个向量的存储从「完整坐标」退化成「96 个码本条目的编号序列」。
PQ 的距离计算与误差从哪来
查询时怎么比距离?逐段用查询子向量与该段码本里 256 个中心算距离,预计算成一张 96 × 256 的小表;之后任意一条库内向量的近似距离只需查 96 次表再求和(ADC,Asymmetric Distance Computation)。这个「查表求和」让 PQ 不仅能省内存,扫描速度也常常优于原始向量。
但误差也随之而来:每个子向量被它所在段的聚类中心替身了,替身与真身的偏差是结构性的。段切得越多、每段聚类越细(m 和每段类数越大),误差越小、内存越大——PQ 的参数本质就是在「替身失真」和「内存预算」之间滑动。对高召回敏感的场景,还有 IVF-PQ 这类混合结构:先用倒排文件把库粗分成若干桶,查询时只扫最近的几个桶,桶内再用 PQ——Faiss 里最常见的组合拳。
标准补偿手法:粗排压缩,精排原始
三种路线有一个共同的补丁,值得单独强调:两段式检索。压缩向量的职责是「别漏」,用压缩距离海选一个比 top_k 大得多的候选池(过采样,比如 top_k 的 5~10 倍);原始向量的职责是「排准」,对候选池用真实距离重打分,选出最终 top_k。这样召回率向原始向量看齐,延迟只多一次小规模精排(几百条向量的点积,微秒级),内存却省了数倍到数十倍。Faiss、Milvus、Qdrant 等主流引擎的 PQ/量化选项都内置了这一层(常叫 refine / rerank with original vectors),开启它几乎是压缩场景的默认动作。
| 方案 | 体积 | 召回损失 | 适用场景 |
|---|---|---|---|
| float32(基线) | 1× | — | 小规模、预算充足 |
| float16 | 0.5× | 几乎为零 | 无脑第一步,人人可用 |
| int8 标量量化 | 0.25× | 个位数百分比 | 通用首选,配合精排基本无感 |
| 二值 + 精排 | 1/32× | 取决于过采样倍数 | 超大规模粗排、内存极限场景 |
| PQ(96 段×256 类) | 1/32× | 明显,需精排补偿 | 亿级规模,内存优先 |
PQ 的进阶形态:OPQ 与残差量化
PQ 有两个常见的强化版,选型时值得知道。OPQ(Optimized Product Quantization)在切段聚类之前,先学一个旋转矩阵把向量空间转一转——让每个段的方差分布尽量均匀,聚类替身的失真随之减小。原理直觉:原始嵌入空间的各维度方差高度不均(某些维度承载了大部分信息量),直接切段会把「高信息维度」和「低信息维度」粗暴分家,旋转把信息摊平到各段,每段的聚类才都「有话可说」。OPQ 的成本只是一次离线的旋转学习加入库前的一次矩阵乘法,压缩率不变、召回改善明显,是开 PQ 时的常规首选。残差量化(RQ)走另一条路:不做并行切段,而是逐级量化残差——第一级码本近似原向量,第二级码本近似「原向量减第一级近似」的残差,依此类推,多级残差相加逼近原向量。同等压缩率下精度通常优于单级 PQ,代价是距离计算变成多级查表求和、实现复杂度更高,Faiss 里 IndexResidualQuantization 是它的落地形态。
参数怎么定:段数与类数的算术
PQ 的两个核心数字有明确的算术关系。每段类数几乎总是 256——恰好一个字节,码本查表与 SIMD 友好;256 个中心的聚类误差对多数分布也已够小。段数 m 由内存预算倒推:目标压缩率除以「原始字节数 ÷ 256」,即 m ≈ 原始字节数 / 目标字节数;768 维 float32 想压到 128 字节,m 就是 96。但 m 不能无限大:每段维度数 d/m 降到 4 以下后,段内聚类开始欠拟合(8 维空间里的 256 个中心已经很难铺匀),失真反而抬头——实践中每段维度保持在 6~16 之间。验证手段没有捷径:几组 (m, 段类数) 组合各建一个索引,评测集跑 recall@k 对比,选满足召回下限的最激进压缩——这套「压缩-验证」的流程,下一篇《评测集构建》的尺子同样是它的前置条件。
与检索流水线的叠加顺序
压缩方案与前面几篇的组件怎么叠?一个亿级语料参考架构:入库时 OPQ 训练旋转矩阵 → PQ 压缩向量进 IVF-PQ 索引做首跳粗排 → 过采样 10 倍候选 → 原始 float16 向量精排 → reranker 语义重排 → top-k 出结果。每一层的账是清楚的:PQ 层把内存压到三十分之一、IVF 层把扫描量压到百分之一、精排层把近似误差补回来、rerank 层把语义质量提上去——压缩管「存得起」,其余环节管「排得准」,职责分离后各层参数可以独立迭代。反过来的叠加要避免:不要用压缩向量做 rerank 的打分输入(近似距离会污染精排精度),精排永远用最保真的向量。
实践顺序与验证
给一个可直接照做的顺序:先 float16(零风险),量不够再 int8(配精排),还不够再 PQ 或二值(务必开 refine,过采样倍数宁大勿小)。每降一级都要用真实查询重测 recall@k——《评测集构建》的尺子在这里再次登场,压缩前后召回率掉多少,必须用同一组查询量出来,而不是看引擎文档里「召回损失很小」的营销措辞。
最后一个常被忽略的账:压缩省的是内存,换来的可能是重嵌入更贵。有些团队为了省内存选了低维模型或激进压缩,结果召回不达标,反复调参的时间成本远超加内存的钱。先算业务规模:百万级以下根本不用压缩,老老实实 float32 + HNSW;压缩是千万级以上才需要认真对待的课题。
一个反直觉的提醒:先看数据规模再选技术
这篇的压缩技术要按规模分档才不吃亏。十万级以下:float32 加 HNSW,内存几个 GB 以内,压缩是过度工程——省下的内存钱抵不上验证召回损失的人力。百万级:float16 起步即可(砍一半、零风险),int8 作为可选项。千万级:int8 加精排是主力方案,PQ 备用。亿级:IVF-PQ 或二值粗排加精排,认真做召回-内存曲线验证。跳档的代价要清楚:百万级就上 PQ 的团队,往往在码本训练、精排管道、召回验证上花掉的工程时间,超过了加几条内存的钱——压缩技术解决的是「存不起」的问题,没到存不起的规模,最好的压缩就是不压缩。先算账(数据规模 × 单向量字节 × 引擎开销系数),再选技术档位,这一篇的方法论才用在对的地方。
小结
向量压缩的三条路线各有分工:标量量化是无脑安全的第一步,二值化是极端场景的粗排利器,PQ 用「切段聚类 + 码本编号」在亿级规模上实现数十倍压缩。它们共享同一个补偿范式——压缩向量粗排、原始向量精排。记住这句话:压缩改变的是距离计算的精度,不是召回的语义;只要精排兜底,召回可以基本无损,而内存账能砍掉一个数量级。
参考资料
- Jégou et al., Product Quantization for Nearest Neighbor Search (IEEE TPAMI 2011) —— PQ 与 ADC 的原始论文
- Faiss Wiki: Faiss indexes —— IVF-PQ 等索引组合的工程说明
- Qdrant: Quantization —— 标量量化与二值量化的引擎侧实现与 refine 选项
- 本系列前篇:《向量数据库选型指南:从暴力搜索到 HNSW》《HNSW 调参实战》
读者留言
COMMENTS 暂无还没有留言,来说第一句?