首页 > 其他分享 >Sparse稀疏检索介绍与实践

Sparse稀疏检索介绍与实践

时间:2024-04-15 14:44:19浏览次数:27  
标签:检索 doc ids 稀疏 topk Sparse recall query 向量

Sparse稀疏检索介绍

在处理大规模文本数据时,我们经常会遇到一些挑战,比如如何有效地表示和检索文档,当前主要有两个主要方法,传统的文本BM25检索,以及将文档映射到向量空间的向量检索。

BM25效果是有上限的,但是文本检索在一些场景仍具备较好的鲁棒性和可解释性,因此不可或缺,那么在NN模型一统天下的今天,是否能用NN模型来增强文本检索呢,答案是有的,也就是我们今天要说的sparse 稀疏检索。

传统的BM25文本检索其实就是典型的sparse稀疏检索,在BM25检索算法中,向量维度为整个词表,但是其中大部分为0,只有出现的关键词或子词(tokens)有值,其余的值都设为零。这种表示方法不仅节省了存储空间,而且提高了检索效率。

向量的形式, 大概类似:

{
   '19828': 0.2085,
   '3508': 0.2374,
   '7919': 0.2544,
   '43': 0.0897,
   '6': 0.0967,
   '79299': 0.3079
}

key是term的编号,value是NN模型计算出来的权重。

稀疏向量与传统方法的比较

当前流行的sparse检索,大概是通过transformer模型,为doc中的term计算weight,这样与传统的BM25等基于频率的方法相比,sparse向量可以利用神经网络的力量,提高了检索的准确性和效率。BM25虽然能够计算文档的相关性,但它无法理解词语的含义或上下文的重要性。而稀疏向量则能够通过神经网络捕捉到这些细微的差别。

稀疏向量的优势

  1. 计算效率:稀疏向量在处理包含零元素的操作时,通常比密集向量更高效。
  2. 信息密度:稀疏向量专注于关键特征,而不是捕捉所有细微的关系,这使得它们在文本搜索等应用中更为高效。
  3. 领域适应性:稀疏向量在处理专业术语或罕见关键词时表现出色,例如在医疗领域,许多专业术语不会出现在通用词汇表中,稀疏向量能够更好地捕捉这些术语的细微差别

稀疏向量举例

SPLADE 是一款开源的transformer模型,提供sparse向量生成,下面是效果对比,可以看到sparse介于BM25和dense之间,比BM25效果好。

Model MRR@10 (MS MARCO Dev) Type
BM25 0.184 Sparse
TCT-ColBERT 0.359 Dense
doc2query-T5 link 0.277 Sparse
SPLADE 0.322 Sparse
SPLADE-max 0.340 Sparse
SPLADE-doc 0.322 Sparse
DistilSPLADE-max 0.368 Sparse

Sparse稀疏检索实践

模型介绍

国内的开源模型中,BAAI的BGE-M3提供sparse向量向量生成能力,我们用这个来进行实践。

BGE是通过RetroMAE的预训练方式训练的类似bert的预训练模型。

常规的Bert预训练采用了将输入文本随机Mask再输出完整文本这种自监督式的任务,RetroMAE采用一种巧妙的方式提高了Embedding的表征能力,具体操作是:将低掩码率的的文本A输入到Encoder种得到Embedding向量,将该Embedding向量与高掩码率的文本A输入到浅层的Decoder向量中,输出完整文本。这种预训练方式迫使Encoder生成强大的Embedding向量,在表征模型中提升效果显著。

image.png

向量生成

  • 先安装

    !pip install -U FlagEmbedding

  • 然后引入模型

from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3',  use_fp16=True)

编写一个函数用于计算embedding:

def embed_with_progress(model, docs, batch_size):
    batch_count = int(len(docs) / batch_size) + 1
    print("start embedding docs", batch_count)
    query_embeddings = []
    for i in tqdm(range(batch_count), desc="Embedding...", unit="batch"):
        start = i * batch_size
        end = min(len(docs), (i + 1) * batch_size)
        if end <= start:
            break
        output = model.encode(docs[start:end], return_dense=False, return_sparse=True, return_colbert_vecs=False)
        query_embeddings.extend(output['lexical_weights'])

    return query_embeddings

然后分别计算query和doc的:

query_embeddings = embed_with_progress(model, test_sets.queries, batch_size)
doc_embeddings = embed_with_progress(model, test_sets.docs, batch_size)

然后是计算query和doc的分数,model.compute_lexical_matching_score(交集的权重相乘,然后累加),注意下面的代码是query和每个doc都计算了,计算量会比较大,在工程实践中需要用类似向量索引的方案(当前qdrant、milvus等都提供sparse检索支持)

# 检索topk
recall_results = []
import numpy as np
for i in tqdm(range(len(test_sets.query_ids)), desc="recall...", unit="query"):
    query_embeding = query_embeddings[i]
    query_id = test_sets.query_ids[i]
    if query_id not in test_sets.relevant_docs:
        continue
    socres = [model.compute_lexical_matching_score(query_embeding, doc_embedding) for doc_embedding in doc_embeddings]
    topk_doc_ids = [test_sets.doc_ids[i] for i in np.argsort(socres)[-20:][::-1]]
    recall_results.append(json.dumps({"query": test_sets.queries[i], "topk_doc_ids": topk_doc_ids, "marked_doc_ids": list(test_sets.relevant_docs[query_id].keys())}))

# recall_results 写入到文件

with open("recall_results.txt", "w", encoding="utf-8") as f:
    f.write("\n".join(recall_results))

最后,基于测试集,我们可以计算召回率:

import json

# 读取 JSON line 文件
topk_doc_ids_list = []
marked_doc_ids_list = []

with open("recall_results.txt", "r") as file:
    for line in file:
        data = json.loads(line)
        topk_doc_ids_list.append(data["topk_doc_ids"])
        marked_doc_ids_list.append(data["marked_doc_ids"])


# 计算 recall@k
def recall_at_k(k):
    recalls = []
    for topk_doc_ids, marked_doc_ids in zip(topk_doc_ids_list, marked_doc_ids_list):
        # 提取前 k 个召回结果
        topk = set(topk_doc_ids[:k])
        # 计算交集
        intersection = topk.intersection(set(marked_doc_ids))
        # 计算 recall
        recall = len(intersection) / min(len(marked_doc_ids), k)
        recalls.append(recall)
    # 计算平均 recall
    average_recall = sum(recalls) / len(recalls)
    return average_recall

# 计算 recall@5, 10, 20
recall_at_5 = recall_at_k(5)
recall_at_10 = recall_at_k(10)
recall_at_20 = recall_at_k(20)

print("Recall@5:", recall_at_5)
print("Recall@10:", recall_at_10)
print("Recall@20:", recall_at_20)

在测试集中,测试结果:

Recall@5: 0.7350086355785777 
Recall@10: 0.8035261945883735 
Recall@20: 0.8926130345462158

在这个测试集上,比BM25测试出来的结果要更好,但是仅凭这个尚不能否定BM25,需要综合看各自的覆盖度,综合考虑成本与效果。

参考

标签:检索,doc,ids,稀疏,topk,Sparse,recall,query,向量
From: https://www.cnblogs.com/xiaoqi/p/18135929/sparse_retrieval

相关文章

  • 用于显著提高检索速度和降低成本的二进制和标量嵌入量化
    我们引入了嵌入量化的概念,并展示了它们对检索速度、内存使用、磁盘空间和成本的影响。我们将讨论理论上和实践中如何对嵌入进行量化,然后介绍一个演示,展示了4100万维基百科文本的真实检索场景。目录为什么使用嵌入?嵌入可能难以扩展提高可扩展性二进制量化SentenceT......
  • 【稳定检索|投稿优惠】2024年哲学探究与教育创新国际会议 (PEEI 2024)
    2024年哲学探究与教育创新国际会议(PEEI2024)2024InternationalConferenceonPhilosophicalExplorationandEducationalInnovation1.【会议简介】 2024年哲学探究与教育创新国际会议即将在杭州召开。本次会议旨在汇聚全球哲学与教育领域的专家学者,共同探讨哲学思......
  • 2024年自然语言处理科学与信息检索技术国际会议(ICNLPSIRT 2024)
    2024InternationalConferenceonNaturalLanguageProcessingScienceandInformationRetrievalTechnology(ICNLPSIRT2024)●会议简介2024年自然语言处理科学与信息检索技术国际会议旨在汇聚来自世界各地的自然语言处理和信息检索领域的专家和学者,共同探讨最新的研......
  • c语言多媒体文件管理及检索系统220
     定制魏:QTWZPW,获取更多源码等目录选题程序设计题1:基于数据分析的小区电量扩容推荐程序程序设计题2:神气的盒子程序设计题3:多媒体文件管理及检索系统程序设计题4: 计算24点游戏程序设计题5:上网计费系统模拟程序设计题6:信息产业发展统计程序设计题7:挖地雷程序设计题8:......
  • PageOffice6 实现 word 全文检索
    在文档服务器中存储有成千上万个文档的情况下,用户想要找到并打开包含特定关键字的文档,无疑是一项艰巨的任务。如何高效地管理和检索大量的Word文档呢?在现有的技术解决方案中,许多方法都依赖于服务器端的ApachePOI技术。这种技术的基本原理是,先将所有文档的文本内容提取出来,然后存......
  • 增强检索问答RAG研究成果综述 Retrieval-Augmented Generation for AI-Generated Cont
    文章目录引言背景贡献*相关工作**路线图*初步*概述**生成器*Transformer模型LSTMDiffusion模型***GAN****检索器*稀疏检索*:*密集检索*:****其他方法**:*方法*RAG基础*基于查询的RAG(Query-basedRAG)*:****基于潜在表示的RAG**(LatentRepresentation-basedRA......
  • 使用Sparse Checkout 排除跟踪Git仓库中指定的目录或文件
    应用场景在一个大工程里包含由不同部门开发的模块时,项目的Git仓库肯定很大,造成每次Git操作相对比较耗时。因为开发人员一般只关心他们部门的模块的代码,所以完全可以排除一些他完全不需要用到的目录。这时候就可以使用Git的SparseCheckout。操作步骤1、若未拉取仓库代码时mk......
  • 【浙江工业大学主办,嘉宾阵容强大 | IEEE出版,往届均已检索!!】第四届IEEE电子,电路和信息
    第四届IEEE电子,电路和信息工程国际学术会议(ECIE2024)将于2024年5月24日至26日于杭州举行。ECIE2024致力于为电子,电路和信息工程等相关领域的学者,工程师和从业人员提供一个分享最新研究成果的平台。会议征稿主题主要包括但不限于单片机技术,电工技术,电力系统通信,信息与通信工程,......
  • 【IEEE出版 | 西安理工大学主办,多高校承办 | EI、SCOPUS双检索 | 计算机领域 ei 会议
    第九届计算机与信息处理技术国际学术会议(ISCIPT2024)将于2024年5月24日-26日在西安召开会议。ISCIPT2024将围绕“计算机与信息处理技术”的新研究领域,为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提供一个分享专业经验,扩大专业网络,面对面交流新......
  • leedcode-区域和检索 - 数组不可变
    自己写的,耗时很长classNumArray:def__init__(self,nums:List[int]):#初始化NumArray类,接收一个整数列表nums作为参数self.nums=nums#将传入的nums列表存储为对象的属性defsumRange(self,left:int,right:int)->int:"""......