使用二进制量化优化高维向量
Qdrant旨在处理典型的扩展挑战:高吞吐量、低延迟和高效索引。 二进制量化(BQ)是我们最新的尝试,旨在为客户提供他们高效扩展所需的优势。该功能特别适用于具有较大向量长度和大量点的集合。
我们的结果是戏剧性的:使用BQ将减少您的内存消耗并提高检索速度,最高可达40倍。
与其他量化方法一样,这些好处是在召回率下降的代价下获得的。然而,我们的实现使您能够在搜索时,而不是在创建索引时平衡速度和召回准确性之间的权衡。
本文的其余部分将涵盖:
- 二进制量化的重要性
- 使用我们的Python客户端的基本实现
- 基准分析和使用建议
什么是二进制量化?
二进制量化(BQ)将任何浮点数的向量嵌入转换为一组二进制或布尔值的向量。这个特性是我们过去在标量量化方面工作的扩展,在那里我们将float32转换为uint8,然后利用特定的SIMD CPU指令来执行快速的向量比较。

这个二值化函数是我们如何将一个范围转换为二进制值。所有大于零的数字标记为1。如果是零或更小,它们变为0。
将向量嵌入减少到二进制值的好处在于布尔操作非常快速,并且需要显著更少的CPU指令。为了将我们的32位嵌入减少到1位嵌入,我们可以看到高达40倍的检索速度提升!
向量搜索仍然能够以如此高的压缩率有效工作的原因之一是,这些大型向量在检索方面过度参数化。这是因为它们是为了排名、聚类和类似的用例而设计的,这些用例通常需要在向量中编码更多的信息。
例如,1536维的OpenAI嵌入在检索和排名方面不如384维的开源对手。具体而言,它在相同的嵌入检索基准上得分49.25,而开源bge-small得分51.82。这2.57分的差异很快就会累积起来。
我们对量化的实现在排名时大型完整向量与搜索和检索时二进制向量之间达成了良好的平衡。它还具备根据您的使用案例调整这一平衡的能力。
更快的搜索和检索
与产品量化不同,二进制量化不依赖于减少每个探测的搜索空间。相反,我们构建一个二进制索引,帮助我们大幅提高搜索速度。

HNSW是近似最近邻搜索。这意味着我们的准确性会提高到一个收益递减的点,因为我们检查索引以寻找更相似的候选者。在二进制量化的上下文中,这被称为过采样率。
例如,如果 oversampling=2.0 和 limit=100,那么将首先通过量化索引选择200个向量。对于这200个向量,将使用它们的HNSW索引与完整的32位向量来获得更准确的100项结果集。与进行完整的HNSW搜索相比,我们对初步搜索进行了过采样,然后仅在这个更小的向量集合上执行完整搜索。
提高存储效率
下面的图显示了二值化函数,通过它我们将32位存储减少到1位信息。
文本嵌入可以有超过1024个32位浮点数元素。例如,请记住,OpenAI嵌入是1536元素的向量。这意味着每个向量仅用于存储向量的大小为6kB。

除了存储向量,我们还需要维护一个索引以便于更快的搜索和检索。Qdrant估算整体内存消耗的公式是:
memory_size = 1.5 * number_of_vectors * vector_dimension * 4 字节
对于100K OpenAI嵌入(ada-002)向量,我们需要900兆字节的RAM和磁盘空间。随着您创建多个集合或向数据库添加更多项目,这种消耗可能会迅速增加。
通过二进制量化,这100K个OpenAI向量只需要128 MB的内存。 我们使用类似于我们标量量化内存估算中介绍的方法来基准测试这个结果。
通过在二进制转换中发生的压缩,实现了RAM使用量的减少。HNSW和量化向量将会驻留在RAM中以便快速访问,而原始向量则可以仅卸载到磁盘。对于搜索,量化的HNSW将提供过采样的候选项,然后它们将使用存储在磁盘上的原始向量重新评估,以精炼最终结果。这一切都是在后台发生的,无需您额外干预。
何时不应该使用BQ?
由于该方法利用了嵌入的过度参数化,因此您可以预期对于小嵌入(即小于1024维)会获得较差的结果。由于元素数量较少,二进制向量中维持的信息不足以取得良好的结果。
你仍然会获得更快的布尔运算和减少的内存使用,但准确性下降可能太高。
示例实现
现在我们已经向您介绍了二进制量化,让我们尝试一个基本的实现。在这个例子中,我们将使用 OpenAI 和 Cohere 以及 Qdrant。
创建一个启用二进制量化的集合
在创建集合时,索引时您应该做以下事情:
- 我们将所有的“完整”向量存储在磁盘上。
- 然后我们将二进制嵌入设置为在RAM中。
默认情况下,完整的向量和BQ都存储在RAM中。我们将完整的向量移动到磁盘,因为这样可以节省内存,并允许我们在RAM中存储更多的向量。通过这样做,我们通过设置 always_ram=True 明确地将二进制向量移动到内存中。
from qdrant_client import QdrantClient
#collect to our Qdrant Server
client = QdrantClient(
url="http://localhost:6333",
prefer_grpc=True,
)
#Create the collection to hold our embeddings
# on_disk=True and the quantization_config are the areas to focus on
collection_name = "binary-quantization"
if not client.collection_exists(collection_name):
client.create_collection(
collection_name=f"{collection_name}",
vectors_config=models.VectorParams(
size=1536,
distance=models.Distance.DOT,
on_disk=True,
),
optimizers_config=models.OptimizersConfigDiff(
default_segment_number=5,
indexing_threshold=0,
),
quantization_config=models.BinaryQuantization(
binary=models.BinaryQuantizationConfig(always_ram=True),
),
)
优化器配置中发生了什么?
我们将 indexing_threshold 设置为 0,即禁用索引。这允许更快地上传向量和有效载荷。一旦所有数据加载完成,我们将在下面重新启用它
接下来,我们将我们的向量上传到这里,然后启用索引:
batch_size = 10000
client.upload_collection(
collection_name=collection_name,
ids=range(len(dataset)),
vectors=dataset["openai"],
payload=[
{"text": x} for x in dataset["text"]
],
parallel=10, # based on the machine
)
再次启用索引:
client.update_collection(
collection_name=f"{collection_name}",
optimizer_config=models.OptimizersConfigDiff(
indexing_threshold=20000
)
)
配置搜索参数:
在设置搜索参数时,我们指定要使用 oversampling 和 rescore。以下是一个示例代码片段:
client.search(
collection_name="{collection_name}",
query_vector=[0.2, 0.1, 0.9, 0.7, ...],
search_params=models.SearchParams(
quantization=models.QuantizationSearchParams(
ignore=False,
rescore=True,
oversampling=2.0,
)
)
)
在Qdrant提取过采样的向量集后,完整的向量将会以1536维度(对于OpenAI)从磁盘中提取。Qdrant计算与查询向量的最近邻并返回准确的重新评分顺序。这种方法产生了更准确的结果。我们通过设置 rescore=True 启用了这个功能。
这两个参数是你如何平衡速度与准确性。你的过采样尺寸越大,您需要从磁盘读取的项目就越多,并且您必须使用相对较慢的完整向量索引搜索更多元素。另一方面,这样做将产生更准确的结果。
如果您的准确性要求较低,您甚至可以尝试做一个小的过采样而不进行重新评分。或者,也许对于您的数据集结合您的准确性与速度要求,您可以仅搜索二进制索引而不进行重新评分,即在搜索查询中省略这两个参数。
基准测试结果
我们获取了一些关于限制和过采样之间关系的早期结果,使用的是DBPedia OpenAI 1M向量数据集。我们在一个Qdrant实例上运行了所有这些实验,其中索引了100K个向量,并使用了100个随机查询。
我们改变了 3 个会影响查询时间和准确性的参数:limit、rescore 和 oversampling。我们将这些作为对这一新特性的初步探索。强烈建议您使用自己的数据集重现这些实验。
附注:由于这是向量数据库中的一种新创新,我们期待听到反馈和结果。加入我们的Discord服务器以进行进一步讨论!
过采样: 在下图中,我们说明了召回率与候选数量之间的关系:

我们可以看到“正确”的结果,即召回率随着潜在“候选者”数量的增加而增加(limit x oversampling)。为了突出改变 limit 的影响,不同的限制值被分解成不同的曲线。例如,我们看到限制为50时,最低的召回率大约为94个正确的结果,拥有100个候选者。这也意味着我们使用了2.0的过采样。
随着过采样的增加,我们看到结果普遍改善——但这在每种情况下并不成立。
重新评分: 如预期,重新评分增加了返回查询所需的时间。 我们还重复进行了过采样实验,但这次我们观察了重新评分对结果准确性的影响。

限制: 我们实验的限制从前1名到前50名,在一个有10万向量的索引中,当限制为50,rescore=True时,我们能够达到100%的召回率。
推荐
量化为您提供了在其他参数之间进行权衡的选项: 维度计数/嵌入大小 吞吐量和延迟要求 召回要求
如果您正在使用OpenAI或Cohere嵌入,我们建议以下过采样设置:
| 方法 | 维度 | 测试数据集 | 召回率 | 过采样 |
|---|---|---|---|---|
| OpenAI文本嵌入-3-大 | 3072 | DBpedia 1M | 0.9966 | 3倍 |
| OpenAI 文本嵌入-3-小型 | 1536 | DBpedia 100K | 0.9847 | 3倍 |
| OpenAI 文本嵌入-3-大 | 1536 | DBpedia 1M | 0.9826 | 3倍 |
| Cohere AI embed-english-v2.0 | 4096 | 维基百科 1M | 0.98 | 2x |
| OpenAI文本嵌入-ada-002 | 1536 | DbPedia 1M | 0.98 | 4x |
| 双子座 | 768 | 没有开放数据 | 0.9563 | 3倍 |
| 米斯特拉尔嵌入 | 768 | 无公开数据 | 0.9445 | 3倍 |
如果您确定二进制量化适合您的数据集和查询,那么我们建议如下:
- 始终使用ram=True的二进制量化
- 存储在磁盘上的向量
- 过采样=2.0(或更多)
- 重新评分=True
接下来是什么?
如果您需要在高召回期望下处理大量数据,二进制量化是非常出色的。您可以通过在本地启动一个 Qdrant 容器镜像,或者让我们通过您在我们云托管服务中的 免费账户 为您创建一个来尝试此功能。
本文提供了您可以使用的数据集和配置示例,以便开始使用。我们的文档涵盖了将大型数据集添加到Qdrant到您的Qdrant实例,以及更多量化方法。
如果您有任何反馈,请在Twitter或LinkedIn上给我们留言,告诉我们您的结果。 加入我们活跃的Discord服务器,如果您想和志同道合的人讨论BQ!

