Elasticsearch 向量搜索入门指南 – wiki基地


Elasticsearch 向量搜索入门指南:释放非结构化数据的语义力量

在数字化爆炸的时代,数据量以前所未有的速度增长。传统上,我们依赖于关键词搜索来查找信息,这种方式在处理结构化数据或进行精确匹配时非常有效。然而,当面对图片、音频、视频、甚至自然语言文本这类非结构化数据时,单纯的关键词搜索往往捉襟见肘。它无法理解词语的深层含义、上下文关系,更无法处理同义词、多义词或概念上的相似性。

试想一下,您想搜索“红色小轿车”的图片,但图片描述可能只包含“车辆”、“交通工具”等词汇;或者您想查找与某篇文章“意义相似”的其他文章,而不仅仅是包含相同关键词的文章。这时,传统的搜索方法就显得力不从心了。

幸运的是,随着人工智能和机器学习的发展,特别是深度学习模型的进步,我们有了新的工具来理解和处理非结构化数据——向量搜索(Vector Search)。它通过将数据转化为高维空间中的向量(即一系列数字),使得具有相似含义或特征的数据点在向量空间中彼此靠近。然后,通过计算向量之间的距离或相似度,我们就可以找到与查询向量最相关的数据。

Elasticsearch,作为业界领先的分布式搜索和分析引擎,一直在不断演进以适应现代数据处理的需求。从最初强大的全文搜索能力,到结构化数据聚合分析,再到近年来对向量搜索功能的深度集成,Elasticsearch 正在成为一个全能的数据平台。

本篇文章将带您深入了解 Elasticsearch 的向量搜索功能。我们将从基础概念讲起,逐步涵盖如何在 Elasticsearch 中存储、索引和搜索向量数据,并探讨如何将向量搜索与传统关键词搜索相结合,构建更强大、更智能的搜索应用。

第一部分:向量搜索基础——理解 Embeddings 和向量空间

在深入 Elasticsearch 的具体实现之前,理解向量搜索背后的核心概念至关重要。

1. 关键词搜索的局限性

关键词搜索依赖于精确匹配或基于词频、位置等统计信息的排序(如 TF-IDF 或 BM25)。它的主要问题在于:
* 同义词/近义词问题: 用户搜索“汽车”,但文档中可能只出现“轿车”、“车辆”。关键词搜索无法理解它们是相似概念。
* 多义词问题: 词语可能有多个含义(如“苹果”可以是水果或公司)。关键词搜索无法区分上下文。
* 概念相似性: “法国首都”和“巴黎”在语义上是等价的,但关键词搜索无法直接关联。
* 非文本数据: 图片、音频等无法直接用关键词描述的内容,难以通过关键词搜索找到。

2. Embeddings (嵌入向量) 的崛起

为了克服关键词搜索的局限性,研究人员开发了将复杂数据类型(如文本、图片、声音等)转化为数值表示的方法。这些数值表示通常是高维空间中的一个点,被称为嵌入向量(Embeddings)

嵌入向量是通过机器学习模型(特别是深度学习模型,如 BERT、GPT 系列、CLIP 等)训练得到的。这些模型学习数据的内在结构和关系,并将这些关系编码到向量的各个维度中。关键在于:
* 语义相近的数据,其嵌入向量在向量空间中也相近。 例如,经过训练的文本嵌入模型会将“汽车”和“轿车”的向量放置在向量空间中彼此靠近的位置。
* 向量的维度数量取决于所使用的模型,通常在几十维到几千维不等。维度越多,理论上可以捕捉的信息越多,但也增加了存储和计算的开销。

3. 向量空间与相似度度量

一旦数据被转化为向量,它们就存在于一个高维的向量空间中。在这个空间里,我们可以使用数学方法来计算任意两个向量之间的“距离”或“相似度”。常用的相似度度量方法包括:
* 余弦相似度 (Cosine Similarity): 衡量两个向量方向的相似程度。即使两个向量的长度(大小)不同,只要它们指向同一个方向,余弦相似度就高。这是文本相似度中最常用的度量。
* 欧几里得距离 (Euclidean Distance / L2 Distance): 衡量两个向量在空间中的直线距离。距离越小,向量越相似。常用于衡量数值特征的相似性。
* 内积 (Dot Product): 衡量两个向量方向和大小的综合相似度。向量方向越一致、长度越大,内积越大。在某些机器学习模型中,内积被用作相似度得分。

Elasticsearch 支持这些常见的相似度度量,允许您根据数据的特性和应用场景选择最合适的度量方式。

通过将查询内容(如一段文本、一张图片)同样转化为一个查询向量,我们就可以在存储了所有数据向量的向量空间中,找到与查询向量“最近”的那些数据向量。这个过程就是向量搜索,也称为最近邻搜索 (Nearest Neighbor Search)相似度搜索 (Similarity Search)

第二部分:为何选择 Elasticsearch 进行向量搜索?

市面上有许多专门的向量数据库(Vector Database),例如 Pinecone、Weaviate、Milvus 等。那么,为什么还要在 Elasticsearch 中进行向量搜索呢?Elasticsearch 的优势在于:

  1. 统一平台: 您可以将传统的结构化/非结构化数据与向量数据存储在同一个平台中。无需维护独立的向量数据库和传统搜索/分析系统,简化了架构和运维。
  2. 强大的混合搜索能力: Elasticsearch 最独特的优势在于能够轻松地将向量搜索与传统的关键词搜索、结构化过滤、聚合分析等功能结合起来。这使得构建“既理解语义,又能精确过滤和排序”的复杂搜索应用成为可能。例如,您可以搜索“与这份报告语义相似”, 并且 “发布日期在最近一个月内”, 并且 “属于某个特定类别”的文档。
  3. 成熟的生态系统和可观测性: Elasticsearch 是 Elastic Stack 的核心,与 Logstash、Kibana、Beats 等工具无缝集成。您可以利用 Kibana 进行数据可视化、性能监控和查询调试,利用 Beats/Logstash 进行数据采集。这对于生产环境的稳定运行至关重要。
  4. 水平扩展和分布式特性: Elasticsearch 从设计之初就考虑了分布式和高可用性。它可以轻松地水平扩展以应对大量数据和高并发请求。向量索引同样受益于其分布式架构。
  5. 实时性: Elasticsearch 提供了近实时(NRT)的索引和搜索能力,这对于需要快速反映数据变化的场景非常重要。

虽然专门的向量数据库可能在某些纯向量搜索场景下提供极致的性能或特定功能,但对于大多数需要结合多种搜索方式的企业应用而言,Elasticsearch 提供了一个强大且统一的解决方案。

第三部分:Elasticsearch 向量搜索入门实践

现在,让我们看看如何在 Elasticsearch 中实际操作向量搜索。

本指南基于较新版本的 Elasticsearch(推荐 8.x 及以上,因为 knn_vector 字段类型和 knn 查询是向量搜索的主流实现方式,在 8.x 版本中得到极大优化和推荐)。

步骤 1:创建索引并定义向量字段映射

在 Elasticsearch 中存储向量,我们需要在索引映射(Mapping)中定义一个专门的字段类型来存放向量。Elasticsearch 提供了 dense_vectorknn_vector 两种主要用于向量存储的字段类型。

  • dense_vector: 这是基本的向量字段类型,用于存储任意浮点数向量。它支持精确 kNN 搜索或通过 script_score 进行相似度计算。
  • knn_vector: 这是专为高性能近似最近邻 (ANN) 搜索设计的字段类型。它内置了对 HNSW (Hierarchical Navigable Small Worlds) 算法的支持。在大多数需要高性能向量搜索的场景下,推荐使用 knn_vector

我们将使用 knn_vector 作为示例,因为它代表了 Elasticsearch 未来向量搜索的主要方向和推荐实践。

创建一个名为 my_vector_index 的索引,并定义一个 text_embedding 字段来存储文本的嵌入向量,一个 image_embedding 字段来存储图片的嵌入向量。

json
PUT /my_vector_index
{
"settings": {
"index": {
"number_of_shards": 1,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"category": {
"type": "keyword"
},
"text_embedding": {
"type": "knn_vector",
"dimension": 768, // 向量维度,取决于您使用的 Embedding 模型
"index": true, // 必须设置为 true,以便构建 HNSW 索引
"similarity": "cosine" // 定义相似度度量,可选 "l2" (欧氏距离), "dot_product", "cosine"
// "index_options": { // 可选,用于微调 HNSW 索引构建
// "type": "hnsw",
// "m": 16, // HNSW 参数 M,控制每层连接数,影响索引大小和精度
// "ef_construction": 100 // HNSW 参数 ef_construction,构建索引时的搜索范围,影响索引时间、大小和精度
// }
},
"image_embedding": {
"type": "knn_vector",
"dimension": 512, // 假设图片 Embedding 模型维度为 512
"index": true,
"similarity": "l2" // 图片相似度可能更适合欧氏距离,具体取决于模型训练方式
}
// 可以添加其他字段,如 timestamp, user_id 等
}
}
}

解释:

  • PUT /my_vector_index: 创建或更新名为 my_vector_index 的索引。
  • settings: 配置索引设置,如分片和副本数。
  • mappings: 定义索引中文档的结构。
  • properties: 文档字段定义。
  • title, content, category: 传统的文本和关键词字段,用于存储原始数据和进行传统搜索。
  • text_embedding, image_embedding: 我们定义的向量字段。
    • "type": "knn_vector": 指定这是用于 ANN 搜索的向量字段。
    • "dimension": 768: 非常重要! 指定向量的维度。这个值必须与您使用的 Embedding 模型生成的向量维度一致。如果您使用的模型输出 768 维向量,这里就写 768。不匹配会导致索引错误。
    • "index": true: 必须! 开启向量索引(HNSW),否则无法进行高效的 knn 查询。
    • "similarity": "cosine" / "l2": 定义用于计算相似度的度量方法。选择合适的度量对于搜索结果的准确性至关重要。如果您不确定,对于文本通常选择 cosine
    • index_options: 可选的高级配置,用于调整 HNSW 索引的构建参数。
      • m: 控制 HNSW 图中每个节点的最大连接数。更大的 M 会提高搜索精度和召回率,但增加索引时间、大小和内存使用。
      • ef_construction: 控制构建 HNSW 图时算法探索的邻居数量。更大的 ef_construction 会提高索引精度,但显著增加索引时间。
      • mef_construction 是索引构建时的参数,影响索引质量和大小。它们与搜索时的 ef_search (通过 num_candidates 控制) 共同决定了 ANN 搜索的性能和精度权衡。

步骤 2:生成并索引向量数据

在将数据索引到 Elasticsearch 之前,您需要使用合适的 Embedding 模型将原始数据(如文本、图片)转化为向量。这个过程通常在 Elasticsearch 外部完成,您可以使用流行的 Embedding 模型库(如 Sentence-Transformers, OpenAI API, Cohere API, Hugging Face 提供的模型等)。

例如,使用一个文本 Embedding 模型将一段文本 "Elasticsearch 是一个强大的搜索引擎" 转化为一个 768 维的浮点数向量:[0.1, -0.5, ..., 0.9]

然后,将原始数据和生成的向量一起索引到 Elasticsearch 中:

“`json
POST /my_vector_index/_doc
{
“title”: “Elasticsearch 向量搜索指南”,
“content”: “本文详细介绍了如何在 Elasticsearch 中进行向量搜索。”,
“category”: “技术文档”,
“text_embedding”: [0.1, -0.5, …, 0.9], // 假设这是文本 “本文详细介绍了如何在 Elasticsearch 中进行向量搜索” 的 Embedding
“image_embedding”: [-0.2, 0.8, …, 0.3] // 假设这是与文档相关的图片 Embedding
}

POST /my_vector_index/_doc
{
“title”: “Elasticsearch 入门”,
“content”: “学习 Elasticsearch 的基础知识。”,
“category”: “技术文档”,
“text_embedding”: [0.15, -0.48, …, 0.88], // 另一个文本 Embedding
“image_embedding”: [-0.25, 0.75, …, 0.35] // 另一个图片 Embedding
}

// … 索引更多文档
“`

注意: text_embeddingimage_embedding 的值应该是一个包含 dimension 个浮点数的数组。在实际应用中,您需要编写程序调用 Embedding 模型 API 或库来生成这些向量。

步骤 3:执行向量搜索

Elasticsearch 提供了 knn 查询来执行基于 HNSW 索引的近似最近邻搜索。

假设您想查找与一段查询文本 "如何在 ES 中进行相似度搜索" 在语义上最相似的文档。首先,您需要使用 与索引时相同的 Embedding 模型 将这段查询文本转化为一个查询向量,例如 [0.05, -0.6, ..., 0.95]

然后,您可以使用 knn 查询来查找与这个查询向量最相似的文档:

json
POST /my_vector_index/_search
{
"knn": {
"field": "text_embedding", // 指定要搜索的向量字段
"query_vector": [0.05, -0.6, ..., 0.95], // 您的查询向量
"k": 10, // 返回最相似的前 k 个结果
"num_candidates": 50 // HNSW 算法在每个分片上内部探索的邻居数量,影响搜索精度和速度
},
"_source": ["title", "content", "category"] // 只返回部分字段
}

解释:

  • POST /my_vector_index/_search: 执行搜索请求。
  • "knn": 指定使用 KNN 查询。
  • "field": "text_embedding": 指定要对哪个 knn_vector 字段执行搜索。
  • "query_vector": [...]: 提供用于搜索的向量。这个向量应该由您的查询内容(文本、图片等)通过相应的 Embedding 模型生成。
  • "k": 10: 指定您希望从所有匹配的候选结果中返回的最相似的前 k 个结果。这是最终返回给用户的结果数量。
  • "num_candidates": 50: 这是 HNSW 搜索算法的一个重要参数,控制了算法在每个分片上内部考虑的潜在候选邻居数量。num_candidates 应该大于或等于 k
    • 更大的 num_candidates 会增加搜索时间(因为它需要探索更多的邻居),但会提高搜索精度和召回率(找到真正最近邻的概率更大)。
    • 更小的 num_candidates 会加快搜索速度,但可能牺牲精度。
    • 调优 num_candidates 是平衡搜索性能和准确性的关键。通常需要根据您的数据集和性能要求进行实验。这个参数类似于索引构建时的 ef_construction,但它是搜索时的参数,对应 HNSW 的 ef_search 参数。

搜索结果将按照与查询向量的相似度从高到低排序,并返回前 k 个文档。每个文档的 _score 默认就是其与查询向量的相似度(例如,对于 cosine 相似度,得分越高越相似)。

您也可以使用图片向量进行搜索,例如,通过上传一张图片生成其 Embedding,然后在 image_embedding 字段上执行 KNN 查询:

json
POST /my_vector_index/_search
{
"knn": {
"field": "image_embedding",
"query_vector": [-0.01, 0.9, ..., 0.25], // 假设这是上传图片的 Embedding
"k": 20,
"num_candidates": 100
},
"_source": ["title", "category"]
}

第四部分:强大的组合拳——混合搜索 (Hybrid Search)

纯粹的向量搜索有时可能不够理想。例如,用户搜索“最新的关于人工智能的报告”,如果只进行向量搜索,可能返回语义上相似但发布日期很旧的文档。在这种情况下,我们需要将向量搜索的语义理解能力与传统搜索的精确过滤和排序能力结合起来,这就是混合搜索 (Hybrid Search)

Elasticsearch 允许您在同一个搜索请求中同时使用 knn 查询和标准的查询 DSL (如 match, term, range, bool 等)。这通常通过 bool 查询实现,将 knn 查询与其他查询子句组合。

常用的组合方式是将 knn 查询放在 bool 查询的 should 子句中,并结合 mustfilter 子句进行传统搜索或过滤。然后,通过某种方式结合两种搜索结果的得分进行最终排序。

示例 1:结合关键词搜索和向量搜索

查找与查询文本语义相似 并且 标题或内容包含特定关键词的文档。

json
POST /my_vector_index/_search
{
"query": { // 传统查询部分
"bool": {
"must": [ // 必须满足的条件
{
"multi_match": {
"query": "报告 人工智能", // 关键词搜索
"fields": ["title", "content"]
}
}
],
"should": [ // 向量搜索作为加权项或并行项
{
"knn": {
"field": "text_embedding",
"query_vector": [0.05, -0.6, ..., 0.95], // 查询文本 "最新的关于人工智能的报告" 的 Embedding
"k": 10,
"num_candidates": 50,
"boost": 2 // 可以为向量搜索结果设置更高的权重
}
}
]
}
},
"_source": ["title", "category"]
}

这种方法的问题在于,默认的得分计算(BM25 + 向量相似度)可能不够理想,或者 knum_candidates 的选择会影响传统搜索的覆盖范围。

示例 2:使用 Reciprocal Rank Fusion (RRF)

Elasticsearch 8.x 引入了 Reciprocal Rank Fusion (RRF) 算法,这是一种更优雅地结合多个搜索结果列表(来自不同的搜索方法,如关键词、向量)进行排序的技术。RRF 不依赖于分数的合并,而是基于结果在各个搜索列表中的排名进行融合排序。

使用 RRF 进行混合搜索的结构如下:

json
POST /my_vector_index/_search
{
"query": { // 传统查询部分,通常用于过滤或作为 RRF 的一个输入
"multi_match": {
"query": "人工智能 报告",
"fields": ["title", "content"]
}
},
"knn": { // 向量搜索部分,作为 RRF 的另一个输入
"field": "text_embedding",
"query_vector": [0.05, -0.6, ..., 0.95],
"k": 10, // RRF 会从 k + num_candidates 中选择
"num_candidates": 50
},
"rank": { // RRF 配置
"fusion": {
"rrf": {
"window_size": 50, // 考虑融合的前 N 个文档
"rank_constant": 60 // RRF 公式中的常数,影响排名靠前文档的权重衰减
}
}
},
"_source": ["title", "category"]
}

解释:

  • 请求同时包含 query (传统搜索) 和 knn (向量搜索)。
  • rank.fusion.rrf: 启用 RRF 排名融合。
  • Elasticsearch 会并行执行 queryknn 两个搜索。
  • 对于每个搜索,它会基于各自的算法生成一个排序后的结果列表(query 基于 BM25/TF-IDF 等,knn 基于向量相似度)。
  • RRF 算法接收这两个排序列表,并根据每个文档在两个列表中的排名,计算一个最终的 RRF 分数,然后根据这个分数对文档进行最终排序。
  • window_size: RRF 在融合时考虑每个搜索结果列表中的前多少个文档。更大的 window_size 会考虑更多候选,可能提高召回率,但也可能引入不那么相关的结果。
  • rank_constant: RRF 公式 1 / (rank + rank_constant) 中的常数。影响排名衰减的速度。较大的 rank_constant 会使得排名靠前的文档权重差异不那么大。
  • 最终返回的文档数量由 knum_candidates 在一定程度上决定,但 RRF 会从 window_size 内的候选中进行融合排序。

RRF 是实现强大混合搜索的推荐方法,它能有效地结合关键词匹配的精确性和向量搜索的语义理解力。

第五部分:性能优化与高级考量

成功部署 Elasticsearch 向量搜索不仅仅是创建索引和执行查询。在生产环境中,您还需要考虑性能、资源和精度之间的权衡。

  1. HNSW 参数调优:

    • 索引构建参数 (m, ef_construction): 影响索引的大小、构建时间以及查询时的精度。
      • 更大的 m:提高索引的连通性,可能提高搜索精度,增加索引大小和构建时间。
      • 更大的 ef_construction:构建更精密的 HNSW 图,显著提高搜索精度(召回率),但大幅增加索引时间、CPU 和内存消耗。
      • 需要在索引时间和搜索精度之间权衡。通常建议从较小的值开始,逐渐增大并测试性能。
    • 搜索参数 (num_candidatesef_search): 影响搜索速度和精度。
      • 更大的 num_candidates: 搜索时探索更多邻居,提高精度和召回率,但增加搜索延迟。
      • 需要根据应用对延迟和精度的要求进行调优。num_candidates 应该大于等于 k
  2. 资源规划:

    • 内存: HNSW 索引是驻留在内存中的,以便快速访问。向量维度和文档数量越多,所需的内存就越多。需要确保 Elasticsearch 节点有足够的内存分配 (indices.memory.index.vector.percent) 来容纳 HNSW 索引。内存不足可能导致索引加载失败或性能下降。
    • CPU: 索引构建和向量搜索都是计算密集型任务,需要足够的 CPU 资源。
    • 磁盘: 存储原始向量数据和 HNSW 索引。
  3. 向量生成效率:

    • Embedding 模型的选择和运行效率直接影响数据索引的速度和成本。选择合适的模型,并考虑批量处理、硬件加速(如 GPU)等方法来提高向量生成效率。
  4. 索引策略:

    • 对于非常大的数据集,考虑使用时间序列索引或按类别分片等策略来管理数据和索引大小。
  5. 召回率与精度:

    • 向量搜索通常是近似搜索(ANN),意味着它不能保证找到绝对最近的邻居,但能以更高的效率找到非常接近的邻居。通过调整 HNSW 参数 (m, ef_construction, num_candidates) 可以平衡召回率(找到所有相关结果的比例)和精度(找到的结果中有多少是相关的)以及搜索速度。

第六部分:总结与展望

Elasticsearch 集成向量搜索功能,是其发展历程中的一个重要里程碑。它使得在单一平台上处理和搜索结构化、非结构化以及向量数据成为可能,极大地简化了构建复杂搜索应用的架构。

通过 knn_vector 字段类型和 knn 查询,结合 HNSW 算法,Elasticsearch 提供了高效的近似最近邻搜索能力。更重要的是,其强大的查询 DSL 和 RRF 等混合搜索技术,让您可以轻松地将向量搜索的语义理解力与传统搜索的精确过滤能力相结合,实现真正智能和灵活的搜索体验。

从文本相似度、图片检索,到推荐系统、异常检测,向量搜索的应用场景极为广泛。Elasticsearch 的向量搜索能力为您打开了新的大门,让您可以更深入地挖掘数据的价值。

当然,向量搜索是一个不断发展的领域。Embedding 模型在不断进步,向量数据库技术也在持续演化。Elasticsearch 作为积极的参与者,也在不断优化其向量搜索的性能和功能,例如引入 ELSER 等原生稀疏向量模型,提供更多向量处理工具等。

入门建议:

  • 从理解 Embedding 概念开始。
  • 选择合适的 Embedding 模型并学习如何生成向量。
  • 在开发环境中搭建 Elasticsearch 并创建带有 knn_vector 字段的索引。
  • 索引一些带有向量的数据。
  • 尝试执行纯粹的 knn 查询,并理解 knum_candidates 的作用。
  • 尝试使用 bool 查询结合 knn 和其他查询进行混合搜索。
  • 学习并实践 RRF 混合排序。
  • 在有足够数据后,开始关注 HNSW 参数的调优,以平衡性能和精度。

掌握 Elasticsearch 的向量搜索,您将能够构建下一代智能搜索和数据发现应用。立即动手实践吧,释放您数据的语义力量!


希望这篇详细的入门指南能够帮助您理解并开始使用 Elasticsearch 的向量搜索功能。祝您探索愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部