赞
踩
作者:来自 Elastic Kathleen DeRusso
本博客分享了最近的 Haystack 2024 演讲 “回顾相关性:平衡关键字和语义搜索的经验教训” 中的一些要点。
相关性调整是用户搜索体验的重要组成部分。 语义搜索尤其面临着几个挑战,其中许多挑战是通过混合搜索和应用相关性调整实践来解决的,这些实践经过数十年的词汇搜索研究的磨练。 我们将探讨其中一些策略,以及如何有效地使用它们来调整混合搜索世界中的相关性。
Haystack US 2024 - Kathleen DeRusso:回顾相关性:平衡关键字和语义搜索的经验教训
像 BM25 这样的文本搜索算法已经存在了几十年,事实上 BM25 经常与文本搜索同义使用。 这篇博文详细介绍了 BM25 的工作原理。
分析器、分词器、过滤器、字段权重和增强都是我们的词法搜索工具箱中的工具,它们使我们能够以非常特定的方式转换文本,以支持一般和非常专业的搜索用例。
但我们还有很多其他工具可供使用:
这些工具用于影响相关性,但更重要的是适应业务规则。 业务规则是自定义规则,它们的用例差异很大,但通常包括使结果集多样化或基于上下文查询结果或其他个性化因素显示赞助内容。
语义搜索在代表你寻找的内容意图方面非常有效,即使返回的结果不包含你指定的确切关键字,也能返回匹配的结果。然而,如果你正在开发一个搜索应用并将语义搜索纳入现有技术栈,那么语义搜索并非没有一些缺陷。
这些缺陷主要分为三类:
成本可能是金钱(训练或许可模型、计算),也可能是时间。时间可以是延迟(摄入或搜索推断延迟),也可以是开发时间的成本。我们不希望在那些可以用现有工具轻松解决的问题上浪费宝贵的工程时间,而是将这些时间用于解决需要工程关注的难题。
还有许多人们在其搜索解决方案中希望拥有的功能;例如,高亮显示、拼写纠正和错字容忍。这些都是语义搜索当前原生支持度较低的功能,但许多 UI/UX 人员将这些视为用户功能的基本要求。
至于语义搜索可能不擅长处理的查询,通常是一些特定领域的查询。例如:
我们还必须考虑包括业务规则(例如基于流行度、转化率或活动的提升)在内的要求,这些语义搜索本身可能无法本地处理。
查询理解是另一个问题。这可能是简单的数字转换和度量单位处理,也可能是非常复杂的处理,比如处理否定语句。你可能曾经有过令人沮丧的搜索经历,例如搜索 “I want a restaurant that doesn't serve meat - 我想找一家不提供肉类食品的餐厅”。LLM 在这里返回素食餐厅可能还可以,但大多数语义搜索会返回提供肉类食品的餐厅!
这些都是难以解决的问题,而我们希望花费宝贵的工程时间来解决这些问题。
混合搜索结合了两全其美的优点:它将 BM25 文本搜索的精确性和功能性与向量搜索的语义理解相结合。这导致了更好的召回率和更高的整体相关性。
为了更好地理解这一点,让我们来看一些例子:
在所有上述例子中,查询都具有一些具体的过滤条件,以及更模糊的文本,这些文本从语义理解中受益。
当前,“hybrid search - 混合搜索” 这个术语有点流行,不同的场景下人们可能会有不同的理解。在一些系统中,如果你有一个单独的向量数据库,这可能涉及到对不同数据存储的多次调用,并将它们与一个服务结合起来。但是,Elasticsearch 的一个超能力是所有这些都可以结合在一个单一的索引和一个搜索调用中。
在 Elasticsearch 中,混合搜索可能像一个布尔查询那样简单。这里有一个 Elasticsearch 中布尔查询结构的示例,它结合了文本搜索、KNN 搜索、文本扩展查询和其他支持的查询类型。当然,这可以与重新评分以及其他使 Elasticsearch 如此强大的功能结合使用。布尔查询是将这些文本和向量搜索结合成一个单一查询的非常简单的方法。关于这个示例的一个注释是,在 8.12 版本中,KNN 被引入作为一个查询,除了顶级搜索外,这使得这个查询结构更加强大。
- POST /my-index-000001/_search
- {
- "query": {
- "bool": {
- "should": [
- {
- "multi_match": { ... }
- },
- {
- "knn": { ... }
- },
- {
- "text_expansion": { ... }
- },
- {
- "rule_query": { ... }
- }
- ],
- "minimum_should_match": 1,
- "boost": 1
- }
- }
- }
另一种选择是使用 retrievers,从 Elasticsearch 8.14.0 开始,检索器是描述这些复杂检索管道的更简单的方法。 下面是一个示例,它将标准查询与 kNN 查询结合起来作为 retriever,所有这些都汇总起来以使用倒数排名融合 (RRF) 对结果进行排名。
- POST /my-index-000001/_search
- {
- "retriever": {
- "rrf": {
- "retrievers": [
- {
- "standard": {
- "query": {
- "term": {
- "text": "purple sneakers"
- }
- }
- }
- },
- {
- "knn": {
- "field": "vector",
- "query_vector": [1.25, 2, 3.5],
- "k": 50,
- "num_candidates": 100
- }
- }
- ],
- "window_size": 50,
- "rank_constant": 20
- }
- }
- }
现在你有了一个混合搜索查询,如何将所有这些合并成一个单一的结果集呢?这是一个难题,特别是当分数几乎肯定会因结果检索方式的不同而大相径庭时。
经典的方法,使用布尔查询示例,是采用线性组合,在较大的查询中对每个单独子句应用提升。这是一种经过验证的、老式的技术,我们都熟悉并喜爱,但它可能会很棘手。它需要调整才能得到正确的结果,而且你可能永远也无法做到完美。
如果你使用 retrievers,你也可以使用 RRF。这更容易 - 你可以依赖一个算法,而不需要做任何调整。但也存在一些折衷 - 你对结果集的精细控制更少。RRF 不考虑 BM25 的提升,因此如果你在业务规则上进行提升,可能无法立即获得想要的结果。
最终,你应该选择的方法取决于你的数据和你的用例。
一旦你创建了查询,为了提高相关性进行调整是一个难题,但你有几种可用的工具:
然而,人们可能会陷入的最大陷阱是为一个或几个 “pet - 宠物” 查询进行调整:你或者你的老板输入的少数查询。你可以改变算法的所有内容,以获得该查询的最佳结果,但这可能会在下游产生连锁效应,因为现在你无意中已经搞乱了大部分其他查询。
好消息是有一些工具可用于帮助你!
还记得那个宠物查询吗?我有好消息告诉你 - 你仍然可以在不修改相关性算法的情况下获得该宠物查询的出色结果,使用固定(pinned)或推广(prompted)文档的概念。在 Elastic 中,我们将这些称为查询规则。查询规则允许你发送某种类型的上下文,例如用户输入的查询字符串,如果它匹配某些条件,我们可以配置要排名的特定文档,例如排在第一、第二、第三等等。
查询规则的一个很好的用例是应用业务规则。另一个用例是 “修复” 相关性。总体相关性不应该太挑剔,我们应该依靠排名、重新排名和/或 RRF 等方法来正确处理它。但总是有例外的。也许总体相关性已经足够好了,但有几个查询让人们抱怨?好吧,只需设置一个规则。但如果你愿意,你还可以进一步:快速检查一下你的头部查询,确保它们返回了正确的信息,并且这些用户获得了良好的搜索体验,这可能是一个值得的投资。校正一些常见的用户输入查询并不是作弊,然后通过语义搜索的强大功能,重点改进你的主体和尾部查询,这是它真正发挥作用的地方。
那么这是如何工作的呢?
Elastic 查询规则通过创建查询规则集或一个或多个规则列表来定义。每个规则都有必须匹配该规则的条件,以便应用查询,然后根据规则匹配的情况执行操作。一个规则可以有多个条件,根据你从客户端发送的元数据。
- PUT /_query_rules/my-ruleset
- {
- "rules": [
- {
- "rule_id": "rule1",
- "type": "pinned",
- "criteria": [
- {
- "type": "fuzzy",
- "metadata": "query_string",
- "values": [ "puggles", "pugs" ]
- },
- {
- "type": "exact",
- "metadata": "user_country",
- "values": [ "us" ]
- }
- ],
- "actions": {
- "ids": [
- "id1",
- "id2"
- ]
- }
- }
- ]
- }
在这个例子中,用户的查询字符串和他们的位置被发送到一个规则中 - 这两个条件都必须满足才能匹配该规则。要在搜索时触发这些规则,你需要发送一个相应的规则查询,指定你希望匹配的元数据。
- POST /my-index-000001/_search
- {
- "query": {
- "rule_query": {
- "organic": {
- "query_string": {
- "query": "puggles"
- }
- },
- "match_criteria": {
- "query_string": "puggles",
- "user_country": "us"
- },
- "ruleset_id": "my-ruleset"
- }
- }
- }
我们将按顺序应用规则集中的所有匹配规则,并固定你想要返回的文档。 我们目前正在制定计划,使此功能普遍可用并扩展功能:例如,支持规则标准的分词器和分析器,使非技术人员更容易管理查询规则,并可能在顶部提供额外的操作只是固定文档。
你可以在本指南和相应的博客文章中阅读有关查询规则以及如何使用它们的更多信息。
接下来,我们来谈谈同义词。 也许你有一些特定于你的业务的特定领域术语,并且不在任何当前模型中 - 并且你不一定愿意承担费用来微调和训练你自己的模型。
例如:虽然 ELSER 会识别 pug 和 beagle 都与 dog 有关,但它不会将 puggle(pug 和 beagle 的杂交品种)识别为 dog。 同义词在这里可以提供帮助!
同义词是翻译该领域特定术语、俚语和单词的替代方式的好方法,这些单词可能过于专业,无法让模型返回我们想要的匹配项。
在 Elasticsearch 中,我们过去以需要大量手动开销的方式来管理此问题 - 你必须上传同义词文件并重新加载分析器。
在 Elasticsearch 8.10 中,我们引入了 synonyms API,使这一切变得更容易。 与查询规则类似,你可以使用一个或多个定义的同义词创建同义词集,然后当你使用 API 添加、更新或删除同义词时,它会为你重新加载分析器 - 非常简单!
- PUT _synonyms/my-synonyms-set
- {
- "synonyms_set": [
- {
- "id": "pc",
- "synonyms": "pc => personal computer"
- },
- {
- "id": "computer",
- "synonyms": "computer, pc, laptop, desktop"
- }
- ]
- }
然后,你可以更新映射以定义使用此同义词集的自定义分析器。
- PUT /my-index-000001
- {
- "settings": {
- "analysis": {
- "filter": {
- "synonyms_filter": {
- "type": "synonym_graph",
- "synonyms_set": "my-synonyms-set",
- "updateable": true
- }
- },
- "analyzer": {
- "my_index_analyzer": {
- "type": "custom",
- "tokenizer": "standard",
- "filter": ["lowercase"]
- },
- "my_search_analyzer": {
- "type": "custom",
- "tokenizer": "standard",
- "filter": ["lowercase", "synonyms_filter"]
- }
- }
- }
- }
- }
分析器支持同义词的好处是,当我们将来在查询规则中支持分析器时,我们也将能够开箱即用地支持同义词。
你可以在本指南和相应的博客文章中阅读有关同义词 API 以及如何使用它的更多信息。
语义搜索不会取代 BM25 搜索,而是对现有搜索技术的增强。 混合搜索解决了语义搜索固有的许多问题,并且在召回率和功能方面都是两全其美。 语义搜索确实在长尾查询和躯干查询中大放异彩。 查询规则和同义词等工具可以帮助提供最佳的搜索体验,同时释放开发人员宝贵的时间来专注于解决重要问题。
随着形势的发展,我们越来越善于解决语义搜索带来的一些难题,并通过简化和工具使语义搜索和混合搜索变得更容易使用。
作为搜索从业者,我们的目标是返回尽可能最好的结果。 我们的另一个目标是尽可能轻松地做到这一点,并最大限度地降低成本 - 这些成本包括金钱和时间,而时间可能意味着延迟或工程开销。 我们不想浪费宝贵的工程时间 - 我们想用它来解决难题!
你可以尝试我今天在云中谈到的功能! 请务必前往我们的讨论论坛并让我们知道你的想法。
准备好将 RAG 构建到你的应用程序中了吗? 想要尝试使用向量数据库的不同 LLMs?
在 Github 上查看我们的 LangChain、Cohere 等示例笔记本,并参加即将开始的 Elasticsearch 工程师培训!
原文:Retro relevance: Balancing keyword and semantic search — Elastic Search Labs
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。