Painless 脚本扩展

通过 Painless 脚本扩展,您可以直接在 Painless 脚本中使用 k-最近邻距离函数来对 knn_vector 字段执行操作。Painless 为每个上下文设定了严格允许的函数和类列表,以确保其脚本的安全性。 UDB-SX 将一些 k-NN 评分脚本 中使用的距离函数添加为 Painless 脚本扩展,因此您可以使用它们来自定义您的 k-NN 工作负载。

开始使用 k-NN Painless 脚本函数

要使用 k-NN Painless 脚本函数,首先需要按照 向量评分脚本入门 中的描述,创建一个包含 knn_vector 字段的索引。创建索引并摄入一些数据后,您就可以使用 Painless 扩展:

GET my-knn-index-2/_search
{
  "size": 2,
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "color": "BLUE"
            }
          }
        }
      },
      "script": {
        "source": "1.0 + cosineSimilarity(params.query_value, doc[params.field])",
        "params": {
          "field": "my_vector",
          "query_value": [9.9, 9.9]
        }
      }
    }
  }
}

field 需要映射到一个 knn_vector 字段,而 query_value 必须是一个浮点数数组,其维度与 field 相同。

函数类型

下表描述了 UDB-SX 提供的 Painless 函数。

函数名 函数签名 描述
l2Squared float l2Squared (float[] queryVector, doc['vector field']) 此函数计算给定查询向量与文档向量之间 L2 距离(欧几里得距离)的平方。距离越短表示文档越相关,因此此示例对 l2Squared 函数的返回值取倒数。如果文档向量与查询向量匹配,结果为 0,因此此示例还在距离上加 1 以避免除零错误。
l1Norm float l1Norm (float[] queryVector, doc['vector field']) 此函数计算给定查询向量与文档向量之间的 L1 范数距离(曼哈顿距离)。
cosineSimilarity float cosineSimilarity (float[] queryVector, doc['vector field']) 余弦相似度是查询向量和文档向量的内积,两者均归一化长度为 1。如果查询向量的模长在整个查询过程中不变,您可以传递查询向量的模长以提高性能,而不是为每个过滤后的文档重复计算模长:
float cosineSimilarity (float[] queryVector, doc['vector field'], float normQueryVector)
通常,余弦相似度的范围是 [-1, 1]。然而,在信息检索中,两个文档的余弦相似度范围是 01,因为 tf-idf 统计量不能为负。因此,UDB-SX 会加上 1.0,以确保余弦相似度评分始终为正。
hamming float hamming (float[] queryVector, doc['vector field']) 此函数计算给定查询向量与文档向量之间的汉明距离。汉明距离是指对应元素不同的位置数量。距离越短表示文档越相关,因此此示例对汉明距离的返回值取倒数。

请注意:汉明空间类型在 UDB-SX 中支持二进制向量。

约束

  1. 如果文档的 knn_vector 字段维度与查询的维度不同,函数会抛出 IllegalArgumentException

  2. 如果向量字段没有值,函数会抛出 IllegalStateException

    您可以通过先检查文档的字段是否包含值来避免此问题:

    "source": "doc[params.field].size() == 0 ? 0 : 1 / (1 + l2Squared(params.query_value, doc[params.field]))",
    

    因为评分只能是正数,所以此脚本会将具有向量字段的文档排在那些没有向量字段的文档之上。

请注意:使用余弦相似度时,传递零向量([0, 0, ...])作为输入是无效的。这是因为此类向量的模长为 0,在对应的公式中会引发 除以 0 异常。包含零向量的请求将被拒绝,并抛出相应的异常。