RAG的前世今生

RAG的前世今生
NeoFii文章探索 RAG(Retrieval Augmented Generation,检索增强生成) 在大语言模型中的实际应用,并详细介绍RAG的原理及相关变体。
前言
大语言模型(Large Language Model,LLM)在当今时代展示出了不凡的能力,但是目前也存在诸如幻觉、知识过时等非可视、难以追踪的挑战。RAG技术是一种主流且有效的解决方案,它能从外部知识数据库中获取知识,从而提高回答的准确性和可信度。
RAG的原理
RAG的原理正如其名,它通过检索外部知识库来增强模型的生成能力。在RAG中,模型首先会检索到与用户输入相关的知识,然后使用这些知识来生成回答。
我们用一个实例来举例:一个用户向ChatGPT提出一个最近被广泛讨论的新闻问题,但是由于ChatGPT依赖于训练数据,因此缺乏提供最新动态的能力,它大概率会输出一个错误答案或回复自己并不知道这个新闻。这时若赋予ChatGPT知识库,它就可以从知识库中检索到相关的新闻,并生成一个更准确的回答。
RAG 流程图
RAG的发展阶段
RAG最初诞生与Transformer架构的兴起相吻合,其重点是通过预训练模型(PTM)纳入额外的知识来增强语言模型。ChatGPT的出现标志着一个关键时刻的到来,LLM展示了强大的语境学习(ICL)能力。RAG 研究转向为 LLMs 提供更好的信息,以便在推理阶段回答更复杂和知识密集型的任务,从而带动了 RAG 研究的快速发展。随着研究的深入,RAG 的改进不再局限于推理阶段,而是开始更多地采用 LLM 微调技术。
RAG研究范式不断发展,这里将其分为三个阶段:Naive RAG, Advanced RAG 和 Modular RAG
Naive RAG
Naive RAG是RAG系统的初级阶段,包括索引、检索和生成,也被称为 检索- 阅读 框架
Naive RAG的流程如下:
索引
首先索引要清理和提取 PDF、HTML、Word 和 Markdown 等不同格式的原始数据,然后将其转换为统一的纯文本格式。 同时为了适应LLM的上下文限制,文本被分割成更小的文本块(chunk)。然后使用embedding模型(嵌入模型)将文本块转换为向量并存储到向量数据库中,这一步对随后的检索阶段进行相似度搜索至关重要。
检索
当收到用户的prompt后RAG系统会使用索引阶段相同的嵌入模型对用户输入进行向量化,然后使用向量数据库进行搜索,找到最匹配的k个文本块,将这些文本块作为上下文添加在prompt中。
生成
LLM将用户输入和检索到的文本块作为输入,生成回答。回答方法可以根据特定任务的标准而有所不同,允许LLM利用固有的训练知识,或将其回答限制在提供的文档所包含的信息范围内。
Naive RAG的优点是简单易用,但也有明显的缺点:
检索挑战:检索阶段往往存在精确度和召回率方面的困难,导致选择错位或不相关的文本块以及丢失关键信息。
生成困难:生成回复时,LLM可能会遇到幻觉问题,即生成的内容与检索到的上下文不符,可能出现输出内容不相关、有毒的情况,从而影响回复的质量和可靠性。
增强障碍:将检索到的信息与不同的任务进行整合可能具有挑战性,有时会导致输出结果脱节或不连贯。当从多个来源检索到类似信息时,这一过程还可能遇到冗余问题,从而导致重复性回答。面对复杂的问题,基于原始查询的单一检索可能不足以获取足够的上下文信息。
Advanced RAG
Advanced RAG 为了提高检索质量,围绕检索前和检索后采用多种策略克服 Naive RAG的局限性。为了解决索引问题,Advanced RAG 通过使用滑动窗口方法、细粒度分割和元数据的整合,改进了索引技术。此外,它还采用了多种优化方法来简化检索过程。
在Naive RAG的基础上,Advanced RAG 引入了以下改进:
预检索过程
这一阶段重点优化索引和原始查询。优化索引的目标是提高索引内容的质量。涉及以下策略:提高数据颗粒度、优化索引结构、增加元数据、优化排列和混合检索。优化查询的目标是使用户的原始问题更加清晰、更适合检索任务。涉及以下策略:查询重写、查询转换、查询扩展等技术。检索后处理
检索到相关的上下文后将其与查询有效整合至关重要。检索后处理主要方法包括重新排序信息块和压缩上下文。重新排序信息块将最相关的内容移到提示信息的边缘以强调关键部分,压缩上下文将信息块合并到更小的文本块中,从而减少上下文长度。
Advanced RAG 在 Naive RAG 的基础上提高了生成文本的相关性和准确性。
Modular RAG
Modular RAG 代表了 RAG 的更高级阶段,通过引入新的模块来丰富 RAG 过程提供更多的灵活性。
这种范式允许模块的替换或重新配置,并可以根据特定的上下文动态组织 RAG 过程。
Modular RAG 引入了额外的专用组件,以增强检索和处理能力。
搜索模块:可适应特定任务场景,使用 LLM 生成的代码和查询语言在搜索引擎、数据库和知识图谱等各种数据源中直接搜索。
RAGFusion:采用多查询策略将用户查询扩展到不同的视角,利用并行向量搜索和智能重新排序来挖掘线性知识和转化知识,从而解决了传统搜索的局限性。
内存模块:利用LLM的记忆来引导检索,通过迭代的自我增强创建一个无限的内存池,从而使文本更紧密地与数据分布对齐。
RAG 与 Fine Tuning(微调)
RAG 相当于为模型提供了量身定做的信息检索教科书,是精确信息检索任务的理想选择,相比之下FT好比在长期学习过程中内化知识,适用于需要复制特定结构风格或格式的场景。
RAG 在动态环境中表现出色,可提供实时知识更新并有效利用外部知识源,具有很高的可解释性。不过,它的延迟较高,而且在数据检索方面需要考虑道德问题。
FT 更为静态,更新时需要重新训练,但可以对模型的行为和风格进行深度定制。它需要大量的计算资源用于数据集的准备和训练,虽然可以减少幻觉,但在处理不熟悉的数据时可能会面临挑战。
RAG 与 FT 适用场景二维图
相关概念
检索粒度
在文本中,检索粒度从细到粗,包括 Token、Phrase、Sentence、Proposition、Chunks、Document。粗粒度的检索单元理论上可以为问题提供更多相关信息,但也可能包含冗余内容,这会分散下游任务中检索器和语言模型的注意力。另一方面,细粒度的检索单元粒度会增加检索的负担,并且不能保证语义的完整性和满足所需的知识。因此,在推理过程中,应当需根据任务的需要选择适当的检索粒度
分块策略
最常见的方法是按固定的标记数(如 100、256、512)将文档分割成块。较大的块可以捕捉到更多的上下文,但也会产生更多的噪音,需要更长的处理时间和更高的成本。较小的语块可能无法完全表达必要的上下文,但它们的噪音较少。语块会导致句子内部的截断,促使人们优化递归分割和滑动窗口方法,通过在多个检索过程中合并全局相关信息来实现分层检索。然而,这些方法仍然无法在语义完整性和上下文长度之间取得平衡。因此,有人提出了 Small2Big 等方法,即把句子(小)作为检索单元,把前后句子作为 LLMs 的(大)上下文。
元数据附件
用页码、文件名、作者、类别时间戳等元数据信息来丰富信息块。随后,可以根据这些元数据对检索进行过滤,从而限制检索范围。在检索过程中为文件时间戳分配不同的权重,可以实现时间感知的 RAG,确保知识的新鲜度,避免过时信息。除了从原始文件中提取元数据外,还可以人为地构建元数据。例如,添加段落摘要,以及引入假设性问题。这种方法也被称为反向 HyDE。具体来说,就是利用 LLM 生成可由文档回答的问题,然后在检索时计算原始问题与假设问题之间的相似度,以缩小问题与答案之间的语义差距。
优化索引结构
为文档建立层次结构。通过构建这种结构,RAG 系统可以加快相关数据的检索和处理。
分层索引结构。文件以父子关系排列,各块与之相连。数据摘要存储在每个节点,有助于快速遍历数据,并帮助 RAG 系统确定提取哪些数据块。这种方法还能减少因块提取问题造成的错觉。
知识图谱索引。利用知识图谱构建文档的层次结构有助于保持一致性。它划定了不同概念和实体之间的联系,明显减少了产生错觉的可能性。另一个优势是将信息检索过程转化为 LLM 可以理解的指令,从而提高知识检索的准确性,并使 LLM 能够生成上下文一致的响应,从而提高 RAG 系统的整体效率。
查询优化
查询扩展:将单个查询扩展为多个查询可丰富查询内容,提供进一步的上下文,以解决缺乏具体细微差别的问题,从而确保生成的答案具有最佳相关性。
多查询。通过 LLMs 采用提示工程来扩展查询,这些查询就可以并行执行。查询的扩展不是随机的,而是经过精心设计的。
子问题。子问题规划过程是指生成必要的子问题,以便结合上下文全面回答原始问题。添加相关上下文的过程原则上类似于查询扩展。具体来说,可以使用从少到多的提示方法,将复杂问题分解为一系列较简单的子问题。
验证链(CoVe)。扩展查询通过 LLM 验证,以达到减少幻觉的效果。经过验证的扩展查询通常具有更高的可靠性。查询扩展:将单个查询扩展为多个查询可丰富查询内容,提供进一步的上下文,以解决缺乏具体细微差别的问题,从而确保生成的答案具有最佳相关性。
查询转换:查询转换:其核心理念是根据转换后的查询而不是用户的原始查询来检索数据块。
查询重写。原始查询并不总是 LLM 检索的最佳选择,尤其是在实际场景中。因此,我们可以促使 LLM 对查询进行重写。除了使用 LLM 进行查询重写外,还可以使用专门的小语言模型,如 RRR(Rewrite-retrieve-read)。
另一种查询转换方法是使用提示工程,让 LLM 根据原始查询生成查询,以便后续检索。HyDE构建了假设文档(原始查询的假定答案)。它的重点是答案与答案之间的嵌入相似性,而不是寻求问题或查询的嵌入相似性。使用回溯提示法,对原始查询进行抽象,生成高级概念问题(回溯问题)。在 RAG 系统中,回溯问题和原始查询都用于检索,检索结果都用作语言模型答案生成的基础。查询转换:其核心理念是根据转换后的查询而不是用户的原始查询来检索数据块。
查询路由:查询路由:根据不同的查询,路由到不同的 RAG 管道,这适用于多功能 RAG 系统,以适应不同的应用场景。
元数据路由器/过滤器。第一步是从查询中提取关键字(实体),然后根据关键字和块中的元数据进行过滤,以缩小搜索范围。
语义路由器(Semantic Router)是另一种涉及利用查询语义信息的路由方法。具体方法见语义路由器。当然,也可以采用混合路由方法,将语义方法和基于元数据的方法结合起来,增强查询路由功能。
迭代检索
迭代检索是根据初始查询和迄今为止生成的文本反复搜索知识库的过程,从而提供更全面的知识。
迭代检索的三种方式
迭代式 (Iterative)
特点:通过多次迭代提供更多上下文信息,逐步优化响应。流程:
Query(查询):接收到用户的初始问题。
Retrieve(检索):检索相关信息。
Generate(生成):生成初步响应。
Judge(评判):对生成的结果进行质量评估。
如果不满足阈值或条件,返回检索和生成模块,进行多次迭代。
达到最大迭代次数或满足质量标准后,生成最终 Response(响应)。应用场景:适合需要补充上下文信息、逐步完善答案的任务,例如多轮问答或文档补全。
递归式 (Recursive)
特点:通过逐步分解问题,将复杂问题分解为多个子问题进行处理。流程:
Query(查询):接收用户的复杂问题。
Retrieve(检索):检索与问题相关的初步信息。
Generate(生成):生成对当前问题的初步响应。
Judge(评判):评估生成结果是否需要进一步细化。
如果需要,进行 Query Transformation/Decomposition(查询转化/分解),将当前问题拆解为多个子问题,进入下一轮递归。
达到最大深度或满足条件时,生成最终 Response(响应)。应用场景:适用于复杂问题,例如长文档的总结、多步骤推理问题、或复杂任务规划。
自适应式 (Adaptive)
特点:灵活控制检索和生成过程,根据需求动态调整。流程:
Query(查询):接收用户问题。
Judge(评判):初步判断是否需要检索。
如果需要检索,则触发 Retrieve on Demand(按需检索)。Retrieve(检索):获取相关信息。
Query Transformation/Decomposition(查询转化/分解):必要时对问题进行动态调整。Generate(生成):生成初步响应。
再次评估生成结果,生成特殊标记或判断是否需要进一步优化。
生成最终 Response(响应)。应用场景:适用于动态性强的任务,能够根据具体需求调整工作流,例如实时信息检索或复杂问题的动态生成。
Summary
实际上我们简单理解的 RAG 即 Naive RAG 的效果是不尽人意的,实际应用中更多的是 Advanced RAG , 展示出更加优越的表现。关于 Modular RAG 应用,由于其更加复杂实际应用可能较少,但其能够满足更加复杂的场景,将会在未来得到更多的应用。
参考资料
Retrieval-Augmented Generation for Large Language Models: A Survey