共计 2117 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
在信息检索领域,BM25 一直是一个经典的算法,特别擅长处理精确匹配的查询。它基于词频和逆文档频率来评分,对于关键词匹配效果非常好。然而,随着应用场景的复杂化,BM25 在处理语义相似性方面的局限性逐渐显现。

- 语义鸿沟问题:BM25 无法理解词语的语义关系,比如搜索 ” 苹果 ” 时,不会联想到 ”iPhone” 或 ”Mac”。
- 同义词挑战:对于 ” 汽车 ” 和 ” 轿车 ” 这种同义词,BM25 会视为完全不同的词汇。
- 上下文缺失:无法理解词语在不同上下文中的含义变化。
技术对比
BM25 的优势与局限
- 优势:
- 计算效率高,响应速度快
- 不需要预先训练模型
-
对于精确匹配查询效果极佳
-
局限:
- 无法处理语义相似性
- 对拼写错误和变体敏感
- 难以捕捉长文档的整体语义
向量检索的优势与局限
- 优势:
- 能够理解语义相似性
- 对同义词和上下文变化有更好的处理
-
可以捕捉文档的整体语义
-
局限:
- 计算成本较高
- 需要预先训练好的嵌入模型
- 对精确匹配可能不如 BM25 敏感
混合方案设计
结合 BM25 和向量检索的优势,我们可以设计一个混合检索系统:
- 并行查询:同时使用 BM25 和向量检索查询文档
- 结果融合:将两种方法的结果按照一定权重合并
- 重排序:对合并后的结果进行最终排序
权重分配策略可以采用静态权重或动态调整的方式。一个简单的静态权重方案是:
final_score = α * bm25_score + (1-α) * vector_score
其中 α 是一个可调参数,通常通过实验确定最佳值。
代码实现
下面是一个使用 Python 结合 BM25 和 Faiss 向量数据库的示例:
import faiss
from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer
# 初始化模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 准备数据
documents = ["文档 1 内容", "文档 2 内容", ...] # 你的文档集合
# BM25 部分
tokenized_docs = [doc.split(" ") for doc in documents]
bm25 = BM25Okapi(tokenized_docs)
# 向量数据库部分
doc_embeddings = model.encode(documents)
dimension = doc_embeddings.shape[1]
index = faiss.IndexFlatIP(dimension)
index.add(doc_embeddings)
# 混合查询函数
def hybrid_search(query, alpha=0.5, top_k=10):
# BM25 查询
tokenized_query = query.split(" ")
bm25_scores = bm25.get_scores(tokenized_query)
bm25_scores = (bm25_scores - bm25_scores.min()) / (bm25_scores.max() - bm25_scores.min())
# 向量查询
query_embedding = model.encode([query])
D, I = index.search(query_embedding, top_k)
vector_scores = D[0]
# 合并结果
combined_scores = alpha * bm25_scores + (1 - alpha) * vector_scores
# 获取最终排序
final_ranking = sorted(zip(range(len(documents)), combined_scores),
key=lambda x: x[1], reverse=True)[:top_k]
return [documents[i] for i, _ in final_ranking]
性能评估
我们在一个新闻数据集上对比了三种方法的性能:
| 方法 | 召回率 @10 | 响应时间(ms) |
|---|---|---|
| 纯 BM25 | 0.72 | 12 |
| 纯向量检索 | 0.68 | 45 |
| 混合方案(α=0.4) | 0.81 | 32 |
结果显示混合方案在召回率上有显著提升,虽然响应时间有所增加,但在大多数应用场景中是可接受的。
生产建议
在实际部署混合检索系统时,有几个关键点需要考虑:
- 索引构建:
- 对 BM25 和向量索引分别优化
-
考虑增量更新策略
-
参数调优:
- 通过 A / B 测试确定最佳 α 值
-
对不同查询类型可能采用不同权重
-
性能优化:
- 对热门查询结果缓存
-
考虑异步处理部分计算
-
监控与迭代:
- 持续监控系统性能
- 定期更新嵌入模型
延伸思考
这种混合检索方案不仅适用于搜索系统,还可以拓展到其他场景:
- 推荐系统 :结合用户历史行为(BM25 风格) 和内容语义 (向量) 进行推荐
- 问答系统:同时匹配问题中的关键词和语义
- 文档去重:利用 BM25 快速筛选候选,再用向量进行精确比对
未来还可以探索更复杂的融合策略,如基于查询类型的动态权重调整,或者引入深度学习模型来自动学习最佳融合方式。
总结
通过结合 BM25 和向量数据库,我们能够构建一个既保留传统检索优势,又具备现代语义理解能力的搜索系统。虽然需要权衡性能和精度,但在大多数实际应用场景中,这种混合方案都能带来显著的效果提升。希望本文提供的思路和代码示例能帮助你在自己的项目中快速落地这种混合检索方案。
正文完
