Elasticsearch:检索多向量文档中的原始信息

2024年5月20日   |   by mebius

作者:来自 ElasticGilad Gal

Elasticsearch(从 8.11 版本及更高版本开始)支持单个字段中每个文档具有多个向量。 这样的文档可以通过文档的最相似向量的排名或者通过每个文档具有多个结果(可能在同一结果集中文档包含的每个向量含有一个)来排名。 对于密集向量和稀疏向量(例如,使用 ELSER 时)都是如此,但为了简单起见,博客的其余部分将与密集向量相关。

这似乎是一个罕见的用例,但实际上它经常发生。 当检查密集向量搜索的两个主要用例时,原因就很清楚了:

文本(text)- 元数据文本通常旨在允许有效发现。 这对于主题列表或搜索关键字来说当然是正确的,但对于像标题这样的元数据来说也是如此,它很短并且通常旨在描述文档。 像 BM25 这样基于 token 频率的算法往往在此类内容上表现得非常好,因此它通常不需要引入基于 ML 的算法和密集向量搜索,也不需要从引入中显著受益。 然而,对于大块文本来说,情况并非如此,例如,即使在搜索几段文本时,像 BM25 这样的算法也很难与 NLP 算法竞争,而向量搜索在这种文本类型上表现出显着的优势。 问题在于,大多数分析文本以生成用于排名的密集向量的 ML 模型仅限于 512 个标记,这大约是单个段落的大小。 换句话说,当向量搜索需要密集向量时,通常会有足够的文本来需要为每个文档生成多个向量。

图像(image) – 在许多情况下,图像描绘了现实世界中的某些事物,并且通常有来自不同角度的图像。 这是一个简单的结果,因为图像是二维的,而现实世界中的事物是三维的,因此二维图像提供了有关它们的非常部分的信息。 在电子商务中演示可能是最简单的,其中通常有一些产品图像,但其他图像搜索用例也是如此。 机器学习模型通常为每个图像生成一个向量,因此如果每个产品有多个图像,则每个产品有多个向量。

显示结果时,通常需要显示文档中导致排名的部分,例如 文本或图像中使文档在结果集中排名靠前的部分。 Elasticsearch 通过嵌套字段支持每个文档多个向量,这种结构非常适合检索生成向量的内容。 为此,只需将原始数据添加为另一个嵌套字段即可。

下面是一个示例:使用以下命令创建具有嵌套向量和文本字段的映射。 你可以在任何无状态(stateless)项目或版本 8.11 或更高版本的 Elasticsearch 部署中使用 Kibana 中的开发控制台。

PUT my-long-text-index
{
  "mappings": {
    "properties": {
      "my_long_text_field": {
        "type": "nested", //because there can be multiple vectors per doc
        "properties": {
          "vector": {
            "type": "dense_vector" //the vector used for ranking
          },
          "text_chunk": {
            "type": "text" //the text from which the vector was created
          }
        }
      }
    }
  }
}
PUT my-long-text-index/_doc/1
{
  "my_long_text_field" : [
    {
      "vector" : [23,14,8],
      "text_chunk" :  "doc 1 chunk 1"
    },
    {
      "vector" : [34,95,17],
      "text_chunk" :  "doc 1 chunk 2"
    }
  ]
}
PUT my-long-text-index/_doc/2
{
  "my_long_text_field" : [
    {
      "vector" : [3,2,890],
      "text_chunk" :  "doc 2 chunk 1"
    },
    {
      "vector" : [129,765,13],
      "text_chunk" :  "doc 2 chunk 2"
    }
  ]
}

使用 inner_hits 查询索引并返回相关文本块:

GET my-long-text-index/_search
{
  "knn": {
    "field": "my_long_text_field.vector",
    "query_vector": [23,14,9],
    "k": 1,
    "num_candidates": 10,
    "inner_hits":{
      "_source": false,
      "fields": [ "my_long_text_field.text_chunk"
        ],
        "size": 1
    }
  }
}

你的结果应如下所示:

Result:
{
  "took": 4,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.999715,
    "hits": [
      {
        "_index": "my-long-text-index",
        "_id": "1",
        "_score": 0.999715,
        "_source": {
          "my_long_text_field": [
            {
              "vector": [
                23,
                14,
                8
              ],
              "text_chunk": "doc 1 chunk 1"
            },
            {
              "vector": [
                34,
                95,
                17
              ],
              "text_chunk": "doc 1 chunk 2"
            }
          ]
        },
        "inner_hits": {
          "my_long_text_field": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 0.999715,
              "hits": [
                {
                  "_index": "my-long-text-index",
                  "_id": "1",
                  "_nested": {
                    "field": "my_long_text_field",
                    "offset": 0
                  },
                  "_score": 0.999715,
                  "fields": {
                    "my_long_text_field": [
                      {
                        "text_chunk": [
                          "doc 1 chunk 1"
                        ]
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

如果希望显示同一文档的多个结果,例如 如果文档是教科书,并且向 RAG 提供同一本书中的几个相关章节(每本书都索引为单个文档)很有用,则查询可以如下所示:

GET my-long-text-index/_search
{
  "knn": {
    "field": "my_long_text_field.vector",
    "query_vector": [23,14,9],
    "k": 3,
    "num_candidates": 10,
    "inner_hits":{
      "size": 3,
      "_source": false,
      "fields": [ "my_long_text_field.text_chunk"
        ]
    }
  }
tgcode}

结果如下:

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.999715,
    "hits": [
      {
        "_index": "my-long-text-index",
        "_id": "1",
        "_score": 0.999715,
        "_source": {
          "my_long_text_field": [
            {
              "vector": [
                23,
                14,
                8
              ],
              "text_chunk": "doc 1 chunk 1"
            },
            {
              "vector": [
                34,
                95,
                17
              ],
              "text_chunk": "doc 1 chunk 2"
            }
          ]
        },
        "inner_hits": {
          "my_long_text_field": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 0.999715,
              "hits": [
                {
                  "_index": "my-long-text-index",
                  "_id": "1",
                  "_nested": {
                    "field": "my_long_text_field",
                    "offset": 0
                  },
                  "_score": 0.999715,
                  "fields": {
                    "my_long_text_field": [
                      {
                        "text_chunk": [
                          "doc 1 chunk 1"
                        ]
                      }
                    ]
                  }
                },
                {
                  "_index": "my-long-text-index",
                  "_id": "1",
                  "_nested": {
                    "field": "my_long_text_field",
                    "offset": 1
                  },
                  "_score": 0.88984984,
                  "fields": {
                    "my_long_text_field": [
                      {
                        "text_chunk": [
                          "doc 1 chunk 2"
                        ]
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
      },
      {
        "_index": "my-long-text-index",
        "_id": "2",
        "_score": 0.81309915,
        "_source": {
          "my_long_text_field": [
            {
              "vector": [
                3,
                2,
                890
              ],
              "text_chunk": "doc 2 chunk 1"
            },
            {
              "vector": [
          tgcode      129,
                765,
                13
              ],
              "text_chunk": "doc 2 chunk 2tgcode"
            }
          ]
        },
        "inner_hits": {
          "my_long_text_field": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 0.81309915,
              "hits": [
                {
                  "_index": "my-long-text-index",
                  "_id": "2",
                  "_nested": {
                    "field": "my_long_text_field",
                    "offset": 1
                  },
                  "_score": 0.81309915,
                  "fields": {
                    "my_long_text_field": [
                      {
                        "text_chunk": [
                          "doc 2 chunk 2"
                        ]
                      }
                    ]
                  }
                },
                {
                  "_index": "my-long-text-index",
                  "_id": "2",
                  "_nested": {
                    "field": "my_long_text_field",
                    "offset": 0
                  },
                  "_score": 0.6604239,
                  "fields": {
                    "my_long_text_field": [
                      {
                        "text_chunk": [
                          "doc 2 chunk 1"
                        ]
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

准备好将 RAG 构建到你的应用程序中了吗? 想要尝试使用向量数据库的不同 LLMs?
Github 上查看我们的 LangChain、Cohere 等示例笔记本,并参加即将开始的 Elasticsearch 工程师培训

原文:Retrieval of originating information in multi-vector documents — Elastic Search Labs

文章来源于互联网:Elasticsearch:检索多向量文档中的原始信息

相关推荐: Elasticsearch:探索 11 种流行的机器学习算法

作者:来自 ElasticElastic Platform Team tgcode过去几年中,机器学习(ML)已经悄然成为我们日常生活中不可或缺的一部分。它影响着从购物网站和流媒体网站上的个性化推荐,到保护我们的收件箱免受我们每天收到的大量垃圾邮件的侵扰。但它…

Tags: ,