java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Elasticsearch数据存储与搜索

向量数据库之如何使用Elasticsearch实现向量数据存储与搜索

作者:马超的博客

这篇文章主要介绍了向量数据库之如何使用Elasticsearch实现向量数据存储与搜索,在向量函数的计算过程中,会对所有匹配的文档进行线性扫描,因此,查询预计时间会随着匹配文档的数量线性增长,本文给大家讲解的非常详细,需要的朋友参考下吧

Here’s the table of contents:

向量数据库:使用Elasticsearch实现向量数据存储与搜索

一、简介

  Elasticsearch在7.x的版本中支持 向量检索 。在向量函数的计算过程中,会对所有匹配的文档进行线性扫描。因此,查询预计时间会随着匹配文档的数量线性增长。出于这个原因,建议使用查询参数来限制匹配文档的数量(类似二次查找的逻辑,先使用match query检索到相关文档,然后使用向量函数计算文档相关度)。

  访问dense_vector的推荐方法是通过cosinessimilarity, dotProduct, 1norm或l2norm函数。但是需要注意,每个DSL脚本只能调用这些函数一次。例如,不要在循环中使用这些函数来计算文档向量和多个其他向量之间的相似性。如果需要该功能,可以通过直接访问向量值来重新实现这些函数。

二、实验前准备

2.1 创建索引设置向量字段

  创建一个支持向量检索的mapping,字段类型为dense_vector

// 7.x 支持的 dims 最大为 1024。
PUT index3
{
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
  }
}

2.2 写入数据

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

三、向量计算函数

3.1 余弦相似度:cosineSimilarity

  cosinessimilarity函数计算给定查询向量和文档向量之间的余弦相似性度量。

POST index3/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "cosineSimilarity(params.queryVector, doc['my_vector'])+1.0",
        "params": {
          "queryVector": [-0.5, 10, 6]
        }
      }
    }
  }
}

脚本样例:

"source": 
"
doc['my_vector'].size() == 0 ? 0 : 
cosineSimilarity(params.queryVector, 'my_vector')
"

  如果文档的dense_vector字段与查询的向量维度不同,就会抛出异常。

3.2 计算点积:dotProduct

  dotProduct函数计算给定查询向量和文档向量之间的点积度量。

POST index3/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": """
        double value = dotProduct(params.queryVector,doc['my_vector']);
        return sigmoid(1, Math.E, -value);
        """,
        "params": {
          "queryVector": [
            -0.5,
            10,
            6
          ]
        }
      }
    }
  }
}

使用标准的sigmoid函数可以防止分数为负。

3.3 曼哈顿距离:l1norm

  l1norm函数计算给定查询向量和文档向量之间的L1距离(曼哈顿距离)。

POST index3/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source":"1 / (1 + l1norm(params.queryVector, doc['my_vector']))",
        "params": {
          "queryVector": [-0.5, 10, 6]
        }
      }
    }
  }
}

1.与表示相似性的余弦相似度不同,1norml2norm表示距离或差异。这意味着,向量越相似,由1norml2norm函数产生的分数就越低。因此,当我们需要相似的向量来获得更高的分数时,我们将1norml2norm的输出反过来。另外,为了避免在文档向量与查询完全匹配时被除0,在分母中加了1。

3.4 欧几里得距离:l2norm

  l2norm函数计算给定查询向量和文档向量之间的L2距离(欧几里德距离)。

POST index3/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "1 / (1 + l2norm(params.queryVector, doc['my_vector']))",
        "params": {
          "queryVector": [
            -0.5,
            10,
            6
          ]
        }
      }
    }
  }
}

3.5 自定义计算函数

  使用函数访问向量的值,自定义实现向量余弦相似度计算。ES 中向量检索 doc[].vectorValue 函数是在 Elasticsearch 7.8.0 版本开始支持的,在ES 7.5.1 或 7.8.0 以下版本会运行失败。

  可以通过以下函数直接访问向量值:

POST index3/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": """
          float[] v = doc['my_vector'].vectorValue;
          float vm = doc['my_vector'].magnitude;
          float dotProduct = 0;
          for (int i = 0; i < v.length; i++) {
            dotProduct += v[i] * params.queryVector[i];
          }
          return dotProduct / (vm * (float) params.queryVectorMag);
        """,
        "params": {
          "queryVector": [
            -0.5,
            10,
            6
          ],
          "queryVectorMag": 5.25357
        }
      }
    }
  }
}

到此这篇关于向量数据库之如何使用Elasticsearch实现向量数据存储与搜索的文章就介绍到这了,更多相关Elasticsearch向量数据存储与搜索内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文