向量搜索
向量搜索(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_norm、dot_product、cosine |
相似度算法
| 算法 | 公式 | 适用场景 |
|---|---|---|
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_candidates为k的 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']}")
小结
本章我们学习了:
- 向量搜索基础:dense_vector 字段类型和相似度算法
- kNN 搜索:基本用法、过滤和混合搜索
- HNSW 算法:索引配置和性能调优
- 语义搜索:使用 ELSER 或外部嵌入模型
- 混合搜索:结合关键词和向量搜索
- 性能优化:索引优化和批量操作
练习
- 使用 Sentence Transformers 生成文本向量并索引到 Elasticsearch
- 实现 kNN 搜索并比较不同相似度算法的效果
- 实现混合搜索,结合关键词和向量搜索
- 使用 ELSER 实现语义搜索