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

news2024/11/27 3:14:48

如果要使用大型语言模型 ()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_dotenv

load_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 tempfile

from minio import Minio
from minio.error import S3Error


def get_pdf_from_minio(bucket_name: str, object_name: str) -> str:
   '''
   Retrieves an object from MinIO, saves it in a temp file and retiurns the
   path to the temp file.
   '''
   try:
       # Create client with access and secret key
       client = 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_err
   except Exception as err:
       raise err

   return 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 openparse

parser = 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 key
       client = Minio(MINIO_URL,  # host.docker.internal
                   MINIO_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_err
   except Exception as err:
       raise err
       

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


import json

chunks = 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 json

temp_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.coloradmin.cn/o/1848825.html

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

相关文章

JupyterLab使用指南(八):更改JupterLab左侧默认打开目录

在JupyterLab中&#xff0c;默认打开路径通常是由其配置文件中的root_dir设置决定的。如果你没有特意设置这个配置项&#xff0c;JupyterLab可能会使用当前用户的主目录或者上一次关闭时的路径作为默认打开路径。 更改JupyterLab默认路径的操作在不同操作系统下大体相似&…

Apache Arrow 和数据的未来:开放标准推动人工智能发展

Apache Arrow 是一种开源列式内存格式&#xff0c;适用于平面数据和分层数据。在现代数据湖中&#xff0c;开放数据格式&#xff08;如 Apache Arrow&#xff09;位于现代对象存储的存储层中。这些格式成为对象存储中的对象。 在最新版本中&#xff0c;Apache Arrow 宣布计划从…

第二十七篇——通信趋势:5G和IOT的商机在哪里?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 借势的重要性&#xff0c;但是要做到借势&#xff0c;得先看到&#xff0…

WPF文本绑定显示格式StringFormat设置-特殊格式时间日期和多数据绑定

WPF文本绑定显示格式StringFormat设置 特殊格式设置日期/时间使用系统默认样式自定义格式&#xff1a; 绑定多个属性&#xff08;多重绑定&#xff09;多重绑定中的特殊字符示例&#xff1a; 特殊格式设置 在Textblock等文本控件中&#xff0c;我们经常要显示一些日期和时间&a…

Android (已解决)Execution failed for task ‘:app:lint‘

文章目录 一、错误原因二、解决方法 一、错误原因 这个错误信息表示在执行 Lint 检查时发现了错误&#xff0c;导致构建过程被中断。Lint 是一个用于检测 Android 项目中潜在问题的工具&#xff0c;比如性能、安全性、可用性等方面的问题。当Lint检查到严重错误时&#xff0c;…

碳钢酸洗线送酸槽蒸汽冷凝水PH计测量装置改进方法

碳钢酸洗线送酸槽蒸汽冷凝水PH计测量装置改进方法 一、项目提出前状况 1)立项背景 轧钢退火酸洗生产线的酸洗过程需要使用大量的硫酸、盐酸、硝酸、氢氟酸等酸液对钢带的表面进行清洗,酸洗过后产生较多的酸洗废水,酸洗废水需要经过处理达到污水排放标准后才能排放。其中酸…

Android企业级实战-界面篇-5

3.colors.xml文件内容&#xff08;此案例可用&#xff09; #ffb2b2b2 #ff14c4bc color/jimeng_text_tertiary_light color/jimeng_green_light color/jimeng_background_secondary_light color/jimeng_background_secondary_light #7f4eb7ba 4.strings.xml文件内容&…

Android系统揭秘(一)-Activity启动流程(上)

public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread (IApplicationThread) contextThread; … try { … int result …

React实现列表列宽可拖拽

1.中文文档上没有&#xff0c;英文文档上有&#xff08;这个老六&#xff01;&#xff01;&#xff09; <Tableborderedcomponents{{header: {cell: ResizableTitle,},}}columns{mergedColumns}dataSource{data} />React - Resizable column

20240622 每日AI必读资讯

&#x1f916;力压GPT-4o&#xff01;新王Claude 3.5 Sonnet来了&#xff0c;直接免费可用 - 新模型在推理、知识和编码能力评估方面超越了以前的版本和竞争对手GPT 4o模型&#xff0c;同时其运行速度是Claude 3 Opus的两倍。 - 该模型可在http://Claude.ai和Claude iOS应用上…

鸿蒙 登录界面示例

1.b站登录界面 我的b站教学视频&#xff1a;https://www.bilibili.com/video/BV1LQgQexEGm/https://www.bilibili.com/video/BV1LQgQexEGm/ 最终实现效果&#xff1a; 需要准备2张图片&#xff0c;分别是向下和向右边的图标 代码&#xff1a; Entry Component struct Index…

CentOS7 部署安装ClickHouse

一、什么是ClickHouse ClickHouse 是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用SQL查询实时生成分析数据报告。 OLAP场景需要…

Python面向对象编程:类和对象的奥秘

更多Python学习内容&#xff1a;ipengtao.com 在Python的世界中&#xff0c;面向对象编程&#xff08;OOP&#xff09;是构建强大应用程序的基石。本文将带你从零开始&#xff0c;一步步探索类和对象的奥秘&#xff0c;让你的代码更加模块化和可重用。面向对象编程是一种编程范…

嵌入式开发二十一:定时器之通用定时器

通用定时器是在基本定时器的基础上扩展而来&#xff0c;增加了输入捕获与输出比较等功能。高级定时器又是在通用定时器基础上扩展而来&#xff0c;增加了可编程死区互补输出、重复计数器、带刹车(断路)功能&#xff0c;这些功能主要针对工业电机控制方面。 本篇博客我们主要来学…

Opencv学习项目5——pyzbar,numpy

上一次我们使用pyzbar进行解码二维码并将其内容显示在图像上&#xff0c;使用的是rect barcode.rect来获取图像的坐标&#xff0c;这次我们使用另一种方法来获取坐标进行画框。 Numpy介绍 NumPy 是一个用于科学计算的开源 Python 库&#xff0c;提供了对大量数值数据进行高效操…

【泛微系统】解决启动非标功能时提示客户ID不一致的问题

解决启动非标时提示CID不一致的问题 泛微OA系统是一个非常丰富的系统,我们在日常工作中会经常遇到很多业务需求,我们会用到很多功能来承载这些需求的实现;OA系统里有标准功能,也有非标准的功能;对于非标准的功能需要打非标补丁包; 有些同学在个人学习系统的过程中会安装本…

STM32项目分享:家庭环境监测系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.…

【性能优化】表分桶实践最佳案例

分桶背景 随着企业的数据不断增长&#xff0c;数据的分布和访问模式变得越来越复杂。我们前面介绍了如何通过对表进行分区来提高查询效率&#xff0c;但对于某些特定的查询模式&#xff0c;特别是需要频繁地进行数据联接查或取样的场景&#xff0c;仍然可能面临性能瓶颈。此外…

VisualBox 虚拟机 Ubunut 18.04 在大显示器上黑屏的问题

在小屏幕上显示没有问题&#xff0c;但是移动到大显示器上就黑屏了&#xff0c;并且不能铺满&#xff0c;如下所示 如果我希望它铺满整个屏幕&#xff0c;如何解决呢&#xff1f; 下面是解决方法&#xff1a; 虚拟机底部这个按钮&#xff0c;右键 产生菜单&#xff0c;按这个选…

基于STM8系列单片机驱动74HC595驱动两个3位一体的数码管

1&#xff09;单片机/ARM硬件设计小知识&#xff0c;分享给将要学习或者正在学习单片机/ARM开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 为了节省单片机MCU的IO口资源驱动6个数码管&…