创建矢量索引

在 Azure AI 搜索中,矢量存储具有定义矢量和非矢量字段的索引架构、用于创建和压缩嵌入空间的算法的矢量配置,以及查询请求中使用的矢量字段定义的设置。

创建或更新索引 API 将创建矢量存储。 请按照以下步骤为矢量数据编制索引:

  • 使用矢量算法和可选压缩定义架构
  • 添加矢量字段定义
  • 加载预矢量化的数据作为单独的步骤,或在索引期间使用集成矢量化进行数据分块和编码

本文解释了工作流,并使用 REST 进行说明。 了解基本工作流后,请继续使用 azure-search-vector-samples 存储库中的 Azure SDK 代码示例,以获得在测试和生产代码中使用这些功能的指导。

提示

使用 Azure 门户创建矢量索引并尝试集成数据分块和矢量化。

先决条件

  • 在任何区域内、任何层级上的 Azure AI 搜索。 大多数现有服务都支持矢量搜索。 对于 2019 年 1 月之前创建的服务,有一小部分无法创建矢量索引。 在这种情况下,必须创建新服务。 如果使用的是集成矢量化(用于调用 Azure AI 的技能集),Azure AI 搜索必须与 Azure OpenAI 或 Azure AI 服务位于同一区域。

  • 预先存在的矢量嵌入,或使用集成矢量化,从索引管道调用嵌入模型。

  • 应该了解用于创建嵌入的模型的维度限制。 有效值为 2 到 3072 维度。 在 Azure OpenAI 中,对于 text-embedding-ada-002,数值矢量的长度为 1536。 对于 text-embedding-3-small 或 text-embedding-3-large,矢量长度为 3072。

  • 还应了解有哪些支持的相似度指标。 对于 Azure OpenAI,使用 cosine 计算相似度。

  • 你应该知道如何创建索引。 架构必须包含文档键的字段、要搜索或筛选的其他字段,以及索引编制和查询期间所需行为的其他配置。

准备为文档编制索引

在编制索引之前,请汇编一个包含矢量和非矢量数据字段的文档有效负载。 文档结构必须符合索引架构。

确保文档:

  1. 提供用于唯一标识每个文档的字段或元数据属性。 所有搜索索引都需要文档键。 若要满足文档键要求,源文档必须包含一个可以在索引中唯一标识它的字段或属性。 此源字段必须映射到搜索索引中类型为 Edm.Stringkey=true 的索引字段。

  2. 在源字段中提供矢量数据(单精度浮点数的数组)。

    矢量字段包含通过嵌入模型生成的数组,每个字段一个嵌入,其中字段是顶级字段(不是嵌套类型或复杂类型的一部分)。 为了使集成尽量简单化,我们建议使用 Azure OpenAI 中的嵌入模型,例如用于文本文档的 text-embedding-ada-002 或用于图像的图像检索 REST API

    如果可以依赖索引器和技能集,请考虑在索引编制过程中使用集成矢量化对图像和字母数字内容进行编码。 字段定义用于矢量字段,但源数据可以是文本或图像,矢量数组是在索引编制过程中创建的。

  3. 为其他字段提供人类可读的字母数字内容,以用于查询响应,以及在同一请求中包含全文搜索或语义排名的混合查询方案。

搜索索引应包含你想要支持的所有查询方案的字段和内容。 假设你想要搜索或筛选产品名称、版本、元数据或地址。 在这种情况下,相似性搜索并不是特别有用。 关键字搜索、地理搜索或筛选器是更好的选择。 包含矢量和非矢量数据综合字段集合的搜索索引为查询构造和响应组合提供了最大的灵活性。

本文的加载矢量数据部分提供了包含矢量和非矢量字段的文档有效负载的简短示例。

添加矢量搜索配置

矢量配置指定在索引编制过程中使用的参数,以便在矢量节点之间创建“最近的邻域”信息:

  • 分层可导航小世界 (HNSW)
  • 穷举 KNN

如果在字段中选择 HNSW,则可以在查询时选择穷举 KNN。 但另一个方向不起作用:如果选择详尽,则之后无法请求 HNSW 搜索,因为实现近似搜索的额外数据结构不存在。

矢量配置还指定用于减小矢量大小的量化方法:

  • Scalar
  • 二进制文件(仅在 2024-07-01 以及较新的 Azure SDK 包中提供)

有关如何迁移到最新版本的说明,请参阅升级 REST API

2024-07-01 已正式发布。 它支持矢量配置,具有:

  • vectorSearch.algorithms 支持 HNSW 和详尽的 KNN。
  • vectorSearch.compressions 支持标量和二进制量化、过度采样以及使用原始矢量重新排名。
  • vectorSearch.profiles 提供算法和压缩配置的多种组合。

请务必制定矢量化内容的策略。 建议使用集成矢量化查询时间矢量器进行内置编码。

  1. 使用创建或更新索引 API 创建索引。

  2. 在索引中添加 vectorSearch 节,以指定用于创建嵌入空间的搜索算法。

     "vectorSearch": {
         "compressions": [
             {
                 "name": "scalar-quantization",
                 "kind": "scalarQuantization",
                 "rerankWithOriginalVectors": true,
                 "defaultOversampling": 10.0,
                     "scalarQuantizationParameters": {
                         "quantizedDataType": "int8"
                     }
             },
             {
                 "name": "binary-quantization",
                 "kind": "binaryQuantization",
                 "rerankWithOriginalVectors": true,
                 "defaultOversampling": 10.0,
             }
         ],
         "algorithms": [
             {
                 "name": "hnsw-1",
                 "kind": "hnsw",
                 "hnswParameters": {
                     "m": 4,
                     "efConstruction": 400,
                     "efSearch": 500,
                     "metric": "cosine"
                 }
             },
             {
                 "name": "hnsw-2",
                 "kind": "hnsw",
                 "hnswParameters": {
                     "m": 8,
                     "efConstruction": 800,
                     "efSearch": 800,
                     "metric": "hamming"
                 }
             },
             {
                 "name": "eknn",
                 "kind": "exhaustiveKnn",
                 "exhaustiveKnnParameters": {
                     "metric": "euclidean"
                 }
             }
    
         ],
         "profiles": [
           {
             "name": "vector-profile-hnsw-scalar",
             "compression": "scalar-quantization",
             "algorithm": "hnsw-1"
           }
         ]
     }
    

    要点

    • 压缩、算法和配置文件的每种配置的名称在索引中必须对其类型具有唯一性。

    • vectorSearch.compressions.kind 可以是 scalarQuantizationbinaryQuantization

    • vectorSearch.compressions.rerankWithOriginalVectors 使用原始的未压缩向量来重新计算相似性,并对初始搜索查询返回的顶部结果进行重新排名。 即使 stored 为 false,未压缩的向量也存在于搜索索引中。 此属性是可选的。 默认值为 true。

    • vectorSearch.compressions.defaultOversampling 将考虑更广泛的潜在结果,以抵消量化导致的信息减少。 潜在结果的公式由查询中的 k 和过采样乘数组成。 例如,如果查询指定 k 为 5,并且过采样为 20,则查询实际上会请求 100 个文档用于重新排序,并为此使用原始未压缩向量。 仅返回前 k 个重新排序的结果。 此属性是可选的。 默认值为 4。

    • vectorSearch.compressions.scalarQuantizationParameters.quantizedDataType 必须设置为 int8。 这是目前唯一支持的基元数据类型。 此属性是可选的。 默认值为 int8

    • vectorSearch.algorithms.kind"hnsw""exhaustiveKnn"。 它们是近似最近邻域 (ANN) 算法,用于在索引编制期间组织矢量内容。

    • vectorSearch.algorithms.m 是双向链接计数。 默认值为 4。 范围为 4 到 10。 较低的值应在结果中返回较少的干扰数据。

    • vectorSearch.algorithms.efConstruction 是索引编制期间使用的最近邻域数量。 默认值为 400。 范围为 100 到 1,000。

    • "vectorSearch.algorithms.fSearch 是搜索期间使用的最近邻域数量。 默认值为 500。 范围为 100 到 1,000。

    • 如果使用的是 Azure OpenAI,则 vectorSearch.algorithms.metric 应为“cosine”,否则请使用与你正在使用的嵌入模型关联的相似性指标。 支持的值为 cosinedotProducteuclideanhamming(用于对二进制数据编制索引)。

    • vectorSearch.profiles 添加一个抽象层以容纳更丰富的定义。 配置文件在 vectorSearch 中定义,然后在每个矢量字段中按名称引用。 它是压缩和算法配置的组合。 这是分配给矢量字段的属性,它可以确定字段的算法和压缩。

将矢量字段添加到字段集合

字段集合必须包含文档键字段、矢量字段以及混合搜索方案所需的任何其他字段。

矢量字段的特征在于其数据类型、基于用于输出矢量的嵌入模型 dimensions 属性以及矢量配置文件。

2024-07-01 已正式发布。

  1. 使用创建或更新索引创建索引。

  2. 使用以下属性定义矢量字段。 可为每个字段存储一个生成的嵌入。 对于每个矢量字段:

    • type 必须是矢量数据类型Collection(Edm.Single) 是嵌入模型最常见的属性。
    • dimensions 是嵌入模型生成的维度数。 对于 text-embedding-ada-002,它是 1536。
    • vectorSearchProfile 是在索引中其他位置定义的配置文件的名称。
    • searchable 必须为 true。
    • retrievable 可以为 true 或 false。 True 以纯文本形式返回原始矢量(1536 个)并消耗存储空间。 如果你要将矢量结果传递给下游应用,请设置为 true。
    • stored 可以为 true 或 false。 它确定是否存储额外的矢量副本以供检索。 有关详细信息,请参阅减小矢量大小
    • filterablefacetablesortable 必须为 false。
  3. 如果要在向量查询调用预筛选或后期筛选,请将可筛选的非函数字段添加到集合,例如 filterable 设置为 true 的“title”。

  4. 添加其他字段用于定义你要编制索引的文本内容的主旨和结构。 至少需要一个文档键。

    还应该添加在查询或其响应中有用的字段。 以下示例显示了标题和内容(等效于矢量)的矢量字段(“titleVector”、“contentVector”)。 它还提供了等效文本内容的字段(“title”、“content”),可用于在搜索结果中进行排序、筛选和读取。

    以下示例显示了字段集合:

    PUT https://my-search-service.search.azure.cn/indexes/my-index?api-version=2024-07-01&allowIndexDowntime=true
    Content-Type: application/json
    api-key: {{admin-api-key}}
    {
        "name": "{{index-name}}",
        "fields": [
            {
                "name": "id",
                "type": "Edm.String",
                "key": true,
                "filterable": true
            },
            {
                "name": "title",
                "type": "Edm.String",
                "searchable": true,
                "filterable": true,
                "sortable": true,
                "retrievable": true
            },
            {
                "name": "titleVector",
                "type": "Collection(Edm.Single)",
                "searchable": true,
                "retrievable": true,
                "stored": true,
                "dimensions": 1536,
                "vectorSearchProfile": "vector-profile-1"
            },
            {
                "name": "content",
                "type": "Edm.String",
                "searchable": true,
                "retrievable": true
            },
            {
                "name": "contentVector",
                "type": "Collection(Edm.Single)",
                "searchable": true,
                "retrievable": false,
                "stored": false,
                "dimensions": 1536,
                "vectorSearchProfile": "vector-profile-1"
            }
        ],
        "vectorSearch": {
            "algorithms": [
                {
                    "name": "hnsw-1",
                    "kind": "hnsw",
                    "hnswParameters": {
                        "m": 4,
                        "efConstruction": 400,
                        "efSearch": 500,
                        "metric": "cosine"
                    }
                }
            ],
            "profiles": [
                {
                    "name": "vector-profile-1",
                    "algorithm": "hnsw-1"
                }
            ]
        }
    }
    

加载要编制索引的矢量数据

你提供的要编制索引的内容必须符合索引架构,并包含文档键的唯一字符串值。 预矢量化数据将加载到一个或多个矢量字段中,这些矢量字段可与包含字母数字内容的其他字段共存。

可以使用推送或拉取方法进行数据引入。

使用文档 - 索引将矢量和非矢量数据加载到索引中。 用于索引的推送 API 在所有稳定版本和预览版本中都是相同的。 使用以下任意 API 加载文档:

POST https://{{search-service-name}}.search.azure.cn/indexes/{{index-name}}/docs/index?api-version=2024-07-01

{
    "value": [
        {
            "id": "1",
            "title": "Azure App Service",
            "content": "Azure App Service is a fully managed platform for building, deploying, and scaling web apps. You can host web apps, mobile app backends, and RESTful APIs. It supports a variety of programming languages and frameworks, such as .NET, Java, Node.js, Python, and PHP. The service offers built-in auto-scaling and load balancing capabilities. It also provides integration with other Azure services, such as Azure DevOps, GitHub, and Bitbucket.",
            "category": "Web",
            "titleVector": [
                -0.02250031754374504,
                 . . . 
                        ],
            "contentVector": [
                -0.024740582332015038,
                 . . .
            ],
            "@search.action": "upload"
        },
        {
            "id": "2",
            "title": "Azure Functions",
            "content": "Azure Functions is a serverless compute service that enables you to run code on-demand without having to manage infrastructure. It allows you to build and deploy event-driven applications that automatically scale with your workload. Functions support various languages, including C#, F#, Node.js, Python, and Java. It offers a variety of triggers and bindings to integrate with other Azure services and external services. You only pay for the compute time you consume.",
            "category": "Compute",
            "titleVector": [
                -0.020159931853413582,
                . . .
            ],
            "contentVector": [
                -0.02780858241021633,
                 . . .
            ],
            "@search.action": "upload"
        }
        . . .
    ]
}

检查矢量内容的索引

要进行验证,可以使用 Azure 门户中的搜索资源管理器或 REST API 调用来查询索引。 由于 Azure AI 搜索无法将矢量转换为人类可读的文本,因此请尝试从同一文档中返回提供匹配证据的字段。 例如,如果矢量查询针对“titleVector”字段,则你可以为搜索结果选择“title”。

必须将字段的属性设置为“retrievable”才能将其包含在结果中。

  • 查看“搜索管理”>“索引”中的索引,以查看总体索引大小和矢量索引大小。 正矢量索引大小指示存在矢量。

  • 使用搜索资源管理器来查询索引。 搜索资源管理器有两个视图:查询视图(默认)和 JSON 视图。

    • 设置“查询选项”>“隐藏搜索结果中的矢量值”以获取更多可读结果。

    • 将 JSON 视图用于矢量查询。 你可以粘贴要执行的矢量查询的 JSON 定义,或者如果索引具有矢量器赋值,则使用内置的文本到矢量或图像到矢量转换。

    • 使用默认查询视图可以快速确认索引是否包含矢量。 查询视图用于全文搜索。 尽管无法将其用于矢量查询,但可以发送空搜索 (search=*) 来检查内容。 所有字段(包括矢量字段)的内容均以纯文本形式返回。

    • 有关详细信息,请参阅创建矢量查询

更新矢量存储

若要更新矢量存储,请修改架构,并在必要时重新加载文档以填充新字段。 用于架构更新的 API 包括创建或更新索引 (REST)Azure SDK for .NET 中的 CreateOrUpdateIndexAzure SDK for Python 中的create_or_update_index,以及其他 Azure SDK 中的类似方法。

有关更新索引的标准指南,请参阅更新或重新生成索引

要点包括:

  • 更新和删除现有字段通常需要进行删除并重新生成。

  • 但是,你可以通过以下修改来更新现有架构,而无需重新生成:

    • 向字段集合添加新字段。
    • 添加新的矢量配置,该配置已分配给新字段而不是尚未矢量化的现有字段。
    • 更改现有字段上的“可检索”(值为 true 或 false)。 矢量字段必须是可搜索和可检索的,但如果希望在无法删除和重新生成的情况下禁用对矢量字段的访问,则可以将“可检索”设置为 false。

后续步骤

接下来,我们建议在搜索索引中查询矢量数据

azure-search-vector 存储库中的代码示例演示了包括架构定义、矢量化、索引和查询的端到端工作流。

有可参阅的 PythonC#JavaScript 的演示代码。