为什么需要BM42
BM25(Best Matching 25)是一个基于概率模型的检索函数,主要用于评估文档与查询之间的相关性。它在信息检索系统中广泛使用,比如搜索引擎和推荐系统。但是自从引入 RAG 以来,文本检索的用例发生了显著变化。BM25 所基于的许多假设不再有效。
- 由于RAG模型的引入,文本检索不再仅依赖于词频和逆文档频率等传统假设,而是结合了语义理解和上下文信息,从而提升了检索的准确性和相关性。
- 传统网络搜索和现代 RAG 系统之间文档和查询的典型长度存在大差异,RAG 中文档的典型长度比网络搜索的文档长度短得多。
BM42是开源向量数据库供应商 Qdrant 开发了的 搜索算法,将向量和标准 BM25 关键字搜索方法相结合,以获得更好的 RAG 结果,声称该方法降低了成本。
BM42的工作原理
- BM42保留了BM25的逆文档频率(IDF)计算,这部分在Qdrant引擎中进行,以确保IDF计算实时更新。
- 利用Transformer模型的注意力矩阵,特别是从[CLS]标记的行提取各个token对整个文档的重要性。注意力权重反映了每个token在文档中的重要性。
- 输入查询和文档,利用Transformer模型生成注意力权重矩阵。注意力矩阵是一个方阵,其中每一行和每一列代表输入序列中的一个token
- 最终的BM42得分公式结合了IDF和注意力权重
- 使用WordPiece tokenization,并在计算注意力权重后合并子词token,确保准确性。在BM42中,Token重组步骤非常关键,它确保了从注意力机制中提取的权重可以准确地应用于词频统计。
- Transformer模型通常使用WordPiece tokenization将单词拆分成更小的子词单元。例如,“programming”可能被拆分成“pro”,“##gram”,“##ming”。
- 为了将注意力权重应用于BM25的IDF计算,必须将子词Token的注意力权重合并回原始单词。例如,对于“programming”:
- 原始子词权重可能是 [0.03, 0.05, 0.07]
- 合并后,整个单词的权重为 0.03 + 0.05 + 0.07 = 0.15
- 通过这种方式,我们可以显著减少 token 的数量,从而最大限度地减少稀疏嵌入的内存占用。我们不会同时损害匹配(几乎)精确 token 的能力。
BM42效果
BM42对于小文档处理的准确性远远高于BM25,但对于长查询和复杂查询,效果可能不如 BM42。
代码解释
在原库中总共进行了三种方法,这里只介绍BM42的代码
要运行的代码主要是两个,一个是index_bm42.py,一个是evaluate_bm42.
需要的库
运行代码前,需要先安装所需要的库。
tqdm
qdrant-client>=1.10.0
fastembed>=0.3.3
tantivy
ipdb
在运行代码之前,请确保已启动 Qdrant 容器。可以通过以下命令启动 Qdrant Docker 容器:
docker run --rm -d --network=host qdrant/qdrant:v1.10.0
index
index 主要是用来将文档嵌入到向量空间中并在 Qdrant 中储存。
在训练的过程中我们只需要文档中的`_id`和`text`
之后我们将每个id和text转换成稀疏向量,上传到Qdrant上面
client.create_collection(
collection_name=DATASET,
vectors_config={},
sparse_vectors_config={
"bm42": models.SparseVectorParams(
modifier=models.Modifier.IDF
)
}
)
在Qdrant中创建一个新的集合,配置其稀疏向量,并使用IDF作为稀疏向量的修饰符,以便后续在该集合中存储和检索文档向量。
使用 BM42 编码后,每个文档的平均向量大小仅为 5.6 个元素。
evaluate
evaluate主要是将query转化成稀疏向量,在Qdrant数据库中搜索最近的向量,结合Bm42model返回前十个相似的文档
dataset:
图中是储存query的json文件,这里面每个query都有id,text,metadata三列
图中是存储每个query应该对应的文档,表示一个查询与一个文档的对应关系,最后一列是文档与query的相关关系,1代表相关
preprocess
通过对两个文件进行预处理后,我们应该得到一个结构的数据
{
"2254": {
"_id": "2254",
"text": "Who were the Aztec?",
"metadata": {},
"doc_ids": ["101", "103"]
}
}
这里doc_ids是相关的文档标签
model
model = SparseTextEmbedding(
model_name="Qdrant/bm42-all-minilm-l6-v2-attentions"
)
SparseTextEmbedding是一个预训练的Transformer模型,专门用于生成稀疏嵌入向量。
result = client.query_points(
collection_name=DATASET,
query=sparse_vector,
using="bm42",
with_payload=True,
limit=limit
)
这里使用bm42计算输入的query与其他文档的得分,并且返回分数最高的前10个文档,这里limit定义为10所以返回前十个,可以更改limit值