BM25 还是向量检索?跑完三个业务数据集,我发现胜负取决于一个能算出来的数

🔑 关键词:BM25,向量检索,混合检索,RRF,RAG召回

📖 摘要:用三个业务数据集的真实对比聊 BM25 和向量检索怎么选:从 k1、b 参数调优,到「稀有词 query 占比」这个可量化的判断标准,附 RRF 融合的落地配置和容易踩的延迟坑。

上个月帮一个做工业品 B2B 的团队看他们的 RAG 问答,准确率卡在 60% 出头整整两个月。团队的第一反应很统一:embedding 模型太弱了,换 bge-large,再不行上 bge-m3,实在不行上 text-embedding-3-large。我让他们先把最近一周的真实用户 query 拉出来,随机抽 100 条看。看完我就不太想说话了——里面至少有 40 条长这样:「SKF 6205-2RS 轴承」「西门子 6ES7215-1AG40-0XB0」「施耐德 LC1D18M7C 接触器」「Inconel 718 圆棒 直径 30」。这些型号串在任何语义模型的训练语料里都是低频甚至零频的,编出来的向量基本等于随机数。你换个更贵的模型,只是把一个随机数换成另一个更好看的随机数。

图片

我们把 BM25 召回重新加回链路,跟向量做 RRF 融合,权重各占一半。同一批 query,top-10 命中率从 0.58 涨到 0.83,改动量是两个下午。这事儿过去两年我至少遇到三四回,每次都是同一个模式:大家默认「向量检索 = 先进」,把 BM25 当成上个时代的老古董扔掉了,然后在专有名词、型号、SKU 上摔得鼻青脸肿。

先搞明白 BM25 那两个旋钮

图片

BM25 这东西教科书上讲得很玄,拆开其实就三块:词命中算一分,罕见词(IDF 高)多给权重,文档越长越惩罚。真正能调的旋钮只有两个,k1 和 b。Elasticsearch 和 Lucene 的默认值是 k1=1.2、b=0.75,很多人装完就用一辈子,这才是 BM25「效果差」的真正原因,不是算法差。

b 控制长度归一化的强度,取 1 是彻底归一化,取 0 是压根不管长度。如果你的文档是产品标题这种长度高度一致的场景,b 必须往 0.1~0.3 压,我一般直接写 0.2。k1 控制词频饱和曲线,1.2 到 2.0 之间调,长文档场景往 2.0 靠。还有一个更省事的招:给型号、SKU 这类字段单独建一个 keyword 字段做精确匹配,boost 给到 5~10,别让分词器把它们切碎。中文分词器我一般用 ik_smart 而不是 ik_max_word,后者会把「65Mn 弹簧钢」切出一堆噪音词,反而稀释了 IDF。

向量在哪赢,赢多少

图片

说个数字对比。同一个月,同一个业务,我把 query 粗分成两类。A 类是「带型号/规格的精确查询」,占 62%,BM25 recall@10 = 0.79,bge-large-zh-v1.5 向量 recall@10 = 0.41。B 类是「口语化描述查询」,占 38%,BM25 recall@10 = 0.33,向量 0.76。两边的胜负完全取决于你的 query 长什么样,而不是哪个技术更先进。

还有个经常被忽略的成本项:线上延迟。10 万条文档,ES 的 BM25 查询 P99 大概 8~15ms,稳定得像块石头。向量检索本身用 HNSW 也不慢,2~8ms,但 query 得先编码。bge-m3 在 T4 上单条 query 编码大概 25~40ms(含 tokenize),扔 CPU 上直接 100ms 起步。你在离线评测里看不出这个差别,上线以后用户搜一个词等三百毫秒,投诉就来了。还有一笔隐性账:10 万条 1024 维 float32 向量是 400MB 内存,量化成 int8 是 100MB,而 ES 索引本身可能只有几十 MB。规模上到千万级,这笔账会变得很难看。

图片

我的判断方法:先数稀有词,再谈技术选型

现在有人问我「该用 BM25 还是向量」,我一般不直接回答,而是让他先跑一个统计:把最近 1000 条真实 query 分词,统计每个词在整个文档库里的文档频率 DF。DF 排在后 1% 的那些极罕见词,看看有多少条 query 里至少含一个。这个比例我叫它「稀有词 query 占比」。如果超过 15%,纯向量检索基本没戏,BM25 或者混合检索是必须的。

图片

工业品、医药、法律、金融这几个领域,这个比例普遍在 30%~50%。反过来,做通用客服问答、生活百科、闲聊,可能只有 5% 以下,那纯向量甚至语义路由就够了,加 BM25 只是徒增维护成本。

融合那一层我用得最多的是 RRF(Reciprocal Rank Fusion),公式是 score = Σ 1/(k + rank_i),k 默认 60,出自 Cormack 等人 2009 年那篇讲大会文档检索的论文。它的好处是不用管两路召回的分数量纲——BM25 分数可能是 12.7,余弦相似度是 0.83,硬加权融合你得先归一化,还得按业务调权重,每次数据变了都要重调。RRF 只看排名,省事得多。代价是丢掉了分数里的一部分信息,如果两路召回质量差距悬殊,RRF 会被拖后腿,那种情况还是老老实实回去调权重。

说到底

图片

我现在的默认配置是:ES + BM25(b=0.2,k1=1.5,型号字段 boost 8)做一路,bge-m3 做一路,RRF k=60 融合,顶 20 条进 rerank。这套东西不新鲜,也不酷,但它在我手上没翻过车。

真正让我警惕的是一种思维方式:把「新」等同于「好」,把「旧」等同于「该淘汰」。技术在论文里的表现,和它在你有脏数据、有长尾 query、有延迟预算的生产系统里的表现,是两码事。你要是也卡在召回率上,先别急着换模型,去把用户 query 导出来看半小时,大概率比换模型管用。

🏷️ 标签: