语义搜索架构:双塔与表示型检索系统设计
FreeGuideOnline
最新
2026-06-24
语义搜索架构:双塔与表示型检索系统设计
1. 为什么需要语义搜索
传统关键词检索(如 BM25)基于字面匹配,无法处理同义词、上下文语境和长尾查询语义偏移的问题。
语义搜索通过理解查询与文档的深层含义,将两者映射到统一语义空间,利用向量距离衡量相关性,实现“懂你意图”的检索。
2. 表示型检索的核心思想
表示型检索将查询和文档分别编码为固定长度的稠密向量,检索过程转化为 最近邻搜索:
- 离线阶段:将所有文档编码成向量并构建索引。
- 在线阶段:输入查询 → 编码查询向量 → 在索引中快速找到最相似的文档向量。
优势:独立编码、索引预计算、召回性能高、支持多模态扩展。
挑战:语义对齐、向量高效搜索、模型更新与冷启动。
3. 双塔模型架构
双塔模型是最经典的表示型检索范式,包含两个独立的神经网络:
- 查询塔(Query Tower):将查询文本编码成向量
q。 - 文档塔(Document Tower):将文档(标题、正文)编码成向量
d。
两个塔共享同一个向量空间,采用对比损失训练,使相关查询–文档对在空间中距离接近,不相关对距离拉远。
典型结构:
Query Input → BERT/DistilBERT → Pooling → q (d-dim)
Doc Input → BERT/DistilBERT → Pooling → d (d-dim)
池化通常用 [CLS] 输出或平均池化,最终按余弦相似度 cos(q, d) 排序。
4. 训练目标与负采样策略
双塔检索系统的训练常采用 批量内负采样(in-batch negatives),高效利用 GPU 并行能力:
- 每个 batch 内正样本为配对的
(query, doc+),batch 内其他文档均视为doc-。 - 损失函数使用 交叉熵损失 或 InfoNCE 损失:
其中Loss = -log( exp(s(q_i, d_i)/τ) / Σ_j exp(s(q_i, d_j)/τ) )s为余弦相似度或点积,τ是温度系数。
实际训练技巧:
- 使用难负样本(Hard Negatives):被当前模型检索到但不相关的文档,或从 BM25 粗召回的错误结果中采样。
- 知识蒸馏:从交互式模型(cross-encoder)中获得更精准的软标签。
- 数据增强:查询改写、回译、随机裁剪等。
5. 工程系统设计
5.1 离线流程
- 文档塔推理:使用训练好的文档塔对所有文档编码,生成向量。
- 向量索引构建:利用近似最近邻(ANN)库(如 Faiss、ScaNN、Annoy)构建索引,支持十亿级向量搜索。
- 元数据存储:保留文档原始内容、ID 等,供在线阶段返回结果。
5.2 在线流程
- 查询编码:接收用户查询,调用查询塔生成向量
q(延迟需控制在 10 ms 内)。 - ANN 检索:基于
q在索引中检索 Top-K 候选文档(通常 K=100~1000)。 - 后处理:可选重排序(用轻量级交叉编码器)、多样性控制、过滤业务规则。
- 返回结果:拼接文档信息并返回。
6. 近似最近邻索引选型
| 库 | 优势 | 适用场景 |
|---|---|---|
| Faiss (CPU/GPU) | 极致性能,支持量化,多种索引结构 | 大规模离线索引、低延迟在线服务 |
| ScaNN (Google) | 超高召回率,针对内积优化 | 十亿级、内存受限 |
| Annoy | 内存映射,无需加载全量索引 | 小规模快速部署 |
| Milvus | 分布式向量数据库,管理便利 | 全栈向量数据管理 |
常见索引策略:IVF+PQ 或 HNSW,根据延迟与召回率权衡选择参数。
7. 双塔模型的实际部署要点
模型选择:
轻量编码器如 MiniLM、distiluse-base 适合在线低延迟;追求极致效果可选用 BERT-large 或 T5 编码器并做蒸馏。
多向量表示:
单向量可能损失细节,可尝试 ColBERT 式多向量(每 token 一向量)再配合 MaxSim 重排序,但会增大索引和检索开销。
冷启动:
- 新文档加入:仅需通过文档塔编码并插入向量索引,对现有索引进行增量更新。
- 新域名/语言:使用领域自适应预训练或微调文档塔。
在线更新:
定期(如每日)用新增文档重新构建索引,或利用支持增删的向量引擎(FAISS 的 IDMap 模式)实时添加。
8. 评估与效果调优
离线指标:
- Recall@K(K=10, 50, 100)、MRR、nDCG
- 尤其关注 Recall@K,因为表示型检索常用于粗排阶段。
A/B 测试:
在线观测点击率、首屏满足度、无结果率等业务指标。
效果提升路径:
- 增大 batch size 和负采样数量(可跨 GPU 汇聚 batch)。
- 引入难负样本挖掘管道。
- 组合多粒度表示(文档 + 段落 + 句子)。
- 蒸馏自更大规模的交互模型或 LLM 重写查询。
9. 从双塔到更高级的表示型架构
| 架构 | 特点 | 代表 |
|---|---|---|
| 纯双塔 | 独立编码,快速检索 | DPR, Sentence-BERT |
| 多向量 | 保留细粒度交互,延迟稍高 | ColBERT |
| 模型“粗排”一体 | 使用单塔模型生成稠密向量并以查询端稀疏性加速 | Splade (学习稀疏表示) |
| 混合检索 | 结合 BM25 与稠密向量,确保字面与语义兼顾 | Elastic 的 hybrid search |
大部分生产环境采用 双塔粗排 + 交叉编码器精排 的混合流水线,平衡效果与性能。
10. 简易代码示例(伪代码)
# 离线索引
from sentence_transformers import SentenceTransformer
import faiss
doc_tower = SentenceTransformer('document-encoder-model')
docs = ["文档1内容...", "文档2内容...", ...]
doc_embeddings = doc_tower.encode(docs, show_progress_bar=True)
dim = doc_embeddings.shape[1]
index = faiss.IndexIVFPQ(faiss.IndexFlatIP(dim), dim, 1024, 128, 8)
index.train(doc_embeddings)
index.add(doc_embeddings)
# 在线检索
query_tower = SentenceTransformer('query-encoder-model')
query_vec = query_tower.encode(["用户查询"])
D, I = index.search(query_vec, k=20)
results = [docs[i] for i in I[0]]
11. 总结
- 双塔架构将查询和文档解耦为独立向量,天然契合大规模检索场景。
- 训练的关键在于负样本构造与对比损失设计,in-batch negatives 让训练高效可扩展。
- 工程落地需重点设计离线索引管道、低延迟 ANN 检索与数据更新机制。
- 持续通过难负样本、蒸馏、混合检索等手段迭代优化效果,最终在召回率和用户体验上达成商业目标。
部署自己的语义搜索系统,从双塔模型切入是最稳健的选择。