《Elasticsearch 向量搜索的工程化实战》文章一经发出,收到很多留言。读者对向量检索和普通检索的区别充满了好奇,所以就有了今天的文章。
向量搜索已经在黑暗中成长了有些年头了,但是随着近几年机器学习和深度学习的蓬勃发展,“特别是万物皆可 embedding“的观点越来越流行之后,向量搜索才逐渐从小众的技术走入人们的视野之中。相较于普通搜索(基于词元和倒排索引),向量搜索会成为一个革命者代替它(们)的位置,还是会与它互补,并有机的整合在一起呢?
首先,我们先来了解一下这两种搜索方案的特点以及各自的优缺点
以广泛被使用的 Lucene
、Elasticsearch
、Solr
,以及最近出来的一些类似 MeiliSearch
、Redisearch
等为代表,基于词元和倒排索引所构建的普通搜索,是建立在准确的搜索内容和检索语句上的,他们往往通过各种方式对文档进行分词(analyze
),通过诸如BKD tree
等数据结构,将拆解出来的词元(token
)进行倒排索引,在检索时也会对检索语句进行同样的分词处理,通过相同词元的匹配进行召回,再通过文本相关性的算法(如TF/IDF
、BM25
等)对结果进行打分排序,最终返回结果。
因此,他们大多具有以下的特点:
ES
的 synonym
是类似在同一个位置把所有预先定义的同义词同时索引来实现的如果你在搜索时不知道确切的query 词元
,或者你希望能对更广泛的同/近义词所指向的内容进行召回,可以考虑通过向量搜索来完成。目前市面上比较流行的向量搜索解决方案,无论是业界流行的 Faiss
、ScANN
库,还是工业级的开源解决方案Milvus
、Jina
,或者Elasticsearch
及其衍生品Elastiknn
、OpenDistro Elasticsearch KNN
,多少会通过 KNN
(K nearest neighbors
)对向量进行预聚类的方式进行存取加速。(参考的benchmark)
所以,他们大多会具有以下一些特点:
通过普通搜索或者向量搜索构建个简单的搜索引擎系统并不难,但是随着数据量的增长、并发请求的增加、数据使用场景的变化,搜索引擎系统需要更多的组件一同完成其功能,如搜索前的数据预处理,到搜索过程中的query理解、改写、自动补全,缓存,分数计算,地理位置信息计算,到返回结果前的结果排序和过滤,结果分页等。
之所以普通搜索和向量搜索会存在上面那些特点和差异,是因为他们构建数据的索引的数据结构以及召回算分的算法有很大差异,我们分别来看他们。
倒排索引是一个类似 hashmap
的数据结构,它的 key
是每个词元,而 value
是一个包含这个词元的所有文档的 id
列表(也可能是 hashset
、链表等结构),这样的数据结构的好处在于对于一个词元,可以用接近 O(1)
的代价来找到包含它的文章。有时倒排索引中也会包含词元在文档中的位置信息,这是为了能在搜索时,在考虑了 query
中的词元信息之外,也把词元的顺序也一并考虑进去。
LSM 树(Log-Structured Merge-Tree),或称为日志结构合并树,被广泛运用于以 hbase
为代表的类数据库存储中,它的特点在于牺牲部分读的性能换取强大的写入性能,因为它作为一种基于硬盘的数据结构,可以明显的减少硬盘磁盘臂的开销,并能在较长的时间内提供文件的高速插入和删除。一般的倒排索引会构建在内存中,但随着数据量增加,我们可能需要通过磁盘来帮忙保存一部分数据,这就用到了 LSM树
,因为硬盘(无论 SSD
还是 HDD
都比 RAM
慢的好几个数量级),而 LSM树
可以在写数据的时候先把数据缓存在内存中,等积累一定量之后再通过归并排序的方式将数据追加到磁盘队尾,以提高写入速度。
LSM树
只解决了数据插入的问题,搜索引擎中还会存在大量的更新操作,这就涉及到了随机读写了,我们知道随机读写会比顺序读写慢得多,特别是在 HDD
硬盘上的读写,这时就需要使用带版本的数据提交操作了。由上一节可知,数据写入时会先写内存中的缓冲区(ES
的translog
等)再通过定时提交的方式追加到磁盘中,在更新操作时也是一样的,不同的是搜索引擎往往会在内存中保留数据的指针,每次的更新(删除)操作作用在硬盘上也是追加操作,不同的是会将数据版本 +1,同时将指针指向新的地址,在召回时访问数据只访问最新版本的数据,同时定时对磁盘中的数据进行merge
操作,清理掉过期的旧版本的数据,从而释放磁盘空间。
存储:
索引:
向量搜索就完全不一样了,由于他们索引的不是【词元 - 文档】的信息而是向量,所以他们会在索引构建的时候会尝试通过聚类而非倒排索引的方式构建。
市面上大部分的向量搜索引擎是靠 KNN
配合距离计算来进行存储的,差别可能会是距离计算公式以及存储结构的优化。常见的距离计算如:
向量搜索的数据索引不同于普通搜索的分词,他们会需要先通过各种 machine learning
、deep learning
技术将文档、句子、词组等转化成向量存进搜索引擎,搜索引擎会根据配置使用距离计算模块对向量进行聚类保存。
常见的向量化(嵌入)的算法:
向量搜索的召回和索引一样是基于向量距离的,从简单到复杂可以大致分为线性搜索、分级导航(Hierarchical Navigable Small Worlds (HNSW))、索引分块及聚类等
query
向量和索引中所有的向量依次比较,再按距离排序ES
对 Dense Vector
字段的处理就是线性搜索KNN
聚类的方式进行索引topK
的向量进行其他一些可用的开源库
索引优化:
针对性能和准确性的权衡:
nlist
、nprobe
以及其他聚类、距离计算公式的调整来调整精度和性能死敌wen,Elastic 认证工程师,搜索架构师,10年+工作经验,毕业于复旦大学。
博客:https://blog.csdn.net/weixin_40601534
Github:https://github.com/godlockin
本文分享自 铭毅天下Elasticsearch 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!