·

一文带你了解大模型的RAG(检索增强生成) | 概念理论介绍+ 代码实操(含源码)

发布时间:2024-08-27 03:29:26阅读量:200
转载请注明来源

针对大型语言模型效果不好的问题,之前人们主要关注大模型再训练、大模型微调、大模型的Prompt增强,但对于专有、快速更新的数据却并没有较好的解决方法,为此检索增强生成(RAG)的出现,弥合了LLM常识和专有数据之间的差距。

今天给大家分享的这篇文章,将介绍RAG的概念理论,并带大家利用LangChain进行编排,OpenAI语言模型、Weaviate 矢量数据库(也可以自己搭建Milvus向量数据库)来实现简单的 RAG 管道。

什么是RAG?

RAG的全称是Retrieval-Augmented Generation,中文翻译为检索增强生成。它是一个为大模型提供外部知识源的概念,这使它们能够生成准确且符合上下文的答案,同时能够减少模型幻觉。

知识更新问题

最先进的LLM会接受大量的训练数据,将广泛的常识知识存储在神经网络的权重中。然而,当我们在提示大模型生成训练数据之外的知识时,例如最新知识、特定领域知识等,LLM的输出可能会导致事实不准确,这就是我们常说的模型幻觉。如下图所示:

因此,弥合大模型的常识与其它背景知识之间的差距非常重要,以帮助LLM生成更准确和更符合背景的结果,同时减少幻觉。

解决方法

传统的解决方法是通过微调神经网络模型来适应特定领域的专有信息。尽管这种技术很有效,但它属于计算密集型的,并且需要技术专业知识,使其难以灵活地适应不断变化的信息。

2020 年Lewis等人,在知识密集型 NLP 任务中,提出了一种更灵活的技术,称为检索增强生成(RAG)[参考论文:https://arxiv.org/abs/2005.11401]。在本文中,研究人员将生成模型与检索器模块相结合,以提供来自外部知识源的附加信息,并且这些信息可以很方便的进行更新维护。

简单来说,RAG 对于LLM来说就像学生的开卷考试一样。在开卷考试中,学生可以携带参考材料,例如课本或笔记,可以用来查找相关信息来回答问题。开卷考试背后的想法是,测试的重点是学生的推理能力,而不是他们记忆特定信息的能力。

同样,事实知识与LLM的推理能力分离,并存储在外部知识源中,可以轻松访问和更新:

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

一般的 RAG 工作流程如下图所示:

「检索(Retrive)」 根据用户请求从外部知识源检索相关上下文。为此,使用嵌入模型将用户查询嵌入到与向量数据库中的附加上下文相同的向量空间中。这允许执行相似性搜索,并返回矢量数据库中最接近的前 k 个数据对象。

「增强(Augment)」 用户查询和检索到的附加上下文被填充到提示模板中。

「生成(Generate)」 最后,检索增强提示被馈送到 LLM。

LangChain实现RAG

上面介绍了RAG产生和工作原理,接下来将展示如何使用LangChain,结合 OpenAI LLM 、Weaviate 矢量数据库在 Python 中实现 RAG Pipeline。

基础环境准备

1、安装所有需要依赖的相关python包,其中包括用于编排的langchain、大模型接口openai、矢量数据库的客户端 weaviate-client。

pip install langchain openai weaviate-client

2、申请OpenAI的账户,要获取 OpenAI API 密钥,如下图所示:

3、在项目根目录创建.env文件,用来存放相关配置文件,如下图所示。

4、在main目录中,加载配置文件信息,这里用到了python-dotenv包。

向量数据库

接下来,你需要准备一个矢量数据库作为保存所有附加信息的外部知识源。该矢量数据库是通过以下步骤填充的:1)加载数据;2)数据分块;3)数据块存储。

「加载数据」:这里选择了一篇斗破苍穹的小说,作为文档输入 。文档是txt文本,要加载文本这里使用 LangChain 的 TextLoader。

from langchain.document_loaders import TextLoader
loader = TextLoader('./斗破苍穹.txt')
documents = loader.load()

「数据分块」:因为文档在其原始状态下太长(将近5万行),无法放入大模型的上下文窗口,所以需要将其分成更小的部分。LangChain 内置了许多用于文本的分割器。这里使用 chunk_size 约为 1024 且 chunk_overlap 为128 的 CharacterTextSplitter 来保持块之间的文本连续性。

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=1024, chunk_overlap=128)
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
)

RAG实现

「第一步:数据检索」 将数据存入矢量数据库后,就可以将其定义为检索器组件,该组件根据用户查询和嵌入块之间的语义相似性获取相关上下文。

retriever = vectorstore.as_retriever()

「第二步:提示增强」 完成数据检索之后,就可以使用相关上下文来增强提示。在这个过程中需要准备一个提示模板。可以通过提示模板轻松自定义提示,如下所示。

from langchain.prompts import ChatPromptTemplate
template = """你是一个问答机器人助手,请使用以下检索到的上下文来回答问题,如果你不知道答案,就说你不知道。问题是:{question},上下文: {context},答案是:
"""
prompt = ChatPromptTemplate.from_template(template)

「第三步:答案生成」 利用 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 = "萧炎的表妹是谁?"
res=rag_chain.invoke(query)
print(f'答案:{res}')

总的来说,RAG的生成过程如下图所示:

总结

本文介绍了 RAG 的概念及其背后的一些理论,本文通过Python、LangChain将其实现,在此过程中使用了 OpenAI的ChatGPT接口(可以自己搭建chatGLM3)、Weaviate矢量数据库(可以自己搭建Milvus )、OpenAI 嵌入模型实现了 RAG 管道。


原文链接:https://blog.csdn.net/m0_59596990/article/details/135310933

0 人喜欢

评论区

暂无评论,来发布第一条评论吧!

弦圈热门内容

Linus Kramer之拓扑群notes:Locally Compact Groups and Lie Groups

本notes顾名思义是关于局部紧致群和李群的,开篇先从最基本的拓扑群开始讲起,我当初就是靠这些内容补充拓扑群相关的基础的。为啥没有进一步往下学这个notes,一来是我不需要,二来是这个notes是残缺的,只写到第二章就没有了😅,即只有下图中画圈的部分。目前这本notes在网上已经绝迹,我今天倒是找到另一份残缺版,不过标题改成了Locally Compact Groups,内容倒是比之前的残缺版多一些。既然是属于稀缺资源,因此本notes除了学习价值以外,还有一定的收藏价值,因此我在此将该notes的两个版本都分享给有需要的人。PS:作者不再提供附件下载。

Charles Rezk拓扑学notes:Compactly Generated Spaces

本notes主要讲的是拓扑学中-spaces与-Hausdorff space的相关概念,之所以保存这份notes是因为我当初学习高阶范畴的时候,刚好需要用到这些概念。比如说,无穷范畴的定义就需要用到他们:A topological category is a category which is enriched over , the category of compactly generated (and weakly Hausdorff) topological spaces. The category of topological categories will be denoted by .而抛开它与无穷范畴的联系,仅仅考虑它在拓扑学本身的意义,我觉得这也是本拓扑学方面有趣的notes,不仅是因为有趣的概念如-空间、-豪斯多夫空间,还有紧致生成的空间,还包括一些有趣的结论。总之,对高阶范畴、或者更深入的拓扑学感兴趣的人,可以看看。PS:作者不再提供附件下载。

加载失败

陈省身微分几何经典教材《微分几何讲义》

一说到陈省身经典的微分几何教材《微分几何讲义》,就勾起我很多回忆。这本书是我初三时期入门微分几何的教材,虽然相比于Loring W Tu微分几何经典入门教材:An Introduction to Manifolds的教材没那么好理解,但是却比王幼宁的《微分几何讲义》更加的友好。我当时真的挺喜欢陈省身的教材的,虽然以我如今的水平看,当时的我并没有真正的看懂这本书,但这是我微分几何的启蒙书。我人生中看的第一本微分几何的书是王幼宁的《微分几何讲义》,但是我虽然很有兴趣,但却没能读下去,因为开篇就直接看不懂。而陈省身的《微分几何讲义》至少我能读下去,不至于开篇就直接来那么难的东西,我也是靠这本教材知道了很多微分几何的重要概念。我到高一还在看陈省身这本教材,直到后来高二为了读懂Jürgen Jost黎曼几何与几何分析教材:Riemannian Geometry and Geometric Analysis,我不得不看自己当时嫌弃的Loring W Tu的An Introduction to Manifolds,才打开了新世界,原来还是这么好看的微分几何入门教材,Loring W Tu的书确实比陈 ...

初中生如何自学数学?

知乎提问:我想这样子自学数学?纯兴趣爱好。我想从高中数学开始自学,用教材帮这本教辅书自学。然后学完高中后整理一下初等数学的知识。是不是就可以开始自学高数了?现在我打开高数好多证明题和不等式都不会做。然后把高等数学,数学分析,线性代数,高等代数,概率论与数理统计,复变函数与积分变换,实分析,复分析,泛函分析,抽象代数,代数几何,长微分方程,偏微分方程,微分几何都学完。大致就是这样的人生规划,初等数学学透了是不是就可以理解学习高等数学了?我的回答:我觉得按部就班的按顺序学习没多大意思,我初三的时候是先把导数、积分这些高中最难但却是微积分最基本的概念“学懂”,然后才学别的比较基础的概念如集合。原因无它,就是因为当时这些更感兴趣。因此与其纠结于把什么学透了再来理解什么,不如换成先尝试理解什么,理解不了再来理解什么。我初三的时候除了学会了导数、积分、加速度这些高中数学、物理的概念,但也没太过深入。顶多再学了个正余弦定理拿来应付中考。我从初中开始养成的习惯就是,对什么感兴趣就直接学它,学不懂再看其他的,因此我初中的时候还直接学了范畴的定义(只是看懂了表面的定义)。直到初三升高一的假期,我才买了高中 ...

点集拓扑求救

以及有没有推荐的点集拓扑教材

我翻译了Wiki、nLab、Stack Project的部分条目,以及一些教材中的定义,全放到了数学百科中

一两个月前,网站浏览人数比较少的时候,我也比较空闲,因此花了一些时间翻译了国外Wiki、nLab、Stack Project的部分条目,同时,我还将一些教材中的定义以及少部分自己写的英文notes中的定义翻译成了中文。然后我将这些翻译好的内容全都放进了数学百科中。现在因为新建了好几个子圈子,我也陆续将这些词条分门别类放进了不同的子圈。我之所以会翻译这些东西,一来是因为中文互联网的数学资源属实是过于稀缺了,每个学数学的人想要更好的发展都离不开英语这一关。但是总有人对数学感兴趣却英语不好,这也意味着有一部分人会欣赏不了英文的一些美妙的数学。二是因为词条是可以插入到文章里的,这会方便看文章的人快速查看相关术语的意思,所以在弦圈里多放些词条不仅有利于网站内容更丰富,而且能让学习交流变得更加顺畅。下面我整理一下我具体翻译了哪些词条,其实也不是很多。主要问题是翻译数学内容本身并不耗时间,真正耗时间的是输入Latex代码😅,即便我写数学好几年了,Latex也早就熟练运用,但我还是感觉在写数学的过程中Latex的输入占用了过多时间。层预层局部赋环空间赋环空间概形凸秩-可除群函数向量向量空间反同态 ...

失业、分配不平衡和结构性转变:人还能否“卷”过AI

白果/文 人类对AI,尤其是AI冲击社会就业与收入分配的担忧,其实由来已久。20世纪70年代至今,我们至少经历过三波AI发展的大潮。当一轮轮潮水退去,人们发现人工智能似乎并没有想象的那么厉害,不禁有了更自信乐观的理由。然而,这一轮AI的发展速度和能力似乎不可同日而语。ChatGPT(Generative Pre-Trained Transformer)及各种生成式AI工具的出现,使人类可以用自然语言的方式给计算机发出指令,这在很大程度上打破了某些专业壁垒。虽然当前AI生成内容在准确度、独创性上还有待提高,但替代人工、降本增效的能力显而易见。那么,此轮AI发展将冲击哪些职业,又是否会如乐观者期待的那样,带来大量新的工作?在尝试回答这两个备受关注的问题之外,笔者也试图分析AI带来的社会结构性转变,以及为了应对这些转变,个体和社会应作出怎样的努力。我们看到,目前AI工具的发展,可能会导致技术性失业、收入分配结构的恶化尤其是“极化”效应,加剧各种社会问题。而要想让技术进步更好地实现普惠价值,我们需对现有制度进行深入反思,尝试对社会系统进行革新和再设计。归根结底,技术的社会价值实现和进步方向最终 ...

叔本华:人类是一步一步地迈向死亡的存在物

丹麦哲学家齐克果(Sren Kierkegaard)说:「什么是诗人?一个不快乐的人:他把深层的痛苦埋在心里;但他的唇舌是如此形塑,以致从中经过的叹息和哀嚎,都成了动人的乐章。」诗人好像真的是比较不快乐。在一个诗人选择自杀后,我们一般都对之予以同情和理解,彷彿诗人们自我了结生命是可以谅解的。种种的思绪,不禁令人想起德国哲学家叔本华(Arthur Schopenhauer)对艺术和自杀的一些想法。叔本华向来以所谓悲观主义哲学闻名,不少没读过他的人也大概知道这点。所谓悲观主义,是一种以负面的角度去理解价值的方案。而所谓负面,又有几个面向。首先,叔本华说,人类是一步一步地迈向死亡的存在物,从这个存在特质去看,人类的存在目标和目的也就指向着死亡。「假如存在的目标是死亡,那为什么不能现在就死?」一位诗人或许正在如此提问。还不能马上就死。正因为人是「步向死亡」的存有者,人的存在处境便是动态的──就于现在的每一刻。因此,「现在」便有了独特的价值。就如他在《作为意志和表象的世界》(The World as Will and Representation)第一册中解释:真正的存在就只在现在。现在一直往过 ...