教程:使用 semantic_text 进行混合搜索

编辑

教程:使用 semantic_text 进行混合搜索

编辑

本教程演示如何执行混合搜索,将语义搜索与传统的全文搜索相结合。

在混合搜索中,语义搜索根据文本的含义检索结果,而全文搜索则侧重于精确的单词匹配。通过结合这两种方法,混合搜索可以提供更相关的结果,尤其是在仅依赖一种方法可能不足的情况下。

在 Elastic Stack 中使用混合搜索的推荐方法是遵循 semantic_text 工作流程。本教程使用 elasticsearch 服务进行演示,但您可以使用 Inference API 提供的任何服务及其支持的模型。

创建索引映射

编辑

目标索引将包含用于语义搜索的嵌入和用于全文搜索的原始文本字段。这种结构能够结合语义搜索和全文搜索。

PUT semantic-embeddings
{
  "mappings": {
    "properties": {
      "semantic_text": { 
        "type": "semantic_text",
      },
      "content": { 
        "type": "text",
        "copy_to": "semantic_text" 
      }
    }
  }
}

用于存储语义搜索生成的嵌入的字段的名称。

用于存储词法搜索的原始文本的字段的名称。

存储在 content 字段中的文本数据将被复制到 semantic_text 中,并由推理端点处理。

如果要对由网络爬虫或连接器填充的索引运行搜索,则必须更新这些索引的索引映射,以包含 semantic_text 字段。更新映射后,您需要运行完整的网络爬取或完整的连接器同步。这确保所有现有文档都被重新处理并使用新的语义嵌入进行更新,从而在更新后的数据上启用混合搜索。

加载数据

编辑

在此步骤中,您加载稍后用于创建嵌入的数据。

使用 msmarco-passagetest2019-top1000 数据集,它是 MS MARCO Passage Ranking 数据集的子集。它包含 200 个查询,每个查询都附带一个相关的文本段落列表。所有唯一的段落及其 ID 都已从该数据集中提取,并编译成一个 tsv 文件

下载该文件并使用机器学习 UI 中的数据可视化工具将其上传到您的集群。分析数据后,单击覆盖设置。在编辑字段名称下,将 id 分配给第一列,将 content 分配给第二列。单击应用,然后单击导入。将索引命名为 test-data,然后单击导入。上传完成后,您将看到一个名为 test-data 的索引,其中包含 182,469 个文档。

重新索引数据以进行混合搜索

编辑

将数据从 test-data 索引重新索引到 semantic-embeddings 索引中。源索引的 content 字段中的数据将复制到目标索引的 content 字段中。索引映射创建中设置的 copy_to 参数确保内容被复制到 semantic_text 字段中。数据在摄取时由推理端点处理以生成嵌入。

此步骤使用 reindex API 来模拟数据摄取。如果您正在使用已编制索引的数据,而不是使用 test-data 集,则仍然需要重新索引,以确保数据由推理端点处理并生成必要的嵌入。

resp = client.reindex(
    wait_for_completion=False,
    source={
        "index": "test-data",
        "size": 10
    },
    dest={
        "index": "semantic-embeddings"
    },
)
print(resp)
const response = await client.reindex({
  wait_for_completion: "false",
  source: {
    index: "test-data",
    size: 10,
  },
  dest: {
    index: "semantic-embeddings",
  },
});
console.log(response);
POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "test-data",
    "size": 10 
  },
  "dest": {
    "index": "semantic-embeddings"
  }
}

重新索引的默认批处理大小为 1000。将大小减小到一个较小的数字可以加快重新索引过程的更新速度,这使您可以密切关注进度并及早发现错误。

该调用返回一个任务 ID 以监视进度

resp = client.tasks.get(
    task_id="<task_id>",
)
print(resp)
const response = await client.tasks.get({
  task_id: "<task_id>",
});
console.log(response);
GET _tasks/<task_id>

重新索引大型数据集可能需要很长时间。您可以使用数据集的子集测试此工作流程。

取消重新索引过程并为重新索引的子集生成嵌入

resp = client.tasks.cancel(
    task_id="<task_id>",
)
print(resp)
const response = await client.tasks.cancel({
  task_id: "<task_id>",
});
console.log(response);
POST _tasks/<task_id>/_cancel

执行混合搜索

编辑

将数据重新索引到 semantic-embeddings 索引后,您可以使用倒数排名融合 (RRF) 执行混合搜索。 RRF 是一种合并语义查询和词法查询的排名技术,它为在任一搜索中排名较高的结果赋予更大的权重。这确保最终结果是平衡且相关的。

resp = client.search(
    index="semantic-embeddings",
    retriever={
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "match": {
                                "content": "How to avoid muscle soreness while running?"
                            }
                        }
                    }
                },
                {
                    "standard": {
                        "query": {
                            "semantic": {
                                "field": "semantic_text",
                                "query": "How to avoid muscle soreness while running?"
                            }
                        }
                    }
                }
            ]
        }
    },
)
print(resp)
const response = await client.search({
  index: "semantic-embeddings",
  retriever: {
    rrf: {
      retrievers: [
        {
          standard: {
            query: {
              match: {
                content: "How to avoid muscle soreness while running?",
              },
            },
          },
        },
        {
          standard: {
            query: {
              semantic: {
                field: "semantic_text",
                query: "How to avoid muscle soreness while running?",
              },
            },
          },
        },
      ],
    },
  },
});
console.log(response);
GET semantic-embeddings/_search
{
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": { 
            "query": {
              "match": {
                "content": "How to avoid muscle soreness while running?" 
              }
            }
          }
        },
        {
          "standard": { 
            "query": {
              "semantic": {
                "field": "semantic_text", 
                "query": "How to avoid muscle soreness while running?"
              }
            }
          }
        }
      ]
    }
  }
}

第一个 standard 检索器表示传统的词法搜索。

使用指定的短语在 content 字段上执行词法搜索。

第二个 standard 检索器指的是语义搜索。

semantic_text 字段用于执行语义搜索。

执行混合搜索后,查询将返回与语义和词法搜索条件匹配的前 10 个文档。结果包括有关每个文档的详细信息

{
  "took": 107,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 473,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      {
        "_index": "semantic-embeddings",
        "_id": "wv65epIBEMBRnhfTsOFM",
        "_score": 0.032786883,
        "_rank": 1,
        "_source": {
          "semantic_text": {
            "inference": {
              "inference_id": "my-elser-endpoint",
              "model_settings": {
                "task_type": "sparse_embedding"
              },
              "chunks": [
                {
                  "text": "What so many out there do not realize is the importance of what you do after you work out. You may have done the majority of the work, but how you treat your body in the minutes and hours after you exercise has a direct effect on muscle soreness, muscle strength and growth, and staying hydrated. Cool Down. After your last exercise, your workout is not over. The first thing you need to do is cool down. Even if running was all that you did, you still should do light cardio for a few minutes. This brings your heart rate down at a slow and steady pace, which helps you avoid feeling sick after a workout.",
                  "embeddings": {
                    "exercise": 1.571044,
                    "after": 1.3603843,
                    "sick": 1.3281639,
                    "cool": 1.3227621,
                    "muscle": 1.2645415,
                    "sore": 1.2561599,
                    "cooling": 1.2335974,
                    "running": 1.1750668,
                    "hours": 1.1104802,
                    "out": 1.0991782,
                    "##io": 1.0794281,
                    "last": 1.0474665,
                   (...)
                  }
                }
              ]
            }
          },
          "id": 8408852,
          "content": "What so many out there do not realize is the importance of (...)"
        }
      }
    ]
  }
}