其他
摆脱传统的字面匹配,RocketQA语义检索效果YYDS
文本搜索
传统的文本搜索系统通过统计学的方法对文本进行特征度量,但这仅考虑了文本间的字面特征,无法充分利用文本中蕴含的丰富的语义知识。因此,在信息搜索领域,如果能够通过编码器将文本映射到具体的语义空间,每段文本对应在该空间下的向量,那么信息搜索问题便可转化为在给定空间下向量间的最近邻搜索问题。例如,DPR(dense passage retriever)通过将文本从高维空间下的稀疏向量,转化为富含语言知识和文本信息的低维空间下的稠密向量,来实现文本的近似搜索,在问答系统、搜索系统等多个领域得到了应用。
预训练语言模型
BERT 系列模型在预训练阶段从大量未标注文本中学习到了丰富的语义知识。较之于自然语言处理领域发展阶段的第二范式,即通过设计模型的拓扑结构来提升模型在某个领域的性能,预训练语言模型(PLM)保证了在较少数据、较低算力的情况下,仅通过对特定任务的微调,便可实现在该任务下 state-of-the-art 的性能。模型蕴含的语义知识使得下游任务不再需要去从头设计一套模型结构并使用大量的任务相关数据重新训练。
Dual encoder 架构使用两个 BERT 模型,query model 和 context model 分别将问题和对应的待查找文本映射到768维的语义空间。为保证在实际检索过程中的准确度,该模型的训练策略为:尽可能的使问题与对应的文本间的距离接近,而与无关的文本间的距离拉远。Cross encoder 则使用一个 BERT 模型同时对问题和文本进行编码。
戳官方 Repo 了解 RocketQA 详情:
系统架构工具
def __init__(self,model_name="zh_dureader_de",use_Cuda=True,device_Id=0,batch_Size=32,*args,**kwargs):
super().__init__(*args, **kwargs)
self.model = rocketqa.load_model(model=model_name,use_cuda=True,device_id=device_Id,batch_size=batch_Size)
@requests(on="/index")
def encode_passage(self,docs:DocumentArray,**kwargs):
embeddings = self.model.encode_para(para=docs.texts)
docs.embeddings = [embedding for embedding in embeddings]
@requests(on="/search")
def encode_query(self,docs,**kwargs):
print("retriever is working......")
start = time.time()
for doc in docs:
generator_temp = self.model.encode_query(query=[doc.text])
for temp in generator_temp:
doc.embedding = temp
end = time.time()
print("retrieve time: ",end-start,"s")
def __init__(self,model_Name="zh_dureader_ce",use_Cuda=True,device_Id=0,batch_Size=32,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model = rocketqa.load_model(model=model_Name,use_cuda=True,device_id=device_Id,batch_size=batch_Size)
@requests(on="/search")
def rerank(self,docs,**kwargs):
print("reranker is working......")
print("召回结果排序中......")
start = time.time()
for doc in docs:
str_list = []
for m in doc.matches:
str_list.append(m.text)
doc.matches = []
scores = []
score_generator = self.model.matching(query=[doc.text]*len(str_list),para=str_list)
for g in score_generator:
scores.append(g)
scores = np.array(scores).argsort()
doc.matches.append(Document(text=str_list[scores[-1]]))
doc.matches.append(Document(text=str_list[scores[-2]]))
doc.matches.append(Document(text=str_list[scores[-3]]))
end = time.time()
print("rerank time:",end-start,"s")
if order == 'index':
if Path('./workspace').exists():
print('./workspace exists, please deleted it if you want to reindexi')
return 0
data_path = sys.argv[2]
if data_path is None:
print("No data_path!")
index(data_path)
elif order == 'query':
query()
def index(path):
with test_flow:
test_flow.index(inputs=read_file(path), show_progress=True)
def query():
with test_flow:
while(True):
query = input("请输入查询选项:")
if query == "exit":
break
query = Document(text=query)
docs = test_flow.search(inputs=query)
matches = docs[0].matches
print("搜索答案为:")
ids = 1
for match in matches:
print("推荐答案排行,NO.",ids)
print(match.text)
ids = ids + 1
效果展示
运行 python wow.py index data_path 来建立索引库:
总结
本文关注稠密文本召回 DPR(Dense Passage Retrieval)技术在文本搜索和人机问答领域的应用,使用百度飞桨框架和 RocketQA 模型,基于 JINA 全家桶构建了含有 Retrieve、Rerank 两个阶段的文本召回系统。本项目在建库和检索阶段,通过使用预训练语言模型蕴含的丰富的语义知识来对输入文本和输入问题进行编码,能够免去传统的手动标记或词频统计等过程。在后续,本项目使用近似最近邻搜索算法来完成召回,使用交互式模型来完成最终的打分。该文本召回系统将搜索过程简洁化,同时提供了人工智能技术在落地应用方面的典型范例。
此外,飞桨自然语言处理模型库 PaddleNLP 也基于 RocketQA 等前沿模型搭建了完整的检索系统、问答系统,亲测好用。传送门:
https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/experimental/pipelines https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/neural_search