RAG进阶(二): RAG 融合(rag fusion)

在上一篇博客中,我们学习了多重查询(Multi Query)技术,Multi Query的基本思想是当用户输入查询语句(自然语言)时,我们让大模型(LLM)基于用户的问题再生成多个查询语句,这些生成的查询语句是对用户查询语句的补充,它们是从不同的视角来补充用户的查询语句,然后每条查询语句都会从向量数据库中检索到一批相关文档,最后所有的相关文档都会被喂给LLM,这样LLM就会生成比较完整和全面的答案。这样就可以避免因为查询语句的差异而导致结果不正确。如下图所示:

今天我们来介绍RAG 融合(rag fusion),它的主要思想是在Multi Query的基础上,对其检索结果进行重新排序(即reranking)后输出Top K个最相关文档,最后将这top k个文档喂给LLM并生成最终的答案(answer)。如下图所示:

 一、环境配置

我们需要安装如下python包:

pip install langchain langchain_openai langchain_pinecone langchainhub

接下来我们需要导入所需要设置本次实验所需要用的几个api key:OPENAI_API_KEY,PINECONE_API_KEY,LANGCHAIN_API_KEY,这里需要说明的是本次实验会使用到openai的gpt-3.5-turbo模型,Pinecone向量数据库(PINECONE_API_KEY), LangSmith(LANGCHAIN_API_KEY).

pinecone云向量数据库是一个在线的云端向量数据库,我们需要去其官网申请api key,   LangSmith 是用来跟踪和分析langchain组件在执行过程中产生的中间结果,这对我们理解langchain组件的功能和作用有很大的帮助,因此我们也需要去langchain官网申请一个api key。

import os
from dotenv import load_dotenv, find_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore#导入项目中需要用到的各种的api_key
_ = load_dotenv(find_dotenv()) # read local .env fileos.environ['OPENAI_API_KEY']=os.environ['OPENAI_API_KEY']
os.environ['PINECONE_API_KEY']=os.environ['PINECONE_API_KEY']# 导入langsmith所需要的api key,用于跟踪中间结果
os.environ['LANGCHAIN_TRACING_V2'] = 'true'
os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'
os.environ['LANGCHAIN_API_KEY'] = os.environ['LANGCHAIN_API_KEY']

接下来我们还需要去pinecone官网创建一个向量数据库,这里我们创建了一个名为:rag-fusion的空向量数据库,后面需要被检索的文档向量会被自动上传到该向量数据库中:

 

下面我们来创建一组本次实验所需的测试文档集合,一共10个文档,每个文档为一句中文的句子,每个文档的内容在语义上基本都和气候变化相关:

all_documents={"doc1": "气候变化和经济影响。","doc2": "气候变化引起的公共卫生问题。","doc3": "气候变化:社会视角。","doc4": "气候变化的技术解决方案。","doc5": "应对气候变化需要改变政策。","doc6": "气候变化及其对生物多样性的影响。","doc7": "气候变化:科学和模型。","doc8": "全球变暖:气候变化的一个子集。","doc9": "气候变化如何影响日常天气。","doc10": "气候变化行动主义的历史。",}

接下来我们需要创建pinecone的向量数据库,在创建向量数据库时,我们指定使用openai的embedding模型,以及向量数据库名(rag-fusion), 需要说明的是这里我们使用的是from_texts的方法来创建向量数据库,它的作用是往云端的向量数据库"rag-fusion"中上传文档向量,这样云端的"rag-fusion"向量库就不再是一个空的数据库了:

vectorstore = PineconeVectorStore.from_texts(list(all_documents.values()), OpenAIEmbeddings(), index_name="rag-fusion"
)

二、定义查询生成器(Multi Quer)

我们现在将定义一个chain来生成多重查询语句,如果对多重查询还不熟悉的朋友,可以查看我之前写的这篇博客。这里我们会首先创建生成多重查询的prompt, 我们可以从langchain官网拉取预先定义好的prompt, 也可以手动定义prompt:

from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain import hubprompt = hub.pull("langchain-ai/rag-fusion-query-generation")prompt 

 同样我们也可以手动创建多重查询的prompt:


# prompt = ChatPromptTemplate.from_messages([
#     ("system", "You are a helpful assistant that generates multiple search queries based on a single input query."),
#     ("user", "Generate multiple search queries related to: {original_query}"),
#     ("user", "OUTPUT (4 queries):")
# ])

接下来我们来创建一个生成多重查询的chain, 该chain会根据用户的query生成4个多角度的query, 这些多角度的query是对用户原始query的补充。

generate_queries = (prompt | ChatOpenAI(temperature=0) | StrOutputParser() | (lambda x: x.split("\n"))
)original_query = "气候变化的影响"
queries = generate_queries.invoke({"original_query": original_query})
queries

 

这里我们看到用户的原始问题是: 气候变化的影响,generate_queries根据用户的问题生成了4个多角度的问题来对用户问题进行补充。

三、定义完整链

我们现在可以将它们放在一起并定义完整的用于检索的chain。该chain的作用是:

 1. 生成一组查询(queries)
 2. 在检索器中对每个query进行检索
 3. 使用倒排序排名融合方法将所有结果连接在一起

请注意,该chain不执行最后的生成步骤(不会将top k的检索结果喂给LLM)

original_query = "气候变化的影响"

接下来我们来创建一个向量库的实例并通过该向量库实例来创建一个检索器。

vectorstore = PineconeVectorStore.from_existing_index("rag-fusion", OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

 下面我们需要定义倒排序排名算法(Reciprocal Rank Fusion (RRF)),该算法来源于这篇论文:Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods,下面是该算法在论文中的定义:

 RRF 是与滑铁卢大学 (CAN) 和 Google 合作开发的,用其作者的话说,“比任何单独的系统产生更好的结果,比标准的”重新排名方法更好。我们简单解释一下该算法的原理,在RRF算法中,D表示相关文档的全集,k是固定常数60,r(d)表示当前文档d在其子集中的位置。该算法会对文档全集D进行二重遍历,外层遍历文档全集D, 内层遍历文档子集,在做内层变量的时候我们会累计当前文档在其所在子集中的位置并取倒数作为其权重(分数)。

下面是RRF算法的python实现: 

from langchain.load import dumps, loadsdef reciprocal_rank_fusion(results: list[list], k=60):""" Reciprocal_rank_fusion that takes multiple lists of ranked documents and an optional parameter k used in the RRF formula """# Initialize a dictionary to hold fused scores for each unique documentfused_scores = {}# Iterate through each list of ranked documentsfor docs in results:# Iterate through each document in the list, with its rank (position in the list)for rank, doc in enumerate(docs):# Convert the document to a string format to use as a key (assumes documents can be serialized to JSON)doc_str = dumps(doc)# If the document is not yet in the fused_scores dictionary, add it with an initial score of 0if doc_str not in fused_scores:fused_scores[doc_str] = 0# Retrieve the current score of the document, if anyprevious_score = fused_scores[doc_str]# Update the score of the document using the RRF formula: 1 / (rank + k)fused_scores[doc_str] += 1 / (rank + k)# Sort the documents based on their fused scores in descending order to get the final reranked resultsreranked_results = [(loads(doc), score)for doc, score in sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)]# Return the reranked results as a list of tuples, each containing the document and its fused scorereturn reranked_results

 下面我们来创建一个完整的chain,它由generate_queries ,retriever.map() ,reciprocal_rank_fusion三部分组成,其中generate_queries会生成4个多角度的query, retriever.map()的作用是根据generate_queries的结果映射出4个retriever(可以理解为同时复制出4个retriever)与中generate_queries会生成4个query对应,并为每个query检索出来的一组相关文档集(默认为4个相关文档),那么4个query总共可以生成16个相关文档。这16个相关文档集最后会经过RRF算法从新排序后输出最终的4个相关度最高的文档:

original_query = "气候变化的影响"chain = generate_queries | retriever.map() | reciprocal_rank_fusionchain.invoke({"original_query": original_query})

 这里我们看到了经过最终的RRF算法进行重拍以后的4个最相关的文档,并且从高倒低罗列出了每个相关文档的得分,下面我们来分析一下这些分数是如何统计出来的,为此我们需要提取那些在执行RRF算法之前的结果:

chain1 = generate_queries | retriever.map() 
chain1_result = chain1.invoke({"original_query": original_query})
chain1_result

 

 下面我们可以根据RRF算法在论文中的定义,手动来计算上面这些相关文档的分数:

#气候变化和经济影响。0.16344044051606188
score0 = 1/60+1/61+1/62+1/60+1/61+1/62+1/63+1/60+1/61+1/62 #气候变化引起的公共卫生问题。0.049189141547682
score1 = 1/60+1/61+1/62 #气候变化及其对生物多样性的影响。0.16344044051606188
score2 = 1/63+1/63 # 气候变化如何影响日常天气。0.015873015873015872
score3 = 1/63 
print(score0)
print(score1)
print(score2)
print(score3)

 

 这里我们看到我们手动计算的分数与RRF的python算法计算的分数是一致的。

下面我们可以在LangSmith平台中查看最终chain的执行过程中的中间结果,如下图的左侧为最终chain的所有组件如:ChatPromptTemplate,ChatOpenAI,Retriever(4个),reciprocal_rank_fusion,下图的右侧为每个组件所对应的输入和输出的内容:

下图为ChatPromptTemplate组件对应的输入和输出结果:

 

 下图为ChatOpenAI(LLM)组件对应的输入和输出结果:

 

  下图为第一个Retriever的输入和输出结果:

 

下图为最后一个Retriever的输入和输出结果: 

下图为RRF算法的输入和输出结果: 

 

 未完待续。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/7220.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis逆向工程笔记小结

🏷️个人主页:牵着猫散步的鼠鼠 🏷️系列专栏:Java全栈-专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 目录 1.前言 2.实现方案 2.1. mybatis-generator生成 2.1.1. 环境说明 2.1.2. 数…

【强训笔记】day7

NO.1 思路:双指针模拟,begin表示最长数字字符串最后一个字符,而len表示数字字符串的长度,i用来遍历,如果为数字,那么定义j变量继续遍历,直到不为数字,i-j如果大于len,就…

博客系统项目测试报告

文章目录 一.报告概要二.测试环境三.手工测试用例四.编写测试用例五.自动化测试Selenium测试项目主要特点 一.报告概要 项目概要 本项目是一个全功能的个人博客系统,旨在提供一个用户友好、功能全面的平台,允许用户注册、登录、浏览博客、查看详细内容、…

周刊是聪明人筛选优质知识的聪明手段!

这是一个信息过载的时代,也是一个信息匮乏的时代。 这种矛盾的现象在 Python 编程语言上的表现非常明显。 它是常年高居编程语言排行榜的最流行语言之一,在国外发展得如火如荼,开发者、项目、文章、播客、会议活动等相关信息如海如潮。 但…

#9松桑前端后花园周刊-React19beta、TS5.5beta、Node22.1.0、const滥用、jsDelivr、douyin-vue

行业动态 Mozilla 提供 Firefox 的 ARM64 Linux二进制文件 此前一直由发行版开发者或其他第三方提供,目前Mozilla提供了nightly版本,正式版仍需要全面测试后再推出。 发布 React 19 Beta 此测试版用于为 React 19 做准备的库。React团队概述React 19…

leetCode76. 最小覆盖子串

leetCode76. 最小覆盖子串 题目思路 代码 // 双指针 哈希表 // 这里cnt维护过程:先找到能够匹配T字符串的滑动窗口,然后这个cnt就固定了,因为i向前移动的同时,j也会维护着向前 // 就是当又出现能够满足T字符串的时候&#xff0…

Chrome浏览器安装React工具

一、如果网络能访问Google商店,直接安装官方插件即可 二、网络不能访问Google商店,使用安装包进行安装 1、下载react工具包 链接:https://pan.baidu.com/s/1qAeqxSafOiNV4CG3FVVtTQ 提取码:vgwj 2、chrome浏览器安装react工具…

基于ESP32和ESP8266的物联网开发过程(一)

给大家演示一个小工具,通过Wifi去连接ESP32或者ESP8266出来的一个热点。连接到这个热点之后,可以输密码,也可以不输密码。这里我设置的是不输密码直接进来,我这个是ESP8266。 进来之后直接点配置Wifi,然后可以看到ESP8…

【简单介绍下7-Zip】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

快速入门!学习鸿蒙App开发的终极指南!

鸿蒙(HarmonyOS)是华为推出的一款分布式操作系统,旨在为不同设备提供统一的操作体验。鸿蒙App开发可以让应用程序在多个设备上实现流畅运行。本文将介绍鸿蒙App开发的终极指南,帮助您快速入门。 开发环境搭建 鸿蒙App开发过程需要…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(五)

本系列课程,将重点讲解Phpsploit-Framework框架软件的基础使用! 本文章仅提供学习,切勿将其用于不法手段! 继续接上一篇文章内容,讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 在下面的图片中&#…

小工具 - 用Astyle的DLL封装一个对目录进行代码格式化的工具

文章目录 小工具 - 用Astyle的DLL封装一个对目录进行代码格式化的工具概述笔记效果编译AStyle的DLL初次使用接口的小疑惑测试程序 - 头文件测试程序 - 实现文件测试程序 - RC备注END 小工具 - 用Astyle的DLL封装一个对目录进行代码格式化的工具 概述 上一个实验(vs2019 - ast…

知识图谱在提升大语言模型性能中的应用:减少幻觉与增强推理的综述

幻觉现象指的是模型在生成文本时可能会产生一些听起来合理但实际上并不准确或相关的输出,这主要是由于模型在训练数据中存在知识盲区所致。 为了解决这一问题,研究人员采取了多种策略,其中包括利用知识图谱作为外部信息源。知识图谱通过将信息…

简单介绍IIC通信协议

文章目录 一,简单介绍二,IIC物理层三,IIC通信时序1.起始位与停止位2.IIC读写地址位信号3.IIC应答信号4.IIC数据位收发信号 四,总线速率五,主机发送数据流程六,主机接收数据流程七,IIC的时钟延展…

【云原生】Pod 的生命周期(二)

【云原生】Pod 的生命周期(一)【云原生】Pod 的生命周期(二) Pod 的生命周期(二) 6.容器探针6.1 检查机制6.2 探测结果6.3 探测类型 7.Pod 的终止7.1 强制终止 Pod7.2 Pod 的垃圾收集 6.容器探针 probe 是…

uniapp文本框上下滚动问题

一个基本需求,textarea标签没有办法通过手拖动的方式进行滚动,当文字超出其容量后,想要编辑上面被遮挡部分的文字这边难以点到,电脑可以鼠标滚轮,但手机需要拖动但无效: 下面提供了我的解决思路&#xff1a…

高精地图是怎么构建的?方案有哪些?高精度语义地图构建的一点思考

高精地图是怎么构建的?方案有哪些?高精度语义地图构建的一点思考 高精度(High-Definition, HD)语义地图是目前自动驾驶领域的一个重要研究方向,近年随着Transformer和BEV的大火,很多大佬团队都开展了HD语义地图构建相关的工作。2…

【005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放】

005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放 文章目录 005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放创作背景MA120X0P输出模式BTLSEPBTLSEBTL 硬件配置方式/硬件Limiter限幅器限幅器作用过程 主要寄存器操作指令 ma120x0p.cma120x0p.h 创作背景 学历代表过去、能…

Vue MVVM这一篇就够啦!

Vue vs React 相似之处: 它们都有使用 Virtual DOM虚拟DOM-CSDN博客;提供了响应式(Reactive)和组件化(Composable)的视图组件。将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。R…

GreptimeDB 助力国家电网数字换流站打造稳定高效的时序数据底座

电网体系作为现代社会运行的支柱之一,为各行各业、千家万户提供了电能的基本支持。从家庭到企业,医院到学校,交通到通讯,电力电网的应用贯穿始终。近年来,特高压换流站成为国家电网的重点建设工程,“十四五…