LLM之RAG实战(三十七)| 高级RAG从理论到LlamaIndex实现

     论文《Retrieval-Augmented Generation for Large Language Models: A Survey》对检索增强生成(RAG)总结了如下三种方式:

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

推荐阅读该论文的详解:

LLM之RAG理论(二)|  RAG综述论文详解

      高级RAG旨在解决Naive RAG的局限性。本文把这些技术,可分为预检索(pre-retrieval)、检索(retrieval)和检索后(post-retrieval)优化。

       在下半部分中,首先使用Llamaindex实现一个简单的RAG管道,然后通过选择以下高级RAG技术将其增强为高级RAG管道:

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

一、高级RAG

高级RAG与Naive RAG对比,如下图所示:

1.1 预检索优化

       预检索优化侧重于数据索引优化和查询优化。数据索引优化技术旨在提高存储数据的检索效率,例如:

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

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

1.2 检索优化

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

  • 微调嵌入模型可根据特定领域的上下文定制嵌入模型,尤其是对于具有不断演变或罕见术语的领域。例如,BAAI/bge-small-en是一种可以微调的高性能嵌入模型(请参阅微调指南[1])。
  • 动态嵌入会根据上下文使用的单词动态给出每个单词的向量,而静态嵌入则为每个单词使用一个向量。例如,OpenAI的embeddings—da-02是一个复杂的动态嵌入模型,可以捕捉上下文理解。

除了矢量搜索之外,还有其他检索技术,例如混合搜索,是指将矢量搜索与基于关键字的搜索相结合。如果您的检索需要精确的关键字匹配,那么混合搜索是非常合适的。

1.3 检索后优化

       有时候需要对检索到的上下文进行额外的处理,比如检索到的内容超出上下文窗口限制或引入了噪声之类的问题。下面介绍一些检索后处理技术:

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

二、代码实现

2.1 安装所需的包

pip install llama-index

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

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

       LlamaIndex提供了一种将矢量嵌入存储在本地JSON文件中以进行持久存储的功能,这对快速构建想法的原型非常有用。然而,在生产环境中,我们将使用矢量数据库进行持久存储。

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

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

2.2 API密钥

       我们将使用Weaviate嵌入式,您可以在不注册API密钥的情况下免费使用。然而,本教程使用了来自OpenAI的嵌入模型和LLM,为此您需要一个OpenAI API密钥。要获得一个,您需要一个OpenAI帐户,然后在API密钥下“创建新密钥”。

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

OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

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

# !pip install python-dotenvimport osfrom dotenv import load_dotenv,find_dotenvload_dotenv(find_dotenv())

2.3 用LlamaIndex实现Naive RAG

      本节讨论如何使用LlamaIndex实现一个简单的RAG管道,完整的Naive RAG管道可以参考这个Jupyter笔记本[2]。对于使用LangChain实现Naive RAG管道,可以参考[3]。

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

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

嵌入模型:用于为文档块和查询生成矢量嵌入。

LLM:用于根据用户查询和相关上下文生成答案。

from llama_index.embeddings.openai import OpenAIEmbeddingfrom llama_index.llms.openai import OpenAIfrom 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许可证)[4]下载一些示例数据。

!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 datadocuments = SimpleDirectoryReader(        input_files=["./data/paul_graham_essay.txt"]).load_data()

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

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

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

步骤4:建立索引

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

     首先,需要连接到Weaviate实例。在这种情况下,我们使用WeaviateEmbedded,它允许您在没有API密钥的情况下免费在笔记本中进行实验。对于生产就绪的解决方案,建议自己部署Weaviate,例如通过Docker或使用托管服务。

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

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

from llama_index.core import VectorStoreIndex, StorageContextfrom llama_index.vector_stores.weaviate import WeaviateVectorStoreindex_name = "MyExternalContext"# Construct vector storevector_store = WeaviateVectorStore(    weaviate_client = client,     index_name = index_name)# Set up the storage for the embeddingsstorage_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 retrievalindex = VectorStoreIndex(    nodes,    storage_context = storage_context,)

步骤5:设置查询引擎

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

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

步骤6:运行一个简单的RAG查询

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

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

2.4 用LlamaIndex实现高级RAG

接下来,我们如下高级RAG技术来提高Naive RAG管道效果:

  • 预检索优化:语句窗口检索
  • 检索优化:混合搜索
  • 检索后优化:重新排序

a)索引优化示例:语句窗口检索

       对于语句窗口检索技术,需要进行两个调整:首先,必须调整数据的存储和后处理方式,使用PensioneWindowNodeParser替换SimpleNodeParser。

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

PensioneWindowNodeParser主要实现如下两个步骤:

  1. 它将文档分隔为单个句子,这些句子将被嵌入;

  2. 对于每个句子,它都会创建一个上下文窗口。如果指定window_size=3,则生成的窗口将有三个句子长,包括嵌入句子的前一个句子和后面一个句子。该窗口将作为元数据存储。

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

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

b)检索优化示例:混合搜索

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

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

c)检索后优化示例:重新排序

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

  1. 首先,定义一个重新排序模型。在这里,我们使用Huggingface中的BAAI/bge-reranker-base;

  2. 在查询引擎中,将reranker模型添加到node_postprocessors列表中;

  3. 增加查询引擎中的similarity_top_k以检索更多的上下文段落,重新排序后可以减少到top_n。

# !pip install torch sentence-transformersfrom llama_index.core.postprocessor import SentenceTransformerRerank# Define reranker modelrerank = SentenceTransformerRerank(    top_n = 2,     model = "BAAI/bge-reranker-base")...# Add reranker to query enginequery_engine = index.as_query_engine(    similarity_top_k = 6,    ...,                node_postprocessors = [rerank],    ...,)

三、总结

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

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

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

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

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

LlamaIndex中的Naive RAG[2]

LlamaIndex中的高级RAG[5]

参考文献:

[1] https://betterprogramming.pub/fine-tuning-your-embedding-model-to-maximize-relevance-retrieval-in-rag-pipeline-2ea3fa231149

[2] https://github.com/weaviate/recipes/blob/main/integrations/llamaindex/retrieval-augmented-generation/naive_rag.ipynb

[3] https://medium.com/towards-data-science/retrieval-augmented-generation-rag-from-theory-to-langchain-implementation-4e9bd5f6a4f2

[4] https://github.com/run-llama/llama_index

[5] https://github.com/weaviate/recipes/blob/main/integrations/llamaindex/retrieval-augmented-generation/advanced_rag.ipynb

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

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

相关文章

Mysql 常用SQL语句

1、查看mysql中所有的数据库&#xff0c; show databases; 2、创建库 create database 库名;&#xff08;也可以用 create database if not exists 库名; 表示如果库不存在再创建&#xff09; 例&#xff1a;create database if not exists ecology; 3、删除库 …

Git Fork后的仓库内容和原仓库保持一致

Git Fork后的仓库内容和原仓库保持一致 ①Fork原仓库内容到自己仓库 ②将项目内容下载到本地 ③使用git命令获取原仓库内容&#xff0c;将原仓库的最新内容合并到自己的分支上并推送 下面从第三步开始演示~ 这里以码云上的若依项目为演示项目 ③使用git命令获取原仓库内容 …

C++两种方法实现二叉搜索树

文章目录 1.二叉搜索树&#xff08;BST Binary Search Tree&#xff09;1.1二叉搜索树的概念2.2二叉搜索树的操作2.2.1二叉搜索树的查找2.2.2二叉搜索树的插入2.2.3二叉搜索树的删除 2.3二叉搜索树的实现2.3.1二叉搜索的基本结构2.3.2查找节点2.3.3插入节点2.3.4删除节点删除度…

百度网站收录提交入口

百度网站收录提交入口 在网站刚建立或者更新内容后&#xff0c;及时将网站提交给搜索引擎是提高网站曝光和获取流量的重要步骤之一。百度作为中国最大的搜索引擎之一&#xff0c;网站在百度中的收录情况尤为重要。下面介绍一下如何通过百度的网站收录提交入口提交网站。 1. 百…

卷积神经网络(CNN)的数学原理解析

文章目录 前言 1、介绍 2、数字图像的数据结构 3、卷积 4、Valid 和 Same 卷积 5、步幅卷积 6、过渡到三维 7、卷积层 8、连接剪枝和参数共享 9、卷积反向传播 10、池化层 11、池化层反向传播 前言 本篇主要分享卷积神经网络&#xff08;CNN&#xff09;的数学原理解析&#xf…

Git:修改的时候在自己的分支修改,如果在其它分支修改代码,会无法切换回自己的分支

如果已经发生了: 首先在当前分支把没问题的代码提交一下&#xff0c; 保存当前修改&#xff1a;在当前分支上进行一次提交&#xff0c;将当前修改保存起来。&#xff08;但是不要推送到远程仓库&#xff09; 切换回自己的分支&#xff1a;尝试切换回自己的分支。如果报错&…

新model开发记录

模型使用 -- 用blender导出为 fbx &#xff0c;修改渲染方式&#xff08;点击模型->Materials->Extract Materials(将材质从fbx中 单独提取出来了)->Materials 选择 Shader -> SimpleURPToonLitExample 点开脸的材质&#xff0c;勾选第一条&#xff09; 解决角色…

深入了解C语言中的结构体类型与内存对齐

引言&#xff1a; 在C语言中&#xff0c;结构体是一种自定义的数据类型&#xff0c;它允许我们将不同类型的数据组合在一起&#xff0c;形成一个新的数据类型。结构体的使用为我们解决了一些复杂数据的表示和处理问题&#xff0c;不仅限于单单的整型或者字符。本文将深入探讨结…

甘特图进度跟踪与风险预警机制

项目管理就像一场跋涉,甘特图则是我们的路线图和指南针。制定好行程计划后,我们就要跟着它一路前行,时刻核对进度,防患于未然。 一张优秀的甘特图不仅绘制了任务的起止时间,更重要的是它将各项工序的逻辑关系和制约条件清晰可见。我们遵循这个时间线前进,就能确保万无一失。通…

你以为BBA电车不行?看看宝马财报里电车有多挣钱

作者 |许行知 编辑 |德新 如果放在中国市场百花齐放、争奇斗艳的电动车里&#xff0c;宝马的电动车乍看确实平平无奇。 但从全球市场来看&#xff0c;电动车早已成为宝马重要的利润增长点。 3月末&#xff0c;宝马集团举办线上沟通会&#xff0c;总结了其2023年的业绩表现&a…

Rustdesk如何编译代码实现,客户端只有控制权限,而且只能控制指定ID,不能控制其他ID

环境&#xff1a; RustDesk1.1.9 自建服务器 问题描述&#xff1a; Rustdesk如何编译代码实现&#xff0c;客户端只有控制权限&#xff0c;而且只能控制指定ID,不能控制其他ID 解决方案&#xff1a; 详细方案&#xff0c;有需要私聊

【Java面试题系列】基础篇

目录 基本常识标识符的命名规则八种基本数据类型的大小&#xff0c;以及他们的封装类3*0.10.3返回值是什么short s1 1; s1 s1 1;有什么错? short s1 1; s1 1;有什么错?简述&&与&的区别&#xff1f;简述break与continue、return的区别&#xff1f;Arrays类的…