在 Azure AI 搜索 中向向量查询添加筛选器

注释

prefilterpostfilter 已在最新的稳定 REST API 版本中正式发布。

在 Azure AI 搜索 中,可以使用 筛选表达式将包含或排除条件添加到 vector 查询。 你还可以指定应用筛选器的筛选模式:

  • 在查询执行前,称为“预筛选”
  • 在查询执行后,称为“后筛选”

本文使用 REST 进行说明。 有关其他语言和包含矢量查询的端到端解决方案中的代码示例,请参阅 azure-search-vector-samples GitHub 存储库。

还可以在Azure门户中使用 Search Explorer 查询矢量内容。 在 JSON 视图中,可以添加筛选器并指定筛选器模式。

筛选在矢量查询中的工作原理

Azure AI 搜索使用分层导航小型世界(HNSW)算法进行近似近邻(ANN)搜索,将 HNSW 图形存储在多个分片中。 每个分片都包含整个索引的一部分。

筛选器适用于 filterable非函数 字段(字符串或数字),以基于筛选条件包括或排除搜索文档。 矢量字段本身不可筛选,但可以在同一索引中的其他字段上使用筛选器来缩小考虑用于矢量搜索的文档的范围。 如果索引缺少合适的文本或数字字段,请检查可能有助于筛选的文档元数据,例如 LastModifiedCreatedBy 属性。

参数 vectorFilterMode 控制在搜索阶段应用筛选器作的位置,这会影响结果如何筛选到项目子集(如按类别、标记或其他属性),并影响延迟、召回率和吞吐量。 有三种模式:

  • preFilter 在 HNSW 遍历期间在每个分片上应用筛选器。 此模式可最大化召回率,但可以遍历更多图形,增加高选择性筛选器的 CPU 和延迟。

  • postFilter 独立地对每个分片执行 HNSW 遍历和筛选,在分片级别对结果进行交集操作,然后将每个分片中的顶部 k 聚合到全局顶部 k。 此模式可以为高度选择性的筛选器或小 k 值创建误报。

  • strictPostFilter(预览版)在应用筛选器k查找未筛选的全局顶部。 对于高度选择性的筛选器和小 k 值,此模式导致返回假阴性的风险最高。

有关这些模式的详细信息,请参阅 “设置筛选器模式”。

定义 筛选器

筛选器确定矢量查询的范围,并使用文档 - 搜索 Post (REST API) 进行定义。 除非想要使用预览功能,否则请使用搜索服务 REST API 的最新稳定版本来构造请求。

此 REST API 提供:

  • filter 的条件。
  • vectorFilterMode 用于指定向量查询期间应用筛选器的时间。 有关支持的模式,请参阅 “设置筛选器模式”。
POST https://{search-endpoint}/indexes/{index-name}/docs/search?api-version={api-version}
Content-Type: application/json
api-key: {admin-api-key}

{
    "count": true,
    "select": "title, content, category",
    "filter": "category eq 'Databases'",
    "vectorFilterMode": "preFilter",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . // Trimmed for readability
                -0.02178128,
                -0.00086512347
            ],
            "fields": "contentVector",
            "k": 50
        }
    ]
}

在此示例中,矢量嵌入面向 contentVector 字段,筛选条件应用于 category(一个可筛选的文本字段)。 由于使用了 preFilter 模式,将在搜索引擎运行查询之前应用筛选器,因此在矢量搜索期间只会考虑 Databases 类别中的文档。

设置筛选器模式

vectorFilterMode 参数确定相对于矢量查询执行应用筛选器的时间和方式。 可以使用以下模式:

  • preFilter(推荐)
  • postFilter

预筛选在查询执行之前应用筛选器,这减少了矢量搜索算法的候选集。 然后,将从此筛选后的集合中选择前 k 个结果。

在矢量查询中,preFilter 是默认模式,因为它更重视召回率和质量,而非延迟。

此模式的工作原理

  1. 在每个分片上,在 HNSW 遍历 期间 应用筛选谓词,扩展图,直到找到 k 候选项。

  2. 为每个分片生成预筛选的本地顶部k 结果。

  3. 将筛选的结果聚合到全球顶级k 结果集中。

此模式的效果

遍历扩展搜索空间以查找更多已筛选的候选对象,特别是在筛选器具有选择性时。 这会在所有分片中生成最相似的最上k 结果。 每个分片标识 k 满足筛选谓词的结果。

预筛选保证 k 结果在索引中存在时返回。 对于高度选择性的筛选器,这可能会导致大量图形被遍历,同时降低吞吐量,从而增加计算成本和延迟。 如果筛选器具有高度选择性(几乎没有匹配项),请考虑使用它 exhaustive: true 来执行详尽的搜索。

前置过滤器的示意图。

比较表

模式 召回(筛选结果) 计算成本 漏检风险 何时使用
preFilter 非常高 更高(随着筛选器选择性和复杂性的增加) 无风险 推荐在所有场景中使用默认设置,尤其是在召回率至关重要(如敏感搜索域)、使用选择性筛选器,或使用小型 k时。
postFilter 中到高(随着筛选器选择性的增强而下降) 与未筛选类似,但随着筛选器复杂性的增加而增加 中等(可能错过分片中的某些匹配项) 用于不太严格的筛选器和高k 查询的选项。
strictPostFilter 最低(随筛选器选择性的增加而最迅速减少) 类似于未经滤过 最高级别(可能对选择性筛选器或小型筛选器返回零结果k 筛选之后显示更多结果影响用户体验比出现假阴性的风险更大,这是分面搜索应用程序的一个选项。 不要与小型 k一起使用。

预筛选和后筛选的基准测试

重要

本部分适用于预筛选和后筛选,不适用于严格后筛选。

为了了解一种筛选器模式比另一种筛选器模式表现更好的条件,我们运行了一系列测试来评估小型、中型和大型索引的查询结果。

  • 小型(100,000 个文档、2.5 GB 索引、1,536 个维度)
  • 中(100 万个文档,25 GB 索引,1,536 个维度)
  • 大型(10 亿个文档,1.9 TB 索引,96 个维度)

对于中小型工作负荷,我们使用了具有 1 个分区和 1 个副本的标准 2 (S2) 服务。 对于大型工作负荷,我们使用了具有 12 个分区和 1 个副本的标准 3 (S3) 服务。

索引具有相同的构造:1 个键字段、1 个矢量字段、1 个文本字段和 1 个数字可筛选字段。 定义以下索引时使用 2023-11-01 语法。

def get_index_schema(self, index_name, dimensions):
    return {
        "name": index_name,
        "fields": [
            {"name": "id", "type": "Edm.String", "key": True, "searchable": True},
            {"name": "content_vector", "type": "Collection(Edm.Single)", "dimensions": dimensions,
              "searchable": True, "retrievable": True, "filterable": False, "facetable": False, "sortable": False,
              "vectorSearchProfile": "defaulthnsw"},
            {"name": "text", "type": "Edm.String", "searchable": True, "filterable": False, "retrievable": True,
              "sortable": False, "facetable": False},
            {"name": "score", "type": "Edm.Double", "searchable": False, "filterable": True,
              "retrievable": True, "sortable": True, "facetable": True}
        ],
      "vectorSearch": {
        "algorithms": [
            {
              "name": "defaulthnsw",
              "kind": "hnsw",
              "hnswParameters": { "metric": "euclidean" }
            }
          ],
          "profiles": [
            {
              "name": "defaulthnsw",
              "algorithm": "defaulthnsw"
            }
        ]
      }
    }

在查询中,我们对前后筛选操作使用了相同的筛选器。 我们使用简单的筛选器来确保性能变化是由于筛选模式,而不是筛选复杂性。

结果以每秒查询(QPS)度量。

要点

  • 前置筛选几乎总是比后置筛选慢,除非是在性能大致相同的小索引上进行筛选。

  • 在较大的数据集上,前置筛选速度要慢几个数量级。

  • 为什么预筛选是默认选项如果它几乎总是更慢? 前置筛选可保证返回k结果(如果它们在索引中存在),优先考虑召回和精确度,而非速度。

  • 使用后过滤技术,如果你:

    • 优先考虑速度而不在乎选择(筛选后返回的结果可能少于 k 个结果)。

    • 不要使用过于挑剔的筛选器。

    • 具有足够大小的索引,使预筛选性能不可接受。

详细信息

  • 给定具有 100,000 个矢量的数据集,其维度为 1,536:

    • 筛选数据集的 30% 以上的内容时,前置筛选和后置筛选具有可比性。

    • 筛选数据集的不到 0.1% 的内容时,前置筛选比后置筛选大约慢 50%。

  • 给定具有 100 万个矢量的数据集,其维度为 1,536:

    • 筛选数据集的 30% 以上的内容时,前置筛选大约慢 30%。

    • 筛选数据集的不到 2% 的内容时,前置筛选大约慢 7 倍。

  • 给定一个包含 10 亿个 96 维向量的数据集:

    • 筛选数据集的 5% 以上的内容时,前置筛选大约慢 50%。

    • 筛选数据集的 10% 以下的内容时,前置筛选大约慢 7 倍。

下图显示了前置筛选器的相对 QPS,计算方式为前置筛选器 QPS 除以后置筛选器 QPS。

显示针对小型、中型和大型索引的相对 QPS 性能的图表。

垂直轴表示预筛选相对于后筛选的相对性能,表示为 QPS 的比率(每秒查询数)。 例如:

  • 预筛选值 0.0 比后期筛选慢 100%。
  • 一个值为 0.5 的情况意味着预筛选速度慢了 50%。
  • 前筛选和后筛选的值为1.0时是等效的。

横轴表示筛选率,或表示应用筛选器后候选文档的百分比。 例如,1.00% 的速率意味着筛选条件选择了百分之一的搜索语料库。