将GPT-3的强大能力与您选择的语料库相结合,生成基于事实的自然语言回答 如果您最近上网,那么您可能已经注意到人们对OpenAI最新的语言模型ChatGPT的兴奋。ChatGPT在许多方面表现出色,包括调试代码和按您要求改写文本的能力。作为GPT-3.5的衍生版本,ChatGPT之所以具有如此丰富的知识,要归功于它在训练过程中看到了互联网的大部分内容,包括Common Crawl语料库和其他数据。 经过几十年的无法记住自己前一刻所说内容的聊天机器人,可以理解人们对ChatGPT这种可以进行对话并创建具有实际智能的模型的兴奋。但是,对于这些大型模型生成的答案的有效性,我们需要保持批判性。特别是对于大型语言模型,它们容易产生幻觉:一开始听起来合理的文本,但经不起仔细审查,以及以事实为基础的东西被虚构出来。 我们在deepset专注于语义搜索引擎,这些引擎通常由抽取式问答模型提供支持。这些模型直接从知识库中提取原始文本片段作为答案,而不是像ChatGPT那样从头生成文本。然而,许多应用程序可以从生成式语言模型的能力中受益。这就是为什么deepset的开源框架Haystack允许您在流程中利用多个GPT模型的原因。通过这种方式,您可以构建一个基于GPT的语义搜索引擎,该引擎使用您自己的数据作为基准,并根据其中包含的信息生成自然语言答案。 您可以将Haystack视为一个全面且高度灵活的工具箱,其主要目标是使构建不同类型的NLP系统变得简单快捷,同时保持透明。除了为OpenAI API提供了一个舒适的入口点外,Haystack还提供了构建GPT端到端NLP系统所需的所有其他组件:矢量数据库、检索模块以及将所有这些元素组合成一个可查询系统的流程。 在本文中,我们将演示如何构建一个生成式问答系统,该系统使用GPT-3“davinci-003”模型以令人信服的自然语言呈现结果。
大型语言模型的出现所有现代语言模型都很庞大吗?这是事实 - 自从Transformer作为基准性能打破各种NLP任务的架构被引入以来,模型的大小一直在增长。但是尽管最大的BERT模型具有3.36亿个参数,OpenAI最大的GPT-3.5模型(ChatGPT基于此)的参数数量是BERT的520倍。 那么GPT将其额外的容量用于什么?通过观察,我们可以得出结论,GPT在理解含义和意图方面非常出色。它可以记住先前对话中讨论过的内容,包括弄清楚您使用诸如“他”或“之前那个”的词指的是什么,并且可以告诉您问题是否合理。所有这些特性都使其具有更高的实际智能性。同时,已经显示出GPT还使用了大量参数来存储事实 - 实际信息,然后将其用于代码调试和回答常识性问题等任务。 但是这里有一个问题:GPT仍然可能犯下严重错误,这些错误更难以检测,因为它在进行对话并使其答案和代码示例看起来正确方面非常出色。12月初,编程问答论坛Stack Overflow暂时禁止使用由ChatGPT生成的解决方案。与此同时,大型语言模型中的幻觉已经引发了一个新的研究领域。 然而,有一种使用GPT模型的方法既更安全又能产生价值。通过将生成模型连接到包含经过策划的领域特定内容的文本数据库中 - 例如产品评论的语料库,金融报告的集合或具有研究论文的数据库 - 您可以将事实准确性与GPT的对话能力相结合。通过Haystack,您可以在短时间内设置此类基于GPT的搜索引擎。该搜索引擎位于您的文本数据库之上,并以对输入查询的自然语言答案的形式返回结果。 ## 不同类型的搜索引擎 语义搜索引擎可以根据返回的答案类型大致分为不同的类型。这些答案可以包括匹配的文档(文档搜索)、答案片段(抽取式问答)或新生成的答案(生成式问答)。
GenerativeQAPipeline:Haystack中的生成式搜索引擎组件对于这些搜索范式,Haystack提供了现成的管道:搜索引擎配置,其中包含最大效率的语言模型的占位符。在本教程中,我们将使用GenerativeQAPipeline。它由检索器(用于查找相关文档)和生成器(用于编写文本)连接在一起。检索器连接到数据库,就像生成器一样,它通常(但不一定)基于Transformer模型。它的任务是根据用户的输入查询从数据库中检索最有可能包含有价值信息的文档。然后,我们的生成模型使用这些文档作为事实基础来编写答案。
如何构建基于GPT-3的搜索引擎在开始之前,让我们快速讨论一下您需要跟随的工具。 先决条件: - 您需要安装Haystack。我们使用版本1.13。 - 要使用OpenAI API,您需要创建一个帐户并生成一个API密钥。请注意,虽然前几次查询是免费的,但一旦达到一定限制,您就需要付费(OpenAI授予您18美元的初始预算,足以完成本教程并玩转该流程)。 - 在这里,我们使用了一个包含18篇有关德国首都柏林的维基百科文章的小型数据集。您当然也可以使用自己的数据集。 由于我们的嵌入和答案是通过OpenAI API提供的,因此在本指南中不需要使用GPU。但是,如果您想尝试不同的模型,我们建议您在Colab笔记本中工作并激活GPU(在“Runtime -> Change runtime type”下)。
转换和预处理在可以设置管道之前,您需要对数据进行预处理并将其添加到文档存储(或数据库)中。Haystack中有许多文档存储的选项。本教程使用FAISS,它是一个面向向量优化的数据库。 在将数据提供给文档存储之前,您需要将其转换为正确的格式。DocumentStore期望以Haystack数据类型的Document形式提供数据,它是一种字典数据类型,将信息存储为一组相关字段(例如文档文本和其元数据)。convert_files_to_docs函数从目录中检索文件并将其转换为Haystack文档。如果您使用的是维基百科的文章,可以使用clean_wiki_text清理函数,它会删除一些特定于维基百科的样板文本。
ini复制代码python from haystack.utils import convert_files_to_docs, clean_wiki_text docs = convert_files_to_docs(dir_path=DOC_DIR, clean_func=clean_wiki_text, split_paragraphs=True) ```
根据您自己数据的格式,您可能需要对预处理采取稍微不同的过程。使用Haystack,您可以从网站中提取数据,或将不同的文件格式(如pdf、txt或docx文件)转换为文本。请查看我们的预处理教程和FileConverter的文档页面,了解更多信息。 许多文件,包括有关热门主题的维基百科文章,可能非常长。您需要确保数据库中的文档长度适合嵌入模型正确捕捉其含义。为此,您可以使用PreProcessor将它们拆分为较短的文本片段。我们建议每个片段的长度为100个标记,并且有3个重叠的标记,以确保不丢失任何信息。
ini复制代码python from haystack.nodes import PreProcessor preprocessor = PreProcessor( clean_empty_lines=True, clean_whitespace=True, clean_header_footer=False, split_by="word", split_length=100, split_overlap=3, split_respect_sentence_boundary=False, ) processed_docs = preprocessor.process(docs)
这些处理过的文档是什么样子的呢?让我们看一下其中一个示例:
css复制代码markdown
每个文档都被转换为Document类的对象,这是一个字典,不仅包含文档的文本,还包含一些自动生成的元数据,例如文本来自哪个文件。 ## 初始化文档存储 现在是时候设置文档存储了 - 例如,面向向量优化的FAISS数据库。在初始化文档存储时,您需要知道检索器的文档向量嵌入维度 - 它为每个文档生成的内部表示。由于您将使用来自OpenAI的高维文本嵌入模型text-embedding-ada-002,因此需要将向量的embedding_dim设置为1536。
ini复制代码python from haystack.document_stores import FAISSDocumentStore document_store = FAISSDocumentStore(faiss_index_factory_str="Flat", embedding_dim=1536)
现在,删除数据库中的任何现有文档,并添加之前生成的预处理文档。
scss复制代码python document_store.delete_documents() document_store.write_documents(processed_docs)
请注意,目前数据库仅包含纯文本文档。为了添加高维向量嵌入 - 这是语言模型可以理解并用于语义搜索的每个文档的表示 - 您需要为检索器设置模型。 ## 检索器 检索器是模块,用于将您的查询与数据库中的文档匹配,并检索出最有可能包含答案的文档。检索器可以基于关键字(如tf-idf和BM25),也可以通过使用Transformer生成的文本向量来编码语义相似性。在后一种情况下,检索器还用于索引数据库中的文档 - 也就是将它们转换为检索器可以搜索和比较的高维嵌入。 您将使用OpenAI最新的检索模型text-embedding-ada-002。在Haystack中初始化它时,您需要提供您的OpenAI API密钥。 ``` python from haystack.nodes import EmbeddingRetriever retriever = EmbeddingRetriever( document_store=document_store, embedding_model="text-embedding-ada-002", batch_size=32, api_key=MY_API_KEY, max_seq_len=1024 )
复制代码
设置检索器时,将其直接连接到文档存储。现在,您可以使用update_embeddings方法将文档存储中的原始文档转换为检索模型可以搜索和比较的高维向量。
scss复制代码python document_store.update_embeddings(retriever) 生成器
现在,您准备初始化GPT模型,以便为您生成文本。OpenAIAnswerGenerator节点可以使用四种不同的GPT模型。您可以使用性能最高的GPT-3.5模型text-davinci-003。 ``` python from haystack.nodes import OpenAIAnswerGenerator generator = OpenAIAnswerGenerator(api_key=MY_API_KEY, model="text-davinci-003", temperature=.5, max_tokens=30)
复制代码
我们建议将max_tokens参数从默认值13增加到30,以便GPT模型可以生成更长的序列。我们还建议将温度设置为0.5(默认值为0.2),这样模型在生成答案时会具有稍微更大的自由度。温度越低,模型对基础源文本保持的忠诚度越高。 ## 管道 现在,您将所有GPT搜索引擎的单独元素设置好了,可以将它们传递给生成式QA管道。
ini复制代码python from haystack.pipelines import GenerativeQAPipeline gpt_search_engine = GenerativeQAPipeline(generator=generator, retriever=retriever)
好了!您的GPT驱动的搜索引擎已经准备好进行查询了。 ## 查询管道 现在,您可以向系统提出关于柏林(或任何其他主题,如果您的数据集涉及其他主题)的一些一般性问题。除了查询本身外,您可以向搜索引擎传递一些参数,例如检索器应该传递给生成器的文档数量以及应该生成的答案数量(都称为“top_k”)。
csharp复制代码python query = "柏林以什么而闻名?" params = {"Retriever": {"top_k": 5}, "Generator": {"top_k": 1}} answer = gpt_search_engine.run(query=query, params=params)
要打印由管道生成的答案,请导入Haystack的便捷print_answers函数。该函数允许您确定在打印答案时要显示的详细程度。将其设置为minimum将仅打印答案字符串。那么搜索引擎对上述问题的答案是什么?
ini复制代码python from haystack.utils import print_answers print_answers(answer, details="minimum")
css复制代码markdown >>> 查询:柏林以什么而闻名? 答案: [{ 'answer': '柏林以其多样化的文化、夜生活、当代艺术和高品质生活而闻名。'}]
请注意,此答案是从零开始生成的:它不是来自任何维基百科文章的引用,而是根据其中的内容编写的答案。 生成的答案是上下文相关的 还记得我们之前说过的吗?GPT-3模型根据接收到的文档生成答案。您现在可以通过仅运行生成器(而没有检索器)来测试它。但是,您无法完全不使用任何文档运行它,因此需要向其传递一个单独的片段。如果使用上面打印出来的有关地铁的片段,会发生什么呢? python generator.predict("柏林以什么而闻名?", documents=[processed_docs[0]]) markdown >>> 查询:柏林以什么而闻名? 答案: [{ 'answer': '柏林地铁。'}] 系统回答柏林以其地下铁路而闻名,因为它只有来自那一个文档的知识。 现在,回到完整版本的搜索引擎 - 即已摄取整个数据集(例如所有关于柏林的维基百科文章)的版本 - 并向其提出一些更多问题,以更好地了解您的搜索引擎的运作方式。 示例1 查询:何时是参观柏林的最佳时间? 答案: [{ 'answer': '柏林是全年都适合参观的好地方,但最佳时间是夏季的6月至8月。'}] 示例2 查询:柏林人是否有方言? 答案: [{ 'answer': '是的,柏林人有方言,它是勃兰登堡方言的一种变体。'}] 示例3 查询:给我介绍一些柏林有趣的建筑。 答案: [{ 'answer': '柏林电视塔是德国柏林的一座电视塔。它是柏林最高的建筑,高度为1207英尺。'}] 示例4 查询:电视塔是如何建造的? 答案: [{ 'answer': '电视塔是由一个建筑师团队于1965年至1969年间建造的。这座塔在短短的53个月内建成,创下了记录。'}] GPT搜索引擎的一个有趣事实是,它并不总是返回相同的答案。当多次使用相同的查询提示时,它将尝试生成不同的答案。当我们自己完成本教程时,我们的模型产生了一个严重的幻觉,在多次接收相同问题后。请看下面的答案: 示例5 查询:电视塔是如何建造的? 答案: [{ 'answer': '电视塔是由苏联军队建造的,他们将内容点燃,将塔变成了一个临时的烟囱。'}] 此答案(排名较低于正确答案)非常荒谬,令人发笑。但它应该提醒我们,生成模型的输出,即使看起来像是一个形式良好的答案,也可能完全是虚构的,应进行事实检查。 与抽取式QA进行比较 与生成式QA相比,基于抽取式QA的搜索引擎从响应查询的自然语言中直接提取与数据库中的文档相关的答案。但由于抽取式QA系统是从文档文本本身中提取答案的,因此与GPT搜索引擎相比,它有一些限制。 与GPT搜索引擎相比,抽取式QA模型无法以与问题的隐含方式回答问题,无法生成对话中的元素,也无法像GPT模型那样从不同的文本中聚合信息。在最后一个示例中,GPT肯定了柏林是一个适合泡夜店的好地方,然后继续列举了一些著名夜店的例子。 但当向抽取式QA模型提出相同的问题时,它只能以隐式方式回答该问题,通过提取它认为与查询最相关的文本片段。以下是由这种抽取式QA搜索引擎返回的两个答案,包括它们提取自的段落(“context”): 示例1 查询:柏林是一个适合泡夜店的好地方吗? 答案: [{ 'answer': '在整个城市范围内都会举行各种庆祝活动。德国的波普尔(Germans)参与者经常用一杯起泡酒来祝贺新年', 'context': '在整个城市范围内都会举行各种庆祝活动。德国的波普尔(Germans)参与者经常用一杯起泡酒来祝贺新年。n' '柏林拥有44个剧院和'}, { 'answer': '柏林的俱乐部文化是一种顶级夜生活场所', 'context': '尤其是在西部和中欧,柏林的俱乐部文化是一种顶级夜生活场所。1989年柏林墙倒塌后,许多高'}] 在何种情况下使用生成式QA或抽取式QA? 与抽取式QA相比,生成式QA的优势很明显:它具有更好的对话能力,以自然语言生成良好格式的句子,并且可以从多个来源中聚合知识以生成单个答案。但正如我们所看到的,它也有一些缺点。您应该在以下情况下考虑使用抽取式而不是生成式QA: 当您想使用更小的开源模型时。正如我们所见,GPT模型非常庞大,一旦达到一定限制,您需要付费使用API。另一方面,Hugging Face模型库托管了数千个免费下载的开源预训练模型。 当您希望了解模型的信息来源时。抽取式模型不会产生幻觉。当然,这些模型也可能返回错误的答案。但是通过检查它们提取自的上下文,这些错误要容易检测得多。 当您希望在下游任务中使用答案时。抽取式问答最流行的应用之一是信息提取系统。这些系统不需要生成式搜索引擎的对话能力,而是需要一种可以从大型语料库中快速而可靠地提取事实信息的搜索功能。 Haystack:构建搜索引擎的主要框架 生成式QA、抽取式QA、翻译、摘要等等:Haystack使您可以使用最新的热门架构构建最适合解决特定问题的系统。 我们以应用为导向的NLP方法为您提供了模块化构建块,以在最短时间内设置自己的系统。查看Haystack存储库以了解更多信息,或查看我们的文档。
相关文章
猜你喜欢