更好的RAG 1:高级基础
一如既往,它建立在许多人的工作之上——如果我遗漏了引用某人,或者你认为我应该添加什么,请告诉我!你通常可以在Twitter上找到我。
大型语言模型已证明自己是出色的少样本学习者,几乎是通用智能,能够学习和执行新任务,处理复杂信息并充当数字助手。然而,其有限的输入/输出窗口(即上下文)一直是使其真正智能的最大障碍。除了预训练中使用的数万亿词——这是一个昂贵且耗时的过程——我们通常只能在数千词(有时少于几页文字)内提供新信息和指令,以生成输出。
如果能将人类数据语料库与日益增长但有限的LLM上下文联系起来,我们就能创建出可以即时学习和适应的系统——就像人类一样。我们可以构建具有持久长期记忆的代理,它们能够记住并利用我们的组织和个人数据。
这个领域存在许多子问题,但其中最简单的一个是问答:给定一个问题(来自用户或AI),我们能否将LLM与它们从未见过的数据结合起来,以提供良好的答案?
检索增强生成(Retrieval-augmented-generation)已成为解决这一方向上许多工作的简单统称。如今大多数系统都采用一个简单的管道:导入信息,嵌入结果(我们将在后续解释嵌入),检索与问题相关的块,将这些块导入模型的有限上下文中,然后要求其回答问题。这看起来相当简单。
然而,在实际操作中,事情很快就会崩溃。仅仅使用问题中包含的信息从非常大的数据集中检索相关部分是一项相当艰巨的任务。根据问题的复杂程度,你可能需要从数据集的多个不同部分获取信息,并利用数据中的引用和关系来查找更多有助于你得出答案的信息。
此外,你所能获取的信息量总是有限的。即使上下文大小不断扩大,模型中的推理和智能也会随着你注入输入窗口的信息增多而降低。结合不断增加的成本、计算和内存,检索更高质量和更少量的信息在可预见的未来仍将是一个问题。这也是现代RAG领域显得令人望而生畏和复杂的原因——所有这些都代表了解决相同问题的不同尝试。
更好的方法
在本系列的三部分中,我们将探讨解决此问题的方法。我们将从最后一步——生成——开始,然后一路回溯到源信息,并着眼于改进每个部分。
这一部分将解释RAG系统目前的工作原理、需要考虑的复杂性以及我们想要解决的常见问题。欢迎跳过此部分!其他部分将假定您对嵌入、检索系统、基于LLM的处理等有工作知识。
第二部分将涵盖循环生成(“行走”)或赋予AI模型请求新信息、发现信息中长程关系并将其结合以获得更好结果的方法。如果您熟悉WalkingRAG,本部分将涵盖此同名概念并向您展示如何自行实现。
第三部分将介绍对查询和源数据进行转换以提高检索性能。
让我们从基础开始。
检索系统
我们在问答场景中遇到的第一个问题是搜索问题。长期以来,人们一直认为智能问题就是搜索问题([1] [2] [3])——如果你能组织并检索与任何问题相关的世界信息,你就解决了构成智能系统90%的问题。
这方面我们曾多次尝试解决
- BM25使用查询中的关键词来估计与源文档的相似性和相关性——这是我们使用了30多年的方法。
- PageRank利用页面之间的引用来估计相似性,并根据与其他信息片段的相关性对信息进行排名。
- TFIDF通过查看源文档中单词的频率来估计相关性。
在所有情况下,我们都试图利用查询中的信息来查找源文档的相关部分。我们当前的AI热潮为我们提供了更多工具:
- LLM预训练是我们将大量信息(万亿词汇)真正索引到小型网络中的最佳方法之一。如果你曾使用ChatGPT进行研究,你就是用它来搜索其预训练语料库。不幸的是,这成本过高——训练运行不稳定,并且在不进行全新训练的情况下难以引入新信息。
- 语境学习利用语言模型的语境窗口(运行时输入空间)将信息注入模型的知识中。然而,这个空间高度受限——最大的有用语境仍比预训练数据集小数千倍(或比一个百人公司一年产生的电子邮件量小一千倍)。语境学习也十分昂贵——LLM内部的注意力机制赋予其强大的学习能力,使得token窗口的成本呈指数级增长。
然而,过去几年人工智能发展中最有价值的成果之一是嵌入和嵌入模型,它们现已嵌入到各地的检索管道中。(也许价值过高——第三部分将介绍我们过早和过于频繁地嵌入时产生的问题。)
这是Google对嵌入的技术定义
嵌入是一种相对低维的空间,你可以将高维向量转换到其中。嵌入使对大型输入(如表示单词的稀疏向量)进行机器学习变得更容易。理想情况下,嵌入通过将语义相似的输入放置在嵌入空间中的相近位置来捕获输入的某些语义。
我承认,即使在我状态最好的时候,我也很难完全理解这句话。我们尝试一个更简单的解释,牺牲一些准确性来换取更好的功能理解。
我将嵌入视为ML模型在听到某些内容时对语言理解的“大脑切片”。想象一下,当您听到一个句子时,有人拍摄了您的大脑快照。您会发现当您试图理解它时,哪些神经元和大脑区域被激活。
就其本身而言,这几乎毫无用处。然而,与我们对大脑的研究类似,当您开始将大脑切片与输入句子进行比较时,这变得极其有用。相似的句子——具有相似概念的句子——开始激活大脑的相同部分,即使实际句子不同。然后,我们可以将这里的相似性用作句子相似性的更高层次的代理。
这正是我们在查看嵌入时所看到的。具有相似概念的句子会聚集在一起,即使文本内容不完全相同。这里有一个例子——TikTok帖子的嵌入,按主题聚类。
这也是为什么不能混用不同模型嵌入的原因。因为每个大脑都不同,所以嵌入(或大脑切片)的相似性仅对相同的大脑有意义——不同训练的模型以不同方式存储信息。来自OpenAI的嵌入不能直接与来自Nomic的嵌入进行比较,就像即使是相同的句子,我的大脑激活模式也与您的不同。
问题
一方面,我们有BM25和TFIDF等传统方法,它们依赖于文本相似性——这些方法比基于机器学习的检索智能程度低,对拼写变化的抵抗力也更弱,但更具可控性和可检查性。
另一方面,我们有基于嵌入的检索方法,它们对语义相似性很有用,但存在黑箱问题。嵌入本质上是难以理解的“大脑切片”,虽然一些数学方法可以帮助我们转换嵌入,但我们才刚刚开始触及理解这些编码真正含义的表面。它们在起作用时令人惊叹,但在不起作用时很难修复——而且它们通常比传统搜索更昂贵。
我们还有上下文检索和学习——这是我们最高质量、最昂贵的方法,即使您能将所有内容都放入上下文窗口。
因此,大多数RAG系统最终都应采用类似的架构:使用更廉价的方法来缩小搜索空间,并在管道的后期阶段添加越来越昂贵和智能的方法来找到答案。
然而,还有两个经常被忽视的复杂性维度。
问题复杂性
这类似于提示复杂性问题——我之前已经讨论过。查询的复杂程度可能差异很大——既体现在它们所要求的内容上,也体现在所需检索的类型上。
- 单一事实检索问题是最简单的。这是指问题要求在您的数据中存在或不存在的单个信息,如果您检索到正确的信息,就算成功。例如,“本文中讨论的一个嵌入模型是什么?”要求您的系统找到本文中存在的特定信息。
- 再上一层,我们可能有多事实检索。“正在讨论的检索器的主要类型有哪些?”需要对所有主要类型给出详尽的答案,如果未能检索到其中任何一种,都可能是一个问题。
- 再往上是不连续多事实检索,其中信息在数据集的同一部分中并非连续存在。“文章中使用的RAG系统描述是什么?”就是一个很好的例子。
- 接下来是简单分析问题。“本系列的主要部分是什么,它们之间如何联系?”需要理解文档的特定部分。
- 复杂分析问题可能更难,需要从数据集的多个部分扩展信息。在我们的案例中,这可能是“根据作者的说法,嵌入与BM25相比如何?”
- 最后,我们有研究级问题。这些问题可以很复杂,例如“为什么 ColBERT 不在本文章的讨论范围之内?”或“本文的主要论点和立场是什么?”。在一个更大的数据集(例如收入预测)上,这可能是“2021年的各项举措在开拓新业务方面表现如何?”
不理解问题复杂性可能是现代RAG系统的致命弱点。如果用户期望与系统设计之间存在固有不匹配,那么系统中做出的特定权衡(速度与成本、更智能与更安全等)可能会导致用户与机器之间的首次交互完全失败。
另一方面,对预期问题类别有一个良好的估计,在从零开始构建系统时会大有帮助。例如,如果你期望复杂性保持在4级以下,那么对输入数据进行早期转换以预提取事实可以显著加快搜索速度——并使其易于确定在评估中使用哪些基准。
引导与可检查性
通常被忽视的问题还有“引导”问题。当一切顺利时,一切都很好,但演示和生产之间的区别在于当问题出现时你拥有的工具。
LLM相较于其他机器学习解决方案的最大优势之一在于它们能够提供中间结果——可以修改、控制或检查的输出,以可预测的方式改变行为。我们将在后续部分详细讨论这一点,但中间结果(例如推理链、标签等)对于使大型系统减少黑箱化至关重要。
在本系列中,我们将讨论如何改进检索性能、使系统更易于检查和控制,以及如何处理日益增长的问题复杂性。下期再见!
互动演示
这里是在上下文学习和基于RAG的方法之间的实时比较
这个Huggingface助手使用本文作为Mistral-7b的上下文的一部分,Mistral-7b是一个参数量仅为70亿的相对较小的模型。
这个GPT使用相同的文章,但利用嵌入和检索来回答相同的问题,尽管它使用的模型大小是Mistral-7b的50倍或更多。
如果您没有直接跳到末尾,那么您现在应该能够向两者提问,以说明关键区别。
在第二部分中,我们将讨论多轮检索——或者说是行走。本系列的很多内容都假定您了解提示(例如任务复杂性),我在此处更详细地介绍了这些内容。