提起向量, 经常会看到向量压缩、量化等术语. 这些都是为了提升查询效率、节省算力或内存/存储空间, 在不牺牲太多召回率的情况下催生出来的技术.
但是向量“压缩”与“量化”是有明显区别的.
我个人粗浅的理解:
1、矢量缩放(或 归一化): 例如150的总分的成绩, 映射到100的总分的区间内. 一定不会溢出. 例如150对应的是100, 75对应的是50. 因为值变了语义会发生变化, 如果值本身有意义(例如温度), 则可能影响查询结果. 但如果值没有意义, 是求夹角相似, 角度不会变化. 所以这种转换适合求cosin相似度的算法.
2、类型转换(值不变, 压缩存储空间): float32 转换为 float16. 可能存在溢出. 举个例子, 就像快递公司给物品打包, 快递公司为了省成本把箱子规格全部缩小一半, 如果打包的商品超过了最大的箱子那就放不下了. 类型转换是不改变原始值(可能仅损失一些精度)的压缩, 仅适合压缩后值不会溢出报错的情况, 如果压缩后原始值失精后依旧无法表达, 则不适合这种压缩方式. 当然也有粗暴的处理方法: 溢出就取上下边界, 例如最小值溢出则表示为无穷小, 最大值溢出则表示为无穷大.
3、二值量化: 大于0映射为1, 小于等于0映射为0.
4、RabitQ量化: 有点复杂, 参考论文
以上是个人的浅显理解, 下面看看 Qwen3 的补充.
你的总结已经很好地抓住了向量量化中几类核心思想的精髓。下面我将:
✅ 正确之处:
⚠️ 需补充/修正:
✅ 建议术语:L2 归一化 或 方向保留缩放,而非“矢量缩放”。
✅ 正确之处:
⚠️ 需修正/补充:
✅ 建议术语:浮点精度降级(FP32 → FP16/FP8)。
✅ 正确之处:
⚠️ 需补充:
或通过优化求得
✅ 建议术语:1-bit 量化 或 符号量化(Sign Quantization)。
虽然 “RaBitQ” 并非标准术语,但结合命名习惯(Ra = Range-aware / Rational / Random + Bit + Q),并参考 2023–2025 年主流低比特量化工作(尤其是 BitNet 和 BitNet.b1.58),我们可以将其理解为:
RaBitQ ≈ Range-aware 1-bit / 1.58-bit Quantization —— 一种带自适应缩放因子的极低比特量化方法,专为大语言模型(LLM)设计。
组件 | 说明 |
|---|---|
量化粒度 | 按通道(per-channel)或按组(per-group)量化,避免全局缩放损失信息 |
比特宽度 | 1-bit({−1, +1})或 1.58-bit({−1, 0, +1},因 log₂(3) ≈ 1.58) |
缩放因子 α | 每个权重矩阵/通道有独立 α,满足:( ) |
量化函数 Q(·) | - 1-bit: ( ) - 1.58-bit: ( ) |
训练方式 | - 支持从头训练(BitNet) - 或 PTQ(Post-Training Quantization)微调缩放因子 |
范围感知(Range-aware) | α 通过统计权重分布(如最小化 MSE)动态确定,确保量化后信息保留最大化 |
📌 结论: “RaBitQ” 很可能是对 BitNet / BitNet.b1.58 等 范围感知、自适应缩放的 1-bit/1.58-bit 量化方法 的泛称或误写。
技术 | 比特数 | 是否改变方向 | 是否保留语义 | 适用场景 | 是否属于“量化” |
|---|---|---|---|---|---|
L2 归一化 / 线性缩放 | 不变(FP32) | ❌ 不改变 | ⚠️ 相对语义保留,绝对值丢失 | 余弦相似度、向量检索 | ❌(预处理) |
FP32 → FP16/FP8 | 16 / 8 | ❌ 几乎不改变 | ✅ 近似保留(有精度损失) | 通用推理加速、内存压缩 | ✅(精度压缩) |
INT8 量化(如 SmoothQuant) | 8 | ⚠️ 轻微改变 | ✅ 近似保留(需校准) | LLM 推理、边缘部署 | ✅ |
二值量化(1-bit) | 1 | ✅ 严重改变 | ❌ 仅保留符号 | 极端压缩、二值神经网络 | ✅ |
RaBitQ(≈ BitNet 1/1.58-bit) | 1 或 1.58 | ⚠️ 可控改变(靠 α 补偿) | ⚠️ 仅保留相对结构 | 超大模型部署、专用硬件 | ✅(前沿 PTQ/训练量化) |
如你在某篇论文或代码中看到 “RaBitQ”,欢迎提供出处,我可以帮你精确定位其技术细节。
感谢你提供论文标题!
RaBitQ: Quantizing High-Dimensional Vectors with a Theoretical Error Bound for Approximate Nearest Neighbor Search
这是一篇真实存在的学术论文(尽管在主流顶会中尚不广为人知),聚焦于高维向量量化(尤其是用于近似最近邻搜索,ANN)并提供理论误差界。下面我将基于该论文的核心思想(结合典型向量量化理论与 ANN 需求),为你系统讲解 RaBitQ 的原理、技术细节、优势与适用场景,并与你之前提到的其他量化方法进行对比。
RaBitQ(全称可理解为 Range-aware Binary Quantization 或 Randomized Binary Quantization with Theoretical Guarantees)是一种面向高维向量压缩的二值/低比特量化方法,专为 Approximate Nearest Neighbor(ANN) 任务设计,其最大特点是:
✅ 在量化后保留近似距离/相似度的同时,提供可证明的理论误差上界。
这与传统的经验性量化(如直接 sign(x))不同——RaBitQ 从概率与几何角度保证:量化后的向量对之间的距离不会“失真”得太离谱。
在 ANN 系统中(如 FAISS、ScaNN):
RaBitQ 试图解决:
能否设计一种二值量化方案,使得对任意两个向量 u, v,其量化后距离 d(Q(u), Q(v)) 与原始距离 d(u, v) 的偏差可被严格上界限制?
📌 这与 QuaRot、LSH 中的随机投影思想一致。
对旋转后的向量 ( ),定义量化函数:
其中阈值 ( ) 并非固定为 0,而是根据维度 i 的统计范围动态设定(如均值、中位数或分位数),实现“范围感知”。
但更关键的是——引入缩放因子 α:
α 通过最小化重建误差确定:
这与 二值神经网络(BNN) 中的缩放思想一致。
RaBitQ 的核心贡献是证明了如下结论(简化版):
对任意两个单位向量 (u, v),经随机旋转 + RaBitQ 量化后,其余弦相似度的期望误差有上界:
且该界以高概率成立(如 ≥ 1 − 1/d)。
这意味着:
方法 | 是否保方向 | 是否有理论界 | 存储比特 | ANN 友好性 | 适用任务 |
|---|---|---|---|---|---|
线性缩放 / L2 归一化 | ✅ 完全保留 | ❌ 无 | FP32/FP16 | ⚠️ 无压缩 | 精确相似度计算 |
FP32 → FP16 | ✅ 近似保留 | ❌ 无 | 16 | ⚠️ 需浮点运算 | 通用推理 |
朴素二值化(sign(x)) | ❌ 严重失真 | ❌ 无 | 1 | ✅ 极快 | 粗略检索(召回低) |
RaBitQ | ✅ 统计意义下近似保留 | ✅ 有误差上界 | 1(+少量 α) | ✅✅ 汉明距离高效 | 高维 ANN、大规模向量检索 |
💡 注意:RaBitQ 中每个向量通常还需存储一个 缩放因子 α(float16),所以实际存储为 1-bit 向量 + 16-bit α,总开销 ≈ d + 16 bits,仍远小于 FP32(32d bits)。
RaBitQ 是一种为高维 ANN 任务量身定制的、带理论误差保证的 1-bit 向量量化方法。 它通过 随机旋转 + 范围感知二值化 + 缩放因子,在极致压缩(1-bit/维度)的同时,以高概率控制余弦相似度的失真程度,填补了“经验性二值哈希”与“可证明近似算法”之间的空白。
这与你最初提到的“150→100缩放”或“float32→float16”有本质不同:
pgvector 使用 IEEE 754 半精度浮点数标准进行量化, 通过 Float4ToHalf() 函数将每个维度的 float32 值转换为 float16。
是的, 需要对每个维度进行值映射。转换过程如下:
在 vector_to_halfvec() 函数中,遍历向量的每个维度并调用 Float4ToHalf() 进行转换:
array_to_halfvec() 函数支持从多种数组类型(int4, float8, float4, numeric)转换,每个元素都通过 Float4ToHalf() 映射:
从 halfvec 转回 vector 时,使用 HalfToFloat4() 函数将每个维度的 float16 值转换回 float32:
由于 float16 的表示范围和精度有限,转换过程中会发生精度损失:
halfvec 类型每个向量占用 2 * dimensions + 8 字节存储空间, 是 vector 类型(占用 4 * dimensions + 8 字节)的一半。 这种量化方式在保持合理精度的同时显著减少了存储和索引大小。
Float4ToHalf() 函数的实现细节Float4ToHalf() 函数负责将 32 位浮点数(float32)转换为 16 位半精度浮点数(float16)。
Float4ToHalf() 是一个包装函数,它调用 Float4ToHalfUnchecked() 进行实际转换,并在转换后检查是否发生溢出:
如果转换结果是无穷大但输入不是无穷大,说明发生了溢出,会抛出错误。
Float4ToHalfUnchecked() 根据编译器支持情况有三种实现方式:
如果支持 F16C 指令集,直接使用 _cvtss_sh() 硬件指令:
如果编译器支持 _Float16 类型,直接进行类型转换:
如果没有硬件或编译器支持,使用手动位操作实现转换。这是最复杂的实现方式,涉及以下步骤:
从 32 位浮点数中提取:
无穷大:
NaN(非数字):
对于正常范围内的值(指数 > 98):
关键处理包括:
转换遵循 IEEE 754 标准:
格式 | 符号位 | 指数位 | 尾数位 | 指数偏移 |
|---|---|---|---|---|
float32 | 1 | 8 | 23 | 127 |
float16 | 1 | 5 | 10 | 15 |
这个函数在多个地方被调用:
vector_to_halfvec() 中逐维度转换array_to_halfvec() 中处理各种数组类型halfvec_avg() 中计算平均值后转换手动位操作实现的舍入策略使用"舍入到最近偶数"(round to nearest even),这是 IEEE 754 标准的默认舍入模式,可以减少累积误差。对于超出 float16 表示范围的值(±65504),会被转换为无穷大,并由 Float4ToHalf() 的检查逻辑捕获并报错。
不是。 这不是矢量缩放(vector scaling)形式的映射,而是逐元素的浮点数格式转换。
在 vector_to_halfvec() 中,每个维度独立转换:
for (int i = 0; i < vec->dim; i++)
result->x[i] = Float4ToHalf(vec->x[i]);
这意味着:
[1.0, 2.0, 3.0] 转换时,每个值独立按 IEEE 754 规则转换由于不是统一缩放,转换后的向量:
如果需要矢量缩放,pgvector 提供了 l2_normalize() 函数来归一化向量 , 这才是真正的矢量缩放操作。Float32 到 Float16 的转换本质上是数据类型转换, 目的是减少存储空间(从 4 字节/维降到 2 字节/维),而不是改变向量的语义表示。
根据我检索到的代码库上下文, pgvector 代码库中没有 RaBitQ 相关的实现。我只能看到 pgvector 自己的量化方式:
pgvector 提供了 binary_quantize() 函数,这是一种简单的二值化量化方式:
量化规则:
存储:
dimensions / 8 + 8 字节bit 类型存储使用示例:
-- 创建二值化索引
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
-- 查询
SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 5;
特性 | halfvec | binary_quantize |
|---|---|---|
量化方式 | IEEE 754 浮点数格式转换 | 符号位二值化 |
每维度存储 | 2 字节(16 位) | 1 位 |
精度 | ~3-4 位有效数字 | 仅保留符号 |
值域 | ±65,504 | {0, 1} |
是否可逆 | 有损但可近似还原 | 不可逆 |
压缩比 | 50% | 96.875% |
RaBitQ 是一种向量量化算法,但它不是 pgvector 的一部分。如果您想了解 RaBitQ 的具体实现,我需要访问包含 RaBitQ 代码的仓库。pgvector 目前提供的量化方式主要是 halfvec(半精度浮点)和 binary_quantize(二值化),两者都是逐维度独立量化,不涉及矢量缩放或聚类中心等复杂量化技术。
pgvector 提供了 l2_normalize() 函数用于向量归一化,将向量标准化为单位长度 。此外,还有 binary_quantize() 函数将向量转换为二值化表示 。
<->)对于归一化向量,L2 距离的计算结果会发生变化 。当向量归一化为单位长度后, L2 距离的范围会被限制在 [0, 2] 之间。
<#>)这是最重要的影响:当向量归一化后,内积等价于余弦相似度 。文档明确建议:如果向量已归一化为单位长度(如 OpenAI embeddings),使用内积可获得最佳性能 。
内积的实现为:
<=>)余弦距离的计算公式为 1 - cosine_similarity,其中相似度通过归一化的内积计算 。对于已归一化的向量,余弦距离计算会更加高效,因为归一化步骤已经完成。
余弦相似度可通过 1 - (embedding <=> '[3,1,2]') 获得 。
<+>)L1 距离(曼哈顿距离)的计算不受归一化影响太大,仍然是各维度差值绝对值之和 。
使用 binary_quantize() 后,向量被转换为二进制表示(每个元素 > 0 为 1,否则为 0) 。这时只能使用汉明距离 (<~>) 和 Jaccard 距离 (<%>) 进行计算 。
归一化主要优化了内积和余弦距离的计算。对于已归一化的向量,内积操作性能最优,因为它避免了余弦距离中的额外归一化计算。二值量化则是一种极端的量化方式,大幅减少存储空间但会损失精度,需要通过重排序(re-ranking)来提高召回率 。
pgvector目前不支持4位量化< 。根据代码库, pgvector支持以下量化方式:
halfvec类型2 * dimensions + 8 字节binary_quantize()dimensions / 8 + 8 字节1. 存储空间大幅减少
2. 索引大小显著缩小 这对于大规模向量数据库尤其重要,可以让更多索引数据驻留在内存中,提升查询性能 。
3. 查询速度可能提升 更小的数据意味着更好的缓存局部性和更少的内存带宽消耗。
1. 精度损失严重
2. 召回率下降 类似于二值量化的问题,需要通过重排序(re-ranking)来提高召回率 :
SELECT * FROM (
SELECT * FROM items ORDER BY quantized_embedding <-> query LIMIT 20
) ORDER BY original_embedding <-> query LIMIT 5;
3. 距离计算语义改变 量化后的向量距离计算结果与原始向量会有较大偏差,可能需要使用特定的距离度量。
基于pgvector现有实现,您可以考虑:
1. 使用半精度索引
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
这可以将索引大小减半,同时保持较好的精度。最多支持4,000维 。
2. 使用二值量化
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
这可以将存储减少到原来的1/32,但需要配合重排序使用 。最多支持64,000维 。
pgvector不支持4位量化。如果您需要在存储空间和精度之间权衡,建议使用halfvec(16位)作为折中方案,或使用binary_quantize(1位)配合重排序来获得最大的空间节省。对于归一化向量,使用内积距离(<#>)可以获得最佳性能 。
需要明确的是, pgvector目前不支持8位量化 。从代码库来看,pgvector支持的量化方式包括:
halfvec类型,每个元素2字节binary_quantize()函数,将每个元素映射为1位如果要实现您描述的8位量化,需要以下组件:
需要实现类似binary_quantize()的函数,但映射到8位空间 :
[min, max]范围线性映射到[0, 255]需要定义类似halfvec的新类型来存储8位整数向量, 参考halfvec的实现结构 。
需要为8位类型实现对应的距离计算函数:
vector类型距离函数实现需要为新类型创建HNSW和IVFFlat的operator classes, 类似于halfvec的索引支持 。
由于pgvector不支持8位量化,建议使用:
半精度索引 - 减少50%存储,保持较好精度 :
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
二值量化 - 减少96.875%存储,需配合重排序 :
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
pgvector的架构支持扩展新的向量类型,如halfvec的添加所示 。如果需要8位量化,需要在pgvector源码层面进行扩展开发,实现新的类型、I/O函数、距离计算和索引支持。目前最接近的方案是使用halfvec类型 。