LLM之RAG实战(五)| 高级RAG 01:使用小块检索,小块所属的大块喂给LLM,可以提高RAG性能

       RAG(Retrieval Augmented Generation,检索增强生成)系统从给定的知识库中检索相关信息,从而使其能够生成事实信息、上下文相关信息和特定领域的信息。然而,在有效检索相关信息和生成高质量响应方面,RAG面临着许多挑战。在这一系列的博客文章/视频中,我将介绍先进的RAG技术,旨在优化RAG工作流程,并解决原始RAG系统中的挑战。

       第一种技术被称为从小到大的检索。在基本的RAG管道中,我们嵌入一个大的文本块进行检索,而这个完全相同的文本块用于合成。但有时嵌入/检索大的文本块可能会感觉不太理想。在一个大的文本块中有很多填充文本,导致文本块的语义可能不明显,导致检索更差。如果我们可以基于更小、更有针对性的块进行嵌入/检索,但仍有足够的上下文供LLM合成响应,该怎么办?具体而言,将用于检索的文本块与用于合成的文本块解耦可能是有利的。使用较小的文本块可以提高检索的准确性,而较大的文本块则提供更多的上下文信息。小到大检索背后的概念是在检索过程中使用较小的文本块,然后将检索到的文本所属的较大文本块提供给大语言模型。

主要有两种技术:

  1. 较小的子块引用较大的父块:在检索过程中首先获取较小的块,然后引用父ID,并返回较大的块;

  2. 句子窗口检索:在检索过程中获取一个句子,并返回句子周围的文本窗口。

       在这篇博客文章中,我们将深入研究这两种方法在LlamaIndex中的实现。为什么我不在LangChain中做这件事?因为LangChain的高级RAG已经有很多资源了。我宁愿不重复这种努力。此外,我同时使用LangChain和LlamaIndex。最好了解更多的工具并灵活使用它们。

一、基本RAG回顾

准备工作:

安装相关包

! pip install -U llama_hub llama_index braintrust autoevals pypdf pillow transformers torch torchvision

设置环境变量

import osos.environ["OPENAI_API_KEY"] = "TYPE YOUR API KEY HERE"

下载数据集

!wget --user-agent "Mozilla" "https://arxiv.org/pdf/2307.09288.pdf" -O "llama2.pdf"

导入所需要的包

from pathlib import Pathfrom llama_hub.file.pdf.base import PDFReaderfrom llama_index.response.notebook_utils import display_source_nodefrom llama_index.retrievers import RecursiveRetrieverfrom llama_index.query_engine import RetrieverQueryEnginefrom llama_index import VectorStoreIndex, ServiceContextfrom llama_index.llms import OpenAIimport json

让我们从RAG的基本实现开始,包括4个简单步骤:

步骤1:加载文档

      我们使用PDFReader加载PDF文件,并将文档的每一页合并为一个document对象。

loader = PDFReader()docs0 = loader.load_data(file=Path("llama2.pdf"))doc_text = "\n\n".join([d.get_content() for d in docs0])docs = [Document(text=doc_text)]

步骤2:将文档解析为文本块(节点)

      然后,我们将文档拆分为文本块,这些文本块在LlamaIndex中被称为“节点”,我们将chuck大小定义为1024。默认的节点ID是随机文本字符串,然后我们可以将节点ID格式化为遵循特定的格式。

node_parser = SimpleNodeParser.from_defaults(chunk_size=1024)base_nodes = node_parser.get_nodes_from_documents(docs)for idx, node in enumerate(base_nodes):node.id_ = f"node-{idx}"

步骤3:选择embedding模型和LLM

我们需要定义两个模型:

  • embedding模型用于为每个文本块创建矢量嵌入。在这里,我们Hugging Face中的FlagEmbedding模型;
  • LLM:用户查询相关文本块喂给LLM,让其生成具有相关上下文的答案。

       我们可以在ServiceContext中将这两个模型捆绑在一起,并在以后的索引和查询步骤中使用它们。

embed_model = resolve_embed_model(“local:BAAI/bge-small-en”)llm = OpenAI(model="gpt-3.5-turbo")service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)

步骤4:创建索引、检索器和查询引擎

索引、检索器和查询引擎是基于用户数据或文档进行问答的三个基本组件:

  • 索引是一种数据结构,使我们能够从外部文档中快速检索用户查询的相关信息。矢量存储索引获取文本块/节点,然后创建每个节点的文本的矢量嵌入,以便LLM查询。
base_index = VectorStoreIndex(base_nodes, service_context=service_context)

  • Retriever用于获取和检索给定用户查询的相关信息。
base_retriever = base_index.as_retriever(similarity_top_k=2)

  • 查询引擎建立在索引和检索器之上,提供了一个通用接口来询问有关数据的问题。
query_engine_base = RetrieverQueryEngine.from_args(    base_retriever, service_context=service_context)response = query_engine_base.query(    "Can you tell me about the key concepts for safety finetuning")print(str(response))

二、高级方法1:较小的子块参照较大的父块

       在上一节中,我们使用1024的固定块大小进行检索和合成。在本节中,我们将探讨如何使用较小的子块进行检索,并引用较大的父块进行合成。第一步是创建更小的子块:

步骤1:创建较小的子块

对于块大小为1024的每个文本块,我们创建更小的文本块:

  • 8个128大小的文本块
  • 4个大小为256的文本块
  • 2个512大小的文本块

我们将大小为1024的原始文本块附加到文本块的列表中。

sub_chunk_sizes = [128, 256, 512]sub_node_parsers = [    SimpleNodeParser.from_defaults(chunk_size=c) for c in sub_chunk_sizes]all_nodes = []for base_node in base_nodes:    for n in sub_node_parsers:        sub_nodes = n.get_nodes_from_documents([base_node])        sub_inodes = [            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes        ]        all_nodes.extend(sub_inodes)    # also add original node to node    original_node = IndexNode.from_text_node(base_node, base_node.node_id)    all_nodes.append(original_node)all_nodes_dict = {n.node_id: n for n in all_nodes}

       当我们查看所有文本块“all_nodes_dict”时,我们可以看到许多较小的块与每个原始文本块相关联,例如“node-0”。事实上,所有较小的块都引用元数据中的较大块,index_id指向较大块的索引id。

步骤2:创建索引、检索器和查询引擎

  • 索引:创建所有文本块的矢量嵌入。
vector_index_chunk = VectorStoreIndex(    all_nodes, service_context=service_context)
  • Retriever:这里的关键是使用RecursiveRetriever遍历节点关系并基于“引用”获取节点。这个检索器将递归地探索从节点到其他检索器/查询引擎的链接。对于任何检索到的节点,如果其中任何节点是IndexNodes,则它将探索链接的检索器/查询引擎并查询该引擎。
vector_retriever_chunk = vector_index_chunk.as_retriever(similarity_top_k=2)retriever_chunk = RecursiveRetriever(    "vector",    retriever_dict={"vector": vector_retriever_chunk},    node_dict=all_nodes_dict,    verbose=True,)

      当我们提出问题并检索最相关的文本块时,它实际上会检索节点id指向父块的文本块,从而检索父块。

  • 现在,通过与以前相同的步骤,我们可以创建一个查询引擎作为通用接口来询问有关数据的问题。
query_engine_chunk = RetrieverQueryEngine.from_args(    retriever_chunk, service_context=service_context)response = query_engine_chunk.query(    "Can you tell me about the key concepts for safety finetuning")print(str(response))

三、高级方法2:语句窗口检索

       为了实现更细粒度的检索,我们可以将文档解析为每个块的一个句子,而不是使用更小的子块。

       在这种情况下,单句将类似于我们在方法1中提到的“子”块概念。句子“窗口”(原句子两侧各5句)将类似于“父”组块概念。换句话说,我们在检索过程中使用单个句子,并将检索到的带有句子窗口的句子传递给LLM。

步骤1:创建句子窗口节点解析器

# 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",)sentence_nodes = node_parser.get_nodes_from_documents(docs)sentence_index = VectorStoreIndex(sentence_nodes, service_context=service_context)

步骤2:创建查询引擎

       当我们创建查询引擎时,我们可以使用MetadataReplacementPostProcessor将句子替换为句子窗口,以便将句子窗口发送到LLM。

query_engine = sentence_index.as_query_engine(    similarity_top_k=2,    # the target key defaults to `window` to match the node_parser's default    node_postprocessors=[        MetadataReplacementPostProcessor(target_metadata_key="window")    ],)window_response = query_engine.query(    "Can you tell me about the key concepts for safety finetuning")print(window_response)

        语句窗口检索能够回答“你能告诉我安全微调的关键概念吗”的问题:

       在这里,您可以看到检索到的实际句子和句子的窗口,它提供了更多的上下文和细节。

结论

       在这篇博客中,我们探讨了如何使用从小到大的检索来改进RAG,重点是使用LlamaIndex的父子递归检索器和句子窗口检索。在未来的博客文章中,我们将深入探讨其他技巧和窍门。请继续关注这场激动人心的先进RAG技术之旅!

参考文献:

[1] https://towardsdatascience.com/advanced-rag-01-small-to-big-retrieval-172181b396d4

[2] https://colab.research.google.com/github/sophiamyang/demos/blob/main/advanced_rag_small_to_big.ipynb

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

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

相关文章

【网络安全】-Linux操作系统—CentOS安装、配置

文章目录 准备工作下载CentOS创建启动盘确保硬件兼容 安装CentOS启动安装程序分区硬盘网络和主机名设置开始安装完成安装 初次登录和配置更新系统安装额外的软件仓库安装网络工具配置防火墙设置SELinux安装文本编辑器配置SSH服务 总结 CentOS是一个基于Red Hat Enterprise Linu…

美颜SDK是什么?视频美颜SDK在直播平台中的集成与接入教程详解

当下,主播们追求更加自然、精致的外观,而观众也期待在屏幕前欣赏到更为清晰、美丽的画面。为了满足这一需求,美颜SDK应运而生,成为直播平台的重要利器之一。 一、什么是美颜SDK? 通过美颜SDK,开发者可以…

Kotlin Multiplatform的现状—2023年网络研讨会

Kotlin Multiplatform的现状—2023年网络研讨会 在2023年,Kotlin Multiplatform因其开发、当前状态和未来潜力而受到了相当大的关注。随着越来越多的开发者对采用KMP进行跨平台解决方案表示兴趣,JetBrains在11月下旬推出了一系列网络研讨会作为回应。首…

“去 Android化”为何蔚然成风?

早在2008年时,国内市场诞生了第一批自研手机OS,由于种种缘由铩羽而归,“优化Android ”貌似成为了本土特色。而从2023年下半年开始掀起了一股"去安卓化"的热潮,像华为、小米、vivo等都不约而同的站在了同一战线。 “去…

bisect_left,bisect_right,bisect的用法,区别以源码分析

bisect_left(*args, **kwargs) 向一个数组插入一个数字,返回应该插入的位置。 如果这个数字不存在于这个数组中,则返回第一个比这个数大的数的索引 如果这个数字存在,则返回数组中这个数的位置的最小值(即最左边那个索引&#xf…

使用Kaptcha实现的验证码功能

目录 一.需求 二.验证码功能实现步骤 验证码 引入kaptcha依赖 完成application.yml配置文件 浏览器显示验证码 前端页面 登录页面 验证成功页面 后端 此验证码功能是以SpringBoot框架下基于kaptcha插件来实现的。 一.需求 1.页面生成验证码 2.输入验证码&#xff…

Kafka-Kafka基本原理与集群快速搭建(实践)

Kafka单机搭建 下载Kafka Apache Download Mirrors 解压 tar -zxvf kafka_2.12-3.4.0.tgz -C /usr/local/src/software/kafkakafka内部bin目录下有个内置的zookeeper(用于单机) 启动zookeeper(在后台启动) nohup bin/zookeeper-server-start.sh conf…

如何实现TensorFlow自定义算子?

在上一篇文章中 Embedding压缩之基于二进制码的Hash Embedding,提供了二进制码的tensorflow算子源码,那就顺便来讲下tensorflow自定义算子的完整实现过程。 前言 制作过程基于tensorflow官方的custom-op仓库以及官网教程,并且在Ubuntu和Mac…

Leetcode—11.盛最多水的容器【中等】

2023每日刷题&#xff08;六十三&#xff09; Leetcode—11.盛最多水的容器 实现代码 #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) int maxArea(int* height, int heightSize) {int left 0, right heightSize - 1;int m…

知识蒸馏:channel wise知识蒸馏CWD

论文:https://arxiv.org/pdf/2011.13256.pdf 1. 摘要 知识蒸馏用于训练紧凑型(轻量)模型被证明是一种简单、高效的方法, 轻量的学生网络通过教师网络的知识迁移来实现监督学习。大部分的KD方法都是通过algin学生网络和教师网络的归一化的feature map, 最小化feature map上的…

数据分析思维导图

参考&#xff1a; https://zhuanlan.zhihu.com/p/567761684?utm_id0 1、数据分析步骤地图 2、数据分析基础知识地图 3、数据分析技术知识地图 4、数据分析业务流程 5、数据分析师能力体系 6、数据分析思路体系 7、电商数据分析核心主题 8、数据科学技能书知识地图 9、数据挖掘…

MATLAB 点云SVD分解计算平面法向量 (41)

MATLAB 点云SVD分解计算平面法向量 (41) 一、算法介绍二、算法实现一、算法介绍 算法主要是采用SVD分解矩阵的方法,计算平面的法向量。 二、算法实现 % 加载点云数据 ptCloud = pcread(D:\shuju\近似平面点集合2.pcd);% 计算点云质心 centroid = mean(ptCloud.</

TensorFlow 2 和 Keras 之间的区别总结

1、什么是TensorFlow 2 TensorFlow 2是谷歌开源的一款深度学习框架&#xff0c;于2019年发布&#xff0c;并且在同年10月1日发布了TensorFlow 2.0.0正式稳定版。这款框架被很多企业与创业公司广泛用于自动化工作任务和开发新系统。 TensorFlow 2在分布式训练支持、可扩展的生…

python使用ctypes访问Windows原生API

在Windows系统中&#xff0c;C语言编写的动态链接库&#xff08;DLL&#xff09;是一种可由多个程序同时使用的代码和数据共享库。DLL文件包含了一些可以被其他程序调用的函数和数据。这些DLL文件通常与应用程序一起发布&#xff0c;并在需要时被加载到内存中&#xff0c;以便应…

【玩转 TableAgent 数据智能分析】股票交易数据分析+预测

文章目录 一、什么是TableAgent二、TableAgent 的特点三、实践前言四、实践准备4.1 打开官网4.2 注册账号4.3 界面介绍4.4 数据准备 五、确认分析需求六、TableAgent体验七、分析结果解读八、总结&展望 一、什么是TableAgent TableAgent是一款面向企业用户的智能数据分析工…

HTML中边框样式、内外边距、盒子模型尺寸计算(附代码图文示例)【详解】

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍HTML中边框样式、内外边距、盒子模型尺寸计算以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问…

用23种设计模式打造一个cocos creator的游戏框架----(二十)解析器模式

1、模式标准 模式名称&#xff1a;解析器模式 模式分类&#xff1a;行为型 模式意图&#xff1a;给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解释器使用该表示来解释语言中的句子。 结构图&#xff1a; 适用于&#xff1…

K8S(十一)—Service详解

目录 Service发布服务&#xff08;服务类型&#xff09;type: ClusterIP选择自己的 IP 地址例子 type: NodePort选择你自己的端口为 type: NodePort 服务自定义 IP 地址配置例子 type: LoadBalancer混合协议类型的负载均衡器禁用负载均衡器节点端口分配设置负载均衡器实现的类别…

java之HikariCP连接池介绍和使用方法 简单易懂!!!

文章目录 一、HikariCP连接池介绍二、导入的jar包三、代码演示配置文件使用配置文件连接运行结果 一、HikariCP连接池介绍 在我们的工作中&#xff0c;免不了要和数据库打交道&#xff0c;而要想和数据库打好交道&#xff0c;选择一款合适的数据库连接池就至关重要&#xff0c…

软件试运行整体方案

一、 试运行目的 &#xff08;一&#xff09; 系统功能、性能与稳定性考核 &#xff08;二&#xff09; 系统在各种环境和工况条件下的工作稳定性和可靠性 &#xff08;三&#xff09; 检验系统实际应用效果和应用功能的完善 &#xff08;四&#xff09; 健全系统运行管理体…