本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
halo大家好,我是广州小井。有段时间没发文章了,最近都在卷一些自己的空白领域,比如文档搜索,k8s部署等等...卷到上厕所的时间都木有了,这不~现在文档搜索初步卷出点效果来了,赶紧来写成实战文章进行分享。
话说 AI 已经火了很长一段时间了,毕竟23年初期的 GPT 带来的冲击(甚至出现了"前端,狗都不做"的言论!),大家都在关注、讨论人工智能了。但说实话,那时候的自己还真的没有任何实战应用 AI 的机会,虽然我也想用,但是确实没场景,更何况做前端开发的,离 AI 更是遥不可及...
背景
直至某一天!突然接到一个文档搜索的需求,我的第一个跟 AI 贴边的应用的齿轮开始转动!
文档,大家一定不陌生,特别是对于开发同学来说...毕竟文档常驻我们的工作中,大到 Vue、React、Antd 等大型外部开源库,小到公司内部的接口文档、产品文档等等...那对于大量的文档内容、篇章,用户要怎么样快速的找到他们想看的内容呢?那无疑就是搜索了!相信下图大家一定不会陌生~
那么说到文档搜索 ,相信绝大部分小伙伴想到的第一实现方式就是关键词搜索 ,而能做关键词搜索的工具大家可能都会想到的是------Elasticsearch 这款开源工具,毕竟从知名度啥的来说,这款开源应用都是大家最有可能听说到的。想详细了解 Elasticsearch 的可以自行去搜索,或者看看这篇站内文章 Elastic:菜鸟上手指南 - 掘金 (juejin.cn) 做简单了解~
那大概了解完概念之后,就该聊聊目前的项目和具体遇到的问题了。之前接手了一个 all in js 的文档项目,其大概技术栈是:前端 react全家桶
;后端 egg+mysql
,并且其中有用到 ES 来做全文检索。好了,就这么一个项目,其中在文档搜索的这个功能中,目前存在两个痛点问题:
- 搜索不准确;
- 搜索时间长;
ES 小试牛刀
这里,我们先仅仅讨论搜索不准确的问题 ,并且为这个问题只针对关键词搜索的不足之处 进行讨论。说到这里可能有小伙伴会疑问,对于文本搜索场景来说,为何关键词的搜索方式还有不足之处?这里,我打算通过一个例子来描述说明,比如现在有如下几个标题的文档:
- 我很开心
- 我很快乐
- 我很高兴
我把他们通过 Kibana
添加到名为 keyword
的索引中,此时有三篇 doc
数据:
模拟用户输入"我很开心"关键词进行文档搜索,那么此时的结果如下:
如上图所示,咦,不是都就能搜出来吗 !但细心的朋友可能发现了,它们之间的得分并不相同。其中除了排名第一的为 4.
多分,其余的两个都为 1.21...
分。
这里我简单介绍一下 ES 的 match
关键字的匹配方式和最后的得分计算原理。当我们通过 match
去搜索,它其实会把我们的搜索内容进行拆词后再去匹配 ,比如拆成"我"、"很"、"开心",然后将拆开的词分别去检索,并且将他们的检索结果的得分进行叠加,就得出来了最终的 _score
。
因此,我们对于上述的搜索结果就并不意外了。很明显,排名靠后的两个搜索结果只有"我很"跟输入的关键词匹配 了,因此他们的得分相同,但又不如第一名的得分高。
ES 的不足
上述我们简要了解了 ES 的关键词搜索原理和默认的算分原理(这里不考虑自定义得分等方案),这里我们改变一下搜索词,看看会出现什么不一样的结果。
比如这次用户缩短了关键词,仅仅输入了"开心"来进行检索,得到的结果如下:
如图所示,改变了关键词之后我们仅仅能搜索出一条数据(看 hits.total.value
便可得知),并且它的得分也跟前文的得分有所不同。按照之前讲的,我们可以知道因为只有一组词命中,所以得分没有达到前文的 4.
也是正常的,毕竟没得叠加了嘛~
虽然说上述搜索结果没什么毛病,但是对于用户来说,这个结果可能并不符合他的期望。因为他的本意就找到跟"开心"相关的文档 来看,所以"高兴"、"快乐"理应也出现在搜索结果中.....啊这,这不是为难关键词搜索了吗 !?毕竟这已经脱离了关键词搜索的本质了。这里我暂且称这种情况为语义模糊 ,下文也会统一使用这个词来代表这种:同义词、语义相近等场景的搜索情况。
虽然针对 ES 搜索质量的调优有很多方向,不限于提升原始数据质量、加标签、深度优化搜索DSL 等等吧,但是关键词搜索依然在语义模糊 的领域并不占优,毕竟自然语言 它并不是结构化数据,试想一下我们想表达"开心"的心情,是不是还有除了"快乐"、"高兴"等等很多同义的词语呢?
再者,我说个极端的案例,如果有一段 js
写的快排代码,用户想通过关键词"快排"将其搜索出来,那对于 ES 来说将是不可能的!(当然,不要搞事哦~代码中不会含有任何跟"快排"二字有关的注释)
文本向量化
前文我有提到"结构化数据"的字眼,而我们的自然语言本身就是非结构化数据 (关于什么是结构化数据,大家可以参考一下 NLP中什么是结构化数据和非结构化数据,这个我觉得比较形象),所以对于更倾向于自然语言领域的文档搜索来说,关键词搜索确实存在短板。
由于每一个人的认知、语言习惯都不同,所以我们基本上不可能要求用户、文档运营者等用到"文档"功能的人做到真正的"规范用语",因此我们要解决语义模糊 这个问题就不能局限在关键词搜索的领域了。试想一下,我们是不是只要能把非结构化的数据都给结构化,就能做到解决语义模糊场景下的搜索难题了呢?
这里我通过一个简单的示例说明这一切的可行性:
假设现有一个结构化引擎,它能理解我们的输入,并将其转换为另一种形式的内容保存下来。图上所示,它可以将"开心"、"高兴"等词语都转化为 A
。
如此一来,不管用户输入开心、高兴等关键词,经过结构化引擎的处理后,它都变成了A
。这样,当用户再次输入开心时,理论上就能搜索到结构化后都是A
的结果,也就是"开心"、"高兴"、"快乐"都的文档可以被搜索出来。
什么是向量
其实向量我们并不陌生,初中物理、数学一定有学习过,当然有看过我写的 WebGL 专栏的话肯定更加不陌生了。这里,我们并不需要有太大的心智负担,因为我们不需要背诵它的概念含义,只要能理解它就行。所以,我们不用把向量想得这么复杂,可以理解为它就是一个n维坐标,甚至你就把它当成是一个数组来理解,也行。
比如二维平面 坐标系中[0, 0]
代表了坐标原点位置,[1, 1]
是其中的一个点,我们可以通过画图将其表示出来:
如上图,我们也可以理解为,我们用数组 [1, 1]
就可以表示出二维平面中的那个点的位置。那三维空间 、n维空间呢?我们只要加上相应的维度不就好了吗?比如三维空间的原点不就是[0, 0, 0]
吗?
因此再次回到什么是向量这个问题上,我们就可以将其理解成:它是用来表述n维空间中的一个位置点的数据信息,我们可以通过数组来呈现,数组的长度就是这个空间的维度!
文本向量化的过程
经过前文的铺垫,我们很容易想到,向量化的过程其实就是将那些自然语言变成向量的过程!当然,并不是仅仅只有自然语言能被处理成向量,音频、视频、图像等非结构化数据,都能通过一个适配的模型将其转化为向量。因此,你是否跟笔者一样突然悟了一点图像搜索的原理呢?
走远了,这里我们接着前文将"开心"等词语结构化成A
的例子来讲,其实前文中提及的A
正是这里向量。也就是说,结构化引擎出来的结果是一个n维的向量:[0.xx, 1.xx, ..., 0.xx, 3.xx]
,而结构化引擎,我们应该称其为 NLP 模型
更为合适(详细的概念大家可以看看 维基百科的自然语言介绍)。
于是乎,我们可以将前文的图改成如下的图:
可以看出,两种图之间有一点点不同,但其实仔细看你会发现,他们的向量化后的位置是非常接近 的。这里我用三维坐标点来做演示:假设经过 NLP embedding 处理后的三个词语"开心"、"高兴"、"快乐"都有了一个属于自己的向量化结果 。这三个结果虽然不完全一致,但是放到三维空间中,他们的距离是相差无几的。
换句话说,如果此时我输入"开心"来做向量搜索,那么将会寻找距离 [0, 1.0001, 0]
最近的其他点一同作为结果返回,于是乎这里的"高兴"、"快乐"是能被搜索出来的。所以这就是文本向量化的意义。
以上的举例和结果不一定准确,但是原理就是这么个原理,大家理解就好,不用深究~
Milvus数据库
有了文本向量化,还差一个向量数据库 就可以完成向量搜索的功能了,这里我选用的是 Milvus 向量数据库。额,至于这玩意是啥,怎么用,具体的建议大家直接去看它的文档吧(Milvus传送门),我不会在这里展开太多,毕竟我自己也是去看文档的。
这里我只突出一个点,它跟普通的数据库相比有什么过人之处 ?其实这也是我们上述提到的,它除了能存放常规的数据类型之外,专门有一个存储向量的字段,并且它可以计算出距离输入向量最近的其他向量点。
官方一点说就是:Milvus 存储并索引向量,可以通过计算两个向量的相似距离来分析它们之间的相关性。如果两个嵌入向量非常相似,这意味着原始数据源也是相似的。
既然原理层面行得通,那么这个 Milvus 能不能用?社区怎么样?我们顺带瞄一眼它的 github,star 数还挺不错:
并且!还有 js-sdk!那看来 all-in-js 依然没有毛病!开干~
好了,我们找到了解决本文一开始提出的语义模糊情况下的关键词搜索不准确问题的方案了!那接下来就看看怎么下手吧~
向量搜索流程
向量搜索要怎么玩的呢?我撸了个简要的流程图如下:
简单来说,我们可以分如下几步走:
- 准备一个 NLP 模型来做 emb。这是将自然语言向量化的核心。
- 文档元数据处理(切片、分类等等)。将标题、内容等数据进行清洗,以提升元数据的质量。
- 文档数据向量化处理、落库。将向量化数据存入 Milvus 中。
- 搜索关键词向量化处理、并丢到 Milvus 中做向量搜索
- 整理搜索结果。根据业务需求,调整返回数据的排名(得分)情况
那么,我把上述的几步通过层级图再将其画出来,大家就能更好的理解了:
此时你可能想问:数据切片是什么意思,为什么要切片?NLP模型做向量化处理这一步要怎么实现?Milvus怎么使用?...emmm,好问题,而此时我只能告诉你:关注我,我会持续更新这个系列~!
最后简单总结一下本文的内容:介绍了通过向量搜索的方式来解决关键词搜索在语义模糊场景下搜索不准确的问题,并且大致介绍了向量搜索的实现方案。
写在最后
ES固然是个很强大的关键词搜索工具,可能可以不断优化它的搜索DSL、优化元数据质量等手段以达到很不错的搜索效果。但术业有专攻,不可否认的是它自然也有不适用的场景...有时候为了满足业务上的需求,我们就需要根据目前的问题,从而选择不同的解决方案。
于是乎在目前我遇到的业务场景中,便有机会用到了 AI 范畴中的能力------向量搜索。当然,AI 是个很广泛的领域,向量搜索只是其中非常小的一个细分领域应用(请原谅我标题党!),所以在实际的工作生活中,我们依然有很多场景可以借助 AI 来帮助我们解决问题,并不仅仅局限在搜索这一块。并且作为一个前端开发,我更想说的是 AI 确实是一个高深有门槛的领域,但其并不影响我们在应用层使用它,我们甚至可以借助它的能力来帮我们解决问题,所以 AI 对于大部分普通人来说并不是那么的遥不可及 ...
当然啦,对于我这种搜索领域的小白来说,目前的文档搜索还在不断地实战和优化中(不管是 milvus 还是 elasticsearch 都在优化ing)。期待有大佬能指出小弟的不足,或者提供一些优秀的实战搜索方案,我们可以深度地交流交流!!感激不尽~
最后,我会把整个文档搜索的实战通过文章记录的形式分享出来,毕竟这一块算是我最近比较感兴趣的领域了。so,对此也感兴趣的小伙伴可以持续关注笔者,来个一键三连,我们下期再见!