游乐游手机版
首页/AI热点日报/热点详情

BM25算法基础使用与RAG应用指南

类型:热点整理2026-06-30
掌握BM25算法,能够显著提升信息检索与问答系统的性能,是构建高效RAG框架的关键技术之一。核心内容:1 BM25算法原理及其在RAG框架中的实际应用场景2 使用Python实现BM25算法的完整操作步骤3 借助rank_bm25库进行文档与查询之间的相关性计算与排序BM25算法被众多RAG开

掌握BM25算法,能够显著提升信息检索与问答系统的性能,是构建高效RAG框架的关键技术之一。

核心内容:
1. BM25算法原理及其在RAG框架中的实际应用场景
2. 使用Python实现BM25算法的完整操作步骤
3. 借助rank_bm25库进行文档与查询之间的相关性计算与排序

BM25算法基本使用以及在RAG中的应用

BM25算法被众多RAG开源框架及向量库的向量查询功能所集成或引用。本文将系统介绍BM25算法的核心原理,并通过一个可直接运行的代码示例,演示如何实际运用BM25算法完成文档检索任务。

BM25(Best Match 25)是一种基于概率模型的信息检索算法,广泛应用于搜索引擎与问答系统。在RAG(检索增强生成)框架中,BM25负责从大规模文档库中快速找出与用户查询最相关的文档。它的核心机制依赖于词频(TF)和逆文档频率(IDF),并引入文档长度归一化因子,能够有效应对不同篇幅的文档——这一特性在真实业务场景中尤为重要。

BM25的基本使用

下面通过一个完整的Python示例,演示BM25算法的实际应用。我们将采用rank_bm25库,这是目前常用的BM25实现工具,能够方便地计算文档与查询之间的相关性得分。

安装依赖库

pip install rank-bm25

编写测试代码

代码的核心逻辑如下:

(1)对每个文档和用户查询分别进行分词处理
(2)使用分词后的文档列表初始化BM25模型
(3)计算查询(query)与每个文档分词列表的相似度得分
(4)从文档列表中找出得分最高的文档,即为与查询最匹配的结果。

具体的代码实现如下:

from rank_bm25 import BM25Okapi
import jieba  # 用于中文分词

# 示例文档库
documents = [
    "猫是一种可爱的动物,喜欢抓老鼠。",
    "狗是人类的好朋友,喜欢追猫。",
    "老鼠是一种小型啮齿动物,猫喜欢抓它们。"
]
print(documents)

# 用户查询
query = "猫喜欢抓什么动物?"
print("问题: "+ query)

# 对文档和查询进行分词
def tokenize(text):
    return list(jieba.cut(text))

# 分词后的文档库
tokenized_documents = [tokenize(doc) for doc in documents]
# 分词后的查询
tokenized_query = tokenize(query)

# 初始化BM25模型
bm25 = BM25Okapi(tokenized_documents)
# 计算查询与文档的相关性得分
doc_scores = bm25.get_scores(tokenized_query)
# 打印每个文档的得分
for i, score in enumerate(doc_scores):
    print(f"文档{i+1} 的 BM25 得分: {score}")

# 找到最相关的文档
most_relevant_doc_index = doc_scores.argmax()
print(f"n最相关的文档是文档{most_relevant_doc_index + 1}: {documents[most_relevant_doc_index]}")

代码说明

  1. 分词处理:借助jieba分词库对中文文档和查询进行切分。例如,文档1的分词结果为:['猫', '是', '一种', '可爱', '的', '动物', '喜欢', '抓', '老鼠', '。']

  2. 初始化BM25模型:使用BM25Okapi类将分词后的文档库传入,完成模型初始化。

  3. 计算相关性得分:通过get_scores方法获得查询与每个文档间的相关性评分。

  4. 排序与输出:依据得分从高到低排序,输出匹配度最高的文档内容。

结果输出

['猫是一种可爱的动物,喜欢抓老鼠。', '狗是人类的好朋友,喜欢追猫。', '老鼠是一种小型啮齿动物,猫喜欢抓它们。']
问题: 猫喜欢抓什么动物?
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.705 seconds.
Prefix dict has been built successfully.
文档1 的 BM25 得分: 0.3001762708496166
文档2 的 BM25 得分: -0.07080064278072501
文档3 的 BM25 得分: -0.2035654844820229

最相关的文档是文档1: 猫是一种可爱的动物,喜欢抓老鼠。

BM25在RAG中的应用

在RAG(检索增强生成)框架中,将基于嵌入向量的语义检索与基于BM25的关键词检索相结合,形成混合检索策略,能够显著提升查询结果的精确度。这种混合检索的核心思路是充分利用两种检索方式的互补优势,从而更全面地捕捉查询与文档之间的相关性。

向量检索和BM25检索的优缺点

向量检索(基于嵌入的相似性搜索)

  • 优点:擅长捕捉语义相似性,即使查询与文档之间没有完全匹配的关键词,也能通过语义理解找到相关内容。适用于处理同义词、上下文关联等复杂语义关系。

  • 缺点:对领域外词汇或罕见术语的识别能力较弱。依赖高质量的嵌入模型(如BERT、Sentence-BERT),且需要较高的计算资源开销。

BM25关键词检索

  • 优点:对精确关键词匹配效果极佳,特别适合处理明确术语和短查询。计算效率高,可轻松应对大规模文档库。

  • 缺点:无法理解语义相似性,对同义词、上下文关联不敏感。在处理长查询或复杂查询时表现欠佳。

混合检索的原理

混合检索的核心思想是综合向量检索与BM25检索各自的优势,通过加权融合或排序整合的方式,生成最终检索结果。具体流程如下:

(1)分别计算向量检索和BM25检索的得分

  • 向量检索:使用嵌入模型(如BERT)将查询和文档转换为向量,再通过余弦相似度计算得分。
  • BM25检索:利用BM25算法计算查询与文档之间的关键词匹配得分。

(2)得分归一化:由于两种检索的得分尺度不同,需要对得分进行归一化处理,使其处于同一可比较的量纲下。

(3)加权融合:将两种得分按权重进行组合,得到最终检索得分。例如:
Final Score = α·BM25 Score + (1-α)·Vector Score
其中,α为权重参数,用于调节BM25与向量检索的贡献比例。

(4)排序与返回:依最终得分对文档降序排序,返回最相关的若干文档。

实际应用中的混合检索策略

在实际项目中,混合检索可以通过以下方式实现:

(1)加权求和:将向量检索得分与BM25得分按一定权重直接相加,得出最终得分。例如:Final Score = 0.5·BM25 Score + 0.5·Vector Score

(2)排序融合:分别对向量检索结果和BM25检索结果进行排序,再通过加权或插值方法合并排序列表。例如,取向量检索的前10个结果与BM25检索的前10个结果,合并后重新排序。

(3)阈值过滤:先使用BM25检索快速筛选出关键词匹配的文档集合,再对该集合应用向量检索进行语义排序,提升整体效率。

混合检索的代码实现

通过本地部署的ollama嵌入模型,可以生成文档的嵌入向量。下面的代码演示了如何嵌入模型计算向量,并基于混合策略进行融合排序。

from rank_bm25 import BM25Okapi
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import jieba
import requests  # 用于发送HTTP请求

# 示例文档库
documents = [
    "猫是一种可爱的动物,喜欢抓老鼠。",
    "狗是人类的好朋友,喜欢追猫。",
    "老鼠是一种小型啮齿动物,猫喜欢抓它们。"
]

# 用户查询
query = "猫喜欢抓什么动物?"

# 分词函数
def tokenize(text):
    return list(jieba.cut(text))

# 分词后的文档库
tokenized_documents = [tokenize(doc) for doc in documents]
# 初始化BM25模型
bm25 = BM25Okapi(tokenized_documents)
# 计算BM25得分
bm25_scores = bm25.get_scores(tokenize(query))
# 嵌入模型API地址
EMBEDDING_MODEL_URL = "http://172.16.1.54:11434/api/embeddings"

# 获取嵌入向量的函数
def get_embedding(text):
    payload = {
        "model": "unclemusclez/jina-embeddings-v2-base-code:latest",
        "prompt": text
    }
    response = requests.post(EMBEDDING_MODEL_URL, json=payload)
    if response.status_code == 200:
        embedding = response.json().get("embedding")
        return np.array(embedding)
    else:
        raise Exception(f"Failed to get embedding: {response.status_code} - {response.text}")

# 获取查询和文档的嵌入向量
query_embedding = get_embedding(query)
document_embeddings = [get_embedding(doc) for doc in documents]
# 计算余弦相似度
vector_scores = cosine_similarity([query_embedding], document_embeddings)[0]

# 归一化得分
bm25_scores_normalized = (bm25_scores - np.min(bm25_scores)) / (np.max(bm25_scores) - np.min(bm25_scores))
vector_scores_normalized = (vector_scores - np.min(vector_scores)) / (np.max(vector_scores) - np.min(vector_scores))

# 加权融合
alpha = 0.5
final_scores = alpha * bm25_scores_normalized + (1 - alpha) * vector_scores_normalized

# 排序并输出结果
sorted_indices = np.argsort(final_scores)[::-1]
for i in sorted_indices:
    print(f"文档{i+1} 的混合得分: {final_scores[i]} - {documents[i]}")

输出:

Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.715 seconds.
Prefix dict has been built successfully.
文档1 的混合得分: 1.0 - 猫是一种可爱的动物,喜欢抓老鼠。
文档2 的混合得分: 0.5282365628163656 - 狗是人类的好朋友,喜欢追猫。
文档3 的混合得分: 0.0 - 老鼠是一种小型啮齿动物,猫喜欢抓它们。

说明:这里的得分是基于当前使用的嵌入模型得到的,不同模型结果可能有所差异。

总结

通过融合向量检索与BM25检索,混合检索方法能够同时兼顾语义理解与关键词匹配,从而有效提升RAG框架的查询准确率。该策略在处理多样化查询、减少漏检与误检方面表现突出,是优化检索增强生成系统性能的重要手段。

来源:https://www.53ai.com/news/RAG/2025022380735.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。