高级RAG:从理论到 LlamaIndex 实现,解决原始 RAG 管道的局限性

原文地址:Advanced Retrieval-Augmented Generation: From Theory to LlamaIndex Implementation

如何通过在 Python 中实现有针对性的高级 RAG 技术来解决原始 RAG 管道的局限性

2024 年 2 月 19 日

如何通过在 Python 中实现有针对性的高级 RAG 技术来解决 naive RAG 管道的局限性

A最近关于检索增强生成(RAG)的调查[1]总结了三种最近发展的范式:

  • Naive RAG
  • 高级 RAG
  • 模块化 RAG

高级 RAG 范式由一组技术组成,旨在解决原始 RAG 的已知局限性。本文首先讨论这些技术,这些技术可以分为检索前、检索和检索后优化

在后半部分中,您将学习如何在 Python 中使用Llamaindex实现简单的 RAG 管道,然后通过选择以下高级 RAG 技术将其增强为高级 RAG 管道:

  • 检索前优化:句子窗口检索
  • 检索优化:混合搜索
  • 检索后优化:重新排名

本文重点介绍高级 RAG 范式及其实现。如果您不熟悉 RAG 的基础知识,可以在这里了解:

检索增强生成(RAG):从理论到 LangChain 实现

什么是高级 RAG

随着 RAG 领域的最新进展,高级 RAG 已发展成为一种新范式,并进行了有针对性的增强,以解决原始 RAG 范式的一些限制。正如最近的一项调查[1]所总结的,先进的 RAG 技术可以分为检索前、检索和检索后优化。

高级检索增强生成 (RAG) 对原始 RAG 管道实施检索前、检索和检索后优化

Naive 和 Advanced RAG 的区别(图片由作者提供,灵感来自 [1])

检索前优化

预检索优化侧重于数据索引优化和查询优化。数据索引优化技术旨在以有助于提高检索效率的方式存储数据,例如[1]:

  • 滑动窗口使用块之间的重叠,是最简单的技术之一。
  • 增强数据粒度应用数据清理技术,例如删除不相关信息、确认事实准确性、更新过时信息等。
  • 添加元数据,例如日期、目的或章节,用于过滤目的。
  • 优化索引结构涉及不同的数据索引策略,例如调整块大小或使用多重索引策略。我们将在本文中实现的一项技术是句子窗口检索,它嵌入单个句子进行检索,并在推理时用更大的文本窗口替换它们。

句子窗口检索通过单个句子嵌入和索引文档,但也将句子周围的上下文窗口存储为元数据。

句子窗口检索

此外,预检索技术不仅限于数据索引,还可以涵盖推理时的技术,例如查询路由、查询重写和查询扩展。

检索优化

检索阶段旨在识别最相关的上下文。通常,检索基于向量搜索,它计算查询和索引数据之间的语义相似度。因此,大多数检索优化技术都围绕嵌入模型[1]:

  • 微调嵌入模型可以根据特定领域的上下文定制嵌入模型,特别是对于具有不断发展或罕见术语的领域。例如,BAAI/bge-small-en 是一个可以微调的高性能嵌入模型(参见微调指南)
  • 动态嵌入适应单词使用的上下文,这与静态嵌入不同,静态嵌入为每个单词使用单个向量。例如,OpenAIembeddings-ada-02是一个复杂的动态嵌入模型,可以捕获上下文理解。[1]

除了矢量搜索之外,还有其他检索技术,例如混合搜索,它通常指的是矢量搜索与基于关键字的搜索相结合的概念。如果您的检索需要精确的关键字匹配,则此检索技术非常有用。

通过混合搜索提高 RAG 管道的检索性能

检索后优化

对检索到的上下文进行额外处理可以帮助解决诸如超出上下文窗口限制或引入噪声等问题,从而阻碍对关键信息的关注。RAG 调查 [1] 中总结的检索后优化技术包括:

  • 提示压缩通过删除不相关的内容并突出显示重要的上下文来减少总体提示长度。
  • 重新排名使用机器学习模型来重新计算检索到的上下文的相关性分数。

Re-ranking根据检索后的查询重新计算相关性分数,仅将前n个发送到LLM的上下文窗口

重新排名

有关如何提高 RAG 管道性能以使其做好生产准备的更多想法,请继续阅读此处:

生产就绪 RAG 应用的 12 种调优策略指南

先决条件

本节讨论本文中所需的包和 API 密钥。

所需包

本文将指导您在 Python 中使用LlamaIndex实现简单和高级的 RAG 管道。

pip install llama-index

在本文中,我们将使用LlamaIndexv0.10。如果您是从较旧的 LlamaIndex 版本升级,则需要运行以下命令才能正确安装和运行 LlamaIndex:

pip uninstall llama-index
pip install llama-index --upgrade --no-cache-dir --force-reinstall

LlamaIndex 提供了一个选项,可以将矢量嵌入本地存储在 JSON 文件中以进行持久存储,这对于快速构建想法原型非常有用。然而,我们将使用矢量数据库进行持久存储,因为先进的 RAG 技术旨在生产就绪的应用程序。

由于除了存储向量嵌入之外,我们还需要元数据存储和混合搜索功能,因此我们将使用支持这些功能的开源向量数据库Weaviate ( v3.26.2)。

pip install weaviate-client llama-index-vector-stores-weaviate

API 密钥

我们将使用嵌入的 Weaviate,您可以免费使用它,无需注册 API 密钥。但是,本教程使用OpenAI的嵌入模型和 LLM ,为此您将需要 OpenAI API 密钥。要获取它,您需要一个 OpenAI 帐户,然后在API 密钥下“创建新密钥” 。

.env接下来,在根目录中创建一个本地文件并在其中定义 API 密钥:

OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

之后,您可以使用以下代码加载 API 密钥:

# !pip install python-dotenv
import os
from dotenv import load_dotenv,find_dotenvload_dotenv(find_dotenv())

使用 LlamaIndex 实施 Naive RAG

本节讨论如何使用 LlamaIndex 实现简单的 RAG 管道。您可以在此Jupyter Notebook中找到整个原始 RAG 管道。对于使用 LangChain 的实现,您可以继续本文(使用 LangChain 的 naive RAG pipeline)。

第 1 步:定义嵌入模型和 LLM

首先,您可以在全局设置对象中定义嵌入模型和 LLM。这样做意味着您不必再次在代码中显式指定模型。

  • 嵌入模型:用于为文档块和查询生成向量嵌入。
  • LLM:用于根据用户查询和相关上下文生成答案。
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.core.settings import SettingsSettings.llm = OpenAI(model="gpt-3.5-turbo", temperature=0.1)
Settings.embed_model = OpenAIEmbedding()

第 2 步:加载数据

接下来,您将创建一个data在根目录中命名的本地目录,并从LlamaIndex GitHub 存储库(MIT 许可证)下载一些示例数据。

!mkdir -p 'data'
!wget '<https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt>' -O 'data/paul_graham_essay.txt'

之后,您可以加载数据以进行进一步处理:

from llama_index.core import SimpleDirectoryReader# Load data
documents = SimpleDirectoryReader(input_files=["./data/paul_graham_essay.txt"]
).load_data()

第 3 步:将文档分块到节点中

由于整个文档太大而无法放入 LLM 的上下文窗口,因此您需要将其划分为较小的文本块,这些文本块Nodes在 LlamaIndex 中调用。SimpleNodeParser您可以使用定义的块大小 1024 将加载的文档解析为节点。

from llama_index.core.node_parser import SimpleNodeParsernode_parser = SimpleNodeParser.from_defaults(chunk_size=1024)# Extract nodes from documents
nodes = node_parser.get_nodes_from_documents(documents)

第 4 步:建立索引

接下来,您将在开源矢量数据库Weaviate中构建存储所有外部知识的索引。

首先,您需要连接到 Weaviate 实例。在本例中,我们使用Wea​​viate Embedded,它允许您在笔记本中免费进行实验,无需 API 密钥。对于生产就绪的解决方案,建议自行部署 Weaviate,例如通过 Docker或利用托管服务。

import weaviate# Connect to your Weaviate instance
client = weaviate.Client(embedded_options=weaviate.embedded.EmbeddedOptions(), 
)

接下来,您将从 Weaviate 客户端构建一个VectorStoreIndex来存储数据并与之交互。

from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.vector_stores.weaviate import WeaviateVectorStoreindex_name = "MyExternalContext"# Construct vector store
vector_store = WeaviateVectorStore(weaviate_client = client, index_name = index_name
)# Set up the storage for the embeddings
storage_context = StorageContext.from_defaults(vector_store=vector_store)# Setup the index
# build VectorStoreIndex that takes care of chunking documents
# and encoding chunks to embeddings for future retrieval
index = VectorStoreIndex(nodes,storage_context = storage_context,
)

第 5 步:设置查询引擎

最后,您将设置索引作为查询引擎。

# The QueryEngine class is equipped with the generator
# and facilitates the retrieval and generation steps
query_engine = index.as_query_engine()

第 6 步:对数据运行简单的 RAG 查询

现在,您可以对数据运行简单的 RAG 查询,如下所示:

# Run your naive RAG query
response = query_engine.query("What happened at Interleaf?"
)

使用 LlamaIndex 实施高级 RAG

在本节中,我们将介绍一些简单的调整,您可以进行一些简单的调整,将上述简单的 RAG 管道转变为高级管道。本演练将涵盖以下高级 RAG 技术选择:

  • 检索前优化:句子窗口检索
  • 检索优化:混合搜索
  • 检索后优化:重新排名

由于我们仅在此处介绍修改,因此您可以在此 Jupyter Notebook 中找到完整的端到端高级 RAG 管道。

索引优化示例:句子窗口检索

对于句子窗口检索技术,您需要进行两个调整:首先,您必须调整数据的存储和后处理方式。SimpleNodeParser我们将使用来代替SentenceWindowNodeParser

from llama_index.core.node_parser import SentenceWindowNodeParser# create the sentence window node parser w/ default settings
node_parser = SentenceWindowNodeParser.from_defaults(window_size=3,window_metadata_key="window",original_text_metadata_key="original_text",
)

SentenceWindowNodeParser做了两件事:

  1. 它将文档分成单个句子,这些句子将被嵌入。
  2. 对于每个句子,它都会创建一个上下文窗口。如果您指定 a window_size = 3,则生成的窗口将是三个句子长,从嵌入句子的前一个句子开始并跨越后面的句子。该窗口将存储为元数据。

在检索过程中,返回与查询最匹配的句子。MetadataReplacementPostProcessor检索后,您需要通过定义 a并在 列表中使用它,将句子替换为元数据中的整个窗口node_postprocessors

from llama_index.core.postprocessor import MetadataReplacementPostProcessor# The target key defaults to `window` to match the node_parser's default
postproc = MetadataReplacementPostProcessor(target_metadata_key="window"
)...query_engine = index.as_query_engine( node_postprocessors = [postproc],
)

检索优化示例:混合搜索

query_engine如果底层矢量数据库支持混合搜索查询,则在 LlamaIndex 中实现混合搜索就像更改两个参数一样简单。该alpha参数指定矢量搜索和基于关键字搜索之间的权重,其中alpha=0表示基于关键字搜索,alpha=1表示纯矢量搜索。

query_engine = index.as_query_engine(...,vector_store_query_mode="hybrid", alpha=0.5,...
)

检索后优化示例:重新排名

将重新排序器添加到高级 RAG 管道只需三个简单步骤:

  1. 首先,定义一个重排序模型。在这里,我们使用BAAI/bge-reranker-baseHugging Face 中的。
  2. 在查询引擎中,将 reranker 模型添加到 的列表中node_postprocessors
  3. 增加similarity_top_k查询引擎中的 来检索更多上下文段落,可以在重新排序后减少top_n
# !pip install torch sentence-transformers
from llama_index.core.postprocessor import SentenceTransformerRerank# Define reranker model
rerank = SentenceTransformerRerank(top_n = 2, model = "BAAI/bge-reranker-base"
)...# Add reranker to query engine
query_engine = index.as_query_engine(similarity_top_k = 6,...,node_postprocessors = [rerank],...,
)

高级 RAG 范例中还有许多不同的技术。如果您对进一步的实施感兴趣,我推荐以下两个资源:

构建和评估高级 RAG 应用程序

高级 RAG 01:从小到大检索

概括

本文介绍了高级 RAG 的概念,其中涵盖了一组解决朴素 RAG 范式局限性的技术。在概述了高级 RAG 技术(可分为预检索、检索和检索后技术)之后,本文使用 LlamaIndex 进行编排实现了一个简单且高级的 RAG 管道。

RAG 管道组件是来自OpenAI的语言模型、来自托管在Hugging Face上的BAAI 的重排序模型以及Weaviate矢量数据库。

我们在 Python 中使用 LlamaIndex 实现了以下技术选择:

  • 检索前优化:句子窗口检索
  • 检索优化:混合搜索
  • 检索后优化:重新排名

您可以在此处找到包含完整端到端管道的 Jupyter Notebook:

  • LlamaIndex 中的 Naive RAG
  • LlamaIndex 中的高级 RAG

参考

[1] Gao, Y., Xiong, Y., Gao, X., Jia, K., Pan, J., Bi, Y., … & Wang, H. (2023). Retrieval-augmented generation for large language models: A survey.arXiv preprint arXiv:2312.10997。

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

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

相关文章

【LeetCode刷题】146. LRU 缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0c;否则返回 -…

【数据结构】B树,B+树,B*树

文章目录 一、B树1.B树的定义2.B树的插入3.B树的中序遍历 二、B树和B*树1.B树的定义2.B树的插入3.B*树的定义4.B树系列总结 三、B树与B树的应用 一、B树 1.B树的定义 1. 在内存中搜索效率高的数据结构有AVL树&#xff0c;红黑树&#xff0c;哈希表等&#xff0c;但这是在内存…

SpreadJS+vue3练手使用

SpreadJS的练手使用 // 首先在 package.json 这个文件里{"name": "app-admin","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite",&quo…

前端——WEB-API那些有意思的api

1.URL和URLSearchParams 一个用于解析URL&#xff0c;一个用于查询URL的Parmas <script >let url http://zyfp-fof.ss.gofund.cn/list?type0&dst1let urlApinew URL(url)let dstnew URLSearchParams(urlApi.search).get(dst)console.log(dst);</script> 我…

猫头虎分享已解决Bug || 节点失联(Node Disconnection):NodeLost, ClusterNodeFailure

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

web.py架构使用database接口连接mysql

安装mysql sudo apt-get update sudo apt-get install mysql-server sudo apt-get install mysql-client测试mysql systemctl status mysql.service配置mysql //修改密码 sudo mysql -u root -p set password for 用户名localhost password(新密码); //修改root的host属性…

常见的socket函数封装和多进程和多线程实现服务器并发

常见的socket函数封装和多进程和多线程实现服务器并发 1.常见的socket函数封装2.多进程和多线程实现服务器的并发2.1多进程服务器2.2多线程服务器2.3运行效果 1.常见的socket函数封装 accept函数或者read函数是阻塞函数&#xff0c;会被信号打断&#xff0c;我们不能让它停止&a…

设计模式(五)-观察者模式

前言 实际业务开发过程中&#xff0c;业务逻辑可能非常复杂&#xff0c;核心业务 N 个子业务。如果都放到一块儿去做&#xff0c;代码可能会很长&#xff0c;耦合度不断攀升&#xff0c;维护起来也麻烦&#xff0c;甚至头疼。还有一些业务场景不需要在一次请求中同步完成&…

使用R语言对线性回归模型中的异方差进行诊断和处理

一、数据准备 序号XY序号XY序号XY110.61143.521817.5211.61244.422813.4310.51345.12384.5411.21455.724930.45221563.4251112.4621.31669.7261213.4722.51768.6271226.2832.2187428127.4932.41975.5   1031.220710.5     二、对y和x,绘制散点图&#xff0c;并进行回归…

​LeetCode解法汇总235. 二叉搜索树的最近公共祖先

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给定一个二叉搜索树, 找到该树中两个指定…

4.8 Verilog过程连续赋值

关键词&#xff1a;解除分配&#xff0c;强制&#xff0c;释放 过程连续赋值是过程赋值的一种。赋值语句能够替换其他所有wire 或 reg 的赋值&#xff0c;改写wire 或 reg 类型变量的当前值。 与过程赋值不同的是&#xff0c;过程连续赋值表达式能被连续的驱动到wire 或 reg …

Selenium IDE插件录制网页,解放双手

1、 国内下载地址 https://www.crx4chrome.com/crx/77585/ &#xff0c;这个网络正常基本可以下载&#xff0c;目前最新版本是3.17.2。 点击Crx4Chrome下载。下载后的文件名称是&#xff1a;mooikfkahbdckldjjndioackbalphokd-3.17.2-Crx4Chrome.com.crx。 2、 安装 直接打开…

Netty NIO 非阻塞模式

1.概要 1.1 说明 使用非阻塞的模式&#xff0c;就可以用一个现场&#xff0c;处理多个客户端的请求了 1.2 要点 ssc.configureBlocking(false);if(sc!null){ sc.configureBlocking(false); channels.add(sc); }if(len>0){ byteBuffer.flip(); 2.代码 2.1 服务端代码 …

JavaWeb 自己给服务器安装SQL Server数据库遇到的坑

之前买的虚拟主机免费送了一个SQL Server数据库&#xff0c;由于服务器提供商今年下架我用的那款虚拟主机产品&#xff0c;所以数据库也被收回了。我买了阿里云云服务器&#xff0c;但是没有数据库&#xff0c;于是自己装了一个SQL Server数据库&#xff0c;总结一下遇到的坑。…

Mysql 的高可用详解

Mysql 高可用 复制 复制是解决系统高可用的常见手段。其思路就是&#xff1a;不要把鸡蛋都放在一个篮子里。 复制解决的基本问题是让一台服务器的数据与其他服务器保持同步。一台主库的数据可以同步到多台备库上&#xff0c;备库本身也可以被配置成另外一台服务器的主库。主…

Mysql的备份还原

模拟环境准备 创建一个名为school的数据库&#xff0c;创建一个名为Stuent的学生信息表 mysql> create database school; Query OK, 1 row affected (0.00 sec)mysql> use school; Database changed mysql> CREATE TABLE Student (-> Sno int(10) NOT NULL COMME…

使用R语言进行多元线性回归分析-多重共线的诊断

一、数据集 序号X1x2x3x4Y序号X1x2x3X4Y12666078.57831224472.51229155274.31954182293.12356850104.3111047426115.92143184787.6111140233483.8155263395.971266912113.311655922109.2111368812109.410771176102.73       1、从中选取主要变量&#xff0c;建立与因变…

Vue源码系列讲解——生命周期篇【八】(挂载阶段)

1. 前言 模板编译阶段完成之后&#xff0c;接下来就进入了挂载阶段&#xff0c;从官方文档给出的生命周期流程图中可以看到&#xff0c;挂载阶段所做的主要工作是创建Vue实例并用其替换el选项对应的DOM元素&#xff0c;同时还要开启对模板中数据&#xff08;状态&#xff09;的…

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture09 Softmax多分类

代码&#xff1a; import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader import torch.nn as nn import torch.nn.functional as Fbatch_size 64 transform transforms.Compose([transforms.ToTensor(), transforms.Normali…

【HarmonyOS】鸿蒙开发之Stage模型-基本概念——第4.1章

Stage模型-基本概念 名词解释 AbilityStage:应用组件的“舞台“ UIAbility:包含UI界面的应用组件&#xff0c;是系统调度的基本单元 WindowStage:组件内窗口的“舞台“ Window&#xff1a;用来绘制UI页面的窗口 HAP:Harmony Ability Package(鸿蒙能力类型的包) HSP:Harmony Sh…