RAG 从零开始

我是一名机器学习工程师,经常使用Claude或ChatGPT来帮助我编写代码。然而,在某些情况下,模型开始重复自己或产生幻觉,特别是在复杂或长时间的任务中。这可能是由于模型的上下文窗口限制或提示信息的性质导致的。当发生这种情况时,我通常会编写伪代码来引导模型,这在大多数情况下都效果很好。"上下文窗口"是指模型在单个输入中可以处理的文本最大量(以标记为单位)。超出这个限制可能会导致信息丢失或在长期任务中产生混乱。

Youtube

在某些情况下,当我感觉模型开始“忘记”我提供的信息时,我会使用一个简单的技巧:我将必要的信息分成几块,以避免超出上下文窗口。这种策略有助于模型在互动过程中保留重要细节。这实际上是检索增强生成(RAG)系统背后的核心思想,经过一些调整,这将在接下来的章节中进行讨论。

Image from the author

索引

Image from the author
  • 文件:这是我们开始收集信息来源的地方,例如书籍,文章或任何其他基于文本的知识。
  • 分块:在这一步骤中,我们将大型文档分解为更小、更易管理的部分。这样做可以更容易地处理和检索特定信息。
  • 嵌入:在这里,我们将每个文本块转换为捕捉其含义的数值表示。这使得计算机能够高效地理解和比较内容。
  • 索引:最后,我们将这些数字表示存储在一个特殊的结构中,以便快速高效地进行搜索。

切块

我相信所有阶段都很清晰,除了分块部分。我们如何分割文件?

许多方法已被引入来实现这个目的。其中一些是静态的,如固定长度分割,涉及将文本分成预定数量的令牌或字符块。其他的则更有语义性。

为了获得更详细的解释,请访问这篇文章:五级RAG中的分块策略。

检索

Image from the author

在将我们的文档分成块,嵌入和索引它们后,我们不会将所有块与查询一起馈送给LLM。 相反,我们将根据用户的问题选择前k个最相关的块。更具体地说,我们将嵌入查询或问题,并将其与我们嵌入的块进行比较,以识别最相关的k个块。

一代

Image from the author

这几乎是我们RAG管道的最后部分。我们现在将选择具有最相关文档的查询,并将其作为提示传递给LLM,LLM将返回最终答案。

这是最基本的 RAG 流程,但它具有 Twitter 图像中基本提到的内容:RAG 用于回答用户关于私人数据的查询,当查询本身并不含糊时。我的意思是,它可以用与文档非常不同的方式来表达。所以问题可能是模型无法理解我们的任务或问题,这意味着我们需要翻译查询。

twitter

查询转换

LangChain

多查询提示

我们从一个原始问题开始,将其改写为几个不同的查询。接下来,我们查看每个查询的相关信息。最后,在将它们与原始问题一起发送到模型之前,我们可以使用不同的方法来组合这些查询及其结果。

RAG-融合

RAG融合技术利用了一种称为Reciprocal Rank Fusion (RRF)的方法。理解RRF至关重要,特别是在后面讨论 Re-ranking 时,我们将再次回顾它。

当我们采用多查询策略时,我们会生成多个查询,为每个查询收集前几个相关的信息块。在收集到这些结果后,我们会审查它们,以消除任何重复项,确保我们仅处理唯一的条目。

现在,让我们考虑一个情况,两个查询返回具有相同分数的块。例如,如果两个查询都产生了一个评分为0.55的块,当我们合并它们时,我们面临着确定它们的顺序的挑战。这就是RRF变得有益的地方。

RRF帮助我们根据每个查询的相关性对这些块进行排序,从而帮助我们确定哪个块应在我们最终列表中占据主导地位。通过应用RRF,我们可以有效地合并结果,同时优先考虑最相关的块,从而实现我们需要的信息更高效准确的表示。

要探索这个公式并查看一些例子,请查看这些文章:

分解

分解查询是一种方法,我们将复杂任务分解成更小、更简单、更容易解决的子任务或部分。之后,我们要么通过递归解决这些答案,要么分别对其进行处理,然后将它们合并。

Image from the author

递归答案

这种方法涉及将复杂查询分解成一系列更简单、更小的问题。当一个LLM(大型语言模型)回答了第一个问题时,我们将问题和答案一起作为下一个问题的上下文。我们继续这个过程,直到所有更简单的问题都被回答。(在某些情况下,我们可以在这里停止。)在其他情况下,我们将所有这些问题和它们的答案作为上下文来解决最初的主查询。如果我们完成了这一最后一步,这就变成了“单独回答”的方法。

Image from the author

路由

在构建一个大型的RAG应用程序时,通常不会只关注一个固定的任务或只处理一个上传的文件。例如,如果你有一个正在备考考试的高中生,你可能会创建一个RAG应用程序,帮助他们各个科目,而不只是一个。然而,这个应用程序需要一个路由器来确定每个查询的科目。例如,如果学生问一个物理问题,路由器会指导RAG搜索学生上传的物理教科书,而忽略其他科目。根据用户的查询,应用程序会选择最合适的路径或链条来获得最佳结果。

现在,让我们来谈一下路由的方法:

使用自定义函数:

只需为每个学科创建定制的链条。例如,如果学科是数学,您可以设计一个链条,告诉模型它是一名数学老师,应该根据数学知识回答问题。同样,您可以为物理学设计另一个链条,依此类推。一个简单的函数将检查查询中的特定单词(例如,“数学”),并选择该学科对应的链条。

机器学习分类器:

如果您有许多来自物理、数学等不同学科的问题,并且希望系统能够确定问题所涉及的学科,您可以训练一个分类器。这个分类器将把查询分类到正确的学科中,一旦分类完成,系统就可以将查询路由到相关教科书或链条。

语义相似性:

更高级的功能可以被用于简单关键词匹配之外。例如,您可以为每个链创建一个嵌入(一个数值表示)。当一个查询进来时,它的嵌入会与每个链的嵌入进行比较,选择与最接近的匹配的链。

混合方法:

如其名所示,您可以使用混合方法来结合上述多种方法,以实现更好和更准确的路由。

路由的重要性在于确保提供给LLM的上下文与所问的问题确实相关。这可以防止模型使用来自不同主题的无关上下文。有效的路由还可以通过确保我们在正确的块中搜索来改善索引过程,节省时间并产生更好的结果。

Image from the author

查询建设

在构建查询时,我们使用的大多数数据文件可能具有元数据,对吧?那么,元数据是什么?元数据就是关于数据的数据。

例如,如果我们有一个视频,数据将是视频内容本身。但是元数据可能是诸如视频来源、制作日期、持续时间或其他详细信息之类的各种内容。

现在,如果我们有一个查询,像这样:

哪个视频更长,A还是B?

第一个解决方案可能是模型查看第一个视频的文字记录中的单词数量,然后将其与第二个视频的文字记录中的单词数量进行比较。单词数较多的那个视频会更长,对吧?

并不完全是这样。一个视频可能比另一个视频更安静,所以即使它的字数更少,它可能仍然较长。但是,即使有更多字数的视频较长,获取答案的更快方式是什么?是让模型分析整个视频内容,还是只是从元数据中获取持续时间并比较长度?

所以,你认为使用元数据来改善分块是怎么样的呢?用户编写的查询可能可以通过元数据直接回答,从而我们可以更容易地提供一个答案。

Image from the author

这篇文章就到这里了,我可能会在接下来的几天里分享另一篇关于更深入概念的文章!

资源:

2024-10-03 04:18:53 AI中文站翻译自原文