简介
RAG(Retrieval-Augmented Generation),LLM相比传统算法最重要的就是zero shot能力,最重要的是从海量的文档中找到和问题相关的片段。找到后使用LLM生成回答其实很鸡肋,尤其是针对技术问题的回答,不需要LLM 去造答案,LLM 就帮我找到相关文档就行了。RAG 的主要目标是通过利用检索的优势来增强生成过程,使 NLG 模型能够生成更合理且适合上下文的响应。通过将检索中的相关信息纳入生成过程,RAG 旨在提高生成内容的准确性、连贯性和信息量。With RAG, we augment the knowledge base of an LLM by inserting relevant context into the prompt and relying upon the in context learning abilities of LLMs to produce better output by using this context.
引入外部知识 的几个示例
大语言模型的原理,就是利用训练样本里面出现的文本的前后关系,通过前面的文本对接下来出现的文本进行概率预测。如果类似的前后文本出现得越多,那么这个概率在训练过程里会收敛到少数正确答案上,回答就准确。如果这样的文本很少,那么训练过程里就会有一定的随机性,对应的答案就容易似是而非。
LLM 擅长于一般的语言理解与推理,而不是某个具体的知识点。如何为ChatGPT/LLM大语言模型添加额外知识?
- 通过fine-tuning来和新知识及私有数据进行对话,OpenAI 模型微调的过程,并不复杂。你只需要把数据提供给 OpenAI 就好了,对应的整个微调的过程是在云端的“黑盒子”里进行的。需要提供的数据格式是一个文本文件,每一行都是一个 Prompt,以及对应这个 Prompt 的 Completion 接口会生成的内容。
{"prompt": "<prompt text>", "completion": "<ideal generated text>"} {"prompt": "<prompt text>", "completion": "<ideal generated text>"} {"prompt": "<prompt text>", "completion": "<ideal generated text>"} ...
有了准备好的数据,我们只要再通过 subprocess 调用 OpenAI 的命令行工具,来提交微调的指令就可以了。
subprocess.run('openai api fine_tunes.create --training_file data/prepared_data_prepared.jsonl --model curie --suffix "ultraman"'.split())
微调模型还有一个能力,不断收集新的数据,不断在前一个微调模型的基础之上继续微调我们的模型。
- 通过word embeddings + pinecone数据库来搭建自己私有知识库。 chatgpt预训练完成后,会生成一个embeddings向量字典,比如我们可以将我们的私有知识库各个章节通过openai的相关api获取到对应的embeddings,然后将这些embeddings保存到向量数据库(比如 Facebook 开源的 Faiss库、Pinecone 和 Weaviate),当用户要对某个领域后者问题进行语义查询时,则将用户的输入同样通过openai的相关api来获取相应的embeddings向量,然后再和向量数据库pinecone中的我们的私有知识库类型做语义相似度查询,然后返回给用户。PS: 内容向量化
- 比如判断某一段文本 是积极还是消极,向chatgpt 查询目标文本的向量,然后计算其与“积极” “消极” 两个词 embedding 向量的“距离”,谁更近,说明这段文本更偏向于积极或消极。
- 过几天openAI的模型版本升级了,这些保存的embedding会失效吗?特定模型也有带日期的快照版本,选取那些快照版本就好了。
- 向量是基于大模型生成的,因此对两段文本向量相似度计算必须基于同一个模型,不同的模型算出来的向量之间是没有任何关系的,甚至连维数都不一样。不过你可以把基于A 模型来算向量相似度进行检索把文本找出来,然后把找到的文本喂给B模型来回答问题。
- 通过langchain这个chatgpt编程框架来给chatgpt赋能。 langchain可以将不同的工具模块和chatgpt给链接(chain)起来。
- chatgpt 插件,比如有一个提供酒旅租车信息的插件
比如针对问题:鲁迅先生去日本学习医学的老师是谁。因为 LLM(大语言模型)对上下文长度的限制,你不能将《藤野先生》整体作为提示语然后问“鲁迅在日本的医学老师是谁?”。 先通过搜索的方式,找到和询问的问题最相关的语料。可以用传统的基于关键词搜索的技术。也可以先分块存到向量数据库中(向量和文本块之间的关系),使用 Embedding 的相似度进行语义搜索的技术。然后,我们将和问题语义最接近的前几条内容,作为提示语的一部分给到 AI(使用检索结果作为 LLM 的 Prompt)。然后请 AI 参考这些内容,再来回答这个问题。
这也是利用大语言模型的一个常见模式(这个模式实在太过常用了,所以有人为它写了一个开源 Python 包,叫做 llama-index)。因为大语言模型其实内含了两种能力。PS:有点像推荐的粗排和精排,纯向量化的召回在一些Benchmark上表现还不如关键字搜索。
- 海量的语料中,本身已经包含了的知识信息。比如,我们前面问 AI 鱼香肉丝的做法,它能回答上来就是因为语料里已经有了充足的相关知识。我们一般称之为“世界知识”。
- 根据你输入的内容,理解和推理的能力。这个能力,不需要训练语料里有一样的内容。而是大语言模型本身有“思维能力”,能够进行阅读理解。这个过程里,“知识”不是模型本身提供的,而是我们找出来,临时提供给模型的。如果不提供这个上下文,再问一次模型相同的问题,它还是答不上来的。
Embedding 生成向量使用的模型 跟最后prompt 调用的模型可以不是同一个,因此有的向量数据库也包含Segment 和 Embedding,通过自然语言就能直接和向量数据库交互。也就是说,我们可以直接把文档扔给数据库,大段文本的切分,以及文本向量化,向量数据库 会帮我们处理。我们也可以直接把问题扔给数据库,请他来查询相似度较高的文本块,问题向量化以及检索的细节,向量数据库会帮我们处理。
- 传统搜索系统基于关键字匹配,在面向:游戏攻略、技术图谱、知识库等业务场景时,缺少对用户问题理解和答案二次处理能力。
- 领域知识不在预训练的数据集中,比如:
- 较新的内容。同一个知识点不断变更:修改、删除、添加。如何反馈当前最新的最全面的知识。比如对于 ChatGpt 而言,训练数据全部来自于 2021.09 之前。
- 未公开的、未联网的内容。
- 基于 LLM 搭建问答系统的解决方案有以下几种:
- Fine-Tuning
- 基于 Prompt Engineering,比如 Few-Shot方式。将特定领域的知识作为输入消息提供给模型。类似于短期记忆,容量有限但是清晰。举个例子给 ChatGPT 发送请求,将特定的知识放在请求中,让 ChatGPT 对消息中蕴含的知识进行分析,并返回处理结果。
- 与普通搜索结合,使用基础模型对搜索结果加工。在做问答时的方式就是把 query 转换成向量,然后在文档向量库中做相似度搜索。
- 用户输入query之后,首先先从知识库搜索到结果,然后基于搜索到的结果进行解析构造,生成新的prompt,然后调用LLM,LLM根据输入的prompt自行进行知识库的检索与plugins的调用
多文档问答难点
上下文注入,即不修改LLM,专注于提示本身,并将相关上下文注入到提示中,让模型参考这个提示进行作答,但是其问题在于如何为提示提供正确的信息。目前我们所能看到的就是相关性召回,其有个假设,即问题的答案在召回的最相似的文档里。
单文档问答,即类似ChatPDF的需求,ChatPDF的目标是尽可能全面地总结出整篇文档的信息。这个需求以目前ChatGPT可接受的16K上下文而言,不少文档可以直接丢进去问答,不需要使用召回工具先做知识检索。PS:慢慢随着技术进步不成问题。
如果我们看一下RAG系统中的流程链:
- 将文本分块(chunking)并生成块(chunk)的Embedding
- 通过语义相似度搜索检索数据块
- 根据top-k块的文本生成响应 我们会看到所有的过程都是有信息损失的,这意味着不能保证所有的信息都能保存在结果中。如果我们把所有的限制放在一起,重新考虑一些公司即将推出的基于RAG的企业搜索,我真的很好奇它们能比传统的全文搜索引擎好多少。
RAG探索之路的血泪史及曙光 值得细读。
深入剖析开源大模型+Langchain框架智能问答系统性能下降原因常规方案中对智能问答系统准确率影响最大的几个因素如下:
- embedding 在 Retrieval 任务中 TopK 的准确率 (受 embedding 模型自身能力、Retrieval 算法、K等三个因素影响)
- K 的大小,K 原则越大越好,但是 LLM 的 tokens 限制导致 K 由不能太大。(上述 1、2 的都是为了从数据库海量 chunks 中选择出包含正确答案的 chunks)
- LLM 自身阅读理解与总结推理能力。
分级
如何让LLM简要、准确回答细粒度知识?如何让LLM回答出全面的粗粒度(跨段落)知识?QA 的难度主要在于回答这个问题所依赖的信息在长文档中的分布情况,具体可以大致分为下面三种情况:
- 相关信息可以出现在不超过一个固定上下文长度(512)限制的 block 以内
- 相关信息出现在多个 block 里,出现的总数不超过 5-10 个,最好是能在大模型支持的有效长度内。
- 需要考虑多个片段,并综合结合上下文信息甚至全部内容才能得到结果。 PS: 可能要知识图谱帮一点忙。
受限于数据集的大小和规模以及问题的难度,目前主要研究偏向于 L1 和 L2 。能比较好的整合和回答 L1 类问题,L2 类问题也有比较不错的结果,但对于 L3 类问题,如果所涉及到的片段长度过长,还是无法做到有效的回答。了解RAG能做什么和不能做什么,可以让我们为RAG寻找最适合的领域,避免强行进入错误的地方。
文档处理
如何将文档拆分为文本片段。主要有两种,一种就是基于策略规则,另外一种是基于算法模型。
- 如何保证文档切片不会造成相关内容的丢失?一般而言,文本分割如果按照字符长度进行分割,这是最简单的方式,但会带来很多问题。例如,如果文本是一段代码,一个函数被分割到两段之后就成了没有意义的字符。因此,我们也通常会使用特定的分隔符进行切分,如句号,换行符,问号等。可以使用专门的模型去切分文本,尽量保证一个chunk的语义是完整的,且长度不会超过限制。
- 文档切片的大小如何控制? 太小则 容易造成信息丢失,太大则不利于向量检索命中。此外还要考虑LLM context 长度的限制,GPT-3.5-turbo 支持的上下文窗口为 4096 个令牌,这意味着输入令牌和生成的输出令牌的总和不能超过 4096,否则会出错。为了保证不超过这个限制,我们可以预留约 2000 个令牌作为输入提示,留下约 2000 个令牌作为返回的消息。这样,如果你提取出了五个相关信息块,那么每个片的大小不应超过 400 个令牌。最详细的文本分块(Chunking)方法——可以直接影响基于LLM应用效果
- 基于算法模型,主要是使用类似 BERT 结构的语义段落分割模型,能够较好的对段落进行切割,并获取尽量完整的上下文语义。需要微调,上手难度高,而且切分出的段落有可能大于向量模型所支持的长度,这样就还需要进行切分。
经常遇到一些复杂文档的情况,这些文档中可能有表格,有图片,有单双栏等情况。尤其是对于一些扫描版本的文档时候,则需要将文档转换成可以编辑的文档,这就变成了版面还原的问题。具体的,可以利用ppstructrue进行文档版面分析,在具体实现路线上,图像首先经过版面分析模型,将图像划分为文本、表格、图像等不同区域,随后对这些区域分别进行识别。
- pdf pdf解析关键问题
- 表格 Langchain下利用MutiVector Retriever更好支持RAG架构下表格文字混合内容问答 完全指南——使用python提取PDF中的文本信息(包括表格和图片OCR)
利用 LLM 对知识语料进行增强和扩充。对一篇文档/chunk生成知识点、问题、短摘要,当根据query 进行匹配时,可能先匹配到知识点、问题、短摘要,再找到原始chunk。MultiVectorRetriever/ParentDocumentRetriever 。
文档切片最好是按照语义切割。
- 将包含主从关系的段落合并,保证每一段在说同一件事情。
- 可以利用NLP的篇章分析(discourse parsing)工具提取出段落之间的主要关系,或利用BERT等模型来实现语义分割。BERT等模型在预训练的时候采用了NSP(next sentence prediction)的训练任务,因此BERT完全可以判断两个句子(段落)是否具有语义衔接关系。这里我们可以设置相似度阈值t,从前往后依次判断相邻两个段落的相似度分数是否大于t,如果大于则合并,否则断开。
- 合并后的段落提取关键信息。
- 利用NLP中的成分句法分析(constituency parsing)工具和命名实体识别(NER)工具提取,前者可以提取核心部分(名词短语、动词短语……),后者可以提取重要实体(货币名、人名、企业名……)。
- 可以用语义角色标注(Semantic Role Labeling)来分析句子的谓词论元结构,提取“谁对谁做了什么”的信息作为关键信息。
- NLP的研究中本来就有关键词提取工作(Keyphrase Extraction)。一个工具是 HanLP ,中文效果好,但是付费,免费版调用次数有限。还有一个开源工具是KeyBERT,英文效果好,但是中文效果差。
- 垂直领域建议的方法。以上两个方法在垂直领域都有准确度低的缺陷,垂直领域可以仿照ChatLaw的做法,即:训练一个生成关键词的模型。ChatLaw就是训练了一个KeyLLM。
- 对关键信息做embedding
- 问题查询时,先query 向量检索到关键信息,再由关键信息找到段落文本
LangChain+LLM大模型问答能力搭建与思考一般通用分段方式,是在固定max_length的基础上,对出现。/;/?/....../\n
等等地方进行切割。但这种方式显然比较武断,面对特殊情况需要进一步优化。比如1.xxx, 2.xxx, ..., 10.xxx
超长内容的情况,直接按这种方法切割就会导致潜在的内容遗漏。对于这种候选语料”内聚性“很强的情况,容易想到,我们要么在切割语料时动手脚(不把连续数字符号所引领的多段文本切开);要么在切割时照常切割、但在召回时动手脚(若命中了带连续数字符号的这种长文本的开头,那么就一并把后面连续数字符号引领的多段文本一起召回)。笔者目前只想到了这两种方法且还没具体做实验,只是凭空想来,前者方案有较明显瑕疵,因为这样会
- 相对于更短文段而言,长文段的语义更丰富,每个单独的语义点更容易被淹没,所以在有明确语义query的召回下这种长文段可能会吃亏;
- 长文段一旦被召回,只要不是针对整段文本的提问,那么也是引入了更多的噪声(不过鉴于LLM的能力,这可能也无伤大雅,就是费点显存or接口费用了) 但后者就显得更灵活些,不过确实也不够聪明。暂时没想到其他办法,有好想法的人可以来交流一下~此外,有研究表明,长文本作为输入LLM输入时,LLM倾向于更关注长文本的开头、结尾处,然而中间部分的语义可能会被忽略。
对于大模型RAG技术的一些思考针对各种类型的文档,分别进行了很多定制化的措施,用于完整的提取文档内容。
- Doc类文档还是比较好处理的,直接解析其实就能得到文本到底是什么元素,比如标题、表格、段落等等。这部分直接将文本段及其对应的属性存储下来,用于后续切分的依据。
- PDF类文档的难点在于,如何完整恢复图片、表格、标题、段落等内容,形成一个文字版的文档。这里使用了多个开源模型进行协同分析,例如版面分析使用了百度的PP-StructureV2,能够对Text、Title、Figure、Figure caption、Table、Table caption、Header、Footer、Reference、Equation10类区域进行检测,统一了OCR和文本属性分类两个任务。
- PPT的难点在于,如何对PPT中大量的流程图,架构图进行提取。因为这些图多以形状元素在PPT中呈现,如果光提取文字,大量潜藏的信息就完全丢失了。于是这里只能先将PPT转换成PDF形式,然后用上述处理PDF的方式来进行解析。
- 当然,这里还没有解决出图片信息如何还原的问题。大量的文档使用了图文混排的形式,例如上述的PPT文件,转换成PDF后,仅仅是能够识别出这一块是一幅图片,对于图片,直接转换成向量,不利于后续的检索。所以我们只能通过一个较为昂贵的方案,即部署了一个多模态模型,通过prompt来对文档中的图片进行关键信息提取,形成一段摘要描述,作为文档图片的索引。
对于每个文档,实际上元素的组织形式是树状形式。例如一个文档包含多个标题,每个标题又包括多个小标题,每个小标题包括一段文本等等。我们只需要根据元素之间的关系,通过遍历这颗文档树,就能取到各个较为完整的语义段落,以及其对应的标题。有些完整语义段落可能较长,于是我们对每一个语义段落,再通过大模型进行摘要。这样文档就形成了一个结构化的表达形式: |id| text| summary| source| type| image_source| |–|–|–|–|–|–| |1| 文本原始段落| 文本摘要| 来源文件| 文本元素类别(主要用于区分图片和文本)| 图片存储位置(在回答中返回这个位置,前端进行渲染)|
文档召回
文档召回过程中如何保证召回内容跟问题是相关的? 或者说,如何尽可能减少无关信息? 召回数据相关性的影响方面很多,既包括文档的切分,也包括文档query输入的清晰度,因此现在也出现了从多query、多召回策略以及排序修正等多个方案。
召回的输入(不一定是用户原输入)?召回的输出(不一定是Vectordb原输出)?召回的方式(不一定直接查vectordb,微调embedding)?
优化召回的输入/如何使检索对用户输入的变异性稳健/优化发送给检索器的搜索查询?
- 查询转换
- 将用户问题采用多个不同的视角去提问,借助子问题检索,然后 LLM 会得出最终结果。大多数人在问问题的过程中,如果不懂 prompt 工程,往往不专业,要么问题过于简单化,要么有歧义,意图不明显。那么向量搜索也是不准确的,导致LLM回答的效果不好。所以需要 LLM 进行问题的修正和多方位解读。MultiQueryRetriever 。使用RAG-Fusion和RRF让RAG在意图搜索方面更进一步
- 结合历史对话的重新表述,在进行多轮对话时,用户的提问中的某个词可能会指代上文中的部分信息,因此可以将历史信息和用户提问一并交给LLM重新表述。
- 假设文档嵌入(HyDE),核心思想是接收用户提问后,先让LLM在没有外部知识的情况下生成一个假设性的回复。然后,将这个假设性回复和原始查询一起用于向量检索。假设回复可能包含虚假信息,但蕴含着LLM认为相关的信息和文档模式,有助于在知识库中寻找类似的文档。
- 退后提示(Step Back Prompting):如果原始查询太复杂或返回的信息太广泛,我们可以选择生成一个抽象层次更高的“退后”问题,与原始问题一起用于检索,以增加返回结果的数量。例如,原问题是“桌子君在特定时期去了哪所学校”,而退后问题可能是关于他的“教育历史”。这种更高层次的问题可能更容易找到答案。
- 关键信息抽取:关键信息抽取是通过自然语言处理技术从用户提问中提取关键词或短语,以便更准确地检索信息。基于LLM的关键词抽取 再看RAG在真实金融文档问答场景的实践方案:SMP2023 金融大模型挑战赛的两种代表实现思路
- 采用In-Context Learning的关键词抽取方案。通过构造history,模拟多轮对话的方式进行,让模型能稳定输出json,对于异常json通过调整temperature=1加上retry多次,使其更稳定输出。PS:有点意思,带有历史记录的 icl
- 有监督方案
- Finetune 向量模型。embedding 模型 可能从未见过你文档的内容,也许你的文档的相似词也没有经过训练。在一些专业领域,通用的向量模型可能无法很好的理解一些专有词汇,所以不能保证召回的内容就非常准确,不准确则导致LLM回答容易产生幻觉(简而言之就是胡说八道)。可以通过 Prompt 暗示 LLM 可能没有相关信息,则会大大减少 LLM 幻觉的问题,实现更好的拒答。
- 大模型应用中大部分人真正需要去关心的核心——Embedding
- 分享Embedding 模型微调的实现 ,此外,原则上:embedding 所得向量长度越长越好,过长的向量也会造成 embedding 模型在训练中越难收敛。
- 手工微调embedding模型,让RAG应用检索能力更强 未细读
- 如何提高LLMs的文本表征(Text Embedding)能力?
- 大模型落地技术总结:大模型幻觉的起因、评估及落地场景下基于知识图谱的缓解策略探索
- 许多向量存储支持了对元数据的操作。LangChain 的 Document 对象中有个 2 个属性,分别是page_content和metadata,metadata就是元数据,我们可以使用metadata属性来过滤掉不符合条件的Document。元数据过滤的方法虽然有用,但需要我们手动来指定过滤条件,我们更希望让 LLM 帮我们自动过滤掉不符合条件的文档。SelfQueryRetriever
-
增加追问机制。这里是通过Prompt就可以实现的功能,只要在Prompt中加入“如果无法从背景知识回答用户的问题,则根据背景知识内容,对用户进行追问,问题限制在3个以内”。这个机制并没有什么技术含量,主要依靠大模型的能力。不过大大改善了用户体验,用户在多轮引导中逐步明确了自己的问题,从而能够得到合适的答案。
def ask_questions(model, context, query, max_follow_ups=3): follow_ups = 0 while follow_ups < max_follow_ups: if model.can_answer(query, context): return model.answer(query, context) else: follow_up_query = generate_follow_up_query(query, context) query = follow_up_query follow_ups += 1 return "I'm sorry, I couldn't find an answer to your question." def generate_follow_up_query(current_query, context): # 这里可以根据上下文和当前查询生成一个追问 # 例如,询问用户是否需要更具体的信息 return "Can you please provide more details or clarify your question?" # 示例 context = "The capital of France is Paris." query = "What is the capital?" answer = ask_questions(model, context, query) print(answer)
在专业的垂直领域,待检索的文档往往都是非常专业的表述,而用户的问题往往是非常不专业的白话表达。所以直接拿用户的query去检索,召回的效果就会比较差。Keyword LLM就是解决这其中GAP的。例如在ChatDoctor中会先让大模型基于用户的query生成一系列的关键词,然后再用关键词去知识库中做检索。ChatDoctor是直接用In-Context Learning的方式进行关键词的生成。我们也可以对大模型在这个任务上进行微调,训练一个专门根据用户问题生成关键词的大模型。这就是ChatLaw中的方案。
多种召回方式
- 同时使用了es 和向量召回。 EnsembleRetriever。 |倒排召回|向量召回|知识图谱召回| |—|—|—| |检索速度更快||| |精确匹配能力强,比如一些专有名词、人名、产品名、缩写、id、低频词等|考虑语义相似性,更加智能|实体-关系检索| |没有语义信息,对”一词多义”现象解决的不好|不理解专有词汇,容易出现语义相似但主题不相似的情况|
- 对于两个不同检索方式(关键词检索、矢量检索)得到的召回数据,分数范围不一致,一个比较直接的想法就是对分数做归一化,然后把归一化后的数据做权重加和,得到最终分数。
- RRF,比单独的lexical search和单独的semantic search的效果要好,RRF存在两个问题:
- RRF只是对召回的topk数据的顺序进行近似排序计算,并有真正的对数据顺序计算。
- RRF只关注topk数据的位置,忽略了真实分数以及分布信息。
- 用rerank 对query + chunk 相关性进行打分。
- RRF,比单独的lexical search和单独的semantic search的效果要好,RRF存在两个问题:
- 合并检索时首先需要将文档按特定的层次结构进行切割,比如按两层结构进行切割即首先将文档按块大小(chunk_size)为1024进行切割,切割成若干个大文档块,然后每个大文档块(chunk_size=1024)再被切分成4个块大小为512的子文档块,那么这些子文档块就是所谓的叶子节点,而子文档块所属的大文档块就是所谓的父节点,而在检索时只拿叶子节点和问题进行匹配,当某个父节点下的多数叶子节点都与问题匹配上则将该父节点作为context返回给LLM。
- 在知识问答系统中,检索成本是一个不容忽视的问题。随着知识库的增长,可能需要遍历大量文档,导致检索速度缓慢,在检索大量信息时,如何确保检索结果的相关性和准确性也是一个挑战。层次检索是一种优化策略,它通过构建知识库的层次结构来减少检索范围,从而提高检索效率。实践方法:分析知识库的结构,建立层次索引;从顶层开始检索,逐步向下深入到具体的文档或信息片段;在每个层次上应用剪枝策略,只保留最相关的部分进行进一步检索。
- 多向量检索同样会给一个知识文档转化成多个向量存入数据库,不同的是,这些向量不仅包括文档在不同大小下的分块,还可以包括该文档的摘要,用户可能提出的问题等等有助于检索的信息。在使用多向量查询的情况下,每个向量可能代表了文档的不同方面,使得系统能够更全面地考虑文档内容,并在回答复杂或多方面的查询时提供更精确的结果。例如,如果查询与文档的某个具体部分或摘要更相关,那么相应的向量就可以帮助提高这部分内容的检索排名。
- 多向量检索也适用于包含文本和表格混合的半结构化文档。在这些情况下,可以提取每个表,生成适合检索的表摘要,但将原始表返回到LLM进行答案合成。
优化召回输出/Post-Processing,如何合并我检索到的文件?因为Content Windows的大小有限。
- 如果一篇文档与查询非常相关,但与已经呈现给用户的文档非常相似,那么这篇文档的边际收益可能就不大。MMR 是一种广泛应用于信息检索和自然语言处理领域的算法。MMR 的主要目标是在文档排序和摘要生成等任务中平衡相关性和新颖性。换句话说,MMR 旨在为用户提供既相关又包含新信息的结果。
- 训练一个排序模型的方式对Topk 进行进一步相关性打分。建议以上游打分 Topk 作为训练数据,特别是结合真实的用户反馈数据。
- 文档重排:LLM 对位置是相对比较敏感的,得分好的放在首或尾,LLM会重点关注。LongContextReorder 。Rerank——RAG中百尺竿头更进一步的神器,从原理到解决方案
上述一些方法是以时间换效果,并且query改写成多个,多个容易漂移,而且选项太多对于排序也有影响,这个提分不明显,是优化阶段要做的事,重点要放在文本切割上。因此,如何增强大模型自身的知识,或许才是正道?但这明显十分漫长。
对LLM的要求
对于 RAG 来说,LLM 最基础也是最重要的能力其实包含:
- 摘要能力;
- 可控性:既 LLM 是否听话,是否会不按照提示要求的内容自由发挥产生幻觉;
- 翻译能力,这对于跨语言 RAG 是必备的。
优化
- 缓存
- LangChain 提供了 CacheBackedEmbeddings , 可以提高 embedings 的二次加载和解析的效率,首次正常速度,后续有一个 3倍效率的提升。
- 多模态。如何从非文本内容中提取有用的信息,并将其转化为可检索的格式?如何将提取的多模态信息与文本信息融合,以便进行统一的检索和回答?如何构建一个能够索引多模态内容的知识库。
- RAG与Long-Context之争—没必要争
- RAG 当前最大的问题是什么?笔者觉得是R 和 G 的割离。如果我们能在检索之前对知识点进行学习理解,去指导检索器应该从哪方面去查找,是不是能解决当下的很多问题?比如:花里胡哨的各路检索策略?、花里胡哨的各路query扩展和排序策略?、lost middle(因为查询到的都是相关的)?比如:法律场景笔者瞎编一个案例,张三走在路上,突然大叫一声,把旁边的老大爷吓的晕倒在地,请问这种情况张三犯法嘛,犯了什么法?当前的检索逻辑能召回什么法条?那如果要改进,该怎么办?笔者思考的是,在检索前让模型先分析案例,提出可能的法条方向,然后告诉检索器进行检索,是不是比瞎蒙的情况要更好?那么这个检索前的模型(暂且称之为检索指导模型)应该做到哪些点?应该对知识点有充分的理解能力。
Self-RAG
Self-RAG 框架:更精准的信息检索与生成 未读 也看引入自我反思的大模型RAG检索增强生成框架:SELF-RAG的数据构造及基本实现思路 未读
Self-RAG 及其实现 Self-RAG 主要步骤概括如下:
- 判断是否需要额外检索事实性信息(retrieve on demand),仅当有需要时才召回
- 平行处理每个片段:生产prompt+一个片段的生成结果。PS: query + chunk ==> 带有反思标记(relevant/supported/partital/inrelevant)的chunk
- 使用反思字段,检查输出是否相关,选择最符合需要的片段;
- 再重复检索
- 生成结果会引用相关片段,以及输出结果是否符合该片段,便于查证事实。
Self-RAG 的一个重要创新是 Reflection tokens (反思字符):通过生成反思字符这一特殊标记来检查输出。这些字符会分为 Retrieve 和 Critique 两种类型,会标示:检查是否有检索的必要,完成检索后检查输出的相关性、完整性、检索片段是否支持输出的观点。模型会基于原有词库和反思字段来生成下一个 token。
要不要微调
如何权衡对需求prompt、continue pretrain 、sft?实时性强的知识以外挂知识库为主,专业的较为稳定的领域知识去微调模型:continue pretain 更多微调领域知识(比如知道感冒是什么),sft 更多是微调领域指令(比如能听懂“给我开个药方”,“下游任务”)。
一般指令数据集都是手工构造的,
效果评价
如何评估 RAG 应用的质量?最典型的方法论和评估工具都在这里了
- 评估指标。标准的 RAG 流程就是用户提出 Query 问题,RAG 应用去召回 Context,然后 LLM 将 Context 组装,生成满足 Query 的 Response 回答。那么在这里出现的三元组:—— Query、Context 和 Response 就是 RAG 整个过程中最重要的三元组,它们之间两两相互牵制。我们可以通过检测三元组之间两两元素的相关度,来评估这个 RAG 应用的效果
无需 ground-truth 也能做评估 1. Context Relevance: 衡量召回的 Context 能够支持 Query 的程度。如果该得分低,反应出了召回了太多与Query 问题无关的内容,这些错误的召回知识会对 LLM 的最终回答造成一定影响。三元指标其中的某个可能还有具体的一些细分,比如 Ragas中就将 Context Relevance 这一步又分为Context Precision、Context Relevancy、Context Recall。 2. Groundedness: 衡量 LLM 的 Response 遵从召回的 Context 的程度。如果该得分低,反应出了 LLM 的回答不遵从召回的知识,那么回答出现幻觉的可能就越大。 3. Answer Relevance: 衡量最终的 Response 回答对 Query 提问的相关度。如果该得分低,反应出了可能答不对题。
- 基于 Ground-truth 的指标,当一个数据集已经标注好了ground-truth 回答,那就可以直接比较 RAG 应用的回答和 ground-truth 之间的相关性,来端到端地进行衡量。
- LLM 回答本身的指标,比如评估回答本身是否友好,是否有害,是否简洁等,它们参考来源的是 LLM 本身的一些评估指标。
具体怎么衡量这三个得分,也有不同的方式。最常见的就是基于目前最好的 LLM(如 GPT-4)做为一个裁判,给输入的这一对元组打分,判断它们的相似度。根据我们目前的观察,GPT-4 这在方面做得已经很好了。人类都有可能打错分,GPT-4 的表现和人类类似,误判的比例保持在很低就可以保证这种方法的有效性。因此,如何设计 prompt 同样重要,这就要用到一些高级的 prompt 工程技巧,比如 multi-shot,或 CoT(Chain-of-Thought)思维链技巧。在设计这些 prompt 时,有时还要考虑 LLM 的一些偏见,比如 LLM 常见的位置偏见:当 prompt 比较长时,LLM 容易注意到 prompt 里前面的一些内容,而忽略一些中间位置的内容。好在这些 prompt 的设计已经被设计和集成在 RAG 应用的评估工具中。
工程架构
值得一看的大模型RAG问答总括性梳理:模块化(Modular)RAG范式的定义、构成及机遇RAG的快速发展早已经突破传统的链式RAG范式,展示出了模块化的特点。