通过开放解析智能分块提高 RAG 性能

如果要使用大型语言模型 ()LLMs 实现生成式 AI 解决方案,则应考虑使用检索增强生成 (RAG) 的策略来生成上下文感知提示LLM。在启用 LLM RAG 的预生产管道中发生的一个重要过程是删除文档文本,以便仅将文档中最相关的部分与用户查询匹配并发送到用户查询以生成内容。LLM这就是 Open-Parse 可以提供帮助的地方。Open-Parse 超越了朴素的文本拆分,以确保相似的文本不会被拆分为两个单独的块。在这篇文章中,我将展示如何从 MinIO 存储桶中获取原始形式的文档,使用 Open-Parse 将它们分块,然后将它们保存到另一个可用于馈送向量数据库的存储桶中。可以在此处找到包含本文中显示的所有代码的 Jupyter 笔记本。

在介绍 Open-Parse 处理文档的功能之前,让我们先看一下 RAG 推理管道。请特别注意如何使用文档块来提高生成对用户查询的响应时的LLMs性能。

RAG 推理管道

下图显示了 RAG 推理管道。它还显示了文档处理管道,这将在后面的章节中讨论。

检索增强生成 (RAG) 是一种技术,它从用户请求(通常是问题)开始,使用向量数据库将请求与其他数据结合在一起,然后将请求和数据传递给 LLM an 以进行内容创建。使用 RAG,不需要任何培训,因为我们LLM通过从自定义文档语料库发送相关文本块来对其进行教育。如下图所示。它的工作原理是这样的,使用问答任务:用户在应用程序的用户界面中提出问题。您的应用程序将接受问题 - 特别是其中的单词 - 并使用向量数据库搜索与上下文相关的文本块。这些块和原始问题被发送到 LLM.整个包 - 问题加块(上下文)被称为提示。将LLM使用此信息来生成您的答案。这似乎是一件愚蠢的事情 - 如果您已经知道答案(片段),为什么还要为?LLM请记住,这是实时发生的,目标是生成文本 - 您可以复制并粘贴到您的研究中。您需要创建LLM包含自定义语料库中信息的文本。使用 RAG,可以实现用户授权,因为在推理时从向量数据库中选择文档(或文档片段)。文档中的信息永远不会成为模型参数参数的一部分。下面列出了RAG的主要优点。

优势

  • 从您的自定义语料库LLM中获取直接知识。

  • 可解释性是可能的。

  • 无需微调。

  • 幻觉显着减少,可以通过检查向量数据库查询的结果来控制。

  • 可以实现文档级授权。

文档处理管道

显然,RAG 的一个重要部分是在通过嵌入模型运行文档并将嵌入保存到向量数据库之前发生的文档处理。如果您有二进制格式的复杂文档,例如 PDF,这并非易事。例如,文档通常包含表格、图形、注释、编辑文本和对其他文档的引用。此外,LLMs对可以使用原始查询发送的上下文的大小进行限制。因此,发送整个文档不是一种选择。即使您可以发送整个文档,也可能无法产生最佳结果。来自多个文档的代码段集合可能是特定用户查询的最佳上下文。为了解决这个问题,许多解析库会根据所需的块长度拆分文档。一种暴力的方法是仅使用块长度来拆分文本。更好的方法是在句子或段落边界上拆分,该边界仍将块保持在限制范围内。虽然这样更好,但它可以将节标题放在它们自己的块和块表中,将它们拆分为几个块。

Open-Parse 是一个用于拆分 PDF 文件的开源库,它超越了朴素的文本拆分。它的设计灵活且易于使用。它通过分析布局和基于简单的启发式创建块来对文档进行分块,这些分块将相关文本保留在同一块中。下面是一个流程图,显示了 Open-Parse 逻辑。注意:文本节点将转换为 Markdown,而表节点将转换为 HTML。

来源: https://filimoa.github.io/open-parse/processing/overview/

让我们看几个简单的例子,看看这到底意味着什么。

使用 Open-Parse 对文档进行分块

在生产环境中,您需要将原始 PDF 和分块对象存储在具有性能和扩展能力的存储解决方案中。这就是 MinIO 的用武之地。本节中的代码假定您设置了两个存储桶,如下所示。

我已将 Open-Parse 存储库中说明书使用的一些示例文档上传到 original-corpus 存储桶。我还上传了我最喜欢的白皮书。

我们需要的第一件事是一个实用程序函数,用于将文件下载到临时目录,以便 open-parse 可以处理它。以下功能将连接到 MinIO,并将 PDF 下载到系统的临时文件夹。

import os
from dotenv import load_dotenvload_dotenv()
MINIO_URL = os.environ['MINIO_URL']
MINIO_ACCESS_KEY = os.environ['MINIO_ACCESS_KEY']
MINIO_SECRET_KEY = os.environ['MINIO_SECRET_KEY']
if os.environ['MINIO_SECURE']=='true': MINIO_SECURE = True
else: MINIO_SECURE = False
import tempfilefrom minio import Minio
from minio.error import S3Errordef get_pdf_from_minio(bucket_name: str, object_name: str) -> str:'''Retrieves an object from MinIO, saves it in a temp file and retiurns thepath to the temp file.'''try:# Create client with access and secret keyclient = Minio(MINIO_URL,MINIO_ACCESS_KEY, MINIO_SECRET_KEY,secure=MINIO_SECURE)# Generate a temp file.temp_dir = tempfile.gettempdir()temp_file = os.path.join(temp_dir, object_name)# Save object to file.client.fget_object(bucket_name, object_name, temp_file)except S3Error as s3_err:raise s3_errexcept Exception as err:raise errreturn temp_file

使用下面的代码片段运行此函数后,我们将在当前系统的临时目录中有一个文件。

original_corpus_bucket_name = 'original-documents'
chunked_corpus_bucket_name = 'document-chunks'
object_name = 'Attention is all you need.pdf'temp_file = get_pdf_from_minio(original_corpus_bucket_name, object_name)

接下来,让我们拆分 PDF。这就像几行代码一样简单。拆分文档后,可以显示节点。如下所示。

import openparseparser = openparse.DocumentParser()
parsed_basic_doc = parser.parse(temp_file)print('Number of chunks:', len(parsed_basic_doc.nodes))for node in parsed_basic_doc.nodes:print(node)

每个节点都包含一个文本块和有关该块来源的其他信息。下面的屏幕截图显示了上述片段的输出,其中的信息相当丰富。

使用此显示技术有助于了解用于表示文档的基础对象模型。然而,Open-Parse 特别好的是它的可视化工具,它可以在原始文档上绘制一个边界框,显示每个块的来源。这只需两行代码即可完成。


pdf = openparse.Pdf(temp_file)
pdf.display_with_bboxes(parsed_basic_doc.nodes[0:4])

在此代码中,我要求 Open-Parse 绘制原始 PDF 以及围绕前四个文本块的边界框。显示如下。请注意,即使文本采用类似网格的格式,作者也被放置在一个块中。Open-Parse 还找出了页面左侧的垂直文本。

如果我们通过类似的代码运行另一个原始文档,我们可以看到 Open-Parse 如何处理部分标题和项目符号文本。

将块保存到 MinIO

一旦我们将文档分块,下一步就是将每个块保存到 MinIO。为此,我们将使用上面屏幕截图中所示的 document-chunks 存储桶。以下函数会将文件保存到 MinIO 存储桶中。我们将使用此函数将每个块保存为单独的对象。


def save_chunk_to_minio(bucket_name: str, object_name: str,file_path: str, metadata: dict) -> None:'''Saves a doument chunk to MinIO.'''try:# Create client with access and secret keyclient = Minio(MINIO_URL,  # host.docker.internalMINIO_ACCESS_KEY, MINIO_SECRET_KEY,secure=MINIO_SECURE)client.fput_object(bucket_name, object_name, file_path, metadata=metadata)except S3Error as s3_err:raise s3_errexcept Exception as err:raise err

Open-Parse 提供了一种model_dump方法,用于将分块文档序列化为字典。下面的代码片段调用此方法并打印一些附加信息,以便您了解此字典的形成方式。


import jsonchunks = parsed_basic_doc.model_dump_json()
chunks = json.loads(chunks)print(chunks.keys())
print(chunks['nodes'][0])
print(type(chunks['nodes'][0]))
chunks

输出如下所示。


dict_keys(['nodes', 'filename', 'num_pages', 'coordinate_system', 'table_parsing_kwargs']){'variant': {'text'}, 'tokens': 140, 'bbox': [{'page': 0, 'page_height': 792.0, 'page_width': 612.0, 'x0': 116.68, 'y0': 436.19, 'x1': 497.21, 'y1': 558.54}], 'text': '...'}<class 'dict'>

{'nodes': [{'variant': {'text'},'tokens': 140,'bbox': [{'page': 0,'page_height': 792.0,'page_width': 612.0,'x0': 116.68,'y0': 436.19,'x1': 497.21,'y1': 558.54}],'Text': ...

字典可以拆开,每个块都可以发送到 MinIO。如下所示。此代码还会将有关原始文档的元数据添加到每个对象。 将原始文件名与每个块一起保存有助于 Rag 推理管道中的可解释性。可解释性允许使用启用LLM的 RAG 的应用程序显示指向用于构建提示上下文的所有文档的链接。对于致力于提高推理管道性能的最终用户和工程师来说,这是一项强大的功能。

import jsontemp_dir = tempfile.gettempdir()
temp_file = os.path.join(temp_dir, 'tmp.json')
print(temp_file)metadata = {}
metadata['filename'] = chunks['filename']
metadata['num_pages'] = chunks['num_pages']
metadata['coordinate_system'] = chunks['coordinate_system']
metadata['table_parsing_kwargs'] = chunks['table_parsing_kwargs']
print(metadata)chunk_num = 0
for node in chunks['nodes']:with open(temp_file, 'w') as f:f.write(json.dumps(node))#pickle.dump(node, f) # Serialize the node.chunk_name = os.path.splitext(object_name)[0]save_chunk_to_minio(chunked_corpus_bucket_name, f'{chunk_num} - {chunk_name}.json',temp_file, metadata)chunk_num += 1

请注意,我们无法将数据转储到 JSON 文件。包含所有块的字典使用 Python “set” 对象,该对象无法序列化为 JSON。对于这种情况,Python pickle 格式工作得很好。完成上述代码后,我们的 document-chunks 存储桶将如下所示。

后续步骤

这篇文章介绍了 Open-Parse 的核心功能。但是,它还具有一些高级功能,在构建生产级推理管道之前应探索这些功能。

  • 解析表可能很棘手。如果默认功能在处理表时遇到问题,请在此处查看高级表解析功能。

  • RAG的主要优势之一是可解释性。它允许用户查看用于生成答案的所有文档的链接。使用 Open-Parse,这是通过与每个文档块一起保留的元数据实现的。当收集语义相关的块时,可以确定使用的所有文档,并且可以与生成的文本一起显示指向这些文档的链接。可以在此处观看此功能的简短视频演示。

  • 语义分块是一种高级技术,如果节点(块)在语义上相似,则它们会组合在一起。点击此处了解详情。

  • 如果希望进一步处理提取的数据,可以向 DocumentParser 类添加自定义处理函数。点击此处了解详情。

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

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

相关文章

[数据集][目标检测]药片药丸检测数据集VOC+YOLO格式152张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;152 标注数量(xml文件个数)&#xff1a;152 标注数量(txt文件个数)&#xff1a;152 标注类别…

听说你还不会用Dagger2?Dagger2 For Android最佳实践教程

Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG,chef.cook()); } } 可以看到&#xff0c;在使用Dagger2的时候&#xff0c;使用者的代码会变得非常简洁。但是&#…

如何用好swoole/webman/workerman/hyperf呢

Webman框架的依赖 "require": { "php": ">7.2", "workerman/webman-framework": "^1.5.0",// "monolog/monolog": "^2.0" }, 依赖的核心框架也是很久的了 webman-framework的核心依赖 &q…

分布式架构的优势与实现

目录 前言1. 什么是分布式架构1.1 分布式架构的定义1.2 分布式架构的基本原理 2. 分布式架构的优势2.1 可扩展性2.2 容错性和高可用性2.3 性能优化2.4 灵活性和可维护性 3. 分布式架构的实现方法3.1 服务拆分3.1.1 功能拆分3.1.2 垂直拆分3.1.3 水平拆分 3.2 数据分布与存储3.2…

java—Mybatis缓存

缓存的作用 缓存(cache&#xff09;的作用是为了减轻数据库的压力&#xff0c;提高查询性能。 为什么使用缓存 mysql数据库保存的数据均在硬盘中&#xff0c;CPU是不会直接和硬盘进行交互的&#xff0c;因为硬盘的数据传输率很低&#xff0c;而CPU的数据传输率很高, CPU和内存直…

VBA技术资料MF165:关闭当前打开的所有工作簿

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

四年Android,终于咸鱼翻身!8K到25K全靠这份高级面试题+解析!

1、哪些情况下的对象会被垃圾回收机制处理掉&#xff1f; 2、讲一下常见编码方式&#xff1f; 3、utf-8 编码中的中文占几个字节&#xff1b;int 型几个字节&#xff1f; 4、静态代理和动态代理的区别&#xff0c;什么场景使用&#xff1f; 5、Java 的异常体系 6、谈谈你对解析…

写一个坏越的个人天地(一)

好久没写什么大点的项目了,今天想着写一个个人博客好了。I did it! 做个人天地。肯定得有个主题色吧。整个下拉界面,先准备三个色系吧 <el-header class="title"><el-dropdown @command="handleCommand"><span class="el-dropdown-…

若依4.7.8版本计划任务rce复现

0x00 背景 最近项目中发现很多单位都使用了若依二开的系统&#xff0c;而最近若依有个后台计划任务rce的漏洞&#xff0c;比较新&#xff0c;我还没复现过&#xff0c;于是本地搭建一个若依环境复现一下这个漏洞。 这个漏洞在4.7.8版本及之前都存在&#xff0c;现在最新版的若…

Golang | Leetcode Golang题解之第179题最大数

题目&#xff1a; 题解&#xff1a; func largestNumber(nums []int) string {sort.Slice(nums, func(i, j int) bool {x, y : nums[i], nums[j]sx, sy : 10, 10for sx < x {sx * 10}for sy < y {sy * 10}return sy*xy > sx*yx})if nums[0] 0 {return "0"…

Android 天气APP(八)城市切换 之 自定义弹窗与使用

然后在模块的utils包中新建一个LiWindow类 代码如下&#xff1a; package com.llw.mvplibrary.utils; import android.app.Activity; import android.content.Context; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; im…

element-ui里message抖动问题

由于element默认屏蔽滚动条&#xff0c;导致取消时弹message时 侧边滚动栏突然回来后引起抖动问题 是由于打开弹窗时出现遮罩层dialog对话框 时引起了元素内容超出自身尺寸 对应的overflow样式内容为hidden&#xff0c;且新建了一个class类内容为增加17 内右边距&#xff0c;当…

某md5魔改-js还原

我们先把js 扣下来看一下 整体扣一下 ,运行后发现结果一致。 到这里就结束了吗,不不,这次我们要看到它里面具体的变动 <-_-> 先看一下md5的初步加密流程 void MD5Init(MD5_CTX *context) {context->count[0] = 0;context->count[1] = 0;context->state[0]…

AI网络爬虫:搜狗图片的时间戳反爬虫应对策略

如何批量爬取下载搜狗图片搜索结果页面的图片&#xff1f;以孙允珠这个关键词的搜索结果为例&#xff1a; https://pic.sogou.com/pics?query%E5%AD%99%E5%85%81%E7%8F%A0&mode2 翻页规律如下&#xff1a; https://pic.sogou.com/napi/pc/searchList?mode2&start38…

Flutter开发环境搭建和调试

[你的Flutter文件夹路径]\flutter\bin 这样我们的Flutter SDK的环境变量就配置完毕了。接下来在命令提示符窗口中输入命令&#xff1a; flutter doctor 它可以帮助我们检查Flutter环境变量是否设置成功&#xff0c;Android SDK是否下载以及配置好环境变量等等。如果有相关的…

【接口自动化测试】第二节.Requests库和接口对象封装

文章目录 前言一、Requests库 1.1 Requests介绍 1.2 Requests发送请求 1.3 Requests查看响应 1.4 案例1登录接口调试-获取验证码 1.5 案例2登录接口调试-登录 1.6 归纳小结二、接口对象封装 2.1 当前代码待优化问题 2.2 接口对象封装思…

仿饿了么加入购物车旋转控件 - 自带闪转腾挪动画 的按钮

, mWidth - mCircleWidth, mHeight - mCircleWidth); canvas.drawRoundRect(rectF, mHintBgRoundValue, mHintBgRoundValue, mHintPaint); //前景文字 mHintPaint.setColor(mHintFgColor); // 计算Baseline绘制的起点X轴坐标 int baseX (int) (mWidth / 2 - mHintPaint.m…

【面试实战】# 并发编程之线程池配置实战

1.先了解线程池的几个参数含义 corePoolSize (核心线程池大小): 作用: 指定了线程池维护的核心线程数量&#xff0c;即使这些线程处于空闲状态&#xff0c;它们也不会被回收。用途: 核心线程用于处理长期的任务&#xff0c;保持最低的线程数量&#xff0c;以减少线程的创建和…

springboot中,将某个函数的日志单独输出的方法

背景 项目中有个节点健康检查扫描功能&#xff0c;每10秒扫描一次节点。 如果节点挂掉&#xff0c;会输出健康检查失败的日志。 测试环境&#xff0c;虽然配置了多个节点&#xff0c;但并没有都启动&#xff0c;所以在扫描的时候&#xff0c;会不断的出现报错&#xff0c; 对于…

【low-ui-vue】实现原生可扩展动态表格组件

本文字数&#xff1a;3520字 预计阅读时间&#xff1a;20分钟 所谓动态列的表格&#xff0c;就是列数不固定。像广为使用的elementUI的table组件就是表头写死的&#xff0c;这种也叫列数固定的表格。 01 效果 当然&#xff0c;动态性增加了&#xff0c;当然要做出一定“牺牲”。…