首页 > 其他分享 >《你的RAG出错了?快来Get这份改进秘籍》

《你的RAG出错了?快来Get这份改进秘籍》

时间:2025-01-10 21:56:31浏览次数:1  
标签:检索 RAG 嵌入 秘籍 Get 查询 搜索 数据

原始 RAG 框架在提升检索和生成答案质量方面,还有一些关键问题没解决:

  1. 找出来的文档真的跟用户问题相关吗?有时候可能找偏了。

  2. 找到的内容够不够回答用户的问题?会不会信息量不足?

  3. 会不会有一堆没用的信息混进来,反而把答案搞乱了?

  4. 检索速度够快吗?会不会让用户等太久?

  5. 万一检索到的信息没法生成好答案,我们该怎么办?

从上面这些问题,我们可以得出两个关键结论:

首先,得给我们的 RAG 系统装上一个“质检员”——也就是一个强大的评估模块。这个模块要能对检索到的内容进行打分,看看信息质量如何,生成的答案是不是真的解决了用户的问题。

其次,咱们得给 RAG 框架来个“升级改造”,直接在算法层面解决检索中的各种短板。这种升级版的 RAG,我们叫它“高级 RAG”。

接下来,我们就重点聊聊第二点,看看怎么才能让 RAG 系统变得更聪明、更好用。核心问题就是:“RAG 系统到底该怎么优化?”


原始的 RAG 设计可以从三个阶段来优化:

  1. 预检索阶段:这个阶段的核心是“打好基础”,主要是对数据进行整理和预处理,比如建立索引、优化查询方式,让后续的检索更高效。

  2. 检索阶段:这个阶段的重点是“精准搜索”,通过改进嵌入模型(embedding model)和利用元数据过滤,让向量搜索的结果更准确、更相关。

  3. 后检索阶段:这个阶段的任务是“去粗取精”,从检索到的文档中剔除无关信息,压缩提示内容,再把干净、简洁的上下文交给大模型(LLM),让它生成高质量的答案。

简单来说,就是从“准备数据”到“精准查找”再到“提炼答案”,每个阶段都有优化的空间。

一、预检索阶段

预检索这个步骤其实有两种不同的玩法:

数据索引:这是RAG(检索增强生成)流程中的一部分。主要是在数据清洗和分块这些环节里做文章,目的是把数据预处理得更好,方便后续索引。

查询优化:这个算法是在用户查询的时候直接上阵,先把用户的查询语句优化一下,再把它转换成嵌入(embedding),最后从向量数据库里捞相关的数据块。

数据索引

当我们用嵌入来表示文档内容的时候,大部分的数据索引技术都在琢磨怎么把数据预处理得更漂亮、结构更合理,这样检索起来才更高效。下面介绍几种现在比较流行的数据索引优化方法:

1. 滑动窗口
滑动窗口这招就是在文本块之间搞点重叠,确保那些在块边界附近的重要信息不会丢,这样一来检索的准确性就提高了。

这招在法律文件、科学论文、客服记录、医疗档案这些领域特别管用,因为关键信息经常是跨好几个部分的。

嵌入不仅针对每个块计算,连重叠部分也不放过。所以滑动窗口通过维护跨边界的上下文,让系统能更好地检索到相关且连贯的信息。

2. 增强数据粒度
这招主要是搞数据清洗,比如删掉那些无关紧要的细节、核实事实的准确性、更新过时的信息。数据干净准确了,检索起来自然就更清晰了。

3. 元数据
给数据加点标签,比如日期、URL、外部ID或者章节标记什么的,这样在检索的时候就能更有效地过滤结果了。

4. 优化索引结构
这招是基于不同的数据索引方法来的,比如用不同的块大小啊,多索引策略啊之类的。

5. 从小到大
这个算法的精髓在于把用于检索的块和用于最终生成答案的提示上下文分开来用。

它用一小段文本来计算嵌入,同时在元数据里保留更宽的上下文窗口。这样一来,小块的文本能提高检索的准确性,而更大的上下文则能给LLM(大语言模型)提供更多的背景信息。

这么做的道理是,如果我们用整篇文本来计算嵌入,可能会引入太多噪音,或者文本里可能包含多个主题,这样一来嵌入的整体语义表示就会变差。

查询优化

在查询优化这块,咱们还可以玩点花样,比如用查询路由、查询重写和查询扩展这些技术,来让LLM(大语言模型)检索到的信息更精准、更细致。

1. 查询路由
想象一下,用户的输入可能五花八门,咱们得根据不同的输入,跟不同类别的数据打交道,还得用不同的方式去查询每个类别。

查询路由这玩意儿,就像是给用户的输入装了个导航,根据输入内容决定接下来该干啥。这有点像编程里的if/else语句,但区别在于,这里的决策完全是用自然语言来做的,而不是冷冰冰的逻辑语句。

举个例子,假设根据用户的输入,咱们为了执行RAG(检索增强生成),可以从几个地方捞数据:用向量搜索查询从向量数据库(Vector DB)里找,或者把用户查询转换成SQL命令去标准SQL数据库(Standard SQL DB)里翻,甚至可以利用REST API调用从互联网(Internet)上抓取其他上下文。

查询路由还有个聪明的地方,它能检测出是否需要额外的上下文,这样就能避免对外部数据存储做多余的调用。另外,它还能为给定的输入挑选出最合适的提示模板。

通常,查询路由会借助LLM来决定走哪条路,或者通过选择向量最相似的路径来嵌入。查询路由跟if/else语句差不多,但由于它直接跟自然语言打交道,所以用起来更灵活、更广泛。


2. 查询重写
有时候,用户一开始的查询可能跟我们的数据结构不太对得上。这时候,查询重写就派上用场了,它通过重新组织问题的表述,来更好地匹配我们索引里的信息。

具体来说,这几种技术可能会用到:

  • 释义:就是把用户的查询换个说法,但意思不变。比如,“气候变化的原因是什么?”可以改成“导致全球变暖的因素有哪些?”。

  • 同义词替换:把一些不太常用的词换成更常见的同义词,这样搜索范围就更广了。比如,“joyful”可以换成“happy”。

  • 子查询:如果查询比较长,我们可以把它拆成几个更短、更聚焦的小查询。这样在检索的时候,就能更精准地找到相关文档。

3. 假设文档嵌入(HyDE)
这个技术需要用到大型语言模型(LLM),让它对用户的查询做一个假设性的回答。然后,这个回答会和原始查询一起,输入到检索阶段。

4. 查询扩展
这个方法的核心是给用户的问题“加点料”,通过添加一些相关的术语或概念,来丰富问题的维度。比如,当用户搜索“疾病”时,我们不仅可以包括“疾病”这个词,还可以加入一些相关的同义词,比如“病痛”,或者其他相关的术语。

5. 自我询问
这个思路是把非结构化的查询“翻译”成结构化的查询。LLM 会从输入的文本中识别出关键的实体、事件和关系。这些信息会被用作过滤条件,来缩小向量搜索的范围。比如,如果查询中提到了“巴黎”,LLM 就会识别出这是一个城市,并把它加到过滤条件里,从而减少搜索空间。

需要注意的是,数据索引和查询优化的预检索技术,都高度依赖于数据的类型、结构和来源。所以,跟其他数据处理流程一样,没有一种方法是万能的。每个用例都有它的特殊性和潜在的坑。优化预检索 RAG 层是一个实验性很强的工作。因此,多尝试几种方法(比如上面提到的这些),反复试验,找到最适合的方案,才是关键。

二、检索阶段

在检索这一步,我们可以通过两种基本方式来优化:

  1. 改进嵌入模型:我们可以优化 RAG 提取管道中使用的嵌入模型,让它更好地对文档分块进行编码,同时在推理时更准确地转换用户的输入。
  2. 利用数据库的过滤和搜索功能:这一步主要在推理时使用,目的是根据用户的输入,快速找到最相似的文档块。

这两种策略的核心目标是一致的:通过提升查询和索引数据之间的语义相似性,来增强向量搜索的效果。

改进嵌入模型时,通常需要对预训练的嵌入模型进行微调,让它更好地适应你所在领域的特定术语和细微差别。尤其是那些术语更新快或者有罕见术语的领域,微调就显得特别重要。

不过,微调模型可能会消耗大量的计算资源和人力。如果你不想走这条路,也可以试试指令模型(比如 Instructor-xl)。这种模型可以在不需要微调的情况下,指导嵌入生成过程,帮你根据数据定制嵌入网络。这可能是一个更省时省力的选择。

下面一段代码是用来基于指令模型生成向量的示例:

from InstructorEmbedding import INSTRUCTOR

model = INSTRUCTOR(“hkunlp/instructor-base”)

sentence = “RAG Fundamentals First”

instruction = “Represent the title of an article about AI:”

embeddings = model.encode([[instruction, sentence]])

print(embeddings.shape) # noqa

# Output: (1, 768)

另一方面,我们还可以通过一些经典的过滤器和数据库搜索功能来优化检索。以下是两种常见的方法:

混合搜索

这是一种结合了向量搜索关键字搜索的混合方法。

  • 关键字搜索擅长找到包含特定关键词的文档。如果你的任务需要高精度,并且检索结果必须包含精确的关键词匹配,那么这种方法非常有用。
  • 向量搜索虽然功能强大,但在精确匹配上可能稍显不足,不过它更擅长捕捉语义上的相似性。

通过把这两种方法结合起来,你可以同时利用关键词匹配和语义相似性的优势。通常,我们会用一个参数(比如叫 alpha)来控制两者的权重。具体来说,算法会分别进行两种独立的搜索,然后将结果标准化并合并。

过滤向量搜索

这种方法利用元数据索引来筛选出符合特定关键词的文档。它和混合搜索的区别在于,你只需要用向量索引检索一次数据,然后在向量搜索之前或之后,通过过滤步骤来缩小搜索范围。

在实际操作中,我们通常会从过滤向量搜索混合搜索开始,因为它们的实现速度比较快。这种方法的好处是,你可以根据实际性能灵活调整策略。

如果结果不太理想,别担心,你随时可以回头微调你的嵌入模型,让它更好地适应你的需求。

三、检索后阶段优

检索后优化主要是对已经检索到的数据进行处理,目的是确保 LLM(大语言模型)的表现不会受到一些问题的干扰,比如上下文窗口有限或者数据中有噪声。

因为有时候检索到的上下文可能会太大,或者包含一些不相关的信息,这些都会让 LLM 分心,影响它的表现。

以下是两种在检索后步骤中常用的方法:

快速压缩

这个方法的核心是去掉不必要的细节,只保留数据的核心内容。简单来说,就是“去粗取精”,让信息更简洁、更聚焦。

重新排序

这个方法会用到一个叫做跨编码器的机器学习模型。它的作用是给用户输入和每个检索到的文档块打分,看看它们之间的匹配度有多高。然后根据这个分数,重新排列检索结果,把最相关的内容排到前面。

这两种方法都是为了在数据进入 LLM 之前,先把它“打磨”得更干净、更有用,从而让 LLM 的表现更上一层楼。


根据这个分数,我们可以对检索到的内容进行排序,只保留前 N 个最相关的结果。就像图 3 展示的那样,这种方法之所以有效,是因为重新排序模型能够捕捉到用户输入和内容之间更复杂的关系,而不仅仅是简单的相似性搜索。

不过,我们不会在初始检索阶段就用这个模型,因为它计算成本比较高。所以,一个常见的策略是:先用嵌入模型通过相似性距离来检索数据,然后再用重新排序模型对检索到的信息进行精细化处理。这个过程可以参考图 4 的示意。

简单来说,就是先粗筛,再精筛,既省资源又提升效果!

结论

上面提到的这些技术,远不是所有可能的解决方案。我们只是用它们来举个例子,让大家更直观地了解在 RAG 工作流程的每个环节中,有哪些地方可以(也应该)进行优化。

实际上,具体用哪些技术,很大程度上取决于你处理的数据类型。比如,如果你处理的是文本和图像这种多模态数据,那前面提到的大多数技术可能就不太适用了,因为它们主要是针对文本的。

总的来说,这些优化的核心目标是在 RAG 算法的三个关键阶段(检索前、检索、检索后)进行增强:

  • 检索前:通过预处理数据来优化向量索引,调整用户查询以提高搜索的准确性。
  • 检索:增强嵌入模型,或者利用经典的数据库过滤操作。
  • 检索后:清理掉噪声数据,确保信息的精准性。

只要牢记这些目标,你就能更好地优化 RAG 工作流程,让数据处理和检索变得更高效、更精准。

添加微信1185918903,关注公众号ChallengeHub获取更所咨询

原文链接

标签:检索,RAG,嵌入,秘籍,Get,查询,搜索,数据
From: https://www.cnblogs.com/quincyqiang/p/18664768

相关文章

  • [NOISG2022 Qualification] Dragonfly Solution in O(d log d)
    [NOISG2022Qualification]DragonflySolutioninO(dlogd)提供一个使用线段树合并、栈、树状数组的严格单\(\log\)离线做法。题目大意:给你一棵树,每个点有权值和颜色,每次问你一个从\(1\)开始的路径,求权值不为\(0\)的节点的颜色种类数,并且把所有权值不为\(0\)的节点权......
  • python学opencv|读取图像(三十)使用cv2.getAffineTransform()函数倾斜拉伸图像
    【1】引言前序已经学习了如何平移和旋转缩放图像,相关文章链接为:python学opencv|读取图像(二十七)使用cv2.warpAffine()函数平移图像-CSDN博客python学opencv|读取图像(二十八)使用cv2.getRotationMatrix2D()函数旋转缩放图像-CSDN博客在此基础上,我们尝试倾斜拉伸图【2】核心代码......
  • NFCAdapter.getNfcV
    NfcVNFCAdapter.getNfcV()基础库2.11.2开始支持,低版本需做兼容处理。小程序插件:支持微信iOS版:不支持微信Android版:支持相关文档:近场通信(NFC)功能描述获取NfcV实例,实例支持NFC-V(ISO15693)标准的读写返回值NfcV......
  • wx.getNFCAdapter
    NFCAdapterwx.getNFCAdapter()基础库2.11.2开始支持,低版本需做兼容处理。小程序插件:支持,需要小程序基础库版本不低于2.11.2微信iOS版:不支持微信Android版:支持相关文档:近场通信(NFC)功能描述获取NFC实例返回值NFCAdapterNFC实例错误错误码错误......
  • MifareClassic.getMaxTransceiveLength
    MifareClassic.getMaxTransceiveLength(Objectobject)基础库2.11.2开始支持,低版本需做兼容处理。以Promise风格调用:不支持小程序插件:支持微信iOS版:不支持微信Android版:支持相关文档:近场通信(NFC)功能描述获取最大传输长度参数Objectobject属性......
  • qt 实现窗口置顶,qtdesigner创建的widget窗口集成程序里的用法
    参考https://blog.csdn.net/Larry_Yanan/article/details/123518788.ui文件如下新建的ui文件,编译一下就会生成对应的ui_xxx.h文件,文件内就有对应的namespaceUi声明的变量,这个变量要在mainwindow.h中声明,然后在mainwindow.cpp中new出来,具体使用如下mainwindow.h#ifnde......
  • RAG项目实战——基于Llamaindex微调BGE Embedding模型(附完整源码和转化好的数据集下载
    在自然语言处理(NLP)领域,检索增强生成(Retrieval-AugmentedGeneration,RAG)模型已经成为一种强大的工具,能够结合检索和生成任务,提供更准确的回答。然而,RAG模型的性能很大程度上依赖于嵌入模型的质量。为了进一步提升RAG模型的检索准确性,我们可以通过对嵌入模型进行微调(Fine-t......
  • cv::reprojectImageTo3D 使用
    cv::reprojectImageTo3D是OpenCV中的一个函数,用于将视差图像转换为3D点云。它依赖于相机的内参和视差值来计算每个像素的3D坐标。以下是该函数的基本使用方法。函数原型voidcv::reprojectImageTo3D(constcv::Mat&disparity,cv::Mat&_3dImage,constcv......
  • Mock post和get请求--实操及解释代码
    Mockpost和get请求:深入理解与实践在现代软件开发中,单元测试是确保代码质量和功能正确性的重要手段。然而,在实际开发过程中,我们经常会遇到一些难以直接测试的场景,例如依赖外部API的HTTP请求。为了解决这一问题,我们可以使用Python的unittest模块和mock库来模拟这些外部请求,......
  • 分块的艺术:提升 RAG 效果的关键
    聪明人往往很“懒”,但这种“懒”其实是高效的体现。他们总能找到解决复杂问题的最佳路径,用最少的力气获得最大的成果。在RAG系统中,这种高效的实现往往是通过“分块”来实现的。你可以把它想象成把一本厚书分成几章——这样一来,阅读和理解就轻松多了。同样地,分块技术把大段复杂......