在混合搜索中使用倒数排名融合 (RRF) 的相关性评分

倒数排名融合 (RRF) 是一种算法,可评估多个以前的排名结果中的搜索分数以生成统一的结果集。 在 Azure AI 搜索中,每当并行执行两个或更多个查询时,都会使用 RRF。 每个查询都会生成一个排名结果集,RRF 可用于将排名合并和同质化为单个结果集,在查询响应中返回。 始终使用 RRF 的示例方案包括混合搜索和并行执行的多个矢量查询。

RRF 基于倒数排名的概念,即搜索结果列表中第一个相关文档的排名的倒数。 该方法的目标是考虑项目在原始排名中的位置,并赋予在多个列表中排名较高的项目更高的重要性。 这有助于提高最终排名的整体质量和可靠性,使其对融合多个有序搜索结果的任务更加有用。

RRF 排名的工作原理

RRF 的工作方式是获取来自多种方法的搜索结果,为结果中的每个文档分配一个倒数排名分数,然后将这些分数结合起来创建一个新的排名。 其概念是,在多个搜索方法中出现在顶部位置的文档很可能相关度更高,因此应在合并结果中排名较高。

下面是 RRF 过程的简要说明:

  1. 从并行执行的多个查询中获取排名的搜索结果。

  2. 为每个排名列表中的结果分配倒数排名分数。 RRF 为每个结果集中的每个匹配项生成新的 @search.score。 对于搜索结果中的每个文档,引擎基于其在列表中的位置分配倒数排名分数。 分数按 1/(rank + k) 计算,其中 rank 是文档在列表中的位置,k 是一个常量,经实验证明,如果将它设置为 60 这样较小的值,则效果最佳。 请注意,这个 k 值是 RRF 算法中的常量,与控制最近的邻域数的 k 完全不同。

  3. 合并分数。 对于每个文档,引擎将从每个搜索系统中获得的倒数排名分数相加,为每个文档生成合并分数。 

  4. 引擎根据合并分数对文档进行排名和排序。 得到的列表即为融合排名。

只有索引中标记为 searchable 或查询中标记为 searchFields 的字段才用于评分。 搜索结果中仅返回标记为 retrievable 的字段或在查询中的 select 中指定的字段,还会返回它们的搜索分数。

并行查询执行

每当有多个查询执行时,都会使用 RRF。 下面的示例演示了出现并行查询执行时的查询模式:

  • 一个全文查询,加上一个矢量查询(简单的混合场景),等于两个查询执行。
  • 一个全文查询,加上一个面向两个矢量字段的矢量查询,等于三个查询执行。
  • 一个全文查询,加上两个面向五个矢量字段的矢量查询,等于 11 个查询执行

混合搜索结果中的分数

每次对结果排名时,@search.score 属性都包含用于对结果进行排序的值。 分数是由每种方法的排名算法生成的。 每种算法都有自己的范围和幅度。

下图标识了根据每个相关性排名算法的每个匹配项、算法和分数范围返回的评分属性。

搜索方法 参数 评分算法 范围
全文搜索 @search.score BM25 算法 没有上限。
矢量搜索 @search.score HNSW 算法,使用 HNSW 配置中指定的相似性指标。 0.333 - 1.00(余弦),0 到 1(欧几里德和点积)。
混合搜索 @search.score RRF 算法 上限受融合的查询数限制,每个查询对 RRF 分数的贡献最大约为 1。 例如,合并三个查询比只合并两个搜索结果生成的 RRF 分数更高。

混合查询响应中的排名结果数

默认情况下,如果你不使用分页,搜索引擎会返回全文搜索的前 50 个最高排名匹配项,以及矢量搜索的最相似的 k 匹配项。 在混合查询中,top 决定了响应中的结果数。 默认情况下,会返回统一结果集的前 50 个最高排名匹配项。

通常,搜索引擎会比 topk 查找到更多结果。 要返回更多结果,请使用分页参数 topskipnext。 分页是确定每个逻辑页面上的结果数并浏览完整有效负载的方式。

全文搜索的最大限制为 1,000 个匹配项(请参阅 API 响应限制)。 找到 1,000 个匹配项后,搜索引擎便不再进行查找。

有关详细信息,请参阅如何处理搜索结果

搜索评分工作流的示意图

下图展示了混合查询,其调用关键字和矢量搜索,并通过计分概要文件进行提升。

Diagram of prefilters.

生成之前的工作流的查询可能如以下所示:

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2023-11-01
Content-Type: application/json
api-key: {{admin-api-key}}
{
   "search":"hello world",
   "searchFields":"field_a, field_b",
   "vectorQueries": [
       {
           "kind":"vector",
           "vector": [1.0, 2.0, 3.0],
           "fields": "field_c, field_d"
       },
       {
           "kind":"vector",
           "vector": [4.0, 5.0, 6.0],
           "fields": "field_d, field_e"
       }
   ],
   "scoringProfile":"my_scoring_profile"
}