[Ash]
in
BM25源码解读(构建稀疏矩阵)
BM25 搜索引擎包含两个主要阶段:索引阶段和检索阶段。索引阶段负责处理原始文档并构建可搜索的索引结构,检索阶段负责处理用户查询并返回最相关的文档。
BM25有多个变体,在库中也有多个不同的计算公式实现,主要公式为:
$$ \text{BM25}=\sum_{t\in\mathcal{Q}}\frac{tf_{t,d}\cdot(k+1)}{tf_{t,d}+k\cdot(1-b+b\cdot\frac{l_d}{mean(l_d)})}\cdot ln(1+\frac{N-df_t+0.5}{df_t+0.5}) $$
代码源码: https://github.com/xhluca/bm25s/blob/main/bm25s/__init__.py
bm25s库中的流程分为index和retrieve两个,我们重点介绍index部分:
index index 方法是索引的入口方法,负责整个索引过程的协调。它接收一个 corpus 参数,这个参数可以是多种形式:可以是分词后的文档列表,也可以是已经转换为 token ID 的形式,或者是自定义的分词对象。
方法会首先判断 corpus 的类型,然后根据不同的类型调用相应的处理方法。如果是分词后的文档列表,就调用 build_index_from_tokens 方法;如果是已转换的形式,就直接调用 build_index_from_ids 方法。
build_index_from_tokens 接收一个List[List],外层List是document,内层List是document被分词之后的各个token,把这些token变为词汇表vocab_dict,然后调用build_index_from_ids函数。
首先,方法会遍历所有文档,收集所有唯一的 token,形成一个集合。然后,它会创建一个词汇表映射,这个映射是一个字典,键是 token 的字符串,值是一个唯一的整数标识符,这个整数就是 token 的 ID。比如 abstract 被分配 ID 0,activ 被分配 ID 1,这样就形成了一个像 {“abstract”: 0, “activ”: 1, …} 这样的词汇表。
接下来,方法会遍历所有文档,将每个文档中的 token 转换为对应的 token ID,最后,方法会调用 build_index_from_ids,将 token ID 列表和词汇表传递给它。
build_index_from_ids build_index_from_ids 是核心索引构建方法,它计算 BM25 分数并构建稀疏矩阵。这个方法包含四个主要步骤。
第一步 _calculate_doc_freqs 文档频率是指每个 token 出现在多少个文档中。这里的关键是不重复计算,即使一个 token 在同一个文档中出现多次,我们也只计算一次。
比如说,如果 token 0 出现在文档 0 和文档 2 中,那么 token 0 的文档频率就是 2。如果 token 1 只出现在文档 1 中,那么 token 1 的文档频率就是 1。