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实现与效果展示

2 回复

我最近在搞一个基于维基百科的问答机器人,用的就是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 这块其实很复杂。

回到顶部