近一年,es发布了很多个版本。本文,主要整理了es关于knn搜索相关的优化项。
也放了官方文档的链接。
What’s new in 8.8 | Elasticsearch Guide [8.8] | Elastic
改添加了倒数排名融合 (RRF),它遵循将结果集合并在一起的基本公式,sum(1/(k+d))其中k 是排名常量,d是文档在查询结果集中的得分位置。以这种方式排名的主要优势是结果集的分数不必相对于彼此进行规范化,因为 RRF 仅依赖于每个结果集中的位置。
PRF是用来更好的融合KNN和BM25相关性分数的方法。有了这个以后,才是真正意义上的支持混合检索。将BM25的相关性和KNN的相关性结合。
遗憾的是,这是一个白金版本,也就是只有氪金才能使用。es的官方授权是非常贵的,它是按照节点来售卖的,大概每个节点每年10w。
{
"query": {
"match": {
"product": {
"query": "brown shoes"
}
}
},
"knn": {
"field": "product-vector",
"query_vector": [54, 10, -2],
"k": 20,
"num_candidates": 75
},
"rank": {
"rrf": {
"window_size": 100,
"rank_constant": 20
}
}
}
这添加了一个新参数,knn允许过滤超出给定相似度的最近邻结果。
num_candidates并且k仍然是必需的,因为这控制着最近邻向量搜索的准确性和探索。对于每个分片,查询将搜索num_candidates并仅保留在提供的similarity边界内的分片,然后最终k像往常一样减少到全局顶部。
例如,当使用l2_norm索引相似度值时,这可以被视为radius对的后过滤knn。
ELSER是非常有用,特别是在英文场景下。找回的相关性排名要远比Bm25好。
不过遗憾的是,这需要用到ML的功能模块。此模块也是白金版功能,同样需要氪金才能使用。如果作为体验,可以试用一个月,来试试效果。
What’s new in 8.9 | Elasticsearch Guide [8.9] | Elastic
从 8.9 开始,text_embedding query_vector_builderkNN 搜索扩展已普遍可用。此功能是通过将文本转换为密集向量来执行Semantic search | Elasticsearch Guide [8.9] | Elastic
该功能主要是支持,将embedding模型放在es集群中跑,然后仅仅通过传参数的方式,选择对我们的query进行embedding。可能会很方便。不过向集群导入模型,也是白金版功能。
What’s new in 8.10 | Elasticsearch Guide [8.10] | Elastic
Elasticsearch 过去一直按顺序跨段执行搜索。Lucene 支持在收集命中(通过收集器管理器)以及重写某些查询(例如 knn 查询)时跨段并行搜索。
此提交还允许在 DFS 阶段执行并发搜索,这将提高资源使用率以及受益于并发重写和收集的 knn 查询的性能。
Elasticsearch 现已准备好支持单个分片内的并发性。搜索已使用收集器管理器执行,最后一个缺失的部分是为索引搜索器提供一个执行器,以便它可以将并发计算卸载到它。
此提交引入了辅助执行器,专门用于执行搜索的并发部分。搜索线程仍然是协调搜索的线程(调用者搜索将从中发起),但实际工作将转移给新引入的执行器。
我们不仅卸载了并行执行,还卸载了顺序执行,以使工作负载更加可预测,因为在两个线程池中执行搜索部分会让人感到惊讶。此外,这可能会导致突然运行大量繁重操作(一些在调用者线程中,一些在单独的线程中),这可能会使系统过载,并使线程池的大小调整更加困难。
请注意,提取操作以及其他操作仍在搜索线程池中执行。此提交不会使搜索线程池成为仅用于协调的线程池,它只针对与 IndexSearcher#search 操作本身有关的操作,尽管这占搜索 API 执行不同阶段的很大一部分。
鉴于搜索器会阻塞以等待所有任务完成,我们采取了一种简单的方法,即引入一个线程池执行器,该执行器的大小与现有搜索线程池相同,但依赖于无界队列。这简化了线程池队列和拒绝的处理。事实上,我们希望保证辅助线程池不会拒绝,并将排队完全委托给搜索线程池,无论如何,搜索线程池是每个搜索操作的入口点。这背后的原理是,如果您在搜索线程池中获得了一个位置,您应该能够完成搜索,而且相当快。
作为此提交的一部分,我们还引入了取消尚未启动的任务的功能,以便如果任何任务引发异常,则其他任务将无法启动不必要的计算。
What’s new in 8.12 | Elasticsearch Guide [8.12] | Elastic
添加了一个名为int8_hnsw 的新索引类型。该类型会自动将 float32 值量化为 int8 字节值。虽然这会增加 25% 的磁盘使用量,但会将快速 HNSW 搜索所需的内存减少 75%。大大减少了密集向量搜索所需的资源开销。但准确度会略有下降。
使用例子
PUT vectors
{
"mappings": {
"properties": {
"my_vector": {
"type": "dense_vector",
"index": true,
"index_options": {
"type": "int8_hnsw"
}
}
}
}
}
在之前,es默认在分片中,是串行执行检索的。单个分片可能有几十个segment,它们都是排队串行执行的。8.12版本,开始默认并发执行。
此前,对索引进行merge,可显著提升性能。分片内改为并行检索后,如果CPU资源充足,即使不做merge也会有提升。
虽然此优化,并不是针对knn检索做的优化,但是knn会受益。
What’s new in 8.14 | Elasticsearch Guide [8.14] | Elastic
已添加针对 aarch64 的优化 int8 向量距离实现。此实现目前仅在合并期间使用。向量距离实现在二进制比较方面比 Lucene 的 Pamana Vector 实现好大约 5 倍(取决于维度数量)。它通过将 SIMD(Neon)内在函数编译到单独的本机库中并由 Panama 的 FFI 链接来实现这一点。比较是在堆外 mmap 向量数据上执行的。宏基准测试 SO_Dense_Vector 在启用标量量化的情况下,合并时间显著改善,大约快了 3 倍。
What’s new in 8.15 | Elasticsearch Guide [8.15] | Elastic
通过两种新索引类型支持新的 int4(半字节)标量量化:int4_hnsw和int4_flat。这可将 减少 8 倍,但会float32造成一定程度的准确度损失。除了所需的内存更少之外,与原始向量相比,这还显著提高了查询和合并速度。
现在,通过将文档拆分成块来压缩存储字段,然后使用 ZStandard 单独压缩这些块。index.codec: default (默认)使用最多 14kB 的块或 128 个文档,以级别 0 压缩,而index.codec: best_compression使用最多 240kB 的块或 2048 个文档,以级别 3 压缩。在我们测试的大多数数据集上,这带来了 10% 左右的存储改进、略快的索引速度和相似的检索延迟。
这里需要验证,是否对向量字段有压缩。如果有压缩,则对预加载向量数据的优化有收益,可以节省内存。