混合检索实战:BM25 + 向量检索怎么融合才有效
谭谭老师605 阅读
纯向量检索有个尴尬场景:用户搜"MySQL 报错 1215",向量检索可能把它理解成"数据库错误处理"给你一堆无关内容,而 BM25 这种关键词匹配反而能精确命中。混合检索就是把两者结合。
为什么要混合:向量检索擅长"语义相近",比如"怎么让程序跑得更快"和"性能优化技巧"能匹配上;但"精确术语"场景(错误码、型号、人名)它反而匹配不好。BM25 恰好相反。两者互补。
两种融合方式:
1. 加权融合。分别得到两个检索结果的得分,按权重合并排序:
Python
final_score = 0.6 * vector_score + 0.4 * bm25_score
问题是两个分数量纲不同,向量是余弦相似度(0-1),BM25 是相关性分(几到几十),直接加权等于向量那部分被淹没。需要先各自归一化。
2. RRF(Reciprocal Rank Fusion)。不关心分数,只用排名:
Python
score = sum(1 / (60 + rank_i))
两个结果集合并,每个文档按它在两个列表里的排名算分,排名越靠前分越高。这个方案不用归一化,实测最稳。
我的实现:用 Qdrant 的 query API,同时传 vector 和 bm25 参数(Qdrant 内置了 BM25 支持),或者分别查再 RRF 合并。
实测数据:在 2000 篇技术文档的测试集上,纯向量 Recall@5 是 0.71,纯 BM25 是 0.63,RRF 混合到了 0.85。提升非常明显。
坑:
-
BM25 对中文分词敏感。Qdrant 内置的 tokenizer 对中文按字切分,效果一般。中文场景最好自己预分词(jieba)后再建索引。
-
混合不等于万能。如果文档本身质量差、内容杂,混合后召回的只是"更差的两个里挑好的",还是不如先把数据清理好。
-
查询改写。混合检索之后再做一轮 rerank(重排),效果还能再上一个台阶,下一篇文章讲。
评论0
还没有评论,来抢沙发~