大模型系列
RAG 是2023年最盛行的基于 LLM 的运行系统架构。有许多产品简直齐全树立在 RAG 之上,笼罩了却合网络搜查引擎和 LLM 的问答服务,到不可胜数个数据聊天的运行程序。很多人将RAG和Agent 作为大模型运行的两种干流架构,但什么是RAG呢?RAG又触及了哪些详细的技术呢?
1. 什么是RAG
RAG即检索增强生成,为 LLM 提供了从某些数据源检索到的信息,并基于此修正生成的答案。RAG 基本上是 Search + LLM 揭示,可以经过大模型回答查问,并将搜查算法所找到的信息作为大模型的高低文。查问和检索到的高低文都会被注入到发送到 LLM 的揭示语中。
嵌入式搜查引擎可以经过 Faiss 来成功,向量搜查畛域成为了RAG的一个助力。像pinecone 这样的向量数据库可以构建开源搜查索引,为输入文本参与了额外的存储空间,还参与了一些其余工具。关于向量数据库,可以参考解读向量数据库。
面向RAG的开发框架,关于基于 LLM 的流水线和运行程序,有两个最驰名的开源工具—— LangChain 和 LlamaIndex,区分是在2022年10月和11月创立的,随着 ChatGPT 迸发,也在2023年取得了少量驳回。LlamaIndex 和 LangChain 都是令人惊叹的开源名目,它们的开展速度十分快。
2. 基础的 RAG 技术
RAG 系统的终点普通是一个文本文档的语料库,便捷看起来是这样的: 把文本宰割成块,而后把这些分块嵌入到向量与transformer编码器模型,把一切这些向量树立索引,最后创立一个 LLM 揭示语,通知模型回答用户的查问,给出在搜查步骤中找到的高低文。在运转时,咱们用相反的编码器模型成功用户查问的向量化,而后口头这个查问向量的索引搜查,找到top-k 的结果,从数据库中检索到相应的文本块,并提供应 LLM 揭示语Prompt作为高低文。
在OpenAI 平台上,揭示词Prompt可以是这样的:
def question_answering(context, query):prompt = f""" my query text..."""response = get_completion(instruction, prompt, model="gpt-3.5-turbo")answer = response.choices[0].message["content"]return answer
关于揭示词和揭示词工程的更多引见可以参考OpenAI 的揭示词工程手册以及解读揭示工程(Prompt Engineering)。
显然,虽然 OpenAI 在LLM 市场上处于上游位置,但还是有很多代替打算,比如 Anthroic 的 Claude,还有最近盛行的更小但配置弱小的模型,比如 Mistral,微软的 Phi-2 以及许多开源选项,比如 Llama2,OpenLLaMA,Falcon等都可以用来开发面向RAG的大模型产品。
3. RAG中的初级技术
虽然并不是一切RAG系统中的初级技术都可以轻松地在一张图中可视化,但给出一个形容外围步骤和算法的打算还是无心义的。
3.1. 分块和矢量化
首先,要创立一个向量索引示意咱们的文档内容,而后在运转时搜查一切这些向量和查问向量之间最小距离对应的最凑近语义。
由于transformer模型有固定的输入序列长度,即使输入高低文的窗口很大,一个或几个句子的向量也比一个在几页文本上取平均值的向量更能代表它们的语义意义 ,所以数据分块是一个无心义的技术。把初始文档分红必定大小的块,同时又不失去它们的意义,也就是把文本分红句子或段落,而不是把一个句子分红两局部。而且,曾经有了各种能够口头此义务的文本宰割器成功。例如,在 LlamaIndex 中,NodeParser 就提供了一些初级选项,如定义自己的文本宰割器、元数据、节点/块相关等。
数据块的大小是一个须要思考的参数,它取决于经常使用的嵌入模型及其token容量,规范的transformer编码模型,如BERT 的句子转换器,最多只能经常使用512个token,OpenAI ada-002能够处置更长的序列,如8191个token,但这里的折衷是足够的高低文,让 LLM 能够推理以及特定的足够文本嵌入,以便有效地口头搜查。
下一步是选用一个模型来消费所选块的嵌入,雷同有很多方法,例如搜查优化的模型( bge-large 或许E5 系列),MTEB 排行榜可以失掉最新的一些方法信息。关于文档分块和向量化步骤的端到端成功,可以详细地参考。
3.2. 搜查的索引
面向RAG的大模型运行的关键局部是用于搜查的索引,它存储前面失掉的向量化内容。当然,查问总是首先向量化,关于 top k 分块也是一样的。最便捷的成功经常使用一个平铺的索引,在查问向量和一切块向量之间启动距离计算并遍历。
一个适合的搜查索引,为了在一万多个元素的尺度上有效地检索而优化,须要一个向量索引,faiss,nmslib 或 annoy等经常使用一些近似最近邻方式成功,如聚类,树或 HNSW 算法。还有一些受治理的处置打算,比如 ElasticSearch以及向量数据库,它们担任处置数据摄取的流水线。
假设有许多文档,就须要能够有效地在其中启动搜查,找到相关信息,并将其聚合在一个带有源援用的答案中。关于大型数据库,一个有效的方法是创立两个索引,一个由摘要组成,另一个由文档块组成,而后分两个步骤启动搜查,首先经过摘要过滤掉相关文档,而后再经过相关组启动搜查。
另一种方法是要求 LLM 为每个块生成一个疑问,并将这些疑问嵌入到向量中,在运转时对这个疑问的向量索引口头查问搜查(在索引中用疑问向量交流块向量) ,而后路由到原始文本块并将它们作为 LLM 取得答案的高低文发送。这种方法提高了搜查品质,由于与实践块相比,查问和假定疑问之间具有更高的语义相似性。还有一种被称为 HyDE 的反向逻辑方法, 要求一个 LLM 生成一个假定的给定查问的照应,而后经常使用它的向量和查问向量来提高搜查品质。
为了取得更好的搜查品质而检索更小的块,就要为 LLM 参与周围的高低文。有两种选用,一个是句子窗口检索,即在检索到的较小块周围按句子开展高低文,另一个是父文档检索,即递归地将文档宰割成若干较大的父块,其中蕴含较小的子块。
在句子窗口检索打算中,文档中的每个句子都是独自嵌入,这为高低文余弦距离搜查提供了很高的准确性。在失掉最相关的单个句子之后,为了更好地推理找到的高低文,在检索到的句子之前和之后将高低文窗口裁减为k个句子,而后将这个裁减的高低文发送给 LLM。
父文档检索与句子窗口检索十分相似,都是搜查更细粒度的信息,而后在将高低文提供应 LLM 启动推理之前裁减过的高低文窗口。文档被拆分红援用较大父块中的较小子块。详细而言,文档被宰割成块的档次结构,而后最小的叶子块被发送到索引。在检索时期,失掉较小的块,而后假设在top-k 检索的块中有超越 n 个块链接到同一个父节点(较大的块) ,就用这个父节点交流提供应 LLM 的高低文。须要留意的是,搜查仅在子节点索引中口头。
还有一个相对较老的思绪,可以像 tf-idf 或BM25这样的稠密检索算法那样从现代语义或向量搜查中失掉最佳结果,并将其联合在一个检索结果中。这里惟一的技巧是将检索到的结果与不同的相似度得分失外地联合起来,这个疑问通常借助于Reciprocal Rank 融合算法(RRF)来处置,对检索到的结果从新排序以失掉最终的输入。
在 LangChain中,这是在集成检索器类中成功的,例如,一个 Faiss 矢量索引和一个基于 BM25的检索器,并经常使用 RRF 启动从新排序。在 LlamaIndex 中,也是以一种十分相似的方式成功的。
混合或融合搜查通常在思考查问和存储文档之间有语义相似性和关键字婚配的状况下,将两种互补的搜查算法联合起来,提供更好的检索结果。
3.3. Rerank和过滤
在失掉了检索结果后,须要经过过滤来从新排序。LlamaIndex 提供了多种可用的后处置程序,依据相似度评分、关键词、元数据过滤掉结果,或许用其余模型对结果启动从新排序,比如基于句子transformer的交叉编码器、 依据元数据(比如日期最近性)内聚从新排序等等。这是将检索到的高低文提供应 LLM 以取得结果答案之前的最后一步。
3.4. query变换
查问转换是一系列经常使用 LLM 作为推理引擎来修正用户输入以提高检索品质的技术,有很多不同的技术选用。
假设查问很复杂,LLM 可以将其分解为几个子查问。例如,假设问“ 在Github上Langchain 或 LlamaIndex 上哪个有更多颗星?”,不太或许在语料库中找到间接的对比,将这个疑问分解为两个子查问是无心义的,前提是要有更便捷和更详细的信息检索,例如“ Langchain 在 Github 上有多少颗星?”“Llamaindex 在 Github 上有多少颗星?”它们将并行口头,而后将检索到的高低文组合在一个揭示语中,以便 LLM 分解对初始查问的最终答案。在 Langchain 作为多查问检索器,在 Llamaindex 作为子疑问查问引擎。
前进揭示(Step-back prompting)经常使用 LLM 生成一个更普通的查问,为此检索取得一个更普通或更初级别的高低文,以便将原始查问的答案树立在这个高低文上。此外,还将口头对原始查问的检索,并在最后的应对生成步骤中将两个高低文提供应 LLM。LangChain 有一个参考成功。query重写经常使用 LLM 从新制订初始查问,以提高检索效率。LangChain 和 LlamaIndex 都有成功,但 LlamaIndex 参考成功更弱小。
假设经常使用多个来源来生成一个答案,要么是由于初始查问的复杂性,须要必定口头多个子查问,而后将检索到的高低文兼并到一个答案中,要么是在多个文档中发现了单个查问的相翻开下文,能够准确地反向援用。可以将这个援用义务拔出到揭示语中,并要求 LLM 提供所经常使用源的 id,而后将生成的照应局部与索引中的原始文本块婚配,Llamaindex 为这种状况提供了一种有效的基于含糊婚配的处置打算。
3.5. 聊天引擎
构建一个可以在单个查问中屡次运转RAG系统的一个关键特性是聊天逻辑,思考到对话高低文,就像在 LLM 时代之前的经典聊天机器人一样。这是允许后续疑问,重复指代,或恣意用户命令相关的以前对话高低文所必需的。查问紧缩技术可以同时思考聊天高低文和用户查问。有几种方法可以成功高低文紧缩,一种盛行且相对便捷的 ContextChatEngine,首先检索与用户查问相关的高低文,而后将其连同聊天历史从存缓发送给 LLM,让 LLM 在生成下一个答案时能够看法到前一个高低文。
更复杂的成功是 CondensePlusContextMode,在每次交互中,聊天历史记载和最后一条信息被紧缩成一个新的查问,而后这个查问进入索引,检索到的高低文被传递给 LLM连同原始用户信息来生成一个答案。
3.6. query 路由
Query路由是由 LLM 驱动的决策步骤,在给定用户查问的状况下,选择接上去做什么。这些选项通常是总结、针对某些数据索引口头搜查或尝试多种不同的路由,而后在一个答案中综合它们的输入。
Query路由还可以用于选用索引,或许更宽泛的数据存储,将用户查问发送到何处,例如,经典的向量存储和图形数据库或相关数据库。关于多文档存储来说,一个十分经典的状况是一个摘要索引和另一个文档块向量索引。
定义Query路由包括设置它可以做出的选用。路由选用是经过一个 LLM 调用来口头的,它以预约义的格局前往结果,用于将查问路由到给定的索引。假设驳回了代理的方式,则将查问路由到子链甚至其余代理,如上方的多文档代理打算所示。LlamaIndex 和 LangChain 都允许Query路由。
3.7. RAG中的智能体Agent
代理自身就是一个渺小的话题,OpenAI 助手基本上曾经成功了很多围绕 LLM 所需的工具,兴许最关键的是函数调用 API。后者提供了将人造言语转换为对外部工具或数据库查问的 API 调用的配置。在 LlamaIndex 中,有一个 OpenAIAgent 类将这种初级逻辑与 ChatEngine 和 QueryEngine 联合在一同,提供基于知识和高低文感知的聊天配置,以及一次性性调用多个 OpenAI 函数的才干,这确实带来了智能代理的经常使用方式。
以多文档代理为例,在每个文档上会初始化一个代理(OpenAIAgent) ,能够启动文档摘要和经典的 QA 机制,以及一个顶级总代理,担任将查问路由到文档代理和最终答案分解。每个文档代理都有两个工具ーー向量存储索引和摘要索引,并依据路由查问选择经常使用哪个工具。该体系结构由每个相关代理做出少量的路由决策。这种方法的好处是能够比拟不同的处置打算或实体,这些处置打算或实体在不同的文档及其摘要以及经典的繁多文档摘要和QA 机制中启动了形容,这基本上涵盖了最经常出现的与文档集聊天的经常使用场景。
该打算由于在外部经常使用 LLM 启动了屡次来回迭代,因此速度有点慢。为了防万一,LLM 调用经过RAG 流水线中最长的搜查操作来优化速度。因此,关于大型多文档存储,可以对该打算启动一些简化,使其具有可伸缩性。
3.8. 照应分解
照应分解是任何 RAG 流水线的最后一步,依据检索的一切高低文和初始用户查问生成一个答案。最便捷的方法是将一切失掉的高低文(高于某个相关性阈值)与查问一同衔接并提供应 LLM。但是,还有其余更复杂的选项触及多个 LLM 调用,以细化检索到的高低文并生成更好的答案。照应分解的关键方法有:
无照顾应分解的更多信息,可以参考文档中的示例。
4. 面向RAG的编码器和大模型微调
对 RAG 流水线中触及的深度学习模型启动一些微调,一个是担任嵌入品质从而提高高低文检索品质的 Transformer Encoder,另一个担任应用提供的高低文来回答用户查问的 LLM。可以经常使用 GPT-4这样的上流 LLM 来生成高品质的分解数据集。但是应该一直留意到,驳回一个大型数据集启动训练的开源模型,并经常使用小型分解数据集启动极速调优,或许会削弱模型的总体才干。
较新版本的transformer编码器优化搜查是相当有效的,bge-large-en-v1.5即使在笔记本电脑环境中仍能够有较大的检索品质优化。
4.1.编码器微调
一个很好的老选用是有一个交叉编码器。假设不齐全信赖基本编码器,交叉编码器可以对检索到的结果从新排序。它的上班原理是把查问和每个最高k个检索到的文本块传递给交叉编码器,用一个标志分隔,而后对它启动微调,相关的块输入为1,不相关的块输入为0。这种调整环节可以参考。
4.2.大模型微调
最近 OpenAI 开局提供 LLM 微调 API,LlamaIndex 有一个关于在 RAG 设置中微调 GPT-3.5-turbo 以“提取”一些 GPT-4知识的教程。基本思绪是失掉一个文档,经常使用 GPT-3.5-turbo 生成一系列疑问,而后经常使用 GPT-4依据文档内容生成这些疑问的答案即构建一个基于 GPT4的 RAG 流水线 ,而后在问答对的数据集上对 GPT-3.5-turbo 启动微调。经过对 RAG 流水线的评价,可以初步确定经过微调的 GPT 3.5-turbo 模型比原始模型能够更好地利用所提供的高低文来生成其答案。
在论文 RA-DIT: Meta AI Research 的检索增强双指令优化中,有一种更为复杂的方法,提出了一种在查问、高低文和答案这个三元组上同时优化 LLM 和检索器(原论文中的双重编码器)的技术。这种技术被用于经过微调 API 和 Llama2开源模型来微调 OpenAI LLM (在原论文中) ,造成知识密集型义务目的参与约5% (与经常使用 RAG 的 Llama265B 相比) ,并且知识推理义务参与了几个百分点。无关实施细节,可以参考。
5. 面向RAG的性能评价
有几个框架都可以运行于RAG 系统的性能评价,目的包括总体答案相关性、答案溯源性、可信度和检索到的高低文相关性等等。
Ragas框架,经常使用可信度和答案相关性作为 RAG 检索局部生成答案的品质目的和经典的高低文准召率。评价框架 Truelens 倡导驳回检索与查问的高低文相关性、答案溯源性以及与查问的答案相关性,将这三个目的作为 RAG 系统的性能评价三元组。其中,关键且最可控的目的是检索到的高低文相关性,其次是答案相关性和溯源性。
LangChain 有一个十分先进的评价框架 LangSmith,可以成功自定义的评价器,还可以跟踪 RAG 流水线的运转形态,以使系统愈加透明。而在LlamaIndex 中有一个rag_evaluator的包,提供了一个简便工具经常使用公共数据集来评价RAG系统。
6. 小结
RAG 系统的关键应战除了答案的相关性和可信度之外,还有就是速度。但是,还有很多其余事件须要思考,比如基于网络搜查的 RAG,与Agent架构的深度融合,以及关于 LLM 常年记忆的一些方式方法。即使如此,RAG 依然有着宽泛的运行范围,咱们在经常使用RAG落地运行的时刻, 宿愿本文中提到的这些技术能够对大家有所协助。
【参考资料】