BM25算法
一、BM25 是什么,它在 RAG 系统里做什么?¶
我们先从宏观上理解一下。
- 它的名字叫 BM25,全称是 "Best Match 25",你可以把它理解成一个非常经典的、经过了20多次迭代优化后诞生的“最佳匹配”算法。
- 它的本质是一个
排名函数或评分函数。
想象一下,在 RAG 流程中,我们根据用户的提问(Query),需要在知识库(一大堆文档)里找到最相关的几篇文档,然后交给语言模型去生成答案。
问题来了:怎么判断哪些文档“最相关”呢?
BM25 就是解决这个问题的工具。它会为每一篇文档都打一个“相关性分数”。分数越高的文档,就代表它和用户的提问越相关,我们也就越应该把它排在前面。
二、BM25 的核心思想¶
核心思想是根据词项的出现频率来评估文档对于查询的相关性。
BM25 主要考虑了三个核心要素来计算这个分数:
- 查询词在文档中出现的次数(TF - Term Frequency)。
- 查询词在所有文档中的普遍程度(IDF - Inverse Document Frequency)。
- 文档的长度。
简单来说,一个词在某篇文档里出现次数越多,这篇文档的相关性可能就越高。但是,我们还需要考虑这个词本身的重要性以及文档的长度,不能一概而论。BM25 就是一个把这三者巧妙结合起来的数学公式。
三、庖丁解牛:一步步拆解 BM25 公式¶
我们再来看一下这个公式:
别怕,我们把它拆成三块来看:
第一部分:\(IDF(q_i)\) - 查询词的重要性权重¶
- \(q_i\) 指的是你查询语句中的第 \(i\) 个词。比如查询是“人工智能的应用”,那 \(q_1\) 就是“人工智能”,\(q_2\) 就是“的”,\(q_3\) 就是“应用”。(当然,像“的”这种停用词通常会预先被去掉)。
- \(IDF(q_i)\) 是“逆文档频率”(Inverse Document Frequency)。它的作用是给每个查询词一个权重。
这个思想很直观:一个词如果在所有文档里都很少出现(比如“量子纠缠”),那它一旦在某篇文档里出现,这篇文档就很可能是我们想要的,所以这个词的权重就应该很高。相反,一个词如果到处都是(比如“技术”、“一个”),那它就没什么区分度,权重自然就应该很低。
补充
IDF 常见的计算方式:
\[ IDF(q_i) = \log\left(\frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1\right) \]
- \(N\) 是文档总数。
- \(n(q_i)\) 是包含词 \(q_i\) 的文档数量。
包含这个词的文档 \(n(q_i)\) 越多,分母就越大,算出来的 IDF 值就越小。这就通过数学公式实现了我们刚才说的那个直观想法。
第二部分:那个复杂的大分数 - 词频与文档长度的平衡艺术¶
这部分是 BM25 的精髓,我们把它也拆开看。
- \(f(q_i, D)\):这个很简单,就是查询词 \(q_i\) 在文档 \(D\) 中出现的次数(词频)。
- \(|D|\):文档 \(D\) 的长度(比如包含多少个词)。
- \(avgdl\):所有文档的平均长度。
这部分引入了两个讲义中提到的重要参数 \(k_1\) 和 \(b\),它们是 BM25 的“调节旋钮”。
旋钮1:\(k_1\) (通常在 1.2 到 2.0 之间)¶
\(k_1\) 用来控制“词频饱和度”。什么意思呢?
想象一下,一个词在一篇文档里从出现 1 次变成出现 3 次,相关性可能会大大增加。但如果它从 30 次变成 32 次,相关性的提升可能就没那么明显了。也就是说,词频带来的收益不是无限线性增长的,它会“饱和”。\(k_1\) 就是用来调节这个饱和曲线的。\(k_1\) 值越小,饱和的速度越快,词频的微小差异带来的影响就越小。
旋钮2:\(b\) (通常为 0.75)¶
\(b\) 用来控制“文档长度”的影响力。
这个也很好理解:一个 100 字的短评里出现了“好吃”,和一个 5000 字的美食报告里出现了“好吃”,前者的重要性显然更高。BM25 认为,长文档有天然的优势(更容易包含查询词),所以需要对它们进行一些“惩罚”。
- 当文档 \(D\) 的长度 \(|D|\) 大于平均长度 \(avgdl\) 时,\(\frac{|D|}{avgdl}\) 大于 1,分母会变大,最终得分会变小(惩罚)。
- 当文档 \(D\) 的长度 \(|D|\) 小于平均长度 \(avgdl\) 时,\(\frac{|D|}{avgdl}\) 小于 1,分母会变小,最终得分会变高(鼓励)。
参数 \(b\) 就是控制这个惩罚/鼓励力度的。如果 \(b=0\),那文档长度就完全不起作用了。如果 \(b=1\),则完全用文档长度来做调节。
第三部分:\(\sum_{i=1}^{n}\) - 汇总所有词的得分¶
这个 \(\sum\) 符号是求和的意思。因为一个查询通常包含多个词(比如 "RAG 系统 培训"),BM25 会为 "RAG"、"系统"、"培训" 这三个词分别计算上面两大块(IDF 和那个大分数)的乘积,最后再把这三个词的得分加起来,就得到了这篇文档相对于整个查询的总分 \(Score(D, Q)\)。
四、总结与梳理¶
好了,现在我们把所有零件都组装起来,重新梳理一下 BM25 的工作流程:
- 接收任务:拿到一个查询 \(Q\)(比如 "人工智能的应用")和一篇待评分的文档 \(D\)。
- 分词:把查询 \(Q\) 拆成一个个的词 \(q_i\) ("人工智能", "应用")。
- 逐词计算:对每一个词 \(q_i\):
a. 计算重要性:计算这个词的 \(IDF(q_i)\) 值。这个词越稀有,值越高。
b. 计算词频相关性:计算那个大分数。它会考虑词 \(q_i\) 在文档 \(D\) 中出现的次数,并用 \(k_1\) 来防止词频影响过大,同时用 \(b\) 和文档长度来对得分进行校正。
c. 相乘:将 a 和 b 的结果乘起来,得到词 \(q_i\) 对总分的贡献。 - 累加总分:把所有查询词("人工智能"、"应用")的得分加起来,就是文档 \(D\) 的最终 BM25 分数。
- 排序:对知识库里所有文档都执行一遍这个流程,得到一个分数列表,然后按分数从高到低排序,排名最靠前的就是与查询最相关的文档。