尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Elasticsearch 语义搜索快速入门:从原理到代码实战

Elasticsearch 语义搜索快速入门:从原理到代码实战 1. 什么是语义搜索传统的关键词搜索基于字面匹配用户输入「苹果手机」时文档中只有同时包含「苹果」和「手机」两个词的记录才会被召回。这种方式无法理解同义词、近义词和上下文含义例如「iPhone」与「苹果手机」在语义上等价但关键词搜索却无法建立关联。语义搜索Semantic Search通过将文本转换为高维向量Embedding在向量空间中计算文本之间的相似度从而理解查询与文档之间的语义关系。即使查询词与文档用词完全不同只要语义相近也能被准确召回。Elasticsearch 从 7.x 开始引入向量检索能力并在 8.x 中提供了完整的语义搜索解决方案包括文本嵌入Text Embedding、向量字段Dense Vector和 KNN 检索K-Nearest Neighbors。2. 语义搜索的核心原理语义搜索的完整流程可以分为三个阶段文本向量化、向量索引构建、向量相似度检索。2.1 文本向量化文本向量化是将自然语言文本转换为固定维度的数值向量。常用的模型包括 BERT、Sentence-BERT、E5 等。Elasticsearch 官方推荐使用 ELSERElastic Learned Sparse EncodeR模型它无需 GPU 即可在 Elasticsearch 内部完成推理。2.2 向量索引构建向量索引使用 HNSWHierarchical Navigable Small World算法构建它通过多层图结构加速最近邻检索。Elasticsearch 的dense_vector字段类型支持配置索引类型、相似度度量方式和向量维度。2.3 向量相似度检索检索阶段使用 KNN 算法在向量空间中查找与查询向量最相似的 K 个文档。常用的相似度度量包括余弦相似度cosine、欧氏距离l2和内积dot_product。flowchart TD A[原始文本] -- B[Embedding 模型] B -- C[向量化结果] C -- D[dense_vector 字段] D -- E[HNSW 索引] E -- F[KNN 检索] F -- G[相似度排序结果]3. 环境准备本文使用 Elasticsearch 8.12 版本进行演示需要准备以下环境Elasticsearch 8.12 及以上版本并启用安全认证Python 3.9 及以上版本Elasticsearch Python 客户端 8.x一个可用的 Embedding 模型服务本文使用 HuggingFace 的 sentence-transformers 模型首先安装 Python 依赖pip install elasticsearch sentence-transformers启动 Elasticsearch 后验证服务是否正常curl -k https://localhost:9200 -u elastic:your_password4. 创建索引并配置向量字段创建索引时需要为向量字段指定维度、相似度度量和索引类型。本文使用 384 维向量相似度度量采用余弦相似度。from elasticsearch import Elasticsearch es Elasticsearch( https://localhost:9200, basic_auth(elastic, your_password), verify_certsFalse ) index_name semantic_articles index_mapping { mappings: { properties: { title: {type: text}, content: {type: text}, title_vector: { type: dense_vector, dims: 384, index: True, similarity: cosine }, content_vector: { type: dense_vector, dims: 384, index: True, similarity: cosine } } } } if not es.indices.exists(indexindex_name): es.indices.create(indexindex_name, bodyindex_mapping) print(f索引 {index_name} 创建成功)上述代码中dims必须与 Embedding 模型输出的向量维度一致否则写入文档时会报错。similarity指定了向量相似度的计算方式index设置为true表示启用 HNSW 索引以加速检索。5. 准备 Embedding 模型本文使用 sentence-transformers 库加载预训练模型将文本转换为 384 维向量。这里选用all-MiniLM-L6-v2模型它在速度和效果之间取得了较好的平衡。from sentence_transformers import SentenceTransformer model SentenceTransformer(all-MiniLM-L6-v2) def get_embedding(text): return model.encode(text).tolist() 测试向量化 sample_text Elasticsearch 语义搜索实战 vector get_embedding(sample_text) print(f向量维度: {len(vector)}) print(f向量前 5 个值: {vector[:5]})运行上述代码后会输出向量的维度信息。如果输出维度为 384说明模型加载成功可以用于后续的文档写入和查询。6. 写入带向量的文档数据写入文档时需要同时保存原始文本和对应的向量。下面准备一组示例文档涵盖不同主题的技术文章。documents [ { title: Elasticsearch 分布式搜索原理, content: Elasticsearch 基于 Lucene 构建通过分片和副本实现分布式搜索支持水平扩展和高可用。 }, { title: Python 爬虫入门教程, content: 使用 Python 编写网络爬虫抓取网页数据并解析 HTML提取结构化信息。 }, { title: 机器学习基础概念, content: 机器学习是人工智能的分支通过数据训练模型实现预测和分类任务。 }, { title: 数据库索引优化技巧, content: 通过建立合适的索引可以显著提升数据库查询性能减少全表扫描。 }, { title: 自然语言处理应用, content: 自然语言处理技术用于文本分类、情感分析和机器翻译等场景。 } ] for doc in documents: doc[title_vector] get_embedding(doc[title]) doc[content_vector] get_embedding(doc[content]) es.index(indexindex_name, documentdoc) es.indices.refresh(indexindex_name) print(f成功写入 {len(documents)} 篇文档)写入完成后调用refresh接口使文档立即可被检索。在实际生产环境中可以依赖 Elasticsearch 的自动刷新机制。7. 执行语义搜索语义搜索的核心是使用 KNN 查询将查询文本向量化后在向量字段上执行最近邻检索。def semantic_search(query_text, top_k3): query_vector get_embedding(query_text) knn_query { field: content_vector, query_vector: query_vector, k: top_k, num_candidates: 100 } response es.search(indexindex_name, knnknn_query) return response[hits][hits] 执行语义搜索 query 如何提升搜索引擎的查询速度 results semantic_search(query) print(f查询: {query}\n) for hit in results: score hit[_score] title hit[_source][title] content hit[_source][content] print(f相似度: {score:.4f}) print(f标题: {title}) print(f内容: {content}) print(- * 50)运行上述代码可以看到即使查询语句中没有出现「索引」「数据库」等关键词系统依然能够召回与查询语义相关的文档这正是语义搜索的核心价值。8. 混合检索语义搜索与关键词搜索结合在实际业务中单纯依赖语义搜索可能无法满足精确匹配的需求。Elasticsearch 支持将 KNN 检索与传统的 BM25 关键词检索结合实现混合检索Hybrid Search。def hybrid_search(query_text, top_k3): query_vector get_embedding(query_text) search_body { query: { bool: { should: [ {match: {title: query_text}}, {match: {content: query_text}} ] } }, knn: { field: content_vector, query_vector: query_vector, k: top_k, num_candidates: 100, boost: 0.5 }, size: top_k } response es.search(indexindex_name, bodysearch_body) return response[hits][hits] 执行混合检索 query 数据库性能优化 results hybrid_search(query) print(f混合检索查询: {query}\n) for hit in results: score hit[_score] title hit[_source][title] print(f综合得分: {score:.4f}) print(f标题: {title}) print(- * 50)混合检索通过boost参数控制语义检索和关键词检索的权重比例可以根据业务场景灵活调整。9. 使用 ELSER 模型实现零向量语义搜索如果不想自行维护 Embedding 模型可以使用 Elasticsearch 内置的 ELSER 模型。ELSER 是稀疏向量模型无需显式生成向量检索时直接使用文本查询即可。# 部署 ELSER 模型 es.ml.put_trained_model( model_id.elser_model_2, body{input: {field_names: [text_field]}} ) 创建使用 ELSER 的索引 elser_index elser_semantic_index elser_mapping { mappings: { properties: { title: {type: text}, content: {type: text}, content_embedding: { type: sparse_vector } } } } if not es.indices.exists(indexelser_index): es.indices.create(indexelser_index, bodyelser_mapping) print(fELSER 索引 {elser_index} 创建成功)ELSER 模型部署后需要通过 Ingest Pipeline 自动为文档生成稀疏向量这里不再展开感兴趣的读者可以查阅官方文档深入了解。10. 性能优化与最佳实践在实际生产环境中语义搜索的性能和效果需要从多个维度进行优化。10.1 向量维度选择向量维度越高表达语义的能力越强但存储和计算开销也越大。建议根据业务数据量和硬件资源选择合适的模型常用维度包括 128、384 和 768。10.2 HNSW 参数调优HNSW 索引的m参数控制每个节点的最大连接数ef_construction控制构建时的搜索宽度。增大这两个参数可以提高召回率但会增加内存占用和构建时间。optimized_mapping { mappings: { properties: { content_vector: { type: dense_vector, dims: 384, index: True, similarity: cosine, index_options: { type: hnsw, m: 32, ef_construction: 200 } } } } }10.3 分页与深度检索KNN 查询默认不支持深度分页如果需要获取大量结果建议使用search_after或增大num_candidates参数。10.4 向量字段存储优化如果不需要返回原始向量可以关闭store选项以减少存储开销。同时建议将向量字段与业务字段分离避免不必要的序列化开销。11. 完整实战代码下面给出一个完整的可运行脚本整合了索引创建、文档写入和语义检索的全部流程。from elasticsearch import Elasticsearch from sentence_transformers import SentenceTransformer 初始化客户端和模型 es Elasticsearch( https://localhost:9200, basic_auth(elastic, your_password), verify_certsFalse ) model SentenceTransformer(all-MiniLM-L6-v2) INDEX_NAME semantic_articles def get_embedding(text): return model.encode(text).tolist() def create_index(): mapping { mappings: { properties: { title: {type: text}, content: {type: text}, title_vector: { type: dense_vector, dims: 384, index: True, similarity: cosine }, content_vector: { type: dense_vector, dims: 384, index: True, similarity: cosine } } } } if not es.indices.exists(indexINDEX_NAME): es.indices.create(indexINDEX_NAME, bodymapping) print(索引创建成功) def index_documents(): docs [ {title: Elasticsearch 分布式搜索原理, content: 基于 Lucene 构建通过分片和副本实现分布式搜索。}, {title: Python 爬虫入门教程, content: 使用 Python 编写网络爬虫抓取网页数据并解析 HTML。}, {title: 机器学习基础概念, content: 通过数据训练模型实现预测和分类任务。}, {title: 数据库索引优化技巧, content: 通过建立合适的索引显著提升数据库查询性能。}, {title: 自然语言处理应用, content: 用于文本分类、情感分析和机器翻译等场景。} ] for doc in docs: doc[title_vector] get_embedding(doc[title]) doc[content_vector] get_embedding(doc[content]) es.index(indexINDEX_NAME, documentdoc) es.indices.refresh(indexINDEX_NAME) print(f写入 {len(docs)} 篇文档) def semantic_search(query, top_k3): query_vector get_embedding(query) knn { field: content_vector, query_vector: query_vector, k: top_k, num_candidates: 100 } response es.search(indexINDEX_NAME, knnknn) return response[hits][hits] if name main: create_index() index_documents() query 如何提升搜索引擎的查询速度 results semantic_search(query) print(f\n查询: {query}\n) for hit in results: print(f相似度: {hit[_score]:.4f}) print(f标题: {hit[_source][title]}) print(f内容: {hit[_source][content]}) print(- * 50)12. 总结本文从语义搜索的基本概念出发详细介绍了 Elasticsearch 语义搜索的完整实现流程包括环境准备、索引创建、文本向量化、文档写入和 KNN 检索。通过代码实战可以看到语义搜索能够有效解决关键词搜索无法理解语义的问题。在实际项目中建议根据业务场景选择合适的 Embedding 模型并结合混合检索策略在语义理解和精确匹配之间取得平衡。同时关注向量索引的参数调优和存储优化确保系统在高并发场景下保持稳定高效的检索性能。
返回列表