Elasticsearch向量函数详解:从基础概念到实战应用

前言

在现代搜索和推荐系统中,向量相似度计算扮演着至关重要的角色。Elasticsearch作为一款强大的搜索引擎,提供了丰富的向量函数来处理各种相似度计算场景。本文将深入解析Elasticsearch中的向量函数,帮助开发者更好地理解和应用这些功能。

向量函数基础

Elasticsearch提供了多种向量计算函数,它们都基于线性扫描匹配文档的方式工作。这意味着查询时间会随着匹配文档数量的增加而线性增长。因此,在实际应用中,我们强烈建议通过query参数限制匹配文档的数量。

核心向量函数列表

  1. 余弦相似度cosineSimilarity):计算两个向量之间的夹角余弦值
  2. 点积dotProduct):计算两个向量的点积
  3. L1距离l1norm):计算曼哈顿距离
  4. 汉明距离hamming):计算二进制向量间的不同位数
  5. L2距离l2norm):计算欧几里得距离
  6. 向量值访问doc[<field>].vectorValue):获取向量值的浮点数组
  7. 向量模长doc[<field>].magnitude):获取向量的模长

重要提示:cosineSimilaritydotProduct函数不支持位向量(bit vectors)。

准备工作:创建索引和测试数据

在深入探讨各个函数之前,我们先建立一个测试环境:

PUT my-vector-index
{
  "mappings": {
    "properties": {
      "dense_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3
      },
      "byte_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3,
        "element_type": "byte"
      },
      "status": {
        "type": "keyword"
      }
    }
  }
}

PUT my-vector-index/_doc/1
{
  "dense_vector": [0.5, 10, 6],
  "byte_vector": [0, 10, 6],
  "status": "published"
}

PUT my-vector-index/_doc/2
{
  "dense_vector": [-0.5, 10, 10],
  "byte_vector": [0, 10, 10],
  "status": "published"
}

POST my-vector-index/_refresh

各向量函数详解

1. 余弦相似度(cosineSimilarity)

余弦相似度衡量的是两个向量在方向上的相似性,而不考虑它们的大小。值范围在[-1,1]之间,1表示完全相同,-1表示完全相反。

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": "cosineSimilarity(params.query_vector, 'dense_vector') + 1.0",
        "params": {
          "query_vector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

技术要点

  • 添加1.0是为了避免负分
  • 查询向量应作为参数传入以提高性能
  • 文档向量维度必须与查询向量一致,否则会报错

2. 点积(dotProduct)

点积衡量的是两个向量在大小和方向上的综合相似性。在Elasticsearch中,通常结合sigmoid函数使用以避免负分。

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": """
          double value = dotProduct(params.query_vector, 'dense_vector');
          return sigmoid(1, Math.E, -value);
        """,
        "params": {
          "query_vector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

3. L1距离(曼哈顿距离)

L1距离计算两个向量在各维度上绝对差值的总和。它代表的是实际路径距离,类似于城市街区距离。

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l1norm(params.queryVector, 'dense_vector'))",
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

注意:距离函数(l1norm/l2norm)与相似度函数不同,距离越小表示越相似,因此需要进行反向处理。

4. 汉明距离(hamming)

专为字节向量和位向量设计,计算两个向量在相同位置上值不同的位数。

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": "(24 - hamming(params.queryVector, 'byte_vector')) / 24",
        "params": {
          "queryVector": [4, 3, 0]
        }
      }
    }
  }
}

5. L2距离(欧几里得距离)

最常用的距离度量,计算两个向量间的直线距离。

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l2norm(params.queryVector, 'dense_vector'))",
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

高级应用技巧

处理缺失值

当文档缺少向量字段时,直接计算会抛出错误。可以通过以下方式检查:

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

直接访问向量值

对于需要自定义计算逻辑的场景,可以直接访问向量值和模长:

GET my-vector-index/_search
{
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "status": "published"
            }
          }
        }
      },
      "script": {
        "source": """
          float[] v = doc['dense_vector'].vectorValue;
          float vm = doc['dense_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": [4, 3.4, -0.2],
          "queryVectorMag": 5.25357
        }
      }
    }
  }
}

位向量特殊处理

位向量仅支持部分函数:

  • hamming:计算位异或和
  • l1norm:等同于汉明距离
  • l2norm:汉明距离的平方根

性能优化建议

  1. 限制计算范围:始终使用查询过滤条件限制参与计算的文档数量
  2. 避免重复计算:不要在循环中多次调用向量函数
  3. 参数化查询向量:将查询向量作为参数传递而非硬编码
  4. 预处理模长:对于自定义实现,预先计算查询向量的模长

总结

Elasticsearch的向量函数为相似度搜索提供了强大支持。理解各种距离/相似度度量的特点及适用场景,结合合理的性能优化措施,可以构建出高效的向量搜索应用。在实际项目中,应根据数据类型(浮点/字节/位)和业务需求(方向敏感/大小敏感)选择合适的向量函数。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐