跳到主要内容

向量搜索

向量搜索(Vector Search)是 Elasticsearch 8.x 的重要新特性,支持 kNN(k-最近邻)搜索和语义搜索。本章节介绍如何使用向量搜索功能实现相似性搜索和语义搜索。

什么是向量搜索?

向量搜索是一种基于向量相似度的搜索方式。与传统关键词搜索不同,向量搜索通过计算向量之间的距离来找到最相似的文档。

应用场景

  • 语义搜索:理解查询意图,返回语义相关的结果
  • 相似图片搜索:查找与给定图片相似的其他图片
  • 推荐系统:基于用户偏好推荐相似内容
  • 问答系统:找到与问题最相似的答案
  • 文档去重:检测内容相似的文档

工作原理

1. 文本/图片/音频 → 嵌入模型 → 向量表示
2. 查询文本 → 嵌入模型 → 查询向量
3. 计算查询向量与文档向量的相似度
4. 返回相似度最高的 k 个文档

dense_vector 字段类型

创建向量字段

PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text"
},
"image_vector": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
}
}
}
}

参数说明

参数说明
dims向量维度,必须与嵌入模型输出一致
index是否为 kNN 搜索建立索引(默认 true)
similarity相似度算法:l2_normdot_productcosine

相似度算法

算法公式适用场景
l2_norm欧几里得距离通用场景
dot_product点积已归一化的向量
cosine余弦相似度文本语义搜索

添加向量文档

POST /products/_doc
{
"name": "智能手机",
"description": "高性能智能手机,配备先进摄像头",
"image_vector": [0.12, -0.34, 0.56, ..., 0.78]
}

实际应用中,向量通常由嵌入模型生成:

from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch

model = SentenceTransformer('all-MiniLM-L6-v2')
es = Elasticsearch("http://localhost:9200")

def index_product(name, description):
text = f"{name} {description}"
vector = model.encode(text).tolist()

es.index(index="products", document={
"name": name,
"description": description,
"image_vector": vector
})

index_product("智能手机", "高性能智能手机,配备先进摄像头")
index_product("笔记本电脑", "轻薄便携,续航持久")

kNN 搜索

基本 kNN 搜索

POST /products/_search
{
"knn": {
"field": "image_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100
}
}

参数说明

参数说明
field向量字段名
query_vector查询向量
k返回的最相似文档数量
num_candidates每个分片考虑的候选数量

带过滤的 kNN 搜索

POST /products/_search
{
"knn": {
"field": "image_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100
},
"filter": {
"term": { "category": "electronics" }
}
}

kNN 与关键词搜索结合

POST /products/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"description": "智能手机"
}
}
],
"should": [
{
"knn": {
"field": "image_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100,
"boost": 0.5
}
}
]
}
}
}

HNSW 算法

Elasticsearch 使用 HNSW(Hierarchical Navigable Small World)算法实现高效的 kNN 搜索。

HNSW 特点

  • 近似搜索:牺牲少量精度换取更快的搜索速度
  • 分层结构:通过多层图结构加速搜索
  • 高效率:适合大规模向量数据

索引配置

PUT /products
{
"mappings": {
"properties": {
"image_vector": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 100
}
}
}
}
}

HNSW 参数

参数说明默认值
type索引类型hnsw
m每个节点的连接数16
ef_construction构建索引时的候选数100

搜索时调优

POST /products/_search
{
"knn": {
"field": "image_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100
},
"size": 10
}
  • num_candidates 越大,结果越精确,但搜索越慢
  • 一般设置 num_candidatesk 的 10 倍以上

语义搜索实战

使用 ELSER(Elastic 官方模型)

PUT /articles
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"content_embedding": {
"type": "semantic_text",
"model_id": ".elser_model_2"
}
}
}
}

索引文档(自动生成向量)

POST /articles/_doc
{
"title": "Elasticsearch 向量搜索指南",
"content": "本文介绍如何在 Elasticsearch 中使用向量搜索功能..."
}

语义搜索

POST /articles/_search
{
"query": {
"semantic": {
"field": "content_embedding",
"query": "如何实现相似内容搜索"
}
}
}

使用外部嵌入模型

from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch
import numpy as np

model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
es = Elasticsearch("http://localhost:9200")

def semantic_search(query_text, top_k=10):
query_vector = model.encode(query_text).tolist()

response = es.search(
index="articles",
knn={
"field": "content_vector",
"query_vector": query_vector,
"k": top_k,
"num_candidates": 100
}
)

return response['hits']['hits']

results = semantic_search("如何实现相似内容搜索")
for hit in results:
print(f"Score: {hit['_score']:.4f}, Title: {hit['_source']['title']}")

混合搜索

混合搜索结合关键词搜索和向量搜索,提供更准确的搜索结果。

RRF(Reciprocal Rank Fusion)

POST /articles/_search
{
"query": {
"match": {
"content": "机器学习入门"
}
},
"knn": {
"field": "content_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100
},
"rank": {
"rrf": {
"window_size": 100,
"rank_constant": 60
}
}
}

自定义权重

POST /articles/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"content": {
"query": "机器学习入门",
"boost": 0.3
}
}
},
{
"knn": {
"field": "content_vector",
"query_vector": [0.1, -0.2, 0.3, ..., 0.5],
"k": 10,
"num_candidates": 100,
"boost": 0.7
}
}
]
}
}
}

性能优化

索引优化

PUT /products
{
"settings": {
"index": {
"knn": true,
"knn.algo_param.ef_search": 100
}
},
"mappings": {
"properties": {
"image_vector": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 200
}
}
}
}
}

批量索引

from elasticsearch import helpers

def bulk_index_vectors(es, index_name, documents):
actions = []
for doc in documents:
vector = model.encode(doc['text']).tolist()
actions.append({
"_index": index_name,
"_source": {
"text": doc['text'],
"vector": vector
}
})

helpers.bulk(es, actions)

documents = [
{"text": "机器学习是人工智能的一个分支"},
{"text": "深度学习使用神经网络进行学习"},
{"text": "自然语言处理处理人类语言"}
]

bulk_index_vectors(es, "articles", documents)

完整示例:语义文档搜索

from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch, helpers
import json

model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
es = Elasticsearch("http://localhost:9200")

def create_index():
es.indices.delete(index="documents", ignore_unavailable=True)
es.indices.create(index="documents", body={
"mappings": {
"properties": {
"title": {"type": "text"},
"content": {"type": "text"},
"content_vector": {
"type": "dense_vector",
"dims": 384,
"index": True,
"similarity": "cosine"
}
}
}
})

def index_documents(docs):
actions = []
for doc in docs:
vector = model.encode(doc['content']).tolist()
actions.append({
"_index": "documents",
"_source": {
"title": doc['title'],
"content": doc['content'],
"content_vector": vector
}
})
helpers.bulk(es, actions)

def search_similar(query, top_k=5):
query_vector = model.encode(query).tolist()

response = es.search(
index="documents",
knn={
"field": "content_vector",
"query_vector": query_vector,
"k": top_k,
"num_candidates": 100
}
)

return response['hits']['hits']

if __name__ == "__main__":
create_index()

documents = [
{"title": "Python 基础", "content": "Python 是一种解释型、面向对象的编程语言"},
{"title": "Java 入门", "content": "Java 是一种面向对象的编程语言,具有跨平台特性"},
{"title": "机器学习简介", "content": "机器学习是人工智能的一个分支,让计算机从数据中学习"},
{"title": "深度学习基础", "content": "深度学习使用多层神经网络进行特征学习和模式识别"},
{"title": "数据库设计", "content": "数据库设计是创建数据库结构的过程,包括表设计和关系定义"}
]

index_documents(documents)

print("搜索: '编程语言'")
results = search_similar("编程语言")
for hit in results:
print(f" Score: {hit['_score']:.4f}, Title: {hit['_source']['title']}")

print("\n搜索: '人工智能'")
results = search_similar("人工智能")
for hit in results:
print(f" Score: {hit['_score']:.4f}, Title: {hit['_source']['title']}")

小结

本章我们学习了:

  1. 向量搜索基础:dense_vector 字段类型和相似度算法
  2. kNN 搜索:基本用法、过滤和混合搜索
  3. HNSW 算法:索引配置和性能调优
  4. 语义搜索:使用 ELSER 或外部嵌入模型
  5. 混合搜索:结合关键词和向量搜索
  6. 性能优化:索引优化和批量操作

练习

  1. 使用 Sentence Transformers 生成文本向量并索引到 Elasticsearch
  2. 实现 kNN 搜索并比较不同相似度算法的效果
  3. 实现混合搜索,结合关键词和向量搜索
  4. 使用 ELSER 实现语义搜索

参考资料