Link Search Menu Expand Document Documentation Menu

自动生成稀疏向量嵌入

自动生成稀疏向量嵌入使得神经稀疏搜索能够像词汇搜索一样运行。为了利用这种封装,请设置一个摄取管道,以便在摄取过程中从文档文本中创建和存储稀疏向量嵌入。在查询时,输入纯文本,它将自动转换为向量嵌入以进行搜索。

神经稀疏搜索的工作原理如下:

  • 在摄取时,神经稀疏搜索使用稀疏编码模型从文本字段生成稀疏向量嵌入。

  • 在查询时,神经稀疏搜索以以下两种搜索模式之一运行:

    • 仅文档模式(默认):稀疏编码模型在摄取时从文档生成稀疏向量嵌入。在查询时,神经稀疏搜索对查询文本进行分词,并从查找表中获取标记权重。这种方法以轻微降低搜索相关性为代价,提供了更快的检索速度。查询时分词可以由以下组件执行:
      • DL 模型分析器(默认)DL 模型分析器使用内置的 ML 模型。这种方法以轻微降低搜索相关性为代价,提供了更快的检索速度。
      • 自定义分词器:您可以使用 模型 API 部署自定义分词器来对查询文本进行分词。这种方法在保持神经稀疏搜索实现中一致分词的同时,提供了更大的灵活性。
    • 双编码器模式:稀疏编码模型从文档和查询文本中生成稀疏向量嵌入。这种方法以增加延迟为代价,提供了更好的搜索相关性。

我们建议使用带 DL 分析器的默认仅文档模式,因为它为大多数用例提供了性能和相关性的最佳平衡。

带分析器的默认仅文档模式工作原理如下:

  1. 在摄取时:
    • 您注册的稀疏编码模型生成稀疏向量嵌入。
    • 这些嵌入以标记-权重对的形式存储在您的索引中。
  2. 在搜索时:
    • 查询文本使用内置的 DL 模型分析器(它使用相应的内置 ML 模型分词器)进行分析。
    • 标记权重从 OpenSearch 中内置的预计算查找表中获取。
    • 分词与稀疏编码模型的预期匹配,因为它们都使用相同的分词方案。

因此,您必须在摄取时选择并应用一个 ML 模型,但在搜索时只需要指定一个分析器(而不是模型)。

稀疏编码模型/分析器兼容性

下表列出了所有可用于仅文档模式的模型。每个模型都与其在搜索时应使用的兼容分析器配对。根据您的语言需求(英语或多语言)和性能要求进行选择。

模型 分析器 BEIR 相关性 MIRACL 相关性 模型参数
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v1 bert-uncased 0.490 不适用 133M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v2-distill bert-uncased 0.504 不适用 67M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v2-mini bert-uncased 0.497 不适用 23M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill bert-uncased 0.517 不适用 67M
amazon/neural-sparse/opensearch-neural-sparse-encoding-multilingual-v1 mbert-uncased 0.500 0.629 168M

示例:使用带分析器的默认仅文档模式

此示例使用推荐的带 DL 模型分析器仅文档模式。在此模式下,OpenSearch 在摄取时应用稀疏编码模型,在搜索时应用兼容的 DL 模型分析器。有关其他模式的示例,请参见使用神经稀疏搜索的自定义配置

在本例中,您将使用神经稀疏搜索以及 OpenSearch 的内置机器学习 (ML) 模型托管和摄取管道。由于文本到嵌入的转换是在 OpenSearch 内部执行的,因此在摄取和搜索文档时,您将使用文本。

先决条件

开始之前,请完成先决条件

步骤 1:配置用于摄取的稀疏编码模型

要使用仅文档模式,首先要选择一个稀疏编码模型,用于摄取时。然后,注册并部署该模型。例如,要注册并部署 opensearch-neural-sparse-encoding-doc-v3-distill 模型,请使用以下请求:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill",
  "version": "1.0.0",
  "model_format": "TORCH_SCRIPT"
}

注册模型是一个异步任务。OpenSearch 为您注册的每个模型返回一个任务 ID:

{
  "task_id": "aFeif4oB5Vm0Tdw8yoN7",
  "status": "CREATED"
}

您可以通过调用 Tasks API 来检查任务状态:

GET /_plugins/_ml/tasks/aFeif4oB5Vm0Tdw8yoN7

一旦任务完成,任务状态将变为 COMPLETED,并且 Tasks API 响应将包含已注册模型的模型 ID:

{
  "model_id": "<model ID>",
  "task_type": "REGISTER_MODEL",
  "function_name": "SPARSE_ENCODING",
  "state": "COMPLETED",
  "worker_node": [
    "4p6FVOmJRtu3wehDD74hzQ"
  ],
  "create_time": 1694358489722,
  "last_update_time": 1694358499139,
  "is_async": true
}

记下您创建的模型的 model_id;后续步骤中将需要它。

步骤 2:创建摄取管道

要生成稀疏向量嵌入,您需要创建一个包含 sparse_encoding 处理器摄取管道,该处理器将文档字段中的文本转换为向量嵌入。处理器的 field_map 决定了从哪些输入字段生成向量嵌入以及将嵌入存储到哪些输出字段。

以下示例请求创建一个摄取管道,其中 passage_text 中的文本将被转换为稀疏向量嵌入,并存储在 passage_embedding 中。在请求中提供已注册模型的模型 ID:

PUT /_ingest/pipeline/nlp-ingest-pipeline-sparse
{
  "description": "An sparse encoding ingest pipeline",
  "processors": [
    {
      "sparse_encoding": {
        "model_id": "<bi-encoder or doc-only model ID>",
        "prune_type": "max_ratio",
        "prune_ratio": 0.1,
        "field_map": {
          "passage_text": "passage_embedding"
        }
      }
    }
  ]
}

要将长文本拆分为段落,请在 sparse_encoding 处理器之前使用 text_chunking 摄取处理器。更多信息,请参阅文本分块

步骤 3:创建用于摄取的索引

为了使用管道中定义的稀疏编码处理器,需要创建一个排名特征索引,并将上一步中创建的管道添加为默认管道。确保 field_map 中定义的字段已映射为正确的类型。继续该示例,passage_embedding 字段必须映射为 rank_features。同样,passage_text 字段必须映射为 text

以下示例请求创建一个配置了默认摄取管道的排名特征索引:

PUT /my-nlp-index
{
  "settings": {
    "default_pipeline": "nlp-ingest-pipeline-sparse"
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_embedding": {
        "type": "rank_features"
      },
      "passage_text": {
        "type": "text"
      }
    }
  }
}

为了节省磁盘空间,您可以从源中排除嵌入向量,如下所示:

PUT /my-nlp-index
{
  "settings": {
    "default_pipeline": "nlp-ingest-pipeline-sparse"
  },
  "mappings": {
    "_source": {
      "excludes": [
        "passage_embedding"
      ]
    },
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_embedding": {
        "type": "rank_features"
      },
      "passage_text": {
        "type": "text"
      }
    }
  }
}

一旦 <token, weight> 对从源中排除,它们将无法恢复。在应用此优化之前,请确保您的应用程序不需要 <token, weight> 对。

步骤 4:将文档摄取到索引中

要将文档摄取到上一步创建的索引中,请发送以下请求:

PUT /my-nlp-index/_doc/1
{
  "passage_text": "Hello world",
  "id": "s1"
}

PUT /my-nlp-index/_doc/2
{
  "passage_text": "Hi planet",
  "id": "s2"
}

在文档摄取到索引之前,摄取管道会在文档上运行 sparse_encoding 处理器,为 passage_text 字段生成向量嵌入。索引文档包含 passage_text 字段(包含原始文本)和 passage_embedding 字段(包含向量嵌入)。

步骤 5:搜索数据

要在您的索引上执行神经稀疏搜索,请在 Query DSL 查询中使用 neural_sparse 查询子句。

以下示例请求使用 neural_sparse 查询通过原始文本查询搜索相关文档。指定与您选择的模型兼容的 analyzer(参见稀疏编码模型/分析器兼容性):

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "analyzer": "bert-uncased"
      }
    }
  }
}

如果您未指定分析器,则使用默认的 bert-uncased 分析器。因此,此查询等同于前一个查询:

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world"
      }
    }
  }
}

响应包含匹配文档:

{
  "took" : 688,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 30.0029,
    "hits" : [
      {
        "_index" : "my-nlp-index",
        "_id" : "1",
        "_score" : 30.0029,
        "_source" : {
          "passage_text" : "Hello world",
          "passage_embedding" : {
            "!" : 0.8708904,
            "door" : 0.8587369,
            "hi" : 2.3929274,
            "worlds" : 2.7839446,
            "yes" : 0.75845814,
            "##world" : 2.5432441,
            "born" : 0.2682308,
            "nothing" : 0.8625516,
            "goodbye" : 0.17146169,
            "greeting" : 0.96817183,
            "birth" : 1.2788506,
            "come" : 0.1623208,
            "global" : 0.4371151,
            "it" : 0.42951578,
            "life" : 1.5750692,
            "thanks" : 0.26481047,
            "world" : 4.7300377,
            "tiny" : 0.5462298,
            "earth" : 2.6555297,
            "universe" : 2.0308156,
            "worldwide" : 1.3903781,
            "hello" : 6.696973,
            "so" : 0.20279501,
            "?" : 0.67785245
          },
          "id" : "s1"
        }
      },
      {
        "_index" : "my-nlp-index",
        "_id" : "2",
        "_score" : 16.480486,
        "_source" : {
          "passage_text" : "Hi planet",
          "passage_embedding" : {
            "hi" : 4.338913,
            "planets" : 2.7755864,
            "planet" : 5.0969057,
            "mars" : 1.7405145,
            "earth" : 2.6087382,
            "hello" : 3.3210192
          },
          "id" : "s2"
        }
      }
    ]
  }
}

为了最大限度地减少与稀疏嵌入源相关的磁盘和网络 I/O 延迟,您可以从查询中排除嵌入向量源,如下所示:

GET my-nlp-index/_search
{
  "_source": {
    "excludes": [
      "passage_embedding"
    ]
  },
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "analyzer": "bert-uncased"
      }
    }
  }
}

双编码器模式

在双编码器模式下,注册并部署一个双编码器模型,以便在摄取和查询时使用:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-v2-distill",
  "version": "1.0.0",
  "model_format": "TORCH_SCRIPT"
}

部署后,搜索时使用相同的 model_id

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "model_id": "<bi-encoder model_id>"
      }
    }
  }
}

有关完整示例,请参阅使用神经稀疏搜索的自定义配置

带自定义分词器的仅文档模式

您可以将仅文档模式与自定义分词器一起使用。要部署分词器,请发送以下请求:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-tokenizer-v1",
  "version": "1.0.1",
  "model_format": "TORCH_SCRIPT"
}

部署后,在查询中使用分词器的 model_id

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "model_id": "<tokenizer model_id>"
      }
    }
  }
}

有关完整示例,请参阅使用神经稀疏搜索的自定义配置

使用语义字段

使用 semantic 字段可简化神经稀疏搜索配置。要使用 semantic 字段,请遵循以下步骤。更多信息,请参阅语义字段类型

步骤 1:注册并部署稀疏编码模型

首先,按照步骤 1 中的说明注册并部署稀疏编码模型。

步骤 2:创建包含语义字段的索引以进行摄取

上一步中配置的稀疏编码模型在摄取时用于生成稀疏向量嵌入。当使用 semantic 字段时,将 model_id 设置为用于摄取的模型 ID。对于仅文档模式,您可以通过在 search_model_id 字段中提供模型 ID 来额外指定查询时要使用的模型。

以下示例展示了如何使用稀疏编码模型在仅文档模式下创建包含 semantic 字段的索引。要启用将长文本自动拆分为更小段落的功能,请在语义字段配置中将 chunking 设置为 true

PUT /my-nlp-index
{
  "mappings": {
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_text": {
        "type": "semantic",
         "model_id": "_kPwYJcBmp4cG9LrUQsE",
         "search_model_id": "AUPwYJcBmp4cG9LrmQy8",
         "chunking": true
      }
    }
  }
}

创建索引后,您可以检索其映射以验证嵌入字段是否已自动创建:

GET /my-nlp-index/_mapping
{
   "my-nlp-index": {
      "mappings": {
         "properties": {
            "id": {
               "type": "text"
            },
            "passage_text": {
               "type": "semantic",
               "model_id": "_kPwYJcBmp4cG9LrUQsE",
               "search_model_id": "AUPwYJcBmp4cG9LrmQy8",
               "raw_field_type": "text",
               "chunking": true
            },
            "passage_text_semantic_info": {
               "properties": {
                  "chunks": {
                     "type": "nested",
                     "properties": {
                        "embedding": {
                           "type": "rank_features"
                        },
                        "text": {
                           "type": "text"
                        }
                     }
                  },
                  "model": {
                     "properties": {
                        "id": {
                           "type": "text",
                           "index": false
                        },
                        "name": {
                           "type": "text",
                           "index": false
                        },
                        "type": {
                           "type": "text",
                           "index": false
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

会自动创建一个名为 passage_text_semantic_info 的对象字段。它包含一个用于存储嵌入的 ran_features 子字段,以及用于捕获模型元数据的其他文本字段。

步骤 3:将文档摄取到索引中

要将文档摄取到上一步创建的索引中,请发送以下请求:

PUT /my-nlp-index/_doc/1
{
  "passage_text": "Hello world",
  "id": "s1"
}

PUT /my-nlp-index/_doc/2
{
  "passage_text": "Hi planet",
  "id": "s2"
}

在文档被摄取到索引中之前,OpenSearch 会自动对文本进行分块,并为每个分块生成稀疏向量嵌入。要验证嵌入是否正确生成,您可以运行搜索请求来检索文档:

GET /my-nlp-index/_doc/1
{
   "_index": "my-nlp-index",
   "_id": "1",
   "_version": 1,
   "_seq_no": 0,
   "_primary_term": 1,
   "found": true,
   "_source": {
      "passage_text": "Hello world",
      "passage_text_semantic_info": {
         "chunks": [
            {
               "text": "Hello world",
               "embedding": {
                  "hi": 0.5843902,
                  ...
               }
            }
         ],
         "model": {
            "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill",
            "id": "_kPwYJcBmp4cG9LrUQsE",
            "type": "SPARSE_ENCODING"
         }
      },
      "id": "s1"
   }
}

步骤 3:搜索数据

要搜索语义字段的嵌入,请在 Query DSL 查询中使用 neural 查询子句。

以下示例使用 neural 查询通过文本输入搜索相关文档。您只需要指定 semantic 字段名称——OpenSearch 会自动重写查询并将其应用于底层嵌入字段,适当地处理任何嵌套对象。无需在查询中提供 model_id,因为 OpenSearch 会从索引映射中的 semantic 字段配置中检索它:

GET my-nlp-index/_search
{
   "_source": {
      "excludes": [
         "passage_text_semantic_info"
      ]
   },
   "query": {
      "neural": {
         "passage_text": {
            "query_text": "Hi world"
         }
      }
   }
}

响应包含匹配文档:

{
   "took": 19,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "skipped": 0,
      "failed": 0
   },
   "hits": {
      "total": {
         "value": 2,
         "relation": "eq"
      },
      "max_score": 6.437132,
      "hits": [
         {
            "_index": "my-nlp-index",
            "_id": "1",
            "_score": 6.437132,
            "_source": {
               "passage_text": "Hello world",
               "id": "s1"
            }
         },
         {
            "_index": "my-nlp-index",
            "_id": "2",
            "_score": 5.063226,
            "_source": {
               "passage_text": "Hi planet",
               "id": "s2"
            }
         }
      ]
   }
}

或者,您可以使用内置分析器对查询文本进行分词:

GET my-nlp-index/_search
{
   "_source": {
      "excludes": [
         "passage_text_semantic_info"
      ]
   },
   "query": {
      "neural": {
         "passage_text": {
            "query_text": "Hi world",
            "semantic_field_search_analyzer": "bert-uncased"
         }
      }
   }
}

为了进一步简化查询,您可以在 semantic 字段配置中定义 semantic_field_search_analyzer。这样,您就可以在查询中省略分析器,因为 OpenSearch 会在搜索期间自动应用配置的分析器。

要了解更多关于提高神经稀疏搜索检索时间的信息,请参阅加速神经稀疏搜索

如果您将 semantic 字段与 neural 查询一起使用,查询加速目前不支持。您可以通过直接对底层 rank_features 字段运行 neural_sparse 查询来实现加速。

故障排除

本节包含有关解决运行神经稀疏搜索时遇到的常见问题的信息。

远程连接器节流异常

当使用连接器调用 Amazon SageMaker 等远程服务时,摄取和搜索调用有时会因远程连接器节流异常而失败。

对于 OpenSearch 2.15 之前的版本,节流异常将作为远程服务的错误返回:

{
  "type": "status_exception",
  "reason": "Error from remote service: {\"message\":null}"
}

为了缓解节流异常,请减少连接器 client_config 对象中 max_connection 设置中指定的最大连接数。这样做可以防止最大并发连接数超过远程服务的阈值。您还可以修改重试设置,以避免在摄取期间出现请求峰值。

后续步骤