首页 > 其他分享 >检索增强生成(RAG)应用构建的最佳实践

检索增强生成(RAG)应用构建的最佳实践

时间:2024-11-19 10:15:35浏览次数:3  
标签:检索 RAG 嵌入 查询 构建 LLM 上下文

RAG,也就是检索增强生成,已经证明是个挺管用的招儿,能让大型语言模型(LLM)的回答更靠谱,还能搞定它们有时候会“幻觉”的问题。简单来说,RAG就是给LLM们提供了一些上下文,帮它们生成更准确、更符合上下文的回答。这些上下文可以来自各种地方,比如你的内部文件、向量数据库、CSV文件、JSON文件等等。

RAG里头有好多东西要一起工作。这些包括处理查询、切分上下文、检索上下文、重新排列上下文,还有LLM自己来生成回答。每个部分都会影响到RAG最后生成的回答质量。但是,要在每个部分找到最好的方法组合,让RAG的性能最优化,这事儿挺难的。

图片

这篇文章里,我们会聊聊RAG里面常用的一些技术,评价一下每个成分的最佳做法,然后根据这篇论文《Searching for Best Practices in Retrieval-Augmented Generation》找出能带来最优化RAG生成回答的最佳组合。那咱们就从RAG的各个部分开始讲起。

就像之前说的,RAG是个挺给力的方法,能帮LLM解决幻觉问题,这问题通常发生在我们问的问题超出了它们训练数据的范围,或者需要专业知识的时候。比如,如果我们问LLM一些关于我们内部数据的问题,可能会得到个不靠谱的答案。RAG通过提供能帮助回答我们问题的相关上下文来解决这个问题。

RAG是由一系列组成工作流程的部分构成的。典型的RAG部分包括:

  • 查询分类: 判断我们的查询是不是需要检索上下文,还是可以直接让LLM处理。

  • 上下文检索: 找到和我们查询最相关的前k个候选上下文。

  • 上下文重排: 对从检索部分拿到的前k个候选进行排序,从最相似的一个开始。

  • 上下文重新打包: 把最相关的上下文整理成更有结构的格式,这样生成回答的时候更好用。

  • 上下文摘要: 从相关的上下文中提取出关键信息,这样能改善生成的回答。

  • 响应生成: 根据查询和相关的上下文生成回答。

图片

图 - RAG组件

虽然RAG的这些组件在生成回答时很有帮助,但在我们真正用上RAG之前,还得考虑其他一些事儿。

首先,我们得把那些上下文文档转换成向量嵌入,这样它们才能在RAG里发挥作用。所以,挑一个最合适的嵌入模型和策略来表示我们的文档,这事儿特别关键。

一个嵌入就是输入文档的语义丰富表示。但如果作为上下文的文档太长,可能会让LLM在生成回答时犯迷糊。解决这问题的常见方法是用分块方法,把文档分成几个小块,然后每个小块都转换成嵌入。选个好的分块方法和大小很重要,因为太小的块可能信息不够,帮不上忙。

图片

图 - RAG工作流程

接下来,我们得想想怎么存这些嵌入。如果你不需要处理太多嵌入,直接存本地内存就行。但通常我们会处理成千上万甚至百万级别的嵌入,这时候就需要用向量数据库,比如Milvus,来存它们。选对向量数据库对我们RAG的成功很关键。

最后,我们还得考虑LLM本身。如果需要的话,我们可以对LLM进行微调,让它更符合我们的特定需求。不过微调成本挺高的,大多数情况下也没必要,尤其是如果我们用的是一个参数很多的高性能LLM。

接下来,我们会聊聊RAG每个部分的最佳实践。然后,我们会探索这些最佳实践的组合,并推荐几种既保持性能又高效的RAG部署策略。

就像之前提到的,RAG在确保LLM生成准确和上下文相关回答时特别有用,尤其是我们需要从内部数据中获取专业知识的时候。不过,RAG也会让生成回答的时间变长。事实上,不是所有查询都需要检索过程,很多查询LLM自己就能处理。所以,如果查询不需要检索,跳过上下文检索这一步会更有好处。

我们可以搞一个查询分类模型来决定是否需要检索上下文,然后再进行回答生成。这种分类模型通常是一个监督模型,比如BERT,主要任务是预测查询是否需要检索。和其他监督模型一样,我们得先训练它才能用。为了训练这个模型,我们需要准备一个数据集,里面包含示例提示和它们是否需要检索的二元标签。

图片

图 - 查询分类数据集示例

论文里头,他们用的是BERT-base-multilingual这个模型来做查询分类。训练数据包含了15种不同类型的提示,比如翻译、摘要、重写、上下文学习等等。这些提示有两种标签:“sufficient”表示提示里头的信息足够了,不需要再检索;“insufficient”则表示信息不全,需要专业知识,得检索一下。用这个方法,模型在准确率和F1分数上都达到了95%。

这个查询分类的步骤能帮我们省不少事儿,它能让RAG过程更高效,避免对那些LLM自己就能处理的查询进行不必要的检索。它就像个过滤器,确保只有真正需要额外上下文的查询才被送到更费时间的检索过程。

图片

图 - 查询分类结果

分块就是把长文档切成小段的过程,这对给LLM提供更细的上下文很有帮助。分块方法有好几种,比如基于令牌的和基于句子的。基于句子的分块在简单性和语义保留之间通常能取得不错的平衡。选分块方法的时候,我们得注意分块的大小,因为太小的块可能给不了LLM啥有用的上下文。

图片

图 - 把长文档切成小段

为了找到最合适的分块大小,他们对Lyft 2021的文档做了评估。选了文档的前60页作为语料库,然后切成了不同大小的块。接着用LLM基于这60页生成了170个查询。用来生成嵌入的模型是text-embedding-ada-002,而用来生成基于所选查询的响应的LLM是Zephyr 7B。

为了评估模型在不同分块大小下的表现,他们用了GPT-3.5 Turbo。他们用了两个指标来评价响应的质量:忠实度和相关性。忠实度是看响应是不是幻觉的,或者是不是和检索到的上下文匹配;相关性则是看检索到的上下文和响应是不是和查询匹配。

图片

图 - 不同分块大小比较

实验结果告诉我们,最大分块大小设为512个令牌是让LLM生成高度相关回答的最好选择。短一点的分块大小,比如256个令牌,表现也不错,而且还能提高RAG应用的整体运行速度。可以用一些高级的分块技术,比如Small2big和滑动窗口,来结合不同分块大小的优势。

Small2big这种分块方法挺聪明的,先用小尺寸的块去匹配查询,然后用包含这些小块信息的大一些的块作为LLM的最终上下文。滑动窗口方法则是在块之间留一些令牌重叠,这样可以保持上下文信息不丢失。

图片

图 - 不同分块技术比较

实验还发现,用175个令牌的小分块和512个令牌的大分块,再加上20个令牌的块重叠,这两种分块技术都能提高LLM回答的忠实度和相关性分数。

接下来,找到最好的嵌入模型来表示每个块作为向量嵌入也特别重要。为了这个目的,他们在namespace-Pt/msmarco上做了测试。结果显示,LLM Embedder和bge-large-en模型表现最好。但是LLM Embedder比bge-large-en小了三倍,所以它被选为实验的默认嵌入模型。

图片

图 - 不同嵌入模型在namespace-Pt/msmarco上的结果

向量数据库在RAG应用里扮演着特别重要的角色,尤其是在存储和检索相关上下文方面。在常见的现实世界RAG应用中,我们得处理一大堆文档,这意味着得存一大堆上下文嵌入。在这种情况下,把嵌入存在本地内存里是不够的,计算在一大堆嵌入里头检索相关上下文会花很长时间。

向量数据库就是用来解决这些问题的。有了向量数据库,我们可以存几百万甚至几十亿的向量嵌入,而且能瞬间完成上下文检索。在选择最好的向量数据库时,我们得考虑几个因素,比如支持的索引类型、能不能支持十亿级别的向量、是否支持混合搜索、有没有云原生能力。

按照这些标准,Milvus作为最好的开源向量数据库脱颖而出,和其他竞争对手比如Weaviate、Chroma、Faiss、Qdrant等相比,Milvus更胜一筹。

图片

图 - 各种向量数据库比较

Milvus在索引类型支持方面做得挺全面的,提供了好几种索引方法来满足不同的需求。比如,有简单的平面索引(FLAT),还有专门用来加速检索过程的倒排文件索引(inverted file index, IVF-FLAT)和 (Hierarchical Navigable Small World, HNSW))。如果你想要压缩存储上下文需要的内存,还可以在嵌入索引的过程中用到产品量化(PQ)。

Milvus还支持混合搜索方法,这让我们能在上下文检索过程中结合两种不同的方法。比如,我们可以同时用密集嵌入和稀疏嵌入来检索相关上下文,这样能提高检索到的上下文与查询的相关性,进而提升LLM生成的响应质量。如果需要的话,我们还可以结合密集嵌入和元数据过滤。

检索组件的主要任务是为给定的查询找到最相关的前k个上下文。不过,这个组件可能会受到查询本身的重大影响,因为原始查询通常写得不够好或者表达不清楚,缺少RAG应用检索相关上下文需要的语义信息。

解决这个问题的几个常用技术包括:

  • 查询重写: 让LLM重写原始查询,提高其清晰度和语义信息。

  • 查询分解: 把原始查询拆分成子查询,然后基于这些子查询执行检索。

  • 伪文档生成: 根据原始查询生成假设性的或合成的文档,然后用这些假设性文档去检索数据库中的类似文档。这种方法最著名的实现就是假设性文档嵌入,也就是HyDE(Hypothetical Document Embeddings)。

实验显示,结合HyDE和混合搜索在TREC DL19/20上取得了最好的结果,比单独使用查询重写和查询分解效果要好。实验中提到的混合搜索结合了LLM Embedder来获得密集嵌入,并结合BM25来获得稀疏嵌入。

HyDe + hybrid search的工作流程是这样的:首先,我们用HyDE生成回答查询的假设性文档。然后,这个假设性文档和原始查询连接起来,接着分别用LLM Embedder和BM25把它们转换成密集和稀疏嵌入。这样,我们就能更有效地检索到与查询最相关的上下文了。

图片

图 - 不同检索方法结果

尽管结合HyDE和hybrid search取得了最佳结果,但它也带来了更高的计算成本。基于对几个NLP数据集的进一步测试,hybrid search和仅使用密集嵌入的结果与HyDE + hybrid search相当,但延迟几乎降低了10倍。因此,建议使用hybrid search

由于我们使用hybrid search,检索到的上下文基于来自密集和稀疏嵌入的向量搜索。因此,还有趣的是,根据这个方程,检查密集和稀疏嵌入之间的权重值对整体相关性分数的影响:

图片

图 - hybrid search不同alpha值结果

实验表明,权重值为0.3在TREC DL19/20上获得了最佳的整体相关性分数。

重排和重新打包技术

重排技术的核心目标是重新排序从检索方法中得到的最相关的前k个上下文,确保最相似的上下文排在列表的最前面。重排上下文主要有两种方法:

  • DLM重排: 这种方法用深度学习模型来进行重排。模型训练时,输入是原始查询和上下文的配对,输出是二元标签,比如“真”表示这对查询和上下文是相关的,“假”则表示不相关。然后,根据模型预测查询和上下文配对为“真”的概率来对上下文进行排序。

  • TILDE重排: 这种方法依据原始查询中每个词的概率来进行重排。在推理阶段,我们可以使用仅基于查询概率的组件(TILDE-QL)来进行更快的重排,或者使用TILDE-QL和文档概率组件(TILDE-DL)的组合,虽然这样计算成本会更高,但能改善重排结果。

图片

图 - 不同重排方法结果

在MS MARCO Passage排名数据集上的实验显示,使用Llama 27B模型的DLM重排方法提供了最好的重排性能。但是,由于这是一个大型模型,使用它会有明显的计算成本。因此,推荐使用Mono T5来进行DLM重排,因为它在性能和计算成本之间取得了平衡。

重排阶段结束后,我们还得考虑如何把重排后的上下文呈现给我们的LLM:是按降序(“正向”)还是升序(“反向”)。根据本文的实验,发现使用“反向”配置能生成质量最好的响应。推测是因为将更相关的上下文放在更接近查询的位置,可能会让LLM产生更准确和连贯的响应。

面对从前面几个组件检索到的长上下文时,我们可能希望让它们更紧凑,去掉冗余信息。为了实现这个目标,通常会采用摘要方法。

上下文摘要技术主要有两种:提取式和抽象式。

提取式摘要是把输入文档切分成小段落,然后根据重要性进行排序。而抽象方法则是生成一个只包含重要信息的新上下文摘要。

图片

图 - 不同摘要方法比较

基于NQ、TriviaQA和HotpotQA三个不同数据集的实验显示,与其他抽象和提取方法相比,使用Recomp的抽象摘要方法表现最佳。

现在我们知道了每个RAG成分的最佳方法,我们可以在更多数据集上测试前面提到的所有方法。结果表明,每个成分都对我们RAG应用的整体性能有所贡献。以下是根据五个不同数据集的结果总结,每个成分中的每种方法:

图片

图 - 寻找最佳RAG实践结果

查询分类组件在提升响应准确性和减少整体运行时间上发挥了重要作用。这个步骤帮助我们判断一个查询是否需要进行上下文检索,或者是否可以由LLM直接处理,从而提高了系统的效率。

检索组件对于确保我们能够获得与查询相关的上下文候选非常关键。对于这个组件,推荐使用像Milvus这样可扩展且性能高的向量数据库。此外,建议采用混合搜索或仅密集嵌入搜索,这些方法在全面匹配上下文和计算效率之间取得了平衡。

重排组件通过重新排序从检索组件获得的前k个上下文,确保我们能够得到最相关的上下文。鉴于其在性能和计算成本之间的平衡,推荐使用Mono T5模型进行重排。这一步进一步细化了上下文的选择,优先考虑与查询最相关的上下文。

在重新打包上下文时,建议采用“反向”方法。这种方法将最相关的上下文放在离查询最近的位置,可能会使LLM生成更准确和连贯的响应。

最后,在上下文摘要方面,使用Recomp的抽象方法表现最佳。这种技术有助于压缩长上下文,同时保留关键信息,使LLM更容易处理并生成相关响应。

在大多数情况下,LLM微调并不是必需的,特别是如果你使用的是一个参数众多的高性能LLM。然而,如果你由于硬件限制只能使用较小的LLM,可能需要对它们进行微调,以使它们在生成与你用例相关的响应时更加健壮。在微调LLM之前,需要考虑你将用作训练数据的数据。

在数据准备阶段,可以收集以提示和上下文作为一对输入,以生成的文本作为输出的训练数据。实验表明,在训练期间混合使用相关和随机选择的上下文数据将获得最佳性能。背后的直觉是,在微调期间混合相关和随机上下文可以提高LLM的鲁棒性。

本文探讨了从查询分类到上下文摘要的各个RAG组件,并讨论并强调了每个组件中的最优方法。这些优化的组件协同工作,提高了RAG系统的整体性能,提升了生成响应的质量和相关性,同时保持了计算效率。通过在每个组件中实施这些最佳实践,我们可以创建一个更强大有效的RAG系统,能够处理广泛的查询和任务。

参考论文:Searching for Best Practices in Retrieval-Augmented Generation, https://arxiv.org/pdf/2407.01219

标签:检索,RAG,嵌入,查询,构建,LLM,上下文
From: https://blog.csdn.net/qianggezhishen/article/details/143849550

相关文章

  • 使用Pytorch构建视觉语言模型(VLM)
    视觉语言模型(VisionLanguageModel,VLM)正在改变计算机对视觉和文本信息的理解与交互方式。本文将介绍VLM的核心组件和实现细节,可以让你全面掌握这项前沿技术。我们的目标是理解并实现能够通过指令微调来执行有用任务的视觉语言模型。总体架构VLM的总体架构包括:图像编码器(I......
  • 大规模向量检索与量化方法
    1.向量检索在向量检索中,KNN(K-NearestNeighbors)和ANN(ApproximateNearestNeighbor)是两种最常见的方法,它们都用于根据特征向量找到数据点之间的相似性,但它们在精确度和效率上有所不同。KNN是一种基本的分类和回归方法,它根据一个样本在特征空间中的K个最近邻样本的类别,来预测该......
  • PGML:向量数据库内一体化的RAG框架
    架构总览特性:●支持数据库中进行的ai和ml分析●支持gpu加速●集成多种开源llm和rag框架●支持传统的机器学习模型使用方法云端试用官方提供了云服务试用,根据要求注册账号即可:注册地址本地部署官方提供了docker镜像,执行如下命令即可安装dockerrun\-it\......
  • 运维系列:Docker学习笔记(3)-- 如何使用Dockerfile构建镜像
    Docker学习笔记(3)--如何使用Dockerfile构建镜像Docker学习笔记(3)--如何使用Dockerfile构建镜像1.Dockerfile的书写规则及指令使用方法(1)FROM(指定基础image)该指令有两种格式:(2)MAINTAINER(用来指定镜像创建者信息)格式:(3)RUN(安装软件用)该指令有两种格式:......
  • 金融行业客户运营知识库:构建数字化知识库
    在金融行业,客户运营知识库的构建是提升服务质量、优化客户体验、增强企业竞争力的关键。随着数字化转型的深入,构建数字化知识库已成为金融行业客户运营的重要趋势。本文将探讨金融行业客户运营知识库构建的重要性、挑战以及如何利用“HelpLook”工具实现数字化知识库的构建。​一......
  • 构建企业级 Agent 系统:核心组件设计与优化
    引言构建企业级AIAgent系统需要仔细考虑组件设计、系统架构和工程实践。本文将探讨构建稳健可扩展的Agent系统的关键组件和最佳实践。1.Prompt模板工程1.1模板设计模式fromtypingimportProtocol,Dictfromjinja2importTemplateclassPromptTemplate(Protocol......
  • 清华姚班校友马腾宇,发布了他的首个多模态嵌入模型:「多模态检索」实现SOTA
    清华姚班校友马腾宇和他的团队,推出了自创业以来的首个多模态嵌入模型voyage-multimodal-3,而且发布即“SOTA”。据介绍,在对3个多模态检索任务(共20个数据集)进行评估时,voyage-multimodal-3比第二名平均高出了19.63%的检索准确率。这是为包含丰富视觉和文本的文档提供......
  • 使用 PyTorch 从头构建最小的 LLM 该项目构建了一个简单的字符级模型
    简介我开始尝试各种受Pokémon启发的猫名变体,试图赋予它独特、略带神秘感的氛围。在尝试了“Flarefluff”和“Nimblepawchu”等名字后,我突然想到:为什么不完全使用人工智能,让字符级语言模型来处理这个问题呢?这似乎是一个完美的小项目,还有什么比创建自定义Pokémon名......
  • Memcached&Redis构建缓存服务器 (主从,持久化,哨兵)
    许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少数据库访问次数,......
  • 大模型实战(二):langchain+Ollama调用本地大模型实现RAG(保姆级)
    文章目录一、任务描述1.环境2.功能二、代码拆解1.导入包2.配置本地模型3.实例化embedding模型4.导入向量化知识库5.加入提示词6.定义查询方法7.问答三、总体代码一、任务描述由于显卡仍然较为昂贵,个人笔记本的硬件条件很难带动大模型,因此我们可以调用一......