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

2023年11月16日 由 alex 发表 1413 0

3

检索增强生成工作流程


什么是检索增强生成


检索增强生成(RAG)是为LLM提供来自外部知识源的额外信息的概念。这允许它们生成更准确和有上下文的答案,同时减少幻觉。


问题


最先进的LLM在大量数据上进行训练,以实现存储在神经网络权重(参数化记忆)中的广泛知识。然而,当提示一个LLM生成一个需要知识的输出,而这些知识没有包含在其训练数据中,如更新的、专有的或特定领域的信息,可能会导致事实上的不准确(幻觉),如以下截图所示:


4


因此,弥合LLM的通用知识与任何附加背景之间的差距是非常重要的,以帮助LLM产生更准确和具有上下文的补全,同时减少幻觉。


解决方案


传统上,神经网络通过微调模型来适应领域特定或专有信息。尽管这种技术效果显著,但它也非常耗费计算资源、昂贵,并需要技术专长,使得它对不断发展的信息的适应性不够灵活。


在2020年,Lewis等人在论文《检索增强生成》中提出了一种更为灵活的技术:检索增强生成(Retrieval-Augmented Generation, RAG),用于知识密集型自然语言处理任务[1]。在这篇论文中,研究人员将生成模型和检索模块结合起来,以从可以更容易更新的外部知识源中提供额外的信息。


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


同样地,事实性知识从LLM的推理能力中剥离出来,并存储在一个外部知识源中,这个知识源可以轻松访问和更新:


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


普通RAG工作流程如下所示:


5


  1. 检索:用户查询用于从外部知识源检索相关上下文。为此,用户查询通过一个嵌入模型被嵌入到与向量数据库中额外上下文相同的向量空间中。这允许执行一个相似性搜索,并从向量数据库中返回最接近的前k个数据对象。
  2. 增强:用户查询和检索到的额外上下文被填充到一个提示模板中。
  3. 生成:最后,增强检索的提示被输入到LLM中。


使用LangChain的检索增强生成实现


先决条件


请确保你已经安装了所需的Python软件包:


  • langchain 用于协调
  • openai 用于嵌入模型和LLM
  • weaviate-client 用于向量数据库


#!pip install langchain openai weaviate-client


此外,在你的根目录中的一个.env文件内定义你的相关环境变量。要获得一个OpenAI API密钥,你需要一个OpenAI账号,然后在API密钥下选择“创建新的密钥”。


OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"


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


import dotenv
dotenv.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()


接下来,将你的文件分块——因为文件在其原始状态下太长,无法完整地适应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
)


第一步:检索


向量数据库被填充后,你可以将其定义为检索器组件,该组件根据用户查询与嵌入块之间的语义相似度抓取额外的上下文。


retriever = vectorstore.as_retriever()


第二步:增强


接下来,为了用额外的上下文增强提示信息,你需要准备一个提示模板。如下所示,可以很容易地从一个提示模板定制提示。


from langchain.prompts import ChatPromptTemplate
template = """You are an assistant for question-answering tasks. 
Use the following pieces of retrieved context to answer the question. 
If you don't know the answer, just say that you don't know. 
Use three sentences maximum and keep the answer concise.
Question: {question} 
Context: {context} 
Answer:
"""
prompt = ChatPromptTemplate.from_template(template)
print(prompt)


第三步:生成


最后,你可以为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 = "What did the president say about Justice Breyer"
rag_chain.invoke(query)


"The president thanked Justice Breyer for his service and acknowledged his dedication to serving the country. 
The president also mentioned that he nominated Judge Ketanji Brown Jackson as a successor to continue Justice Breyer's legacy of excellence."


你可以在下面看到针对这个特定示例的结果RAG管道图。


6


总结


本文讨论了 RAG(检索增强生成)的概念,该概念在2020年的论文《知识密集型NLP任务中的检索增强生成》中被提出。在介绍了一些背后的理论,包括动机和问题解决方案之后,本文将其在Python中的实现转换了一遍。本文实现了一个RAG流水线,结合了OpenAI的大型语言模型(LLM)、Weaviate向量数据库以及OpenAI嵌入模型。LangChain用于协调工作。

文章来源:https://medium.com/towards-data-science/retrieval-augmented-generation-rag-from-theory-to-langchain-implementation-4e9bd5f6a4f2
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消