关键字搜索中的相关性(BM25 评分)
本文介绍用于计算全文搜索的搜索分数的 BM25 相关性评分算法。 BM25 相关性是全文搜索所独有的。 不会对筛选查询、自动完成和建议查询、通配符搜索或模糊搜索查询的相关性进行评分或排名。
全文搜索中使用的评分算法
Azure AI 搜索为全文搜索提供了以下评分算法:
算法 | 使用情况 | 范围 |
---|---|---|
BM25Similarity |
修复了 2020 年 7 月之后创建的所有搜索服务的算法。 可以配置此算法,但不能切换到较旧的算法(经典)。 | 无限制。 |
ClassicSimilarity |
在较旧的搜索服务上显示。 可以选择加入 BM25,并根据索引选择算法。 | 0 < 1.00 |
BM25 和 Classic 都是类似于 TF-IDF 的检索函数,它们使用字词频率 (TF) 和逆向文档频率 (IDF) 作为变量来计算每个文档-查询对的相关性评分,然后使用这些评分来获得排名结果。 虽然在概念上与 Classic 类似,但 BM25 立足于概率信息检索来生成更直观的匹配项(通过用户研究进行度量)。
BM25 提供高级自定义选项,例如,允许用户确定如何根据匹配字词的字词频率调整相关性评分。 有关详细信息,请参阅配置评分算法。
注意
如果你使用的是在 2020 年 7 月之前创建的搜索服务,则评分算法很可能是以前的默认设置 ClassicSimilarity
,该算法可以根据索引进行升级。 有关详细信息,请参阅在较旧的服务上启用 BM25 评分。
BM25 排名的工作原理
相关性评分是指搜索分数 (@search.score) 的计算,它作为一个项在当前查询上下文中的相关性的指示器。 范围是无界的。 不过,分数越高,项就越相关。
根据字符串输入和查询本身的统计属性计算搜索分数。 Azure AI 搜索会查找与搜索词匹配的文档(与部分搜索词或全部搜索词匹配,具体取决于 searchMode),并优先列出包含该搜索词多个实例的文档。 如果搜索词在数据索引中很少见,但在文档中很常见,搜索分数仍升至更高。 这种计算相关性的方法的基本原理称为 TF-IDF(字词频率-逆向文档频率)。
搜索分数可以在整个结果集中重复。 当多个命中具有相同的搜索评分时,相同评分项的顺序就不会是明确、稳定的。 再次运行查询,你可能会看到项偏移位置,尤其是使用免费服务或包含多个副本的可计费服务时。 对于具有相同分数的两个项,无法保证先显示哪一个。
若要打破重复分数之间的平局,可添加一个 $orderby 子句,以便先按分数进行排序,然后按另一个可排序字段(例如 $orderby=search.score() desc,Rating desc
)进行排序。 有关详细信息,请参阅 $orderby。
只有索引中标记为 searchable
或查询中标记为 searchFields
的字段才用于评分。 搜索结果中仅返回标记为 retrievable
的字段或在查询中的 select
中指定的字段,还会返回它们的搜索分数。
注意
@search.score = 1
指示未评分或未排名的结果集。 评分在所有结果中是统一的。 如果查询形式是模糊搜索、通配符或正则表达式查询,或者是空搜索(search=*
,有时与筛选器配对,其中的筛选器是返回匹配项的主要方式),则会出现未评分的结果。
文本结果中的分数
每次对结果排名时,@search.score
属性都包含用于对结果进行排序的值。
下表标识在每个匹配、算法和范围上返回的评分属性。
搜索方法 | 参数 | 评分算法 | 范围 |
---|---|---|---|
全文搜索 | @search.score |
BM25 算法,使用索引中指定的参数。 | 无限制。 |
分数变体
搜索评分表达了对相关性的总体认知,反映了相对于同一结果集中的其他文档的匹配强度。 但是,评分在不同的查询之间并非始终一致,因此,在处理查询时,你可能会注意到搜索文档的排序方式存在细微差别。 下面对发生这种情况的可能原因给出了几条解释。
原因 | 说明 |
---|---|
相同的评分 | 如果多个文档具有相同的评分,其中的任何一个文档都可能会首先出现。 |
数据易变性 | 添加、修改或删除文档时,索引内容会发生变化。 在处理索引更新的过程中,字词频率会发生变化,从而影响了匹配文档的搜索评分。 |
多个副本 | 对于使用多个副本的服务,将并行针对每个副本发出查询。 用于计算搜索评分的索引统计信息是根据每个副本计算的,结果将在查询响应中合并和排序。 副本基本上是彼此的镜像,但由于状态存在细微差别,因此统计信息可能有所不同。 例如,一个副本可能删除了对统计信息有影响的文档,这些统计信息由其他副本合并而来。 通常,按副本的统计信息的差异在较小索引中更明显。 以下部分提供有关此条件的详细信息。 |
分片对查询结果的影响
分片是索引的一个块。 Azure AI 搜索将索引划分为分片,以便更快地添加分区(通过将分片移动到新的搜索单位)。 在搜索服务中,分片管理是实现细节且不可配置,但知道索引是分片的有助于你了解排名和自动完成行为中的偶然异常:
排名异常:搜索评分首先在分片级别计算,然后聚合成单个结果集。 根据分片内容的特征,一个分片中的匹配项的排名可能高于另一个分片中的匹配项。 如果你在搜索结果中发现与预料相反的排名,则很可能是由于分片的影响,尤其是在索引较小的情况下。 你可以通过选择在整个索引中全局计算评分来避免这些排名异常,但这样做会导致性能下降。
自动完成异常:自动完成查询(根据仅输入了一部分内容的单词的前几个字符进行匹配)接受一个模糊参数,该参数允许有微小的拼写差异。 对于自动完成,模糊匹配被限制为当前分片中的字词。 例如,如果某个分片包含“Microsoft”,并且输入了部分字词“micro”,则搜索引擎将针对该分片中的“Microsoft”进行匹配,但不会在包含索引剩余部分的其他分片中进行匹配。
下图显示了副本、分区、分片与搜索单位之间的关系。 它显示了一个示例,该示例说明了在具有两个副本和两个分区的服务中,单个索引如何跨越四个搜索单位。 这四个搜索单位每个都只存储索引的一半分片。 左列中的搜索单位存储分片的第一半,构成第一个分区,而右列中的搜索单位存储分片的第二半,构成第二个分区。 由于有两个副本,因此每个索引分片有两个副本。 顶部行中的搜索单位存储着一个副本,构成第一个副本,而底部行中的搜索单位存储着另一个副本,构成第二个副本。
上图只是一个示例。 分区和副本有许多可能的组合,最多可包含 36 个搜索单位(总计)。
注意
副本数和分区数必须能被 12 整除(具体而言,为 1、2、3、4、6、12)。 Azure AI 搜索将每个索引预先分割为 12 个分片,以便将其平均分散到所有分区。 例如,如果服务有三个分区,而你创建了新索引,则每个分区将包含该索引的四个分片。 Azure AI 搜索为索引分片的方法属于实现细节,在将来的版本中可能发生变化。 尽管目前的分区数为 12,但请不要料想将来该数字永远都是 12。
评分统计信息和粘滞会话
为了实现可伸缩性,Azure AI 搜索会通过分片过程横向分布每个索引,这意味着,索引的某些部分在物理上是独立的。
默认情况下,文档的评分是根据分片中数据的统计属性计算的。 此方法对于大型数据集而言通常不会造成问题,与基于所有分片中的信息计算评分相比,此方法可提供更好的性能。 也就是说,使用这种性能优化可能会导致两个非常相似的文档(甚至相同的文档)最终可能出现不同的相关性评分(如果这些文档出现在不同的分片中)。
如果你偏向于基于所有分片中的统计属性计算分数,可通过添加 scoringStatistics=global
作为查询参数(或者添加 "scoringStatistics": "global"
作为查询请求的正文参数)来执行此操作。
POST https://[service name].search.azure.cn/indexes/hotels/docs/search?api-version=2024-07-01
{
"search": "<query string>",
"scoringStatistics": "global"
}
使用 scoringStatistics
可确保同一副本中的所有分片提供相同的结果。 也就是说,不同的副本可能相互之间略有不同,因为它们总是会随着索引的最新更改而更新。 在某些情况下,你可能希望用户在“查询会话”期间获得更一致的结果。 在这种情况下,可以提供 sessionId
作为查询的一部分。 sessionId
是你创建的用于引用唯一用户会话的唯一字符串。
POST https://[service name].search.azure.cn/indexes/hotels/docs/search?api-version=2024-07-01
{
"search": "<query string>",
"sessionId": "<string>"
}
只要使用相同的 sessionId
,就会尽最大努力以同一副本为目标,从而提高用户将看到的结果的一致性。
注意
反复使用相同的 sessionId
值可能会干扰跨副本对请求进行负载均衡,并对搜索服务的性能产生负面影响。 用作 sessionId 的值不能以“_”字符开头。
相关性优化
在 Azure AI 搜索中,可以配置 BM25 算法参数,并通过以下机制调整搜索相关性和提高搜索分数:
方法 | 实现 | 说明 |
---|---|---|
评分算法配置 | 搜索索引 | |
为配置文件评分 | 搜索索引 | 提供根据内容特征提高匹配项的搜索分数的标准。 例如,你可能想要根据创收能力提升匹配项、提升新项或提升库存时间太长的项。 计分概要文件属于索引定义的一部分,由加权字段、函数和参数组成。 可以使用计分概要文件更改来更新现有索引,无需重新生成索引。 |
featuresMode 参数 | 查询请求 | 此参数主要用于解包分数,但可用于提供自定义评分解决方案的代码。 |
featuresMode 参数(预览版)
搜索文档请求有一个新的 featuresMode 参数,该参数可以在字段级别提供有关相关性的更多详细信息。 尽管 @searchScore
是针对整个文档计算的(该文档在此查询的上下文中的相关性如何),但你可以通过 featuresMode 获取有关各个字段的信息,以 @search.features
结构表示。 该结构包含查询中使用的所有字段(查询中通过 searchFields 指定的特定字段,或索引中属性为“可搜索”的所有字段)。 对于每个字段,可获得以下值:
- 在字段中找到的唯一标记数
- 相似性得分,即字段内容相对于查询项的相似程度的度量
- 字词频率,即在字段中找到查询项的次数
对于以“description”和“title”字段为目标的查询,包含 @search.features
的响应可能如下所示:
"value": [
{
"@search.score": 5.1958685,
"@search.features": {
"description": {
"uniqueTokenMatches": 1.0,
"similarityScore": 0.29541412,
"termFrequency" : 2
},
"title": {
"uniqueTokenMatches": 3.0,
"similarityScore": 1.75451557,
"termFrequency" : 6
}
}
}
]
可以使用自定义评分解决方案中的这些数据点或使用相关信息来调试搜索相关性问题。
全文查询响应中的排名结果数
默认情况下,如果你不使用分页,搜索引擎会返回全文搜索的前 50 个最高排名匹配项。 可以使用 top
参数返回较少或较多的项(单个响应中最多 1,000 个)。 全文搜索的最大限制为 1,000 个匹配项(请参阅 API 响应限制)。 找到 1,000 个匹配项后,搜索引擎便不再进行查找。
要返回更多或更少结果,请使用分页参数 top
、skip
和 next
。 分页是确定每个逻辑页面上的结果数并浏览完整有效负载的方式。 有关详细信息,请参阅如何处理搜索结果。