RAG:BM25算法

BM25 算法索引建立流程和检索流程总结


BM25(Best Matching 25)是一种基于词频和逆文档频率的排名函数,用于衡量文档与查询之间的相关性。在全文检索系统中,BM25 被广泛应用于搜索引擎、信息检索等领域。

您提供的代码实现了一个简单的 BM25 模型,包括索引建立和检索过程。以下将基于该代码,详细总结 BM25 的索引建立流程和检索时的流程。


一、BM25 索引建立流程

BM25 的索引建立主要包括以下步骤:

1. 文本预处理

  • 分词(Tokenization):将文档和查询文本分割成单独的词语或词项。代码中,docs 是已经分词后的文档列表,每个文档是一个词汇列表。

  • 可选的预处理步骤(代码中未体现):

    • 去除停用词(Stop Words Removal):如 “的”、“是”、“了” 等常用但信息量低的词语。
    • 词干提取或词形还原:将词语还原为基本形式,如将 “running” 还原为 “run”。

2. 统计词频和文档频率

  • 词频(Term Frequency,TF)

    • 文档词频:对于每个文档,统计其中每个词语出现的次数。
    • 在代码中,通过 Counter(doc) 统计每个文档的词频,结果存储在 self.doc_freqs 中。
  • 文档频率(Document Frequency,DF)

    • 词项文档频率:统计每个词语出现在多少个不同的文档中。
    • 在代码中,通过遍历每个文档的词汇集合(set(doc)),更新每个词的文档频率 df

3. 计算逆文档频率(IDF)

  • 逆文档频率(Inverse Document Frequency,IDF)

    • 公式: idf ( w ) = ln ⁡ ( N − df ( w ) + 0.5 df ( w ) + 0.5 + 1 ) \text{idf}(w) = \ln\left(\frac{N - \text{df}(w) + 0.5}{\text{df}(w) + 0.5} + 1\right) idf(w)=ln(df(w)+0.5Ndf(w)+0.5+1)
      • N N N:文档总数。
      • df ( w ) \text{df}(w) df(w):包含词语 w w w 的文档数量。
    • IDF 反映了词语的辨识度,词频越低,IDF 值越高,说明该词对区分文档越有用。
  • 在代码中,计算每个词的 IDF 值并存储在 self.idf 中。

4. 计算文档长度和平均文档长度

  • 文档长度(Document Length)

    • 计算每个文档的长度,即词语的总数。
    • 在代码中,self.doc_len 存储了每个文档的长度。
  • 平均文档长度(Average Document Length)

    • 计算所有文档长度的平均值。
    • 在代码中,self.avgdl 表示平均文档长度。

5. 建立索引结构

  • 存储必要的数据结构

    • 词典(Vocabulary):所有出现过的词语集合。
    • 倒排索引(Inverted Index):在 BM25 的实现中,虽然未显式构建倒排索引,但通过词频和文档频率的统计,可以快速检索包含查询词语的文档及其相关统计信息。
  • 在实际应用中,通常会建立倒排索引,以便高效地检索和计算相关性。


二、BM25 检索时的流程

在检索阶段,BM25 算法根据查询计算每个文档与查询的相关性得分,并排序返回相关文档。具体流程如下:

1. 查询预处理

  • 分词:将用户的查询文本分割成词语列表。

    • 在代码中,查询 query 是已经分词后的词语列表。
  • 可选的预处理步骤(代码中未体现):

    • 去除停用词:过滤掉无意义的常用词。
    • 词干提取或词形还原:标准化词语形式。

2. 计算 BM25 相关性得分

对于每个文档,计算其与查询的 BM25 得分:

  • 初始化得分score = 0.0

  • 遍历查询词语

    • 对于查询中的每个词 word,执行以下操作:

      1. 检查词是否在文档中

        • 如果 word 在文档 doc 中(即 word in self.doc_freqs[doc]),则继续计算。
      2. 获取词频

        • 获取词 word 在文档 doc 中的出现次数 freq
      3. 获取词的 IDF 值

        • 从预先计算的 self.idf 中获取 word 的 IDF 值。
      4. 计算 BM25 部分得分

        • 使用 BM25 公式计算该词对文档得分的贡献:
          [
          \text{score} += \text{idf}[w] \times \frac{\text{freq} \times (k1 + 1)}{\text{freq} + k1 \times (1 - b + b \times \frac{\text{dl}}{\text{avgdl}})}
          ]
          • freq \text{freq} freq:词 word 在文档中的频率。
          • dl \text{dl} dl:文档长度(词语总数)。
          • avgdl \text{avgdl} avgdl:平均文档长度。
          • k 1 k1 k1 b b b:BM25 的调节参数。
  • 累加得分:将每个查询词的得分累加,得到文档与查询的总相关性得分。

3. 排序和返回结果

  • 汇总得分:对所有文档计算与查询的 BM25 得分,形成一个文档得分列表。

  • 排序:根据得分从高到低排序文档,得分越高,文档与查询的相关性越强。

  • 返回结果:根据排序结果,返回最相关的文档列表给用户。


三、BM25 算法的核心公式

BM25 的核心计算公式如下:

[
\text{score}(D, Q) = \sum_{w \in Q} \text{idf}(w) \times \frac{\text{tf}(w, D) \times (k1 + 1)}{\text{tf}(w, D) + k1 \times \left(1 - b + b \times \frac{\text{dl}}{\text{avgdl}}\right)}
]

  • score ( D , Q ) \text{score}(D, Q) score(D,Q):文档 D D D 与查询 Q Q Q 的相关性得分。
  • idf ( w ) \text{idf}(w) idf(w):词语 w w w 的逆文档频率。
  • tf ( w , D ) \text{tf}(w, D) tf(w,D):词语 w w w 在文档 D D D 中的词频。
  • k 1 k1 k1 b b b:调节参数,通常取 k 1 = 1.5 k1 = 1.5 k1=1.5 b = 0.75 b = 0.75 b=0.75
  • dl \text{dl} dl:文档 D D D 的长度。
  • avgdl \text{avgdl} avgdl:所有文档的平均长度。

四、代码中的具体实现

1. 初始化和索引建立

def __init__(self, docs, k1=1.5, b=0.75):
    self.docs = docs
    self.k1 = k1
    self.b = b
    self.doc_len = [len(doc) for doc in docs]  # 计算每个文档的长度
    self.avgdl = sum(self.doc_len) / len(docs)  # 计算平均文档长度
    self.doc_freqs = []  # 存储每个文档的词频
    self.idf = {}  # 存储每个词的逆文档频率
    self.initialize()
  • 计算文档长度和平均文档长度
  • 初始化词频统计和 IDF 字典
def initialize(self):
    df = {}  # 词项文档频率
    for doc in self.docs:
        # 统计每个文档的词频
        self.doc_freqs.append(Counter(doc))
        # 更新词项的文档频率
        for word in set(doc):
            df[word] = df.get(word, 0) + 1
    # 计算每个词的 IDF 值
    for word, freq in df.items():
        self.idf[word] = math.log((len(self.docs) - freq + 0.5) / (freq + 0.5) + 1)
  • 遍历文档,统计词频和文档频率
  • 计算并存储每个词的 IDF 值

2. 计算 BM25 得分

def score(self, doc, query):
    score = 0.0
    for word in query:
        if word in self.doc_freqs[doc]:
            freq = self.doc_freqs[doc][word]  # 词在文档中的频率
            # 应用 BM25 公式计算得分
            score += (self.idf[word] * freq * (self.k1 + 1)) / (
                freq + self.k1 * (1 - self.b + self.b * self.doc_len[doc] / self.avgdl)
            )
    return score
  • 遍历查询词语,计算每个词对文档得分的贡献
  • 累加得分,得到文档与查询的总相关性得分

五、示例说明

1. 示例文档集

docs = [
    ["the", "quick", "brown", "fox"],
    ["the", "lazy", "dog"],
    ["the", "quick", "dog"],
    ["the", "quick", "brown", "brown", "fox"]
]
  • 四个文档,已经分词。

2. 查询

query = ["quick", "brown"]
  • 查询包含两个词:“quick” 和 “brown”。

3. 计算得分

# 初始化 BM25 模型
bm25 = BM25(docs)
# 计算每个文档与查询的得分
scores = [bm25.score(i, query) for i in range(len(docs))]

4. 得分结果

scores = [1.0192447810666774, 0.0, 0.3919504878447609, 1.2045355839511414]
  • 解释
    • 第 0 个文档得分约为 1.0192
    • 第 1 个文档得分为 0.0(因为不包含查询词语)
    • 第 2 个文档得分约为 0.3920
    • 第 3 个文档得分约为 1.2045(得分最高,最相关)

六、BM25 的优势与注意事项

优势

  • 考虑了词频和文档长度:BM25 平衡了词频和文档长度的影响,避免了长文档得分过高或过低的问题。
  • 简单高效:计算公式相对简单,易于实现和优化。
  • 适用性广泛:在各种信息检索任务中表现良好。

注意事项

  • 参数调节 k 1 k1 k1 b b b 参数的选择会影响模型性能,通常需要根据实际数据进行调优。
  • 预处理质量:文本预处理(如分词、去除停用词)对结果有较大影响,需确保预处理的合理性。
  • 索引结构:在大型数据集上,需构建高效的索引结构(如倒排索引)以支持快速检索。

七、总结

  • 索引建立流程

    1. 文本预处理:分词、去除停用词等。
    2. 统计词频和文档频率:计算每个词在文档中的词频和在所有文档中的文档频率。
    3. 计算 IDF 值:根据文档频率计算每个词的逆文档频率。
    4. 计算文档长度和平均文档长度
    5. 构建索引结构:存储词频、文档频率、IDF 等必要信息,方便检索时快速访问。
  • 检索时的流程

    1. 查询预处理:对查询进行分词等预处理。
    2. 计算 BM25 得分:对于每个文档,计算其与查询的相关性得分。
    3. 排序:根据得分对文档进行排序,得分越高,相关性越强。
    4. 返回结果:向用户展示排序后的文档列表。

通过上述流程,BM25 可以有效地衡量文档与查询之间的相关性,为信息检索提供可靠的排序机制。

参考:https://blog.csdn.net/weixin_40959890/article/details/138121459

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵海之森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值