跳转到内容

4.4 SQ(Scalar Quantization):标量量化

SQ 比 PQ 简单得多——4 倍压缩,精度损失小,不需要训练(大部分模式)


这一节在讲什么?

PQ 和 OPQ 通过子空间划分和聚类来实现高压缩比,但实现复杂、需要训练。SQ(Scalar Quantization)走了另一条路——它把每个 float32 维度直接映射到 int8,压缩比只有 4 倍,但实现简单、精度损失小、大部分模式不需要训练。这一节我们快速过一遍 FAISS 的 SQ 实现和 6 种量化模式。


SQ 原理

SQ 的原理非常简单——找到每个维度的最小值和最大值,然后把 float32 线性映射到 int8 的范围(-128~127):

SQ 量化过程:

  原始向量(float32):
  [0.12, -0.34, 0.56, ..., 0.78]  → 768 × 4 = 3072 字节

  每个维度独立量化:
  维度1: 范围 [-1.0, 1.0] → 0.12 映射到 int8 值
  维度2: 范围 [-0.8, 0.8] → -0.34 映射到 int8 值
  ...

  量化后(int8):
  [31, -87, 143, ..., 200]         → 768 × 1 = 768 字节

  压缩比:4 倍

FAISS 的 SQ 实现

python
import faiss
import numpy as np

d = 768
n = 1000000
vectors = np.random.rand(n, d).astype('float32')

# IndexScalarQuantizer——6 种量化模式
# QT_8bit:每个维度用 8 bit 量化(最常用)
index = faiss.IndexScalarQuantizer(d, faiss.ScalarQuantizer.QT_8bit)

# 不需要训练!——量化参数从数据统计得到
index.add(vectors)

query = np.random.rand(1, d).astype('float32')
distances, indices = index.search(query, k=5)

6 种量化模式

模式每维度比特数压缩比需要训练精度
QT_8bit8 bit4 倍
QT_6bit6 bit5.3 倍
QT_4bit4 bit8 倍较差
QT_8bit_uniform8 bit4 倍好(均匀量化)
QT_fp1616 bit2 倍极好
QT_8bit_direct8 bit4 倍取决于输入

最常用的是 QT_8bit——它不需要训练,4 倍压缩,精度损失小。QT_fp16 把 float32 压缩成 float16,只有 2 倍压缩但精度几乎无损。

IndexIVFScalarQuantizer:IVF + SQ

python
nlist = 1000
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFScalarQuantizer(
    quantizer, d, nlist,
    faiss.ScalarQuantizer.QT_8bit
)

index.train(vectors[:50000])
index.add(vectors)
index.nprobe = 32

query = np.random.rand(1, d).astype('float32')
distances, indices = index.search(query, k=5)

SQ vs PQ

维度SQPQ
压缩比4 倍(QT_8bit)8~64 倍
精度损失
是否需要训练大部分模式不需要需要
实现复杂度
距离计算速度快(查表法)
推荐场景精度优先、压缩比要求不高内存有限、需要高压缩比

常见误区:SQ 不需要训练

大部分 SQ 模式确实不需要训练——因为量化参数(每个维度的最小值和最大值)可以从数据统计得到。但 QT_8bit_uniform 模式需要训练——它使用全局统一的量化范围,需要通过训练来确定最优的范围值。如果你不确定该用哪种模式,就用 QT_8bit——它不需要训练,效果也不错。


小结

这一节我们聊了 SQ 标量量化:把 float32 线性映射到 int8,压缩比 4 倍,精度损失小,大部分模式不需要训练。FAISS 提供 6 种 SQ 模式,最常用的是 QT_8bit。SQ 适合精度优先、压缩比要求不高的场景;PQ 适合内存有限、需要高压缩比的场景。下一节开始我们进入第 5 章,探索 FAISS 的 GPU 加速和高级特性。

基于 MIT 许可发布