方法和引擎
_方法_定义了在索引时组织向量数据并在搜索时进行搜索的算法,用于 近似 k-NN 搜索。
UDB-SX 支持以下方法:
分层可导航小世界(HNSW):创建向量之间连接的层次图结构。有关该算法的更多信息,请参阅 使用分层可导航小世界图进行高效且稳健的近似最近邻搜索。
倒排文件索引(IVF):基于聚类将向量组织到桶中,并在搜索时仅搜索桶的子集。
_引擎_是实现这些方法的库。不同的引擎可以实现相同的方法,有时具有不同的优化或特性。例如,所有支持的引擎都实现了 HNSW,每个引擎都有自己的优势。
UDB-SX 支持以下引擎:
Lucene:原生搜索库,提供具有高效过滤功能的 HNSW 实现
Faiss(Facebook AI 相似性搜索):一个综合库,实现了 HNSW 和 IVF 方法,并提供了额外的向量压缩选项
NMSLIB(非度量空间库):HNSW 的旧版实现(现已弃用)
方法定义示例
方法定义包含以下组件:
方法的
name(例如,hnsw或ivf)方法构建的
space_type(例如,l2或cosinesimil)将实现该方法的
engine(例如,faiss或lucene)特定于该实现的
parameters映射
以下示例配置了一个 hnsw 方法,使用 l2 空间类型、faiss 引擎以及方法特定参数:
PUT test-index
{
"settings": {
"index": {
"knn": true,
"knn.algo_param.ef_search": 100
}
},
"mappings": {
"properties": {
"my_vector1": {
"type": "knn_vector",
"dimension": 1024,
"method": {
"name": "hnsw",
"space_type": "l2",
"engine": "faiss",
"parameters": {
"ef_construction": 128,
"m": 24
}
}
}
}
}
}
并非每个方法/引擎组合都支持所有空间。有关支持的空间列表,请参阅特定引擎的部分。
通用参数
以下参数是所有方法定义通用的。
| 映射参数 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
name |
是 | 不适用 | 否 | 最近邻方法。有效值为 hnsw 和 ivf。并非每个引擎组合都支持所有方法。有关支持的方法列表,请参阅特定引擎的部分。 |
space_type |
否 | l2 |
否 | 用于计算向量之间距离的向量空间。有效值为 l1、l2、linf、cosinesimil、innerproduct、hamming 和 hammingbit。并非每个方法/引擎组合都支持所有空间。有关支持的空间列表,请参阅特定引擎的部分。注意:此值也可以在映射的顶层指定。更多信息,请参阅 空间。 |
engine |
否 | faiss |
否 | 用于索引和搜索的近似 k-NN 库。有效值为 faiss、lucene 和 nmslib(已弃用)。 |
parameters |
否 | null |
否 | 用于最近邻方法的参数。更多信息,请参阅特定引擎的部分。 |
Lucene 引擎
Lucene 引擎直接在 Lucene 内部提供了向量搜索的原生实现。它提供高效的过滤功能,非常适合较小的部署。
支持的方法
Lucene 引擎支持以下方法。
| 方法名称 | 需要训练 | 支持的空间 |
|---|---|---|
hnsw |
否 | l2、cosinesimil、innerproduct |
HNSW 参数
HNSW 方法支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
ef_construction |
否 | 100 | 否 | 在 k-NN 图创建期间使用的动态列表的大小。值越高,图越精确,但索引速度越慢。 注意:Lucene 内部使用术语 beam_width,但 UDB-SX 文档为了保持一致性使用 ef_construction。 |
m |
否 | 16 | 否 | 为每个新元素创建的双向链接数。显著影响内存消耗。保持在 2 到 100 之间。注意:Lucene 内部使用术语 max_connections,但 UDB-SX 文档为了保持一致性使用 m。 |
Lucene 的 HNSW 实现会忽略 ef_search 并将其动态设置为搜索请求中的 “k” 值。因此,在使用 Lucene 引擎时无需配置 ef_search 的设置。
示例配置
"method": {
"name": "hnsw",
"engine": "lucene",
"parameters": {
"m": 2048,
"ef_construction": 245
}
}
Faiss 引擎
Faiss 引擎提供高级向量索引功能,支持多种方法和编码选项,以优化内存使用和搜索性能。
支持的方法
Faiss 引擎支持以下方法。
| 方法名称 | 需要训练 | 支持的空间 |
|---|---|---|
hnsw |
否 | l2、innerproduct(使用 PQ 时不可用)、hamming |
ivf |
是 | l2、innerproduct、hamming(UDB-SX 支持二进制向量。更多信息,请参阅 二进制 k-NN 向量)。 |
HNSW 参数
hnsw 方法支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
ef_search |
否 | 100 | 否 | 在 k-NN 搜索期间使用的动态列表的大小。值越高,搜索结果越精确,但搜索速度越慢。 |
ef_construction |
否 | 100 | 否 | 在 k-NN 图创建期间使用的动态列表的大小。值越高,图越精确,但索引速度越慢。 |
m |
否 | 16 | 否 | 插件为每个新元素创建的双向链接数。增加和减少此值会对内存消耗产生很大影响。将此值保持在 2 到 100 之间。 |
encoder |
否 | flat | 否 | 用于编码向量的编码器定义。编码器可以减少索引的内存占用,但会牺牲搜索精度。 |
IVF 参数
IVF 方法支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
nlist |
否 | 4 | 否 | 用于分区向量的桶数。值越高可能提高精度,但也会增加内存和训练延迟。 |
nprobes |
否 | 1 | 否 | 查询期间搜索的桶数。值越高,搜索结果越精确,但搜索速度越慢。 |
encoder |
否 | flat | 否 | 用于编码向量的编码器定义。 |
有关这些参数的更多信息,请参阅 Faiss 文档。
IVF 训练要求
IVF 算法需要训练步骤。要创建使用 IVF 的索引,您需要使用 训练 API 训练一个模型,并传递 IVF 方法定义。IVF 至少需要 nlist 个训练数据点,但我们建议 您使用比这更多的数据点。训练数据可以是您计划索引的数据,也可以来自单独的数据集。
支持的编码器
您可以使用编码器来减少向量索引的内存占用,但会牺牲搜索精度。
UDB-SX 目前支持 Faiss 库中的以下编码器。
| 编码器名称 | 需要训练 | 描述 |
|---|---|---|
flat(默认) |
否 | 将向量编码为浮点数组。此编码不会减少内存占用。 |
pq |
是 | 乘积量化(Product Quantization)的缩写,PQ 是一种有损压缩技术,使用聚类将向量编码为固定字节大小,旨在最小化 k-NN 搜索精度的下降。简而言之,向量被分成 m 个子向量,然后每个子向量由训练期间生成的码本中的 code_size 代码表示。有关乘积量化的更多信息,请参阅 这篇博客文章。 |
sq |
否 | 标量量化(Scalar Quantization)的缩写。您可以使用 sq 编码器将 32 位浮点向量量化为 16 位浮点数。在 2.13 版本中,内置的 sq 编码器是 SQFP16 Faiss 编码器。该编码器以最小的精度损失减少了内存占用,并通过使用 SIMD 优化(在 x86 架构上使用 AVX2 或在 ARM64 架构上使用 Neon)提高了性能。更多信息,请参阅 Faiss 标量量化。 |
PQ 参数
pq 编码器支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
m |
否 | 1 |
否 | 确定将向量分割成的子向量数量。子向量彼此独立编码。向量维度必须能被 m 整除。最大值为 1,024。 |
code_size |
否 | 8 |
否 | 确定用于编码子向量的比特数。最大值为 8。对于 ivf,此值必须小于或等于 8。对于 hnsw,此值必须为 8。 |
hnsw 方法支持 UDB-SX 及更高版本的 pq 编码器。带有 hnsw 方法的 pq 编码器的 code_size 参数必须为 8。
SQ 参数
sq 编码器支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
type |
否 | fp16 |
否 | 用于将 32 位浮点向量编码为相应类型的标量量化类型。对于 fp16 编码器,向量值必须在 [-65504.0, 65504.0] 范围内。 |
clip |
否 | false |
否 | 如果为 true,则任何超出指定向量类型支持范围的向量值将被舍入到范围内。如果为 false,则如果任何向量值超出支持范围,请求将被拒绝。将 clip 设置为 true 可能会降低召回率。 |
更多信息和示例,请参阅 使用 Faiss 标量量化。
SIMD 优化
从 2.13 版本开始,如果底层硬件支持 SIMD 指令(x64 架构上的 AVX2 和 ARM64 架构上的 Neon),UDB-SX 支持 单指令多数据(SIMD) 处理。SIMD 在 Linux 机器上默认支持,仅适用于 Faiss 引擎。SIMD 架构通过提高索引吞吐量和减少搜索延迟来提升整体性能。从 2.18 版本开始,UDB-SX 在 x64 架构上支持 AVX-512 SIMD 指令。UDB-SX 在 x64 架构上为 Intel Sapphire Rapids 或更新一代处理器支持高级 AVX-512 SIMD 指令,提高了汉明距离计算的性能。
仅当向量维度是 8 的倍数时,SIMD 优化才适用。
x64 架构
对于 x64 架构,Faiss 库的以下版本随制品一起构建和分发:
libopensearchknn_faiss_avx512_spr.so:包含针对新一代处理器的高级 AVX-512 SIMD 指令的 Faiss 库,可在公共云(如 AWS 的 c/m/r 7i 或更新实例)上使用。libopensearchknn_faiss_avx512.so:包含 AVX-512 SIMD 指令的 Faiss 库。libopensearchknn_faiss_avx2.so:包含 AVX2 SIMD 指令的 Faiss 库。libopensearchknn_faiss.so:未优化的 Faiss 库,不含 SIMD 指令。
使用 Faiss 库时,性能排名如下:高级 AVX-512 > AVX-512 > AVX2 > 无优化。
如果您的硬件支持高级 AVX-512(spr),UDB-SX 会在运行时加载 libopensearchknn_faiss_avx512_spr.so 库。
如果您的硬件支持 AVX-512,UDB-SX 会在运行时加载 libopensearchknn_faiss_avx512.so 库。
如果您的硬件支持 AVX2 但不支持 AVX-512,UDB-SX 会在运行时加载 libopensearchknn_faiss_avx2.so 库。
要禁用高级 AVX-512(针对 Sapphire Rapids 或更新一代处理器)、AVX-512 和 AVX2 SIMD 指令并加载未优化的 Faiss 库(libopensearchknn_faiss.so),请在 opensearch.yml 中将 knn.faiss.avx512_spr.disabled、knn.faiss.avx512.disabled 和 knn.faiss.avx2.disabled 静态设置指定为 true(默认情况下,所有这些都设置为 false)。
请注意,更新静态设置需要停止集群,更改设置,然后重新启动集群。
ARM64 架构
对于 ARM64 架构,仅构建和分发一个性能提升的 Faiss 库(libopensearchknn_faiss.so)。该库包含 Neon SIMD 指令,无法禁用。
示例配置
以下示例使用 ivf 方法,未指定编码器(默认情况下,UDB-SX 使用 flat 编码器):
"method": {
"name":"ivf",
"engine":"faiss",
"parameters":{
"nlist": 4,
"nprobes": 2
}
}
以下示例使用带有 pq 编码器的 ivf 方法:
"method": {
"name":"ivf",
"engine":"faiss",
"parameters":{
"encoder":{
"name":"pq",
"parameters":{
"code_size": 8,
"m": 8
}
}
}
}
以下示例使用 hnsw 方法,未指定编码器(默认情况下,UDB-SX 使用 flat 编码器):
"method": {
"name":"hnsw",
"engine":"faiss",
"parameters":{
"ef_construction": 256,
"m": 8
}
}
以下示例使用带有 fp16 类型 sq 编码器的 ivf 方法:
"method": {
"name":"ivf",
"engine":"faiss",
"parameters":{
"encoder": {
"name": "sq",
"parameters": {
"type": "fp16",
"clip": false
}
},
"nprobes": 2
}
}
以下示例使用带有启用了 clip 的 fp16 类型 sq 编码器的 hnsw 方法:
"method": {
"name":"hnsw",
"engine":"faiss",
"parameters":{
"encoder": {
"name": "sq",
"parameters": {
"type": "fp16",
"clip": true
}
},
"ef_construction": 256,
"m": 8
}
}
NMSLIB 引擎(已弃用)
非度量空间库(NMSLIB)引擎是 UDB-SX 中最早的向量搜索实现之一。虽然仍然支持,但已被 Faiss 和 Lucene 引擎取代,现已弃用。
支持的方法
NMSLIB 引擎支持以下方法。
| 方法名称 | 需要训练 | 支持的空间 |
|---|---|---|
hnsw |
否 | l2、innerproduct、cosinesimil、l1、linf |
HNSW 参数
HNSW 方法支持以下参数。
| 参数名称 | 必需 | 默认值 | 可更新 | 描述 |
|---|---|---|---|---|
ef_construction |
否 | 100 | 否 | 在 k-NN 图创建期间使用的动态列表的大小。值越高,图越精确,但索引速度越慢。 |
m |
否 | 16 | 否 | 为每个新元素创建的双向链接数。显著影响内存消耗。保持在 2 到 100 之间。 |
对于 NMSLIB(已弃用),ef_search 在 索引设置 中设置。
示例配置
"method": {
"name": "hnsw",
"engine": "nmslib",
"space_type": "l2",
"parameters": {
"ef_construction": 100,
"m": 16
}
}
选择正确的方法
在构建 knn_vector 字段时,有多个选项可供选择。要选择正确的方法和参数,您应首先了解工作负载的需求以及您愿意做出的权衡。需要考虑的因素包括:(1) 查询延迟,(2) 查询质量,(3) 内存限制,以及 (4) 索引延迟。
如果内存不是问题,HNSW 在查询延迟/查询质量权衡方面表现良好。
如果您希望与 HNSW 相比使用更少的内存并提高索引速度,同时保持相似的查询质量,您应该评估 IVF。
如果内存是一个问题,请考虑在 HNSW 或 IVF 索引中添加 PQ 编码器。由于 PQ 是有损编码,查询质量会下降。
您可以通过使用 fp_16 编码器 将内存占用减少 2 倍,且搜索质量损失最小。如果您的向量维度在 [-128, 127] 字节范围内,我们建议使用 字节量化器 将内存占用减少 4 倍。要了解更多关于向量量化的选项,请参阅 k-NN 向量量化。
引擎推荐
一般来说,对于大规模用例选择 Faiss。Lucene 是较小部署的良好选择,并提供诸如智能过滤等优势,其中根据情况自动应用最佳过滤策略——预过滤、后过滤或精确 k-NN。下表总结了每个选项之间的差异。
| Faiss/HNSW | Faiss/IVF | Lucene/HNSW | |
|---|---|---|---|
| 最大维度 | 16,000 | 16,000 | 16,000 |
| 过滤 | 后过滤 | 后过滤 | 搜索期间过滤 |
| 需要训练 | 否(PQ 需要) | 是 | 否 |
| 相似度度量 | l2、innerproduct |
l2、innerproduct |
l2、cosinesimil |
| 向量数量 | 数百亿 | 数百亿 | 少于 1000 万 |
| 索引延迟 | 低 | 最低 | 低 |
| 查询延迟和质量 | 低延迟和高质量 | 低延迟和低质量 | 高延迟和高质量 |
| 向量压缩 | 平铺 PQ |
平铺 PQ |
平铺 |
| 内存消耗 | 高 PQ 下低 |
中 PQ 下低 |
高 |
内存估算
在典型的 UDB-SX 集群中,一部分 RAM 被保留给 JVM 堆。UDB-SX 将原生库索引分配到剩余 RAM 的一部分。这部分的大小由 circuit_breaker_limit 集群设置决定。默认情况下,该限制设置为 50%。
使用副本会使向量总数翻倍。
有关将内存估算与向量量化结合使用的信息,请参阅 向量量化。
HNSW 内存估算
HNSW 所需的内存估计为 1.1 * (4 * dimension + 8 * m) 字节/向量。
例如,假设您有 100 万个向量,dimension 为 256,m 为 16。内存需求可以估算如下:
1.1 * (4 * 256 + 8 * 16) * 1,000,000 ~= 1.267 GB
IVF 内存估算
IVF 所需的内存估计为 1.1 * (((4 * dimension) * num_vectors) + (4 * nlist * d)) 字节。
例如,假设您有 100 万个向量,dimension 为 256,nlist 为 128。内存需求可以估算如下:
1.1 * (((4 * 256) * 1,000,000) + (4 * 128 * 256)) ~= 1.126 GB