【问题标题】:Dense vector array and cosine similarity密集向量数组和余弦相似度
【发布时间】:2020-04-22 22:43:45
【问题描述】:

我想在我的文档中存储一个 dense_vector 数组,但这不像其他数据类型那样有效。

PUT my_index
{
  "mappings": {
    "properties": {
      "my_vectors": {
        "type": "dense_vector",
        "dims": 3  
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
  }
}

PUT my_index/_doc/1
{
  "my_text" : "text1",
  "my_vector" : [[0.5, 10, 6], [-0.5, 10, 10]]
}

返回:

'1 document(s) failed to index.',
    {'_index': 'my_index', '_type': '_doc', '_id': 'some_id', 'status': 400, 'error': 
      {'type': 'mapper_parsing_exception', 'reason': 'failed to parse', 'caused_by': 
        {'type': 'parsing_exception', 
         'reason': 'Failed to parse object: expecting token of type [VALUE_NUMBER] but found [START_ARRAY]'
        }
      }
    }

我如何实现这一目标?不同的文档会有不同数量的向量,但不会超过少数。

此外,我想通过为该数组中的每个值执行cosineSimilarity 来查询它。下面的代码是我在文档中只有一个向量时通常会这样做的方式。

"script_score": {
    "query": {
        "match_all": {}
    },
    "script": {
        "source": "(1.0+cosineSimilarity(params.query_vector, doc['my_vectors']))",
        "params": {"query_vector": query_vector}
    }
}

理想情况下,我想要最接近的相似度或平均值。

【问题讨论】:

    标签: elasticsearch vector elasticsearch-query


    【解决方案1】:

    dense_vector 数据类型要求每个文档有一个数值数组,如下所示:

    PUT my_index/_doc/1
    {
      "my_text" : "text1",
      "my_vector" : [0.5, 10, 6]
    }
    

    要存储任意数量的向量,您可以将my_vector 字段设为“嵌套”类型,其中包含对象数组,每个对象都包含一个向量:

    PUT my_index
    {
      "mappings": {
        "properties": {
          "my_vectors": {
            "type": "nested",
            "properties": {
              "vector": {
                "type": "dense_vector",
                "dims": 3  
              }
            }
          },
          "my_text" : {
            "type" : "keyword"
          }
        }
      }
    }
    
    PUT my_index/_doc/1
    {
      "my_text" : "text1",
      "my_vector" : [
        {"vector": [0.5, 10, 6]}, 
        {"vector": [-0.5, 10, 10]}
      ]
    }
    

    编辑

    然后,要查询文档,您可以使用以下(从 ES v7.6.1 开始)

    {
      "query": {
        "nested": {
          "path": "my_vectors",
          "score_mode": "max", 
          "query": {
            "function_score": {
              "script_score": {
                "script": {
                  "source": "(1.0+cosineSimilarity(params.query_vector, 'my_vectors.vector'))",
                  "params": {"query_vector": query_vector}
                }
              }
            }
          }
        }
      }
    }
    

    注意事项:

    • 查询需要包含在 nested 声明中(由于使用嵌套对象来存储向量)
    • 因为嵌套对象是单独的 Lucene 文档,嵌套对象单独评分,默认情况下,父文档被分配匹配嵌套文档的平均分数。您可以指定嵌套属性score_mode 来更改评分行为。对于您的情况,“max”将根据描述最相似文档的最大余弦相似度得分进行评分。
    • 如果您有兴趣查看每个嵌套向量的分数,可以使用嵌套属性inner_hits
    • 如果有人好奇为什么 +1.0 会添加到余弦相似度分数,那是因为 Cos. Sim。计算值 [-1,1],但 ElasticSearch 不能有负分。因此,分数被转换为 [0,2]。

    【讨论】:

    • 您好,格伦,感谢您的回复。我想过这样做。我猜想在将向量存储在嵌套字段中之后,在查询时,我必须对它们中的每一个执行余弦相似度,然后使用计算出的距离的平均值,或者可能是最小(最小)距离。关于如何构建此类查询的任何想法?
    • 嗨,Leo,我已经用示例查询更新了答案。希望这会有所帮助。
    • score_mode 必须是 max,因为最佳匹配文档的相似度最高。
    • @Konstantin 谢谢。我已经编辑了答案。
    【解决方案2】:

    dense_vector 数据类型旨在

    存储 float 值的密集向量(来自 documentation)...。dense_vector 字段是单值字段

    在您的示例中,您希望在同一属性中索引多个向量。但正如文档中所说,您的字段必须是单值的。如果您的文档有多个向量,则需要在不同的属性中分派它们。

    没有解决方法:(

    因此,您需要在不同的字段中分派向量,然后在脚本中使用循环并保留最合适的值。

    【讨论】:

    • 感谢您的回复...我很想看一个例子来说明您的意思。
    猜你喜欢
    • 2020-08-12
    • 2011-03-08
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 2018-06-30
    • 2011-01-01
    • 2019-05-31
    相关资源
    最近更新 更多