Elasticsearch如何构建基于向量召回 + rerank 的双阶段检索系统

本文将深入解析现代搜索系统中的“双阶段检索架构”,结合向量检索(ANN)与精排模型(rerank),帮助你从零构建高性能、高相关度的语义搜索系统,适用于问答系统、RAG、多轮检索、企业知识库等场景。

目录

  1. 双阶段检索系统背景与价值
  2. 系统架构图解
  3. 向量召回阶段详解
  4. 精排(rerank)阶段详解
  5. 全流程代码实战(Elasticsearch + BGE + rerank)
  6. 多文档样例效果展示
  7. 性能优化与工程部署建议
  8. 总结与延伸方向

一、双阶段检索系统背景与价值

为什么要双阶段?

单一方法局限性
BM25精度低,无法理解语义
向量检索速度快但相关性不稳定,特别是前几位
rerank高精度,但计算代价大

→ 所以常用组合是:

向量召回(粗排)+ rerank(精排)
先快速筛出相关文档,再用强模型精确重排序。

二、系统架构图解(文字图)

+-----------------------------+
|       用户查询 Query       |
+-----------------------------+
               |
               v
+-----------------------------+
|     向量嵌入模型(BGE)      |
+-----------------------------+
               |
               v
+-----------------------------+
| 向量召回(Elasticsearch/HNSW)|
|  - 取 Top-k 相关文档         |
+-----------------------------+
               |
               v
+-----------------------------+
| rerank 精排(cross-encoder) |
|  - 针对每个候选文档打分     |
|  - 得到最终排序结果         |
+-----------------------------+
               |
               v
+-----------------------------+
|         返回最终结果         |
+-----------------------------+

三、向量召回阶段详解

3.1 嵌入模型选择

推荐使用:BAAI/bge-base-zh

安装:

pip install sentence-transformers

使用:

from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-base-zh")
query_embedding = model.encode("请问2024年社保缴费标准是多少?")

3.2 向量入库(Elasticsearch)

假设文档段落已分段 + 向量化:

es.index(index="docs", document={
    "text": "2024年北京社保缴费基数上限为...",
    "embedding": embedding.tolist(),
    "doc_id": "doc_001"
})

3.3 向量召回查询

query_vector = model.encode(query)
results = es.search(index="docs", knn={
    "field": "embedding",
    "query_vector": query_vector.tolist(),
    "k": 20,
    "num_candidates": 100
})

四、rerank 阶段详解

4.1 精排模型介绍

精排模型通常使用 cross-encoder,能联合输入 query + 文档,更好建模语义相关性。

推荐模型:

  • cross-encoder/ms-marco-MiniLM-L-6-v2(英文)
  • bce-reranker-base_v1(中文)

4.2 安装并使用

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

tokenizer = AutoTokenizer.from_pretrained("shibing624/bce-reranker-base_v1")
model = AutoModelForSequenceClassification.from_pretrained("shibing624/bce-reranker-base_v1")
model.eval()

4.3 精排打分代码

def rerank(query, passages):
    scores = []
    for passage in passages:
        inputs = tokenizer(
            query, passage["text"],
            return_tensors="pt", padding=True, truncation=True
        )
        with torch.no_grad():
            output = model(**inputs)
            score = torch.sigmoid(output.logits)[0].item()
        scores.append((passage["text"], score))
    return sorted(scores, key=lambda x: x[1], reverse=True)

五、完整流程代码实战(简化版)

from sentence_transformers import SentenceTransformer
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from elasticsearch import Elasticsearch
import torch

# 初始化
es = Elasticsearch()
retriever = SentenceTransformer("BAAI/bge-base-zh")
tokenizer = AutoTokenizer.from_pretrained("shibing624/bce-reranker-base_v1")
rerank_model = AutoModelForSequenceClassification.from_pretrained("shibing624/bce-reranker-base_v1")
rerank_model.eval()

query = "2024年企业职工社保缴费政策"

# Step 1:向量检索召回
query_vec = retriever.encode(query)
resp = es.search(index="docs", knn={
    "field": "embedding",
    "query_vector": query_vec.tolist(),
    "k": 20,
    "num_candidates": 100
})
candidates = [hit["_source"] for hit in resp["hits"]["hits"]]

# Step 2:精排
results = []
for c in candidates:
    inputs = tokenizer(query, c["text"], return_tensors="pt", truncation=True, padding=True)
    with torch.no_grad():
        logits = rerank_model(**inputs).logits
        score = torch.sigmoid(logits)[0].item()
    results.append((c["text"], score))

# 排序
results = sorted(results, key=lambda x: x[1], reverse=True)

# 输出结果
for text, score in results[:5]:
    print(f"得分:{score:.3f} 文档:{text}")

六、多文档样例效果展示(示意)

查询:

“北京2024年社保缴费基数变化”

向量召回前5段(示意)

  1. “2024年社保缴费基数上限为29200元”
  2. “社保缴纳截止日为每月15日”
  3. “医保缴费基数为此前年度平均工资”
  4. “养老保险与社保的区别...”
  5. “2023年社保标准是...”

rerank 之后结果重排序:

  1. “2024年社保缴费基数上限为29200元”
  2. “医保缴费基数为此前年度平均工资”
  3. “2023年社保标准是...”
  4. “社保缴纳截止日为每月15日”
  5. “养老保险与社保的区别...”

→ 前排结果更加聚焦“基数变化”,而不是关键词相似性。


七、性能优化与工程部署建议

模块建议
向量召回使用 HNSW + num\_candidates ≥ 100
精排模型小模型部署 FastAPI / ONNX 加速
批量 reranktokenizer + model 支持批量输入
数据更新向量可离线生成,每天批量入库
多语言支持使用 M3E/BGE-m3/LaBSE 等通用模型

八、总结与延伸方向

阶段技术方案优点
粗排(召回)向量搜索(ANN)快速语义定位
精排cross-encoder rerank精准相关性建模
合作使用双阶段精度与效率兼得

延伸:

  • 第三阶段:rerank 后再进行摘要生成(如 RAG)
  • 多模态检索:将图像/PDF嵌入纳入同一向量索引
  • 向量压缩:使用 Faiss/ScaNN + 向量量化提升性能

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日