向量数据库:使用Elasticsearch实现向量数据存储与搜索
Here’s the table of contents:
向量数据库:使用Elasticsearch实现向量数据存储与搜索
一、简介
二、实验前准备
2.1 创建索引设置向量字段
// 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
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]
}
}
}
}
}
- 要限制
script_score
计算的文档数量,需要提供一个过滤器 (query)。 script
脚本在cosineSimilarity
上增加了1.0,以防止得分为负。- 为了更好的利用DSL优化器,可以使用参数的方式提供一个查询向量。
- 检查缺失值:如果文档中没有用于执行向量函数的向量字段的值,会抛出错误。可以使用
doc['my_vector'].size() == 0
来检查文档是否有my_vector
字段的值。脚本样例:
"source":
"
doc['my_vector'].size() == 0 ? 0 :
cosineSimilarity(params.queryVector, 'my_vector')
"
3.2 计算点积: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
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]
}
}
}
}
}
- 与表示相似性的余弦相似度不同,
1norm
和l2norm
表示距离或差异。这意味着,向量越相似,由1norm
和l2norm
函数产生的分数就越低。因此,当我们需要相似的向量来获得更高的分数时,我们将1norm
和l2norm
的输出反过来。另外,为了避免在文档向量与查询完全匹配时被除0,在分母中加了1。
3.4 欧几里得距离:l2norm
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 自定义计算函数
-
doc[<field>].vectorValue
– 以浮点数数组的形式返回向量的值。 -
doc[<field>].magnitude
– 将向量的大小作为浮点数返回(对于7.5版本之前创建的向量,其向量的大小不会被存储)。所以这个函数每次被调用时都会进行重新计算。
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
}
}
}
}
}