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

news2024/12/23 4:19:36

在上一篇博客中,我们学习了多重查询(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 file


os.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 hub

prompt = 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, loads

def 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 document
    fused_scores = {}

    # Iterate through each list of ranked documents
    for 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 0
            if doc_str not in fused_scores:
                fused_scores[doc_str] = 0
            # Retrieve the current score of the document, if any
            previous_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 results
    reranked_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 score
    return 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_fusion


chain.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.coloradmin.cn/o/1647030.html

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

相关文章

okcc最新版本会被盗打吗?

OKCC是一款智能外呼系统,它提供了多种安全措施来防止系统被盗打。以下是一些关键的安全配置和管理措施: 立即挂失SIM卡:一旦发现OKCC系统被盗打,应立即联系运营商进行SIM卡的挂失,以阻止盗打者继续使用您的号码进行通信…

Mybatis进阶3--注解开发

先看&#xff1a; Mybatis进阶1-CSDN博客 Mybatis进阶2-CSDN博客 mybatis注解开发 前置&#xff1a;不需要xxxMapper..xml文件&#xff08;映射文件&#xff09; 在核心配置文件中&#xff1a;<mappers>标签只能使用&#xff1a;<package name"扫描的包&quo…

visio studio 中.NET Core(.net8.0)框架和.net framewok 框架有什么区别?

更新vs到2022版本后&#xff0c;新建项目时就多出不少选项&#xff0c;这里来个大家分享下.NET Core&#xff08;.net8.0&#xff09;框架和.net framewok的区别 如下图&#xff0c;不带后缀的就是使用.NET Core框架&#xff0c;后续选项是.net8.0。 .net framewok框架选项&am…

PDF高效编辑:一键批量,PDF转图片的快速解决方案

在数字化时代&#xff0c;PDF文件已成为工作和学习中不可或缺的一部分。然而&#xff0c;有时我们可能需要将PDF转换为图片&#xff0c;以便更轻松地编辑、共享或处理。为了满足这一需求&#xff0c;许多高效的PDF编辑工具应运而生&#xff0c;其中“办公提效工具”一键批量PDF…

ESP8266做主机 手机网络助手为从机

ATCIFSR查看地址&#xff0c;一般ESP8266 为192.168.4.1 在手机上下载网络调试助手&#xff0c;打开TCP客户端 创建后192.168.4.1 端口8089然后连接ESP8266热点。 ESP向手机发数据前先发送要发几个数据ATCIPSEND0,8表示发8个&#xff0c;然后再发8个数 上面创建好热点后&…

MFC实现点击列表头进行排序

MFC实现点击列表头排序 1、添加消息处理函数 在列表窗口右键&#xff0c;类向导。选择 IDC_LIST1&#xff08;我的列表控件的ID&#xff09;&#xff0c;消息选择LVN_COLUMNCLICK。 2、消息映射如下 然后会在 cpp 文件中生成以下函数 void CFLashSearchDlg::OnLvnColumnclic…

权益商城系统源码,支持多种支付方式

权益商城系统源码&#xff0c;支持多种支付方式&#xff0c;后台商品管理&#xff0c;订单管理&#xff0c;串货管理&#xff0c;分站管理&#xff0c; 会员列表&#xff0c;分销日志&#xff0c;应用配置。 上传到服务器&#xff0c;修改数据库信息&#xff0c;导入数据库&a…

rust调用SQLite实例

rusqlite库介绍 Rusqlite是一个用Rust编写的SQLite库&#xff0c;它提供了对SQLite数据库的操作功能。Rusqlite的设计目标是提供一个简洁易用的API&#xff0c;以便于Rust程序员能够方便地访问和操作SQLite数据库。 Rusqlite的主要特点包括&#xff1a; 遵循Rust的类型系统和…

Mysql数据在磁盘上的存储结构

一. 前言 一行数据的存储格式大致如下所示: 变长字段的长度列表&#xff0c;null值列表&#xff0c;数据头&#xff0c;column01的值&#xff0c;column02的值&#xff0c;column0n的值… 二. 变长字段 在MySQL里有一些字段的长度是变长的&#xff0c;是不固定的&#xff0c;…

21_Scala集合_可变数组ArrayBuffer

文章目录 ArrayBuffer1.创建可变数组ArrayBuffer2.增加数据3.删除数据4.修改数据5.查询数据Appendix ArrayBuffer ArrayBuffer可变数组–感觉优点类似于Java的ArrayList 1.创建可变数组ArrayBuffer –请注意下面的mutable import scala.collection.mutable.ArrayBuffer //m…

在Linux上使用Selenium驱动Chrome浏览器无头模式

大家好&#xff0c;我们平时在做UI自动化测试的时候&#xff0c;经常会用到Chrome浏览器的无头模式&#xff08;无界面模式&#xff09;&#xff0c;并且将测试代码部署到Linux系统中执行&#xff0c;或者平时我们写个爬虫爬取网站的数据也会使用到&#xff0c;接下来和大家分享…

前端框架 Vue 主要用来做什么的?

Vue.js 是一个流行的前端框架&#xff0c;主要用于构建交互式的用户界面。它的设计目标是通过简单的 API 提供高效的数据驱动视图层。Vue 具有响应式数据绑定和组件化的特性&#xff0c;使得开发者可以轻松地构建复杂的单页面应用 (SPA) 和动态网页。 1. 数据驱动视图 Vue 的…

IIS部署vue项目 IIS重写URL

【第一步】安装IIS {1&#xff09;打开控制面板 -> 打开程序和功能 -> 打开启用或关闭windows功能 &#xff08;2&#xff09;找到 Internet Information Services 勾选【web管理工具】和【万维网服务】&#xff0c;然后 确定 【第二步】安装URL重写模块 1). 安装URL …

企业微信hook接口协议,ipad协议http,设置是否自动同意

设置是否自动同意 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信 请求示例 {"uuid":"bc4800492083fdec4c1a7e5c94","state":1 //1 是需要验证同意&#xff08;需要手动点击同意&#xff09; 0关闭验证…

下一代自动化,国外厂商如何通过生成性AI重塑RPA?

企业自动化的未来趋势是什么&#xff1f;科技巨头们普遍认为&#xff0c;由生成性AI驱动的AI Agent将成为下一个重大发展方向。尽管“AI Agent”这一术语尚无统一定义&#xff0c;但它通常指的是那些能够根据指令通过模拟人类互动&#xff0c;在软件和网络平台上执行复杂任务的…

Skywalking的重要功能详解

学习本篇文章之前首先要了解一下Sky walking的基础知识 分布式链路追踪工具Sky walking详解 一&#xff0c;Sky walking监控数据库 在admin服务中&#xff0c;连接数据库查询user表中所有数据 引入依赖 <dependency><groupId>mysql</groupId><artifactI…

分享几个.NET开源的AI和LLM相关项目框架

前言 现如今人工智能&#xff08;AI&#xff09;技术的发展可谓是如火如荼&#xff0c;它们在各个领域都展现出了巨大的潜力和影响力。今天大姚给大家分享4个.NET开源的AI和LLM相关的项目框架&#xff0c;希望能为大家提供一些参考。如果你有更好的推荐&#xff0c;欢迎RP投稿或…

YOLOv5入门(四)训练自己的目标检测模型

前言 通过前面几篇文章&#xff0c;已经完成数据集制作和环境配置&#xff08;服务器&#xff09;&#xff0c;接下来将继续实践如何开始训练自己数据集~ 往期回顾 YOLOv5入门&#xff08;一&#xff09;利用Labelimg标注自己数据集 YOLOv5入门&#xff08;二&#xff09;处…

Oceanbase all-in-one单机版部署,通过MySQL客户端连接OB租户,DBEAVER 客户端连接MySQL租户。

一.Oceanbase all-in-one单机版部署 1.修改资源限制。 vim /etc/security/limits.conf root soft nofile 655350 root hard nofile 655350 * soft nofile 655350 * hard nofile 655350 * soft stack unlimited * hard stack unlimited * soft nproc 655360 * hard nproc 6553…

webstorm 常用插件

安装插件步骤&#xff1a; 打开软件&#xff0c;文件 -- 设置-- 插件 -- 输入插件名称 -- 安装 代码截图: code screenShots 先选中代码&#xff0c;按 ctrl shift alt a&#xff0c;就可截取选中的代码颜色注释: comments highlighter 对注释的文字改变颜色高亮成对符号: h…