Press "Enter" to skip to content

“检索增强生成(RAG):从理论到LangChain 实现”

从原始学术论文的理论到使用OpenAI、Weaviate和LangChain进行Python实现

检索增强生成工作流程

自从认识到可以利用专有数据来增强大型语言模型(LLM)以来,关于如何最有效地弥合LLM的通用知识与专有数据之间的差距有了一些讨论。有关这个问题曾经引发了很多辩论,涉及到细调(fine-tuning)和检索增强生成(Retrieval-Augmented Generation, RAG)哪个更适合(剧透:两者都适用)。

本文首先关注RAG的概念,并首先介绍了其理论。然后,它展示了如何使用LangChain进行编排、OpenAI语言模型和Weaviate向量数据库实现一个简单的RAG流水线。

什么是检索增强生成

检索增强生成(Retrieval-Augmented Generation, RAG)是一种概念,它为LLMs提供来自外部知识源的附加信息,使其能够生成更准确和上下文相关的答案,同时减少错误生成。

问题

最先进的LLMs在大量数据上进行训练,以实现存储在神经网络权重(参数内存)中的广泛的通用知识。然而,当要求LLM生成需要其训练数据中未包含的知识(如新的、专有的或领域特定的信息)的补全时,可能会导致错误(错误生成),如下图所示:

ChatGPT对于“总统对于Breyer大法官的评论是什么?”问题的回答

因此,重要的是弥合LLM的通用知识和任何额外上下文之间的差距,以帮助LLM生成更准确和上下文相关的补全,同时减少错误生成。

解决方案

传统上,通过对模型进行微调的方式将神经网络适应于领域特定或专有信息。尽管这种技术是有效的,但它计算密集、昂贵,并且需要技术专长,使得它在适应新信息时不够灵活。

2020年,Lewis等人在论文《检索增强生成用于知识密集型NLP任务》[1]中提出了一种更灵活的技术,称为检索增强生成(RAG)。在这篇论文中,研究人员将生成模型与检索器模块结合起来,以提供来自外部知识源的额外信息,这样可以更容易地更新。

简单地说,RAG对于LLMs就像开卷考试对于人类一样。在开卷考试中,学生被允许带上参考资料,如教科书或笔记,他们可以使用这些参考资料查找相关信息来回答问题。开卷考试的理念是考试侧重于学生的推理能力而不是他们记忆特定信息的能力。

类似地,事实知识与LLM的推理能力分开,存储在一个可以轻松访问和更新的外部知识源中:

  • 参数化知识:在训练时学习并隐含地存储在神经网络权重中。
  • 非参数化知识:存储在外部知识源中,如向量数据库。

(顺便说一句,这个天才比较不是我想出来的。据我所知,这个比较是在Kaggle – LLM科学考试竞赛期间由JJ首次提到的。)

香草RAG工作流程如下所示:

检索增强生成工作流程
  1. 检索:使用用户查询从外部知识源中获取相关上下文。为此,使用嵌入模型将用户查询嵌入到与向量数据库中的其他上下文相同的向量空间中。这允许进行相似性搜索,并返回向量数据库中距离最近的前k个数据对象。
  2. 增强:将用户查询和检索到的其他上下文插入到提示模板中。
  3. 生成:最后,将检索增强型提示输入LLM。

使用LangChain实现检索增强生成

本节在Python中使用OpenAI LLM结合Weaviate向量数据库和OpenAI嵌入模型实现了一个RAG流水线。使用LangChain进行编排。

如果您对LangChain或Weaviate不熟悉,您可能希望查看以下这两篇文章:

LangChain入门:构建基于LLM的应用程序的初学者指南

使用LangChain构建任何基于大型语言模型的Python教程

towardsdatascience.com

Weaviate入门:使用向量数据库进行语义搜索的初学者指南

如何使用向量数据库进行语义搜索、问题回答和生成搜索(Python版,使用OpenAI和…)

towardsdatascience.com

先决条件

确保已安装所需的Python软件包:

  • langchain:用于编排
  • openai:用于嵌入模型和LLM
  • weaviate-client:用于向量数据库
#!pip install langchain openai weaviate-client

此外,在根目录中的.env文件中定义您的相关环境变量。要获取OpenAI API Key,您需要一个OpenAI帐户,然后在API密钥下“创建新的密钥”。

OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

然后,运行以下命令以加载相关环境变量。

import dotenvdotenv.load_dotenv()

准备工作

作为准备步骤,您需要准备一个向量数据库作为外部知识源,其中包含所有附加信息。通过以下步骤来填充该向量数据库:

  1. 收集和加载数据
  2. 切成块的文件
  3. 嵌入和存储块

第一步是收集和加载数据—在这个例子中,您将使用2022年拜登总统的国情咨文演讲作为额外的上下文。原始文本文档可以在LangChain的GitHub存储库中找到。要加载数据,您可以使用LangChain的许多内置的DocumentLoader之一。一个Document是一个带有文本和元数据的字典。要加载文本,您将使用LangChain的TextLoader

import requests
from langchain.document_loaders import TextLoader
url = "https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/docs/modules/state_of_the_union.txt"
res = requests.get(url)
with open("state_of_the_union.txt", "w") as f:
    f.write(res.text)
loader = TextLoader('./state_of_the_union.txt')
documents = loader.load()

接下来,对文档进行分块—由于Document在其原始状态下太长而无法适应LLM的上下文窗口,您需要将其分块成较小的片段。LangChain提供了许多内置的文本分割器来实现这个目的。对于这个简单的例子,您可以使用CharacterTextSplitter,设置chunk_size约为500,chunk_overlap约为50,以保持片段之间的文本连贯性。

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)

最后,嵌入和存储片段—为了实现对文本片段的语义搜索,您需要为每个片段生成向量嵌入,然后将它们连同它们的嵌入一起存储起来。为了生成向量嵌入,可以使用OpenAI的嵌入模型,并使用Weaviate向量数据库进行存储。通过调用.from_documents(),向量数据库会自动填充片段。

from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Weaviate
import weaviate
from weaviate.embedded import EmbeddedOptions
client = weaviate.Client(embedded_options=EmbeddedOptions())
vectorstore = Weaviate.from_documents(
    client=client,
    documents=chunks,
    embedding=OpenAIEmbeddings(),
    by_text=False)

步骤1:检索

一旦向量数据库被填充,您可以将其定义为检索器组件,根据用户查询与嵌入片段之间的语义相似性获取额外的上下文。

retriever = vectorstore.as_retriever()

步骤2:增强

接下来,为了增强提示的额外上下文,您需要准备一个提示模板。提示可以从一个提示模板轻松自定义,如下所示。

from langchain.prompts import ChatPromptTemplate
template = """您是一个问题回答任务的助手。使用以下检索到的上下文片段来回答问题。如果您不知道答案,只需说您不知道。最多使用三个句子,并保持答案简洁。问题:{question} 上下文:{context} 答案:"""
prompt = ChatPromptTemplate.from_template(template)
print(prompt)

步骤3:生成

最后,您可以为RAG流水线构建一个链条,将检索器、提示模板和LLM连接在一起。一旦RAG链条被定义,您就可以调用它。

from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
query = "总统对布雷耶法官说了什么"
rag_chain.invoke(query)

"总统感谢布雷耶法官的服务,并承认他对国家的奉献。总统还提到,他提名凯特安吉·布朗·杰克逊法官作为继任者,继续布雷耶法官的卓越传统。"

您可以在下面看到此特定示例的结果RAG流程图:

检索增强生成工作流程

摘要

本文介绍了RAG的概念,该概念在2020年发表的论文《用于知识密集型NLP任务的检索增强生成》[1] 中提出。在涵盖一些概念背后的理论,包括动机和问题解决之后,本文将其在Python中进行了实现。本文使用OpenAI LLM与Weaviate向量数据库和OpenAI嵌入模型的组合实现了一个RAG流程。流程的协调由LangChain负责。

喜欢这个故事吗?

免费订阅以在我发布新故事时收到通知。

每当Leonie Monigatti发布时得到电子邮件。

每当Leonie Monigatti发布时得到电子邮件。通过注册,您将创建一个VoAGI账户(如果您还没有…)

VoAGI.com

领英推特Kaggle上找到我!

免责声明

本文撰写时,我是Weaviate的开发者倡导者。除了本文外,我还将相同的示例添加到LangChain文档中的Weaviate笔记本中。或者,您可以通过遵循LangChain中的rag-weaviate 模板开始。

参考资料

文献

[1] Lewis, P., et al. (2020). Retrieval-augmented generation for knowledge-intensive NLP tasks. Advances in Neural Information Processing Systems, 33, 9459–9474.

图片

除非另有说明,所有图片均由作者创建。

Leave a Reply

Your email address will not be published. Required fields are marked *