Python实战教程:基于维基百科数据的开放域问答机器人DrQA实现与效果展示
DrQA 基于维基百科数据的开放域问答机器人实战教程
DrQA 是一个基于维基百科数据的开放域问答系统,它由检索器和阅读器组成。其中检索器用于从海量的文本(例如维基百科)中获得相关的文章;阅读器用于从文章中获得相应的答案。
官方介绍: DrQA 是一个应用于开放域问答的阅读理解系统。特别是,DrQA 的目标是“大规模机读”( MRS )。在这个设定中,我们在可能非常大的非结构化文档集中搜索问题的答案。因此,系统必须将文档检索(查找相关文档)的挑战与机器对文本的理解(从这些文档中识别答案)的挑战相结合。
我们使用 DrQA 的实验侧重于回答 factoid 问题,同时使用 Wikipedia 作为文档的独特知识源。维基百科是一个非常适合大规模,丰富,详细信息的来源。为了回答任何问题,必须首先在超过 500 万个文章中检索可能相关的文章,然后仔细扫描它们以确定答案。
请注意,DrQA 将 Wikipedia 视为一个通用的文章集合,并不依赖于其内部知识结构。因此,DrQA 可以直接应用于任何文档集合。
数据集:维基百科
框架:PyTorch
版本:PyTorch torch-0.3.0
论文:Reading Wikipedia to Answer Open-Domain Questions
项目: https://github.com/facebookresearch/DrQA
系统架构:
实战: 交互模式下提问:
where is stanford university
可以看到检索到的文档是 Stanford University,问题的答案是:浅绿色标注的部分,答案非常的精准。
交互模式下提问:
where is Barack Hussein Obama from
交互模式下提问: 回答不是很精准,我其实想问的是奥巴马来自哪里。答案返回的是奥巴马在哪里,奥巴马在华盛顿,不过也还是相当不错。
who is Donald Trump 这个问题并没有找到准确的答案,虽然文档是相关的。
原文链接: http://tf86.com/2018/07/20/reading-wikipedia-to-answer-open-domain-questions/
Python实战教程:基于维基百科数据的开放域问答机器人DrQA实现与效果展示
我最近在搞一个基于维基百科的问答机器人,用的就是Facebook Research开源的DrQA框架。这玩意儿挺有意思的,核心就是把信息检索和阅读理解结合起来。
简单说下实现思路:先用文档检索器从维基百科里找到相关文章,然后用阅读理解模型从文章里提取答案。我用的TF-IDF做检索,阅读理解模型是预训练的BERT。
import wikipedia
from rank_bm25 import BM25Okapi
from transformers import BertTokenizer, BertForQuestionAnswering
import torch
class SimpleDrQA:
def __init__(self):
self.tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
self.model = BertForQuestionAnswering.from_pretrained('bert-base-uncased')
self.documents = []
def fetch_wiki_data(self, query, num_pages=3):
"""从维基百科获取相关页面"""
search_results = wikipedia.search(query, results=num_pages)
for title in search_results:
try:
page = wikipedia.page(title, auto_suggest=False)
self.documents.append(page.content)
except:
continue
def retrieve_documents(self, question, top_k=3):
"""用BM25检索相关文档段落"""
tokenized_docs = [doc.split() for doc in self.documents]
bm25 = BM25Okapi(tokenized_docs)
tokenized_query = question.split()
scores = bm25.get_scores(tokenized_query)
top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:top_k]
return [self.documents[i] for i in top_indices]
def extract_answer(self, question, context):
"""用BERT模型提取答案"""
inputs = self.tokenizer.encode_plus(
question, context,
add_special_tokens=True,
return_tensors='pt',
max_length=512,
truncation=True
)
with torch.no_grad():
outputs = self.model(**inputs)
start_scores = outputs.start_logits
end_scores = outputs.end_logits
start_idx = torch.argmax(start_scores)
end_idx = torch.argmax(end_scores) + 1
answer_tokens = inputs['input_ids'][0][start_idx:end_idx]
answer = self.tokenizer.decode(answer_tokens)
return answer
def ask(self, question):
"""主问答接口"""
# 获取维基数据
self.fetch_wiki_data(question)
# 检索相关文档
contexts = self.retrieve_documents(question)
# 从最佳文档中提取答案
best_answer = ""
best_score = -1
for context in contexts:
answer = self.extract_answer(question, context[:2000]) # 限制上下文长度
# 这里可以加个置信度评分
if answer and len(answer) > 5: # 简单过滤短答案
best_answer = answer
break
return best_answer
# 使用示例
if __name__ == "__main__":
qa = SimpleDrQA()
question = "Who invented Python programming language?"
answer = qa.ask(question)
print(f"Q: {question}")
print(f"A: {answer}")
跑起来效果还行,对事实类问题回答得比较准。比如问"Python谁发明的",它能从维基百科里找到Guido van Rossum。不过处理复杂问题或者需要推理的时候就不太行了。
这只是一个简化版,真正的DrQA系统要复杂得多,包括更好的文档处理、多段落答案融合、置信度校准这些。但基本框架就是这样:检索+阅读理解。
建议试试用更先进的检索模型和更大的预训练模型来提升效果。
这个开源有一阵子了。OpenDomainQA 这块其实很复杂。

