使用 ELSER 进行语义搜索
Elastic Stack Serverless
Elastic Learned Sparse EncodeR(或 ELSER)是由 Elastic 训练的 NLP 模型,使您能够通过使用稀疏向量表示执行语义搜索。 语义搜索不是对搜索词进行字面匹配,而是根据搜索查询的意图和上下文含义检索结果。
本教程中的说明向您展示了如何使用 ELSER 对您的数据执行语义搜索。
要以最简单的方式在 Elastic Stack 中执行语义搜索,请参阅semantic_text
端到端教程。
在使用 ELSER 进行语义搜索时,仅考虑每个字段中提取的前 512 个令牌。 有关更多信息,请参阅此页面。
要使用 ELSER 执行语义搜索,您必须在集群中部署 NLP 模型。 请参阅 ELSER 文档,了解如何下载和部署模型。
如果部署自动缩放已关闭,则在 Elastic Cloud Hosted 中部署和使用 ELSER 模型的最小专用 ML 节点大小为 4 GB。 建议启用自动缩放,因为它允许您的部署根据需求动态调整资源。 通过使用更多分配或每个分配更多线程可以获得更好的性能,这需要更大的 ML 节点。 自动缩放会在需要时提供更大的节点。 如果关闭自动缩放,则必须自己提供适当大小的节点。
首先,必须创建目标索引(包含模型基于您的文本创建的令牌的索引)的映射。 目标索引必须具有带有 sparse_vector
或 rank_features
字段类型的字段,以索引 ELSER 输出。
ELSER 输出必须提取到具有 sparse_vector
或 rank_features
字段类型的字段中。 否则,Elasticsearch 会将令牌权重对解释为文档中的大量字段。 如果您收到类似于此的错误:"Limit of total fields [1000] has been exceeded while adding new fields"
,则 ELSER 输出字段未正确映射,并且它具有与 sparse_vector
或 rank_features
不同的字段类型。
PUT my-index
{
"mappings": {
"properties": {
"content_embedding": {
"type": "sparse_vector"
},
"content": {
"type": "text"
}
}
}
}
- 包含生成的令牌的字段的名称。 必须在下一步的推理管道配置中引用它。
- 包含令牌的字段是一个
sparse_vector
字段。 - 从中创建稀疏向量表示的字段的名称。 在此示例中,字段的名称为
content
。 必须在下一步的推理管道配置中引用它。 - 此示例中的字段类型为文本。
要了解如何优化空间,请参阅具有 推理处理器的通过从文档源中排除 ELSER 令牌来节省磁盘空间,以使用 ELSER 对管道中提取的数据进行推理。
PUT _ingest/pipeline/elser-v2-test
{
"processors": [
{
"inference": {
"model_id": ".elser_model_2",
"input_output": [
{
"input_field": "content",
"output_field": "content_embedding"
}
]
}
}
]
}
- 配置对象,用于定义推理过程的
input_field
和包含推理结果的output_field
。
在此步骤中,您加载稍后在推理提取管道中使用的数据,以从中提取令牌。
使用 msmarco-passagetest2019-top1000
数据集,该数据集是 MS MARCO Passage Ranking 数据集的子集。 它包含 200 个查询,每个查询都附带一个相关文本段落列表。 所有唯一的段落及其 ID 都已从该数据集中提取并编译到一个 tsv 文件中。
msmarco-passagetest2019-top1000
数据集未用于训练模型。 我们在本教程中使用此示例数据集,因为它易于访问以进行演示。 您可以使用不同的数据集来测试工作流程并熟悉它。
下载该文件并使用 UI 中的文件上传器将其上传到您的集群。 分析完数据后,单击覆盖设置。 在编辑字段名称下,将 id
分配给第一列,将 content
分配给第二列。 单击应用,然后单击导入。 将索引命名为 test-data
,然后单击导入。 上传完成后,您将看到一个名为 test-data
的索引,其中包含 182,469 个文档。
通过使用 ELSER 作为推理模型的推理管道重新索引数据,从文本创建令牌。
POST _reindex?wait_for_completion=false
{
"source": {
"index": "test-data",
"size": 50
},
"dest": {
"index": "my-index",
"pipeline": "elser-v2-test"
}
}
- 重新索引的默认批处理大小为 1000。 将
size
减小到更小的数字可以加快重新索引过程的更新速度,这使您可以密切关注进度并尽早检测到错误。
该调用返回一个任务 ID,以监控进度
GET _tasks/<task_id>
您还可以打开“已训练模型”UI,选择 ELSER 下的“管道”选项卡以关注进度。
重新索引大型数据集可能需要很长时间。 您可以使用数据集的子集来测试此工作流程。 为此,请取消重新索引过程,并且仅为重新索引的子集生成嵌入。 以下 API 请求将取消重新索引任务
POST _tasks/<task_id>/_cancel
要执行语义搜索,请使用 sparse_vector
query,并提供与您的 ELSER 模型关联的查询文本和推理 ID。 以下示例使用查询文本“How to avoid muscle soreness after running?”,content_embedding
字段包含生成的 ELSER 输出
GET my-index/_search
{
"query":{
"sparse_vector":{
"field": "content_embedding",
"inference_id": "my-elser-endpoint",
"query": "How to avoid muscle soreness after running?"
}
}
}
结果是与您的查询文本在含义上最接近的 my-index
索引中的前 10 个文档,按其相关性排序。 结果还包含每个相关搜索结果的提取令牌及其权重。 令牌是捕获相关性的学习关联,它们不是同义词。 要了解有关令牌的更多信息,请参阅此页面。 可以从源中排除令牌,请参阅 本节了解更多信息。
"hits": {
"total": {
"value": 10000,
"relation": "gte"
},
"max_score": 26.199875,
"hits": [
{
"_index": "my-index",
"_id": "FPr9HYsBag9jXmT8lEpI",
"_score": 26.199875,
"_source": {
"content_embedding": {
"muscular": 0.2821541,
"bleeding": 0.37929374,
"foods": 1.1718726,
"delayed": 1.2112266,
"cure": 0.6848574,
"during": 0.5886185,
"fighting": 0.35022718,
"rid": 0.2752442,
"soon": 0.2967024,
"leg": 0.37649947,
"preparation": 0.32974035,
"advance": 0.09652356,
(...)
},
"id": 1713868,
"model_id": ".elser_model_2",
"content": "For example, if you go for a run, you will mostly use the muscles in your lower body. Give yourself 2 days to rest those muscles so they have a chance to heal before you exercise them again. Not giving your muscles enough time to rest can cause muscle damage, rather than muscle development."
}
},
(...)
]
}
您可以将sparse_vector
与 复合查询中的其他查询组合。 例如,在 Boolean 中使用过滤器子句,或使用与 sparse_vector
查询相同的(或不同的)查询文本进行全文查询。 这使您可以组合来自两个查询的搜索结果。
来自 sparse_vector
查询的搜索命中倾向于比其他 Elasticsearch 查询得分更高。 这些分数可以通过使用 boost
参数增加或减少每个查询的相关性分数来进行规范化。 在存在大量不太相关的结果的情况下,sparse_vector
查询的召回率可能很高。 使用 min_score
参数来修剪那些不太相关的文档。
GET my-index/_search
{
"query": {
"bool": {
"should": [
{
"sparse_vector": {
"field": "content_embedding",
"inference_id": "my-elser-endpoint",
"query": "How to avoid muscle soreness after running?",
"boost": 1
}
},
{
"query_string": {
"query": "toxins",
"boost": 4
}
}
]
}
},
"min_score": 10
}
sparse_vector
和query_string
查询都在bool
查询的should
子句中。sparse_vector
查询的boost
值为1
,这是默认值。 这意味着此查询的结果的相关性分数不会提高。query_string
查询的boost
值为4
。 此查询的结果的相关性分数会增加,从而使它们在搜索结果中的排名更高。- 仅显示得分等于或高于
10
的结果。
必须索引 ELSER 生成的令牌才能在 sparse_vector 查询中使用。 但是,没有必要在文档源中保留这些术语。 您可以使用 source exclude 映射从文档源中删除 ELSER 术语,从而节省磁盘空间。
重新索引使用文档源来填充目标索引。一旦 ELSER 词条从源中排除,就无法通过重新索引恢复。 从源中排除这些令牌是一种节省空间的优化,只有在您确定将来不需要重新索引时才应应用! 仔细考虑这种权衡非常重要,并确保从源中排除 ELSER 词条符合您的特定需求和用例。 请仔细查看禁用 _source
字段和从 _source
中包含/排除字段部分,以了解更多有关从 _source
中排除令牌可能造成的后果。
可以通过以下 API 调用创建从 _source
字段中排除 content_embedding
的映射
PUT my-index
{
"mappings": {
"_source": {
"excludes": [
"content_embedding"
]
},
"properties": {
"content_embedding": {
"type": "sparse_vector"
},
"content": {
"type": "text"
}
}
}
}
根据您的数据,使用 track_total_hits: false
时,sparse_vector
查询可能会更快。
elasticsearch-labs
仓库有一个使用 Elasticsearch Python 客户端运行 ELSER 驱动的语义搜索的交互式示例。