跳转到内容

BM25 — 用概率框架给搜索结果排队

待复核

BM25 是一套给搜索结果打分排序的方法;这篇 2009 综述讲的不是某个新公式,而是 BM25 背后的整条概率检索框架(PRF)。

日常类比:你问图书馆管理员”有没有讲概率检索的书”,管理员不会把所有含”概率”两个字的书都抱来,而是估计哪本最可能真正回答你的需求。BM25 就像这个管理员的一张打分表:稀有词更重要,重复出现有用但会饱和,短文档里集中命中比长文档里随手提一句更强。

这篇的核心价值是把 1960 年以来的概率检索思想串起来:从”相关性是一个概率”到 RSJ 权重、BIM、相关反馈、BM25、BM25F,再到参数怎么调。它是理解传统稀疏检索排序函数的入口。

读它时不要只盯公式。更重要的是看每一步假设:哪些证据被认为和相关性有关,哪些变换只保留排序顺序,哪些参数需要靠实验调出来。

不理解这篇,下面这些事都没法解释:

  • 为什么 BM25 不是”TF-IDF 加两个参数”,而是从 Probability Ranking Principle 推出来的一族模型
  • 为什么 IDF 里会出现 +0.5,k1 控制词频饱和,b 控制文档长度归一化
  • 为什么标题、正文、锚文本要先按字段合并成 BM25F,而不是给每个字段单独跑 BM25 再相加
  • 为什么现代 RAG 仍然把 BM25 当第一道召回基线,再接向量检索、RRF 或重排序
  1. 先问”相关的概率”。PRF 的起点是:对某个 query-document pair,系统只能估计 P(相关 | 文档, 查询)。类比:医生没有绝对答案,只能根据症状和检查结果估计患病概率。

  2. 把复杂概率改写成可加的词权重。论文用 odds、Bayes 变换、取 log、去掉与文档无关的项,最后得到”只对 query 中且文档里出现的词求和”。类比:复杂账本被整理成每个命中词一张小票,最后把小票加起来。

  3. BM25 是 PRF 的工程化版本。2-Poisson / eliteness 模型解释了为什么词频贡献会饱和,文档长度模型解释了为什么要除以 1-b+b·dl/avgdl。类比:一个词第一次出现很关键,第十次出现只是重复强调,不能无限加分。

这里有个很重要的边界:BM25 分数适合排序,不适合直接解释成”这篇文档有 80% 概率相关”。论文在推导中多次使用 rank equivalence,只保证顺序尽量合理。

import math
def bm25(tf, df, N, dl, avgdl, k1=1.5, b=0.75):
idf = math.log((N - df + 0.5) / (df + 0.5))
norm = k1 * (1 - b + b * dl / avgdl)
return idf * tf / (norm + tf)

逐部分解释

  • idf:词越少见,说明它越能区分文档;+0.5 是概率估计里的平滑
  • norm:长文档分母变大,防止”篇幅长所以碰巧命中多”占便宜
  • tf / (norm + tf):词频越高分越高,但增长越来越慢,这就是饱和
for tf in [1, 3, 10, 30]:
print(tf, round(tf / (1.5 + tf), 3))

输出大概是 0.4, 0.667, 0.87, 0.952。第 1 次到第 3 次增长明显,第 10 次到第 30 次增长很小。

逐部分解释

  • k1 越大,饱和越慢,重复词还能继续加不少分
  • b 越大,长度归一越强,长文档里的命中会被压得更低
  • 如果语料都是商品标题这种短文本,b 太大反而可能过度惩罚
def weighted_tf(title_tf, body_tf):
return 5 * title_tf + 1 * body_tf
print(weighted_tf(title_tf=1, body_tf=0))
print(weighted_tf(title_tf=0, body_tf=5))

逐部分解释

  • 标题命中一次,可能比正文命中五次还强,因为标题更浓缩地表达主题
  • BM25F 的思想是:先把 title/body/anchor 等字段转成一个加权词频,再做一次饱和
  • 这避免了”每个字段各自饱和后再相加”导致同一个词在多个字段里被重复奖励

实际系统里,title、body、anchor 的权重通常不是拍脑袋,而是拿一批 query 和人工相关性判断做参数搜索;这也是论文最后一章讨论优化的原因。

  1. 把 BM25 当成普通 TF-IDF:错在忽略 PRF 推导;BM25 的 IDF、饱和、长度归一都来自概率假设和工程近似。
  2. 以为 tf 越高越相关:原因是线性直觉太强;论文强调 eliteness 只能提供有限证据,所以词频必须饱和。
  3. 把默认参数当真理:原因是 k1、b 没有模型内生答案;论文说它们鲁棒,但不同语料仍要用 judged queries 调。
  4. 字段分数直接相加:原因是没理解 BM25F;正确做法通常是先跨字段积累同一个词的证据,再让这个词统一饱和。

适用

  • 文档库、代码库、日志、知识库这种需要关键词精确命中的检索
  • 没有训练数据的新语料,先要一个强、快、可解释的 baseline
  • RAG 的第一阶段召回,再接 rrf-cormack-2009dpr-2020colbert-2020
  • 标题、正文、锚文本等字段明确的 Web / 企业搜索,用 BM25F 建模

不适用

  • 同义改写很多的查询,比如”车险理赔” vs “汽车保险报案”,BM25 不懂语义等价
  • 跨语言、图片、音频等没有共享词表的检索
  • 已有大量点击和标注数据、目标是最终排序时,应接学习排序或神经重排
  • 需要输出校准概率的场景;PRF 为了排序做了 rank-equivalent 变换,分数不等于真实概率

判断边界的简单方法:如果用户必须输入同一个词才能命中,BM25 很强;如果用户说法经常换词,BM25 就需要扩展、向量或重排帮忙。

  • 1960 年:Maron 和 Kuhns 把”相关性概率”带进信息检索,但还没有现代 query-doc 排序公式。
  • 1970s:Robertson 和 Spärck Jones 发展 PRP 与 RSJ 权重,奠定”按相关概率排序”的理论入口。
  • 1980s:概率模型逐步接入相关反馈,系统可以根据用户判定重新加权和扩展 query。
  • 1994 年:Okapi 线索把 2-Poisson 的复杂曲线近似成 BM25,稀疏检索有了稳定公式。
  • 2004 年:BM25F 把标题、正文、锚文本等字段纳入同一个概率框架。
  • 2009 年:Robertson 和 Zaragoza 写下这篇综述,把 30 年模型、假设、变体和调参方法合成一张地图。
  1. BM25 的背后是一套概率检索观:先定义相关性,再讨论哪些证据能改变相关 odds。
  2. 好公式来自假设 + 近似:PRF 给方向,2-Poisson 给形状,BM25 用少数参数把它落地。
  3. 可解释性来自可加证据:每个 query term 都是一条证据,为什么加分、为什么饱和都能说清。
  4. 传统模型没有过时:在没有训练数据、需要低延迟和可解释 baseline 时,BM25 仍然是第一选择。
  • maron-kuhns-1960 —— 早期把检索问题改写成”文档相关概率”的源头之一
  • croft-harper-1979 —— 解释没有 relevance feedback 时概率检索怎么启动
  • okapi-bm25-1994 —— 把 PRF / 2-Poisson 的思想压成 BM25 公式
  • indri-2005 —— 语言模型检索路线,与 PRF / BM25 是同一时代的平行框架
  • anserini-2017 —— 现代实验中最常复现 BM25 的 Lucene 工具链
  • rrf-cormack-2009 —— 常把 BM25 排名和向量检索排名融合成混合召回
  • splade-2021 —— 用神经网络学习稀疏词权重,是 BM25 路线的现代延伸
  • anserini-2017 —— Anserini — 把工业搜索引擎 Lucene 改造成学术 IR 实验台
  • croft-harper-1979 —— Croft-Harper 1979 — 没有相关性反馈也能跑概率检索
  • rm3-2001 —— RM3 — 让搜索引擎自己看一眼结果再重搜一次
  • rrf-cormack-2009 —— RRF — 把多个搜索结果列表合并成一个的最简单办法
  • llama-index —— LlamaIndex — 给大模型接上私有资料库
  • llamaindex —— LlamaIndex — LLM 数据框架