BM25 算法详解:从原理到代码实现,掌握搜索与 RAG 的核心检索技术

发布时间:2026/8/25 15:31:10
BM25 算法详解:从原理到代码实现,掌握搜索与 RAG 的核心检索技术 BM25 算法详解从原理到代码实现掌握搜索与 RAG 的核心检索技术前言在信息爆炸的时代如何从海量文本中快速找到与用户查询最相关的内容是搜索引擎、问答系统和 RAG检索增强生成等应用的核心问题。BM25Best Match 25作为目前最经典、最广泛使用的关键词相关性排序算法之一自 1994 年由 Stephen Robertson 等人提出以来已经成为信息检索领域的事实标准。无论是 Google、百度等传统搜索引擎还是 LangChain、LlamaIndex 等现代 RAG 框架都将 BM25 作为基础检索组件。本文将从最基础的概念出发逐步深入 BM25 的核心思想、数学原理、代码实现和实际应用帮助你彻底理解这一算法并能够将其应用到自己的项目中。目录学习目标什么是 BM25为什么需要 BM25从 TF-IDF 的局限性说起BM25 的核心思想BM25 的数学公式与参数详解BM25 的直观理解BM25 的代码实现与进阶用法BM25 在搜索与 RAG 中的实际应用BM25 的参数调优技巧BM25 与其他检索算法的对比常见问题与误区总结学习目标理解 BM25 是什么以及它在信息检索领域的地位掌握 BM25 的核心计算逻辑和数学原理了解 BM25 在传统搜索与现代 RAG 系统中的具体应用能够用直观思维理解关键词相关性排序的本质能够独立实现 BM25 检索系统并进行基本的参数调优什么是 BM25一句话理解BM25 是一种用于衡量查询语句与文档之间相关性的概率排序算法是目前搜索引擎和信息检索系统中最常用的基础排序算法之一。举个例子假设你在搜索引擎中输入“大模型课程”系统的数据库中有以下四段文本文档 A介绍大模型基础课程文档 B介绍 Python 课程文档 C介绍大模型实战课程文档 D大模型是人工智能的重要分支问题哪个文档与你的查询最相关直觉上文档 C 应该是最相关的因为它同时包含了大模型和课程两个关键词而且实战课程比基础课程更具体。文档 A 次之文档 D 再次之文档 B 最不相关。BM25 做的事情就是给每个文档计算一个相关性分数然后按照分数从高到低排序将最相关的结果展示给用户。为什么需要 BM25从 TF-IDF 的局限性说起在 BM25 出现之前最常用的文本相关性计算方法是 TF-IDF。虽然 TF-IDF 简单易懂但它存在几个明显的缺陷。传统 TF-IDF 方式的问题TF-IDF 的计算公式是TF-IDF(q, D) TF(q, D) × IDF(q)其中TFTerm Frequency词频指某个词在文档中出现的次数与文档总词数的比值IDFInverse Document Frequency逆文档频率指 log(文档总数量 / (包含该词的文档数量 1))TF-IDF 的核心思想是一个词在文档中出现的次数越多同时在整个语料库中出现的次数越少那么这个词对该文档的重要性就越高。但是TF-IDF 存在以下几个严重的问题词频线性增长问题TF-IDF 认为词频越高相关性越高这会导致废话重复的文档获得过高的分数。例如一个文档中大模型出现了 100 次但都是无意义的重复它的分数会远高于只出现 1 次但内容有价值的文档。长文档不公平问题长文档天然包含更多的词因此更容易命中查询关键词获得更高的分数。这对短文档是不公平的。没有考虑词之间的位置关系TF-IDF 只关心词是否出现以及出现的次数不关心词在文档中的位置和上下文。BM25 解决了什么问题BM25 本质上是对 TF-IDF 的改进它通过回答三个关键问题来解决 TF-IDF 的缺陷词出现得多就一定更好吗→ 不是词频的收益应该是边际递减的文档越长就一定越好吗→ 不是长文档应该受到惩罚所有词的重要性都一样吗→ 不是罕见词比常见词更重要BM25 的核心思想BM25 的核心思想可以概括为相关性得分由三个因素共同决定词频、逆文档频率和文档长度。我们可以将其拆分为三个核心部分来理解。词频的非线性增长解释一个词在文档中出现的次数越多文档与该词的相关性就越高但这种相关性的增长不是线性的而是随着词频的增加逐渐放缓最终趋近于一个上限。举例搜索词大模型文档 A大模型出现 1 次文档 B大模型出现 5 次文档 C大模型出现 100 次直觉上文档 B 比文档 A 更相关但文档 C 并不比文档 B 相关 100 倍甚至可能因为过度重复而显得不相关。BM25 的处理方式BM25 使用了一个饱和函数来处理词频词频得分 f(q, D) × (k1 1) / (f(q, D) k1)其中 k1 是一个可调参数通常取值在 1.2 到 2.0 之间。这个函数的特点是当 f(q, D)0 时得分为 0当 f(q, D)→∞ 时得分趋近于 k11当 f(q, D)k1 时得分达到最大值的一半这意味着词频的增加会带来相关性的提升但提升的速度会越来越慢最终趋于稳定。逆文档频率IDF核心思想越少见的词区分度越高对相关性的贡献就越大。举例“的”、“是”、一个等停用词在几乎所有文档中都出现因此它们的 IDF 值很低对相关性的贡献几乎为 0“向量数据库”、“RAG 检索”、大模型微调等专业术语只在少数文档中出现因此它们的 IDF 值很高对相关性的贡献很大BM25 的 IDF 公式IDF(q) log((N - n(q) 0.5) / (n(q) 0.5) 1)其中N 是语料库中的总文档数n(q) 是包含查询词 q 的文档数这个公式比传统的 IDF 公式更加平滑避免了出现负数的情况。文档长度归一化问题长文档天然包含更多的词因此更容易命中查询关键词。如果不进行归一化处理长文档会获得不公平的高分。BM25 的解决方式BM25 引入了文档长度归一化因子长度归一化因子 1 - b b × (|D| / avgD)其中|D| 是当前文档的长度词数avgD 是语料库中所有文档的平均长度b 是一个可调参数通常取值为 0.75这个因子的特点是当文档长度等于平均长度时归一化因子为 1不影响得分当文档长度大于平均长度时归一化因子大于 1会降低得分当文档长度小于平均长度时归一化因子小于 1会提高得分BM25 的数学公式与参数详解核心公式将上述三个部分结合起来就得到了 BM25 的完整公式score(D, Q) Σ_{q ∈ Q} IDF(q) × [f(q, D) × (k1 1)] / [f(q, D) k1 × (1 - b b × |D| / avgD)]对于包含多个查询词的查询 QBM25 会计算每个查询词的得分然后将它们相加得到文档 D 与查询 Q 的总相关性得分。所有变量的详细解释变量含义说明score(D, Q)文档 D 与查询 Q 的相关性得分得分越高相关性越强q查询 Q 中的单个词对于多词查询会遍历所有查询词IDF(q)查询词 q 的逆文档频率衡量词 q 的稀有程度f(q, D)查询词 q 在文档 D 中出现的次数原始词频不是归一化后的词频k1词频饱和度参数控制词频对得分的影响程度通常取 1.2-2.0b长度惩罚参数控制文档长度对得分的影响程度通常取 0.75DavgD语料库中所有文档的平均长度用于归一化文档长度通俗总结BM25 的公式可以用一句话概括相关性得分 每个查询词的重要性 × 该词在文档中的出现情况经过词频饱和和长度归一化调整。BM25 的直观理解我们可以把 BM25 想象成一个严格又公平的老师打分系统。老师要给学生的作文打分看这篇作文与题目大模型课程的相关性有多高。老师的打分标准是关键词出现加分作文中提到大模型加 1 分提到课程加 1 分关键词稀有度加分大模型是比较稀有的词加 2 分课程是比较常见的词加 1 分词频边际递减第一次提到大模型加 2 分第二次加 1 分第三次加 0.5 分之后再提就不加分了文章长度扣分如果文章写得太长老师会觉得你在凑字数会适当扣分如果文章写得太短老师会觉得你表达得很精炼会适当加分最后老师把所有的加分和扣分加起来得到这篇作文的最终成绩也就是 BM25 得分。BM25 的代码实现与进阶用法基础环境准备首先我们需要安装必要的依赖库pipinstallrank_bm25 jieba numpy基础版 BM25 检索实现这是一个最基础的 BM25 检索示例包含分词、建索引、检索和排序的完整流程# -*- coding: utf-8 -*- BM25 基础检索示例 功能演示内存语料的BM25索引构建与检索 importjiebafromrank_bm25importBM25Okapi# 示例文档库可替换为自己的知识库DOCS[Python是一种编程语言适合做机器学习和数据分析,Java是面向对象的编程语言常用于企业级开发,机器学习需要大量的数据和算力支持,数据分析常用Python的pandas和numpy库,大模型是人工智能的重要分支基于深度学习技术,RAG技术结合了检索和生成能够提升大模型的回答准确性,向量数据库用于存储和检索高维向量是RAG系统的核心组件,BM25是一种经典的关键词检索算法广泛应用于搜索引擎]deftokenize(text:str)-list: 文本分词函数 使用jieba的搜索引擎模式分词适合检索场景 texttext.lower()# 转换为小写避免大小写敏感returnlist(jieba.cut_for_search(text))defmain():# 1. 对所有文档进行分词print(正在对文档进行分词...)tokenized_docs[tokenize(doc)fordocinDOCS]print(f分词完成共处理{len(tokenized_docs)}个文档)# 2. 构建BM25索引print(正在构建BM25索引...)bm25BM25Okapi(tokenized_docs)print(BM25索引构建完成)# 3. 执行检索queryPython 机器学习 数据分析print(f\n查询语句: 「{query}」)query_tokenstokenize(query)print(f查询分词结果:{query_tokens})scoresbm25.get_scores(query_tokens)print(f\n所有文档的BM25得分:{[round(score,4)forscoreinscores]})# 4. 按得分排序取Top-K结果k5top_indicessorted(range(len(scores)),keylambdai:scores[i],reverseTrue)[:k]print(f\nTop-{k}检索结果:)forrank,idxinenumerate(top_indices,1):print(f{rank}. [得分{scores[idx]:.4f}]{DOCS[idx]})if__name____main__:main()进阶版 BM25 检索实现进阶版增加了停用词过滤、索引保存与加载、批量检索等功能更适合实际项目使用# -*- coding: utf-8 -*- BM25 进阶检索示例 功能支持停用词过滤、索引保存与加载、批量检索 importjiebaimportpicklefromrank_bm25importBM25OkapifromtypingimportList,Tuple# 中文停用词列表可根据需要扩展STOPWORDSset([的,是,在,了,和,与,或,也,都,就,才,又,再,有,没有,这个,那个,这些,那些,一个,一种,什么,怎么,为什么,哪里,何时,谁,吗,呢,吧,啊,哦,嗯,,。,,,,,、,“,”,‘,’,,,【,】])deftokenize_with_stopwords(text:str)-list: 带停用词过滤的分词函数 texttext.lower()tokensjieba.cut_for_search(text)return[tokenfortokenintokensiftokennotinSTOPWORDSandtoken.strip()]defbuild_bm25_index(docs:List[str],save_path:strNone)-BM25Okapi: 构建BM25索引并可选保存到文件 tokenized_docs[tokenize_with_stopwords(doc)fordocindocs]bm25BM25Okapi(tokenized_docs)ifsave_path:withopen(save_path,wb)asf:pickle.dump(bm25,f)print(fBM25索引已保存到:{save_path})returnbm25defload_bm25_index(load_path:str)-BM25Okapi: 从文件加载BM25索引 withopen(load_path,rb)asf:bm25pickle.load(f)print(fBM25索引已从:{load_path}加载)returnbm25defsearch_bm25(bm25:BM25Okapi,docs:List[str],query:str,top_k:int5)-List[Tuple[int,float,str]]: 执行BM25检索返回Top-K结果 query_tokenstokenize_with_stopwords(query)scoresbm25.get_scores(query_tokens)top_indicessorted(range(len(scores)),keylambdai:scores[i],reverseTrue)[:top_k]return[(rank1,scores[idx],docs[idx])forrank,idxinenumerate(top_indices)]defbatch_search_bm25(bm25:BM25Okapi,docs:List[str],queries:List[str],top_k:int5)-dict: 批量执行BM25检索 return{query:search_bm25(bm25,docs,query,top_k)forqueryinqueries}defmain():DOCS[Python是一种编程语言适合做机器学习和数据分析,Java是面向对象的编程语言常用于企业级开发,机器学习需要大量的数据和算力支持,数据分析常用Python的pandas和numpy库,大模型是人工智能的重要分支基于深度学习技术,RAG技术结合了检索和生成能够提升大模型的回答准确性,向量数据库用于存储和检索高维向量是RAG系统的核心组件,BM25是一种经典的关键词检索算法广泛应用于搜索引擎]bm25build_bm25_index(DOCS,save_pathbm25_index.pkl)# 实际项目中可直接加载bm25 load_bm25_index(bm25_index.pkl)queryPython 机器学习 数据分析print(f\n单个查询: 「{query}」)forrank,score,docinsearch_bm25(bm25,DOCS,query,top_k3):print(f{rank}. [得分{score:.4f}]{doc})queries[大模型 RAG 技术,Java 企业级开发,向量数据库 检索]print(\n*50)print(批量查询结果:)forq,resultsinbatch_search_bm25(bm25,DOCS,queries,top_k2).items():print(f\n查询: 「{q}」)forrank,score,docinresults:print(f{rank}. [得分{score:.4f}]{doc})if__name____main__:main()BM25 在搜索与 RAG 中的实际应用在传统搜索引擎中的应用BM25 是传统搜索引擎的核心排序算法其工作流程如下爬虫抓取爬虫从互联网上抓取大量网页文本预处理对网页内容进行分词、去重、过滤等处理构建倒排索引建立词 → 文档的倒排索引BM25 排序当用户输入查询时先通过倒排索引找到包含查询词的所有文档然后使用 BM25 算法计算每个文档的相关性得分结果展示按照得分从高到低排序展示给用户在 RAG 系统中的应用RAG检索增强生成是目前解决大模型幻觉问题和知识更新问题的最有效方法之一。BM25 在 RAG 系统中扮演着检索器的角色其工作流程如下文档切分将长文档切分成适合检索的小块Chunk构建索引为每个文档块构建 BM25 索引查询处理将用户的问题转换为查询语句BM25 检索使用 BM25 算法从文档库中检索出与用户问题最相关的 Top-K 个文档块上下文拼接将检索到的文档块与用户的问题拼接成提示词大模型生成将提示词输入大模型生成回答BM25 与向量检索的混合使用在现代 RAG 系统中单独使用 BM25 或单独使用向量检索都有各自的局限性BM25 擅长精确的关键词匹配但不擅长语义匹配向量检索擅长语义匹配但不擅长精确的关键词匹配因此最常用的做法是将两者结合起来形成混合检索系统同时使用 BM25 和向量检索分别检索出 Top-K 个结果使用某种融合算法如 RRF 融合将两个结果列表合并将合并后的结果输入重排序模型如 CrossEncoder进行进一步排序将最终的 Top-N 个结果输入大模型生成回答BM25 的参数调优技巧BM25 有两个关键参数 k1 和 b它们的取值会显著影响检索效果。k1 参数调优作用控制词频对得分的影响程度默认值1.2取值范围通常在 1.0 到 2.0 之间调优建议如果你的文档比较短或者查询词通常只出现 1-2 次可以适当增大 k1如 1.5-2.0如果你的文档比较长或者查询词可能出现多次可以适当减小 k1如 1.0-1.2b 参数调优作用控制文档长度对得分的影响程度默认值0.75取值范围通常在 0.0 到 1.0 之间调优建议如果你的文档长度差异很大可以适当增大 b如 0.8-0.9加强对长文档的惩罚如果你的文档长度比较均匀可以适当减小 b如 0.5-0.7如果 b0意味着完全不考虑文档长度的影响如果 b1意味着完全按照文档长度进行归一化调优方法网格搜索在 k1∈[1.0, 1.2, 1.5, 2.0] 和 b∈[0.5, 0.6, 0.7, 0.75, 0.8, 0.9] 的范围内进行网格搜索找到在验证集上效果最好的参数组合经验法则对于大多数中文文本检索任务k11.5 和 b0.75 是一个不错的起点BM25 与其他检索算法的对比算法核心思想优点缺点适用场景TF-IDF词频 × 逆文档频率简单易懂计算速度快词频线性增长长文档不公平简单的文本分类、关键词提取BM25改进的 TF-IDF加入词频饱和和长度归一化效果好计算速度快参数少不考虑语义不考虑词之间的位置关系传统搜索引擎、RAG 系统的基础检索BM25FBM25 的扩展支持多字段可以为不同字段设置不同的权重实现复杂参数多网页检索、结构化文档检索向量检索将文本转换为向量计算向量相似度支持语义匹配能够理解同义词和近义词计算速度慢需要大量的训练数据语义检索、相似文本匹配混合检索结合 BM25 和向量检索兼顾关键词匹配和语义匹配实现复杂需要更多的计算资源现代 RAG 系统、智能问答系统常见问题与误区1. BM25 得分是绝对值吗不是。BM25 得分是相对值只在同一个查询和同一个语料库中有意义。不同查询或不同语料库的 BM25 得分不能直接比较。2. BM25 得分越高文档就一定越相关吗不一定。BM25 是基于关键词匹配的算法它只能衡量文档与查询在关键词层面的相关性不能衡量语义层面的相关性。例如查询苹果BM25 会将包含苹果的文档排在前面但无法区分是水果苹果还是苹果公司。3. BM25 适合处理长查询吗BM25 对短查询1-3 个词的效果最好。对于长查询BM25 的效果会下降因为长查询包含太多的词每个词的贡献被稀释了。4. BM25 需要大量的训练数据吗不需要。BM25 是一种无监督算法不需要任何标注数据只需要语料库本身就可以构建索引。这是 BM25 的一个重要优点。总结BM25 是什么BM25 是一种用于衡量查询与文档相关性的概率排序算法是信息检索领域的事实标准。核心思想BM25 的核心思想是相关性得分由词频、逆文档频率和文档长度三个因素共同决定。数学原理BM25 通过词频饱和函数解决了 TF-IDF 的词频线性增长问题通过文档长度归一化解决了长文档不公平问题。代码实现使用 rank_bm25 库可以非常方便地实现 BM25 检索支持分词、建索引、检索和排序等功能。实际应用BM25 广泛应用于传统搜索引擎和现代 RAG 系统中通常与向量检索结合使用形成混合检索系统。参数调优k1 和 b 是 BM25 的两个关键参数通过网格搜索可以找到适合自己任务的最佳参数组合。BM25 虽然已经有 30 多年的历史但它仍然是目前最有效、最实用的文本检索算法之一。掌握 BM25 算法对于理解搜索引擎的工作原理和构建高质量的 RAG 系统都具有重要的意义。