纯向量检索漏掉的召回,BM25 加向量混合能补回来吗?5 组实测

举报
deli007 发表于 2026/10/12 23:21:05 2026/10/12
【摘要】 纯向量检索漏掉的召回,BM25 加向量混合能补回来吗?5 组实测

图 1:混合检索实验室首屏。参数区能改 Top-K、BM25 的 k1 和 b、SVD 维度、RRF 常数;中间三列是同一个查询在 BM25、向量(SVD)、混合(RRF)三种检索器下的 Top-5;下面接着是五组查询的对照表

先给结论:能补回来一部分,但补不全。

「微服务通信和接口调用」这组,纯向量检索 Top-5 一篇相关文档都没捞到,recall 是 0.00。

两路排名融合之后补回了 D12,recall 变成 0.50,D11 还是没进来。

「数据库索引和查询优化」那组,纯向量只命中 1/2,融合补回 D03,回到 2/2。

反过来的例子也有。

5 组查询里有 2 组,融合的 recall 低于两路里更强的那一路:「深度学习和神经网络框架」融合 0.33,纯向量单跑 0.67。

这些数字不是我写死的。

页面打开时当场算:30 篇文档、两路候选、RRF 融合、15 条自检。

成品是一个单文件 index.html,双击就能跑,不联网,不引第三方库。

案例目录在这里,下载下来直接打开:

https://atomgit.com/deli007/demo_park/tree/main/codearts-hybrid-search-lab

为什么把「漏召回」摆到台面上

BM25 这路检索很好解释:一个词只在少数文档里出现,它的 IDF 就高,命中就被顶上去。

向量检索听起来更聪明,它把文档投影到低维空间,按余弦相似度排序。

降维是有代价的,精确的实词特别容易被摊薄。

我自己量过这件事:查询「微服务通信和接口调用」,纯向量给 D16、D14、D08、D25、D07 都打了 0.93 以上的分,人工标注的 D11、D12 一个都没进前五。

D11 在纯向量的完整排名里掉到第 14 位。

于是这个页面要回答的问题就很具体了:把两路排名融合起来,那些被降维挤掉的文档能回来吗。

我做成实验室而不是直接给结论,是因为答案跟语料强相关。

换一批文档、换一个 K,结论都可能变。

三种检索器各干什么

检索器 排序依据 这次用的参数 实测里的一句话结论
BM25 词法 词频 + 文档长度归一 + IDF k1=1.2,b=0.75 精确实词的召回最稳,5 组里 4 组是它最高
向量(SVD) TF-IDF 矩阵降到 8 维后算余弦相似度 维度 8,幂迭代 100 轮 对同义说法有反应,但会把实词挤下去
混合(RRF) 两路排名倒数相加 k=60 能把向量漏掉的实词文档捞回一部分

RRF 那一行的算法叫 Reciprocal Rank Fusion,公式是每个文档把两路里的排名倒数加起来,k 用来压平高位排名的差距。

中文这边我没有用分词库,BM25 走的是单字加双字切分,这样做的好处是不用装依赖,代价是词表偏大。

五组查询逐组看

图 2:五组查询对照表。每行是一组查询,三列 recall 分别是 BM25、向量、融合;后面是命中数、漏召回的文档编号,最后一列是这三路里最好的那一路

查询 BM25 向量 融合 融合漏掉 这一组最优
数据库索引和查询优化 1.00 0.50 1.00 — 混合
深度学习和神经网络框架 0.33 0.67 0.33 D08、D09 向量
微服务通信和接口调用 0.50 0.00 0.50 D11 混合
分布式协调和一致性协议 1.00 0.00 0.50 D13 BM25
前端构建和开发工具 0.67 1.00 0.67 D19 向量

这 5 行里,融合拿到 2 次最优,向量 2 次,BM25 1 次。

注意第三列全是 0.00 和 0.50 那种粗粒度,是因为 Top-K 只有 5,每组标注的相关文档只有 2 到 3 篇,一篇就是 0.33 到 0.50。

这也是我不想把话说满的原因:样本太小,粒度太粗。

失败路径:纯向量漏掉的两篇,融合只捞回一篇

图 4:失败路径演示。页面自动挑出纯向量漏得最多的那组查询,左右两列分别是纯向量和融合的 Top-5,下面是按实测拼出来的漏召回分析

这块我没有写死查询,页面自己挑「纯向量漏得最多、融合补回最多」的那一组。

这次挑中的是「微服务通信和接口调用」,人工标注的相关文档是 D11、D12。

纯向量的 Top-5 是 D16、D14、D08、D25、D07,两篇相关文档全漏。

融合的 Top-5 是 D08、D14、D12、D16、D28,D12 回来了,D11 依然没有。

页面上那行分析文字也是算出来的:D11 掉到第 14 位,两路都没把它排进前五。

捞回一半,这就是这组数据的真实结果。

验证与踩坑:15 条断言,和我改掉的七处

页面底部的自检会把 15 条断言真跑一遍,逐条给出实测值和结论,最后一行是总数。

改之前是 14/15。

图 5:修复前的自检面板。15 条里第 9 条「空查询兜底」标红,底部写着通过 14 / 共 15

第一处,也是最要紧的一处:空查询会返回整个语料库。

BM25 和向量检索各自都有「查询没切出词」的分支,第一版两个分支都直接返回了全部 30 篇文档。

断言 9 期望的是 Top-K 条,于是当场报红。

改成返回空列表之后,空查询不再吐 30 条噪声,断言也跟着改成「返回空列表」。

第二处:结果不可复现。

幂迭代 SVD 的初始向量用了 Math.random()。

同一份文件两次打开,同一组查询的向量 recall 一次是 0.50,一次是 0.00,我自己都说不清该信哪一次。

修法是把初始向量换成固定种子的线性同余发生器,改完两次刷新结果完全一致。

第三处:融合吃得太少。

第一版把两路的 Top-5 直接丢给 RRF,等于每个候选最多只有两票。

改成用完整排名融合,再把结果切 Top-5,「数据库索引和查询优化」从 0.50 涨到 1.00。

同一处改动也让「前端构建和开发工具」从 1.00 掉到 0.67。

5 组里 1 组涨、1 组跌、3 组不动,平均 0.57 到 0.60,不能算纯赚,两组数字我都留着。

第四处:断言 14 是恒真的。

它的判据写成「两个名单不同,或者两个名单完全相同」,两种情况都通过。

换成一条真能测的不变量:融合分数必须按降序单调不增。

第五处:「最优」那一列只看融合一路。

向量单独更强的时候,那一列显示一个横线,等于把反例藏起来了。

改成三路比较之后,5 组里 2 组的最优是向量,2 组是混合,1 组是 BM25。

第六处:断言 7 和 8 的预期本身就写错了。

第一版写的是「融合 recall ≥ 任一路」。

实测 5 组里有 2 组不成立,这条断言测的是我的愿望,不是代码。

我把它换成两条真能测的:融合候选必须来自两路并集;融合 recall 不低于两路里较弱的一路。

范围写进标题,不写满。

第七处:失败路径那块的分析文字是写死的。

第一版固定查询「索引 查询优化」,正文里也写死了一句「D01 未能进入 Top-K」。

修完之后这个查询两路都能召回,写死的那句话就成了假话。

现在页面自己挑那一组,结论按实测数据拼出来。

自检面板长什么样

图 3:自检面板。15 条断言逐条列出实测值,底部是「通过 15 / 共 15」,下面接着失败路径演示

几条我自己会回头看的实测值:

断言 实测
IDF 效果 idf(聚簇)=3.0285,idf(的)=1.1827
空查询兜底 BM25 结果数 0,向量结果数 0
融合候选来源 校验 5 组查询,全部来自两路并集
融合分数单调性 0.0325、0.0313、0.0308、0.0308、0.0297
语料库完整性 共 30 篇,ID 唯一 30 个

改完这些之后,自检是 15/15 通过,跑两遍结果完全一样。

整个页面是纯 JS,没有引任何库,SVD、BM25、RRF 都是自己写的。

本地复现

把案例目录下载下来,随便起一个静态服务就行:

cd codearts-hybrid-search-lab
python -m http.server 8000

然后浏览器打开 http://localhost:8000/index.html 就能看到跟截图一样的页面。

不起服务直接双击 index.html 也可以。

想看修复前的样子,把自检面板那一版翻出来跑一遍就行,断言 9 会红。

准备环境:打开码道 Web 版

浏览器打开码道 Web 版:https://devcloud.cn-north-4.huaweicloud.com/chat?source=dmzntgwsf&sourcead=dmzntgwwbwz

登录后在对话框里把需求说清楚就行,不用装软件。

码道有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件),本文用的是 WebUI。

用码道做这个页面的一点体会

我把需求一次说完:三种检索器、语料规模、对照表、自检面板、失败路径演示。

它一次就给了 35.8 KB 的单文件,接的是内置预览,跑起来就能点。

但「能跑」和「对」是两件事。

上面那七处,没有一处是读代码读出来的,全靠自检和两次复现的对比抓出来。

我自己的结论只有一句:让 AI 写代码,验收这一半得自己扛。

边界,我写清楚

语料只有 30 篇,ground truth 是我手标的,5 组查询的样本量很小。

向量那一路是幂迭代 SVD 的近似降维,不是真的 embedding 模型,同义词效果有限。

Top-5 的粒度太粗,一篇文档就是 0.33,别拿这里的小数点当精度。

融合不保证优于任何单路,这份数据里就有 2 组反例。

断言里的期望值是我写的,它只能证明代码算出了我预期的东西,证明不了我的预期是对的。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。