在 Azure AI 搜索的矢量查询中添加筛选器
你可以定义包含筛选器表达式的矢量查询请求,以向查询添加包含或排除条件。 在本文中,将学习以下内容:
本文使用 REST 进行说明。 有关其他语言的代码示例,请参阅 azure-search-vector-samples GitHub 存储库,了解包含矢量查询的端到端解决方案。
还可以在 Azure 门户中使用搜索资源管理器来查询矢量内容。 如果使用 JSON 视图,可以添加筛选器并指定筛选器模式。
筛选在矢量查询中的工作原理
筛选器适用于 filterable
非矢量字段(字符串字段或数字),以根据筛选条件包括或排除搜索文档。 尽管矢量字段本身不可筛选,但筛选器可以应用于同一索引中的其他字段,从而包括或排除也包含矢量字段的文档。
筛选器将根据 vectorFilterMode
参数在查询执行之前或之后应用。
定义 筛选器
筛选器确定了矢量查询的范围。 筛选器是在索引中其属性设置为 filterable
的非矢量字符串和数字字段上设置的,会针对这些字段进行迭代,但筛选器的目的是决定矢量查询的执行范围:可搜索空间,或者搜索结果的内容。
如果源字段不包含文本或数字值,请检查文档元数据(例如 LastModified 或 CreatedBy 属性),这在元数据筛选器中可能有用。
2024-07-01 是此 API 的稳定版本。 它具有:
- 用于前筛选(默认)或后筛选筛选模式的
vectorFilterMode
。 filter
提供条件。
在以下示例中,矢量是此查询字符串的表示形式:“what Azure services support full text search”。 查询面向 contentVector
字段。 实际矢量有 1536 个嵌入,因此在本示例中对其进行了修剪以便于阅读。
在搜索引擎执行矢量查询之前,筛选条件将应用于可筛选文本字段(在本示例中为 category
)。
POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/search?api-version=2024-07-01
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,
. . .
-0.02178128,
-0.00086512347
],
"exhaustive": true,
"fields": "contentVector",
"k": 5
}
]
}
设置 vectorFilterMode
vectorFilterMode 查询参数可确定是在执行矢量查询之前还是之后应用筛选器。
使用预筛选模式
前置筛选在查询执行之前应用筛选器,从而减少矢量搜索算法查找相似内容时所基于的搜索表面积。
在矢量查询中,preFilter
是默认值。
使用后筛选模式
后置筛选在查询执行后应用筛选器,缩小搜索结果范围。
矢量筛选器模式的基准测试
为了了解一种筛选器模式比另一种筛选器模式表现更好的条件,我们运行了一系列测试来评估小型、中型和大型索引的查询结果。
- 小型(100,000 个文档,2.5 GB 索引,1536 个维度)
- 中型(100 万个文档,25 GB 索引,1536 个维度)
- 大型(10 亿个文档,1.9 TB 索引,96 个维度)
对于中小型工作负荷,我们使用了具有 1 个分区和 1 个副本的标准 2 (S2) 服务。 对于大型工作负荷,我们使用了具有 12 个分区和 1 个副本的标准 3 (S3) 服务。
索引具有相同的构造:1 个键字段、1 个矢量字段、1 个文本字段和 1 个数字可筛选字段。 以下索引是使用 2023-11-03 语法定义的。
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
个结果) - 所用筛选器的筛选条件较为宽松
- 索引的大小足够大,导致前置筛选性能不可接受
- 相对于选择而言更重视速度(后置筛选可能返回不到
详细信息
给定一个在维度数为 1536 时矢量数为 100,000 的数据集:
- 筛选数据集的 30% 以上的内容时,前置筛选和后置筛选具有可比性。
- 筛选数据集的不到 0.1% 的内容时,前置筛选比后置筛选大约慢 50%。
给定一个在维度数为 1536 时矢量数为 100 万的数据集:
- 筛选数据集的 30% 以上的内容时,前置筛选大约慢 30%。
- 筛选数据集的不到 2% 的内容时,前置筛选大约慢 7 倍。
给定一个在维度数为 96 时矢量数为 10 亿的数据集:
- 筛选数据集的 5% 以上的内容时,前置筛选大约慢 50%。
- 筛选数据集的 10% 以下的内容时,前置筛选大约慢 7 倍。
下图显示了前置筛选器的相对 QPS,计算方式为前置筛选器 QPS 除以后置筛选器 QPS。
纵轴是前置筛选的 QPS 与后置筛选的 QPS 之比。 例如,纵轴上的值为 0.0 表示前置筛选速度慢 100%,0.5 表示前置筛选速度慢 50%,1.0 表示前置筛选和后置筛选的速度相当。
横轴表示筛选率,或表示应用筛选器后候选文档的百分比。 例如,1.00%
表示筛选条件选择了搜索语料库的 1%。