Sentence Transformers 怎样用二进制量化保存嵌入向量
来源:17golang原创
时间:2026-10-29 16:43:28 150浏览 收藏
Sentence Transformers 可以在生成嵌入时直接使用二进制量化,也可以先生成 float32 向量,再调用 quantize_embeddings 转换。二进制量化把每个维度压成 1 bit,官方实现会把这些 bit 打包进字节,因此 1024 维向量通常变成形状为 (1024 / 8) = 128 的 int8 或 uint8 数组。
官方地址:https://www.sbert.net/
如果目标是降低向量索引的内存和磁盘占用,最小做法是让encode使用precision="binary",保存返回数组及其模型、维度和 dtype 元数据;如果还要保证召回质量,则把二进制结果用于初筛,再保留较高精度的重排数据。
先确定二进制量化放在哪一层
最直接的入口是 SentenceTransformer.encode 的 precision 参数。它适合从文本开始就按二进制格式生成结果;如果程序已经缓存了默认的浮点嵌入,则可以在后面调用 quantize_embeddings,避免改动前面的编码流程。
from sentence_transformers import SentenceTransformer
from sentence_transformers.quantization import quantize_embeddings
# 加载同一个模型,文档向量和查询向量必须保持模型一致
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
texts = ["如何降低向量索引的存储占用", "二进制向量怎样参与召回"]
# 方案一:编码阶段直接得到二进制数组
binary_embeddings = model.encode(texts, precision="binary")
# 方案二:已有 float32 结果时再量化,便于保留原始向量做重排
float_embeddings = model.encode(texts)
binary_from_float = quantize_embeddings(float_embeddings, precision="binary")
print(binary_embeddings.shape, binary_embeddings.dtype)
print(binary_from_float.shape, binary_from_float.dtype)
两种入口的量化目标相同,但数据生命周期不同:直接量化可以少保留一份中间结果;后置量化则适合已经有浮点缓存、还需要用原始向量做重排的检索系统。不要只保存一个没有元数据的二进制文件,否则后续很难判断它对应哪个模型和维度。

保存时重点记录 shape 和 dtype
二进制量化不是把数组简单改成更小的浮点类型。官方说明是先按零阈值把归一化嵌入映射为 0/1,再用 np.packbits 把 8 个 bit 收进一个字节。因此一个 1024 维的 float32 向量原本需要 4096 字节,打包后的二进制数组通常只有 128 字节,形状也从 (1024,) 变成 (128,)。
import json
import numpy as np
# 保存数组与描述文件,恢复时必须按同一 dtype 读取
np.save("doc_embeddings.binary.npy", binary_embeddings)
metadata = {
"model": "mixedbread-ai/mxbai-embed-large-v1",
"precision": "binary",
"shape": list(binary_embeddings.shape),
"dtype": str(binary_embeddings.dtype),
"metric": "hamming"
}
with open("doc_embeddings.binary.json", "w", encoding="utf-8") as fp:
# ensure_ascii=False 让中文元数据保持可读
json.dump(metadata, fp, ensure_ascii=False, indent=2)
# 恢复时先读元数据,再检查数组的形状和类型
restored = np.load("doc_embeddings.binary.npy")
assert tuple(metadata["shape"]) == restored.shape
assert metadata["dtype"] == str(restored.dtype)
如果向量库需要无符号字节格式,可以把精度指定为 "ubinary";二者不要在没有转换规则的情况下混用。实际项目还应把模型名、量化精度、维度、dtype 和相似度度量一起写进索引元数据,避免查询向量和文档向量使用了不同的编码协议。
二进制适合召回,重排负责精度
二进制向量的优势是占用小、Hamming 距离计算快,但压缩后不应被当作完整的浮点相似度替代品。一个稳妥的链路是:先把查询向量也量化为 binary,在二进制索引中取出较大的候选集,再用 float32 查询向量和更高精度的文档向量进行重排,最后返回较小的 top-k。
例如目标结果是 10 条,可以先召回 rescore_multiplier * 10 条候选,再对候选做 dot-product 重排。这样二进制索引负责缩小搜索范围,重排索引负责恢复排序质量;两者的模型、向量维度和文档 ID 必须一一对应。

几个容易混淆的边界
- 不要把 128 当成模型输出维度。它是 1024 个 bit 打包成字节后的数组长度,原始语义维度仍是 1024。
- 不要只保存二进制文件。模型标识、precision、shape、dtype 和度量方式缺一项,都可能导致恢复或查询不匹配。
- 不要用很小的样本估计 int8 标定范围。本文讨论的是 binary;如果改用 int8,官方建议使用更大的校准嵌入集合或明确的 min/max 范围。
- 不要把二进制召回误写成质量不变。最终效果取决于模型、数据分布、召回倍率和重排策略,应按自己的检索集评估。
常见问题
二进制量化后为什么 dtype 还是 int8?因为实现把 bit 打包进字节,数组元素承载的是字节,不是 Python 层面的单 bit 类型;需要无符号格式时使用 ubinary。
可以只用二进制向量做最终排序吗?可以,但要接受排序精度和模型数据相关的取舍。对质量敏感的检索,通常使用二进制初筛加高精度重排更稳妥。
总结:从 encode(precision="binary") 开始,保存数组和完整元数据;用 Hamming 距离做快速候选召回,再根据需求保留 float32 或 int8 数据做重排。这样才能同时得到较低的存储成本和可控的检索质量。
-
284 收藏
-
387 收藏
-
328 收藏
-
426 收藏
-
147 收藏
-
483 收藏
-
185 收藏
-
420 收藏
-
415 收藏
-
224 收藏
-
269 收藏
-
372 收藏
-
298 收藏
-
472 收藏
-
350 收藏
-
科技周边 · 人工智能 | 1小时前 | 性能优化 · Hugging Face Datasets IterableDataset flatten_indices indices mapping Arrow222 收藏
-
326 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习