BM25词频为什么会饱和:把长度惩罚和IDF约定拆开算

29分钟前 3阅读

做知识库检索时,很容易把“关键词出现四次”理解成“比出现两次更相关”。BM25恰好提醒我们,次数、文档长度和词的稀有程度是不同的量。要检查排序实现,先做一个能拆出每项贡献的小例子,比直接对照最终名次更有效。

BM25词频为什么会饱和:把长度惩罚和IDF约定拆开算

AI生成概念示意图:文档与趋于平缓的曲线用于表达词频饱和,图中位置和高度不对应本文精确数值。

先把打分约定写全

本文只讨论一个查询词,不含查询词频因子、字段加权或额外提升系数。记f为该词在文档中的次数,L为文档分词后的长度,avgL为同一索引集合的平均长度。采用教学公式:score=IDF×f(k₁+1)/(f+K),其中K=k₁(1-b+bL/avgL)。

设k₁=1.2、b=0.75、avgL=100,集合共有N=10篇文档,查询词出现在df=2篇中。两篇命中文档分别长50和200;另外八篇总长度为750,便能使平均长度确实等于100。这里df数的是命中文档数,绝不是把所有出现次数加起来。

IDF先采用Lucene文档列出的正值形式:ln[1+(N-df+0.5)/(df+0.5)]。代入得到ln(4.4),约1.481605。本文保留常见教学公式中的(k₁+1)因子;具体引擎可能把不影响当前设置下排序的常数吸收掉,所以这些数字不是对Lucene逐位复现的承诺。

两次出现,怎样胜过四次出现

短文档L=50,K=1.2×(0.25+0.75×0.5)=0.75。若f=2,词频因子为4.4/2.75=1.6,最终得分约2.370567。长文档L=200,K=2.1;即使f=4,因子也只有8.8/6.1≈1.442623,得分约2.137397。短文档因此排在前面。

这不是因为算法偷偷忽略了两次命中,而是长文档给相同词更多出现机会,公式选择用长度进行调节。再把长文档词频提升到8,得分约2.581806,它又能超过短文档的2.370567。长度归一化并非无条件偏爱短文档,排序取决于两个量的共同作用。

还可以换一种更尖锐的对照:短文档只命中一次,得分约1.862589;长文档命中两次,约1.590015,仍然是短文档更高。因此看检索日志时,仅展示命中次数不足以解释顺序,至少还应展示长度、平均长度、词频因子与IDF。

饱和是边际收益递减

固定短文档长度不变,把f依次设为1、2、4,得分约为1.862589、2.370567、2.744867。从一次增加到两次,多得约0.507979;再增加两次,也只多得约0.374300。这里是在固定长度的文档里替换非查询词,并非无限追加文本。

在固定L的函数中,当f趋于无穷,f(k₁+1)/(f+K)趋于k₁+1,也就是2.2。有限长度的真实文档当然不能包含无限多次词;这个极限是在说明函数形状,而不是构造可实现的语料。若不断追加查询词,L也会变化,必须一起重算分母。

如果把b改为0,K不再含长度,同词频文档得到相同因子;b为1时长度作用更强。把k₁设为0,则对已经命中的词,词频因子成为1,频次差异消失。未命中词应直接贡献0,不能把f=0、k₁=0硬代入得到零除零后再随意修补。

两种IDF不能混着验收

另一种常见形式为ln[(N-df+0.5)/(df+0.5)],外面没有“1+”。若N=10、df=8,它约为-1.223775;正值形式则约为0.257829。前者允许高频词产生负贡献,后者不会在合法文档频数范围内为负。对负值截零又是另一种约定。

因此“你的BM25分数为负,肯定算错了”并不成立。必须先看采用什么IDF、是否截零以及候选集合如何形成。反过来,换成正值IDF也不是简单给每篇总分加同一个常数:不同文档的词频因子不同,多词查询的命中组合也不同,排名可能变化。

例子只有一个查询词,且各文档共享相同正IDF,因此比较词频因子就能比较排名。这个简化不应照搬到多个词:一个稀有词与多个常见词会形成权衡。词根归并、中文切词、同义词展开也会改变f、df和L;词表改变后保留旧统计,会制造难以解释的分数。

把检查对象从“看起来相关”换成可复算量

本文用有理数单独计算词频因子,再与浮点公式对照,验证短文两次命中胜过长文四次,以及b=0时长度影响消失。实际实现可保存每个查询词的分项结果,并先用同一批统计重算,再比较引擎输出;如果只差统一倍数,应先检查常数约定,而不是马上修改排序逻辑。

为了让故障暴露得更清楚,小测试还应覆盖完全未命中、只命中一个高频词、同词频不同长度,以及平均长度变化后的重新评分。对字段稀疏的数据,文档总数与包含该字段的文档数也可能不是同一个分母。实际采用哪个,应和对应引擎文档及索引统计保持一致。

还应区分“比较两个候选文档”与“改变整个索引”。前面的频次对照固定了集合统计。如果真正把新文档加入库中,平均长度和文档频数可能一起改变,原有文档即使一个字没改,也会重新得到不同分数。这是统计参照系变化,不能仅凭分数变动判断索引损坏。

拆分长文档同样不是纯粹的存储操作。把一篇长资料切成多个短片段,可能增加包含查询词的文档数,并改变平均长度。片段越短不一定越有利:上下文可能被割裂,同一句答案的证据也可能散在几个候选里。应同时检查排序与下游证据是否完整。

若查询词根本没有进入索引词表,应返回无词项匹配,而不是因为文档频数为零、算出了较大权重,就凭空生成命中贡献。平滑权重解决不了缺失词项,也无法自行理解同义表达。

排查时可以保留一份冻结统计的小集合作为回归样例,令词表、分词器和文档边界保持不变。参数实验一次只改一项,记录分项分数与预期顺序。如果还没有人工相关性判断,最多只能说明实现符合公式,不能把手算通过写成检索质量已经提高;这两类验收需要分别进行。

在检索增强生成中,BM25分数适合表达这套词项匹配规则下的相对顺序,不是答案正确概率,也不是跨索引通用置信度。把新批次分数直接接到旧阈值上,可能受到平均长度或文档频数变化影响。可解释的拆分有助于定位原因,最终质量仍需用带相关性判断的查询集评估。

资料核对日期:2026年10月3日。本文语料长度与频数均为原创教学设定,已本地复算;没有测量真实检索系统的召回率或业务收益。

参考资料

Stanford《Introduction to Information Retrieval》:Okapi BM25,第11.4.3节

Apache Lucene 9.12.1:BM25Similarity参数与IDF定义


文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。