通过 Markdown 改进 RAG 文档处理

news2025/4/16 23:15:31

通过 Markdown 改进 RAG 文档处理

作者:Tableau

原文地址:https://zhuanlan.zhihu.com/p/29139791931

通过 Markdown 改进 RAG 文档处理https://mp.weixin.qq.com/s/LOBOKNA71dANXHuwxe7yxw

如何将 PDF 转换为 Markdown 以获得更好的 LLM RAG 结果

Markdown 是一种轻量级、易读的格式化文本语言。许多人可能通过 GitHub 的 README.md 文件熟悉 Markdown。

以下是一些基本的 Markdown 语法示例:

# Heading level 1
## Heading level 2
### Heading level 3

This is **bold text**.

This is *italicized text*.

> This text is a quote

This is how to do a link [Link Text](https://www.example.org)


This text is code


| Header 1   | Header 2   |
|------------|------------|
| table data | table data |

Markdown 正在成为大语言模型(LLMs)的流行格式。

Markdown 具有一些重要优势,例如[1]:

  • 为标题、表格、列表、链接等提供结构

  • 添加粗体或斜体等排版强调元素

  • 易于编写且人类可读

  • 已经广泛使用,例如在 GitHub 和 Jupyter notebooks 中

Markdown 不仅在 LLM 输入文档的上下文中有用,它也是像 ChatGPT 这样的聊天机器人格式化其响应的方式。注意 ChatGPT 的响应如何以大号粗体字呈现标题,并对关键词使用粗体文本。

图片

在本文中,我们将在 LLM 和检索增强生成(RAG)的背景下探讨 Markdown。

比较 PDF 库

我们首先测试两个生成纯文本的流行 PDF 阅读器库。然后我们将尝试两个专门为 LLM 设计的新 PDF 阅读器,它们可以生成 Markup。

为了比较不同的 PDF 阅读器,我将使用 Docling 技术报告 2408.09869v3.pdf 作为我的输入 PDF 文件[2],该文件采用 CC BY 4.0 许可。

FILE = "./2408.09869v3.pdf"

PyPDF

PyPDF 是一个免费开源的 Python 库,我们可以用它来轻松读取 PDF 文档。

以下是如何使用 PyPDF 来从 PDF 文件中提取文本:

# pip install pypdf
from pypdf import PdfReader

reader = PdfReader(FILE)
pages = [page.extract_text() for page in reader.pages]
pypdf_text = "\n\n".join(pages)

输出的 pypdf_text 是一个包含提取文本的字符串。

Docling Technical Report
Version 1.0
Christoph Auer Maksym Lysak Ahmed Nassar Michele Dolfi Nikolaos Livathinos
Panos Vagenas Cesar Berrospi Ramis Matteo Omenetti Fabian Lindlbauer
Kasper Dinkla Lokesh Mishra Yusik Kim Shubham Gupta Rafael Teixeira de Lima
Valery Weber Lucas Morin Ingmar Meijer Viktor Kuropiatnyk Peter W. J. Staar
AI4K Group, IBM Research
R¨uschlikon, Switzerland
Abstract
This technical report introduces Docling , an easy to use, self-contained, MIT-
licensed open-source package for PDF document conversion. It is powered by
state-of-the-art specialized AI models for layout analysis (DocLayNet) and table
structure recognition (TableFormer), and runs efficiently on commodity hardware
in a small resource budget. The code interface allows for easy extensibility and
addition of new features and models.
1 Introduction
Converting PDF documents back into a machine-processable format has been a major challenge

然而,我注意到 PyPDF 提取的文本有一些问题:

  • 标题除了被换行符包围外,与文本格式没有区别

  • 文本高亮,如粗体文本,都丢失了

  • 页码在换行符上(就像标题一样)

  • 表格没有被正确提取

以下是真实 PDF 表格与 PyPDF 提取的表格的比较:

图片

来自文档[2]的原始 PDF 表格与 PyPDF 提取的文本对比。作者提供的图片。

我怀疑任何人或 LLM 都无法使用这个变形的表格做出任何正确的陈述。

Unstructured

链接:http://Unstructured.io

流行的开源库 unstructured 对于 PDF 文档的处理与 PyPDF 类似。

以下是如何使用 Unstructured 来从 PDF 文件中提取文本:

# pip install unstructured[pdf]==0.16.5 
from unstructured.partition.pdf import partition_pdf

elements = partition_pdf(FILE)
unstructured_text = "\n\n".join([str(el) for el in elements])

输出格式和问题与 PyPDF 类似。

Unstructured 将上面的表格提取为单行,这也不是我们想要的:

CPU Thread budget TTS native backend Pages/s Mem pypdfium backend TTS Pages/s Mem Apple M3 Max (16 cores) 4 16 177 s 167 s 1.27 1.34 6.20 GB 103 s 92 s 2.18 2.45 2.56 GB Intel(R) Xeon E5-2690 4 16 375 s 244 s 0.60 0.92 6.16 GB 239 s 143 s 0.94 1.57 2.42 GB (16 cores)

PyMuPDF4LLM

PyMuPDF4LLM 是一个专门设计用于提取 PDF 内容并将其转换为 Markdown 格式以用于 LLM 和 RAG 用例的 Python 库。

PyMuPDF4LLM 是开源的,采用 AGPL-3.0 许可。

以下是如何使用 PyMuPDF4LLM 来从 PDF 文件中提取 Markdown 文本:

# pip install pymupdf4llm==0.0.17
import pymupdf4llm

md_text = pymupdf4llm.to_markdown(FILE)

在下图中,我使用 print(md_text) 生成上面的图像,使用 IPython.display 中的 Markdown(md_text) 生成下面的图像。

图片

从文档[2]中 PyMuPDF4LLM 提取的 Markdown 输出。作者提供的图片。

与之前的 PDF 阅读器相比,现在标题使用 Markdown 清晰地格式化。总的来说,输出非常干净。提取的文本中不再有随机的页码。

然而,PyMuPDF4LLM 没有正确解析表格示例:

Thread native backend pypdfium backend
CPU
budget

TTS Pages/s Mem TTS Pages/s Mem

Apple M3 Max 4 177 s 1.27 103 s 2.18
6.20 GB 2.56 GB
(16 cores) 16 167 s 1.34 92 s 2.45


Intel(R) Xeon
E5-2690
(16 cores)

Docling

图片

Docling 目前是 GitHub 上的热门仓库。图片由作者使用 https://star-history.com 创建。

IBM 最近发布的 Docling 可以解析文档(PDF、DOCX、PPTX、图像、HTML、AsciiDoc、Markdown)并将它们导出为 Markdown 或 JSON 格式,用于 LLM 和 RAG 用例。

Docling 是开源的,采用 MIT 许可。

以下是如何使用 Docling 来从 PDF 文件中提取 Markdown 文本:

# pip install docling==2.5.2
from docling.document_converter import DocumentConverter

converter = DocumentConverter()
result = converter.convert(FILE)
docling_text = result.document.export_to_markdown()

docling_text 的输出与 PyMuPDF4LLM 的输出类似。然而,Docling 在提取我们的示例表格方面做得更好:

图片

因为输入到 LLM 的表格已经是 Markdown 格式,当我们在 RAG 用例中将这些数据输入到 LLM 时,LLM 可以简单地向用户重现相同的表格,并且它将以人类可读的格式呈现。

Docling 具有出色的表格提取功能的原因是它包含了专门用于表格结构识别的 AI 模型[2]。

基于我的 PDF 文件的结果,Docling 产生了迄今为止最好的结果。 输出的 docling_text 完美地以 Markdown 格式呈现,可以用于下游的 LLM 任务。

处理速度

然而,使用 Docling 有一个缺点,那就是处理速度。我使用 timeit 计算了每个库处理我的 9 页 PDF 示例文件的平均处理速度。

虽然 Docling 给出了最好的结果,但它处理文件也花费了大约 38 秒。另一方面,PyPDF 非常快,只需 461 毫秒。

图片

处理 9 页 PDF 文件的时间。PyPDF:461 ms,Unstructured:1.28s,PyMuPDF4LLM:5.4s,Docling:38.1s。作者提供的图片。

分块

在 RAG 上下文中处理 Markdown 的一个重要优势是我们可以使用标题将文档分成连贯的片段。

在读取 PDF 文档并将其转换为 Markdown 后,我们可以使用 LangChain 的 RecursiveCharacterTextSplitter 根据特定的 Markdown 语法进行分块。

LangChain 在 Language.MARKDOWN 中定义了这些默认分隔符:

separators = [
    # First, try to split along Markdown headings (starting with level 2)
    "\n#{1,6} ",
    # End of code block
    "```\n",
    # Horizontal lines
    "\n\\*\\*\\*+\n",
    "\n---+\n",
    "\n___+\n",
    "\n\n",
    "\n",
    " ",
    "",
]

使用 langchain_text_splitter,我们现在可以使用Markdown 特定的分隔符对我们的 Markdown 文件进行分块:

# pip install langchain-text-splitters==0.3.2
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_text_splitters.base import Language

text_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.MARKDOWN,
    chunk_size=1000,
    chunk_overlap=100,
)
documents = text_splitter.create_documents(texts=[docling_text])
print(documents[1].page_content)

在 https://langchain-text-splitter.streamlit.app 有一个 LangChain 各种文本分割器的在线演示,你可以尝试使用。

当我比较基本 PyPDF 输出上的 RecursiveCharacter 文本分割器与 Docling 输出上的 MARKDOWN 文本分割器时,Markdown 分割器明显更胜一筹。

向 Markdown 添加元数据

我们可以对 Markdown 文件做的另一件好事是添加 YAML front matter 元数据。

YAML front matter 必须放在文档的开头,所有元数据都包含在三个破折号之间。

以下是可以添加到我们文档中的 YAML front matter 示例:

---
title: document title
filename: document filename
tags: keyword1 keyword2 keyword3
description: summary of the document
---

我们可以从 PDF 文件中提取这些元数据(Docling 的元数据提取功能“即将推出”),或者我们可以使用 LLM 生成必要的元数据。

Anthropic 最近发布了他们称为上下文检索的想法,其中每个文档块都包含一个由 AI 生成的块上下文的简短摘要[3]。

同样,我们可以将 YAML front matter 元数据添加到每个块中。这将为 LLM 提供关于每个块的额外信息,并提高 RAG 检索性能。

让我们从 Docling documents 向每个块添加元数据:

metadata = """---
title: Docling Technical Report
filename: 2408.09869v3.pdf
description: This technical report introduces Docling, an easy to use, self-contained, MIT licensed open-source package for PDF document conversion.
---"""

for doc in documents:
    doc.page_content = "\n".join([metadata, doc.page_content])

现在我们可以将这些块移动到向量数据库中。每个块都以 Markdown 格式精美呈现,并带有额外的元数据。

例如,看看 documents[19].page_content 中的表格有多漂亮。如果没有额外的元数据,表格块将孤立存在,没有任何上下文。

---
title: Docling Technical Report
filename: 2408.09869v3.pdf
description: This technical report introduces Docling, an easy to use, self-contained, MIT licensed open-source package for PDF document conversion.
---
| CPU                   | Thread budget   | native backend   | native backend   | native backend   | pypdfium backend   | pypdfium backend   | pypdfium backend   |
|-----------------------|-----------------|------------------|------------------|------------------|--------------------|--------------------|--------------------|
|                       | Thread budget   | TTS              | Pages/s          | Mem              | TTS                | Pages/s            | Mem                |
| Apple M3 Max          | 4               | 177 s            | 1.27             | 6.20 GB          | 103 s              | 2.18               | 2.56 GB            |
| (16 cores)            | 16              | 167 s            | 1.34             | 6.20 GB          | 92 s               | 2.45               | 2.56 GB            |
| Intel(R) Xeon E5-2690 | 4 16            | 375 s 244 s      | 0.60 0.92        | 6.16 GB          | 239 s 143 s        | 0.94 1.57          | 2.42 GB            |

总之,这是如何使用 Docling 为 RAG 准备 PDF 文件:

from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_text_splitters.base import Language
from docling.document_converter import DocumentConverter


def process_file(filename: str, metadata: str = None):
    """read file, convert to markdown, split into chunks and optionally add metadata"""

    # read file and export to markdown
    converter = DocumentConverter()
    result = converter.convert(filename)
    docling_text = result.document.export_to_markdown()

    # chunk document into smaller chunks
    text_splitter = RecursiveCharacterTextSplitter.from_language(
        language=Language.MARKDOWN,
        chunk_size=1000,
        chunk_overlap=100,
    )

    docling_documents = text_splitter.create_documents(texts=[docling_text])

    if metadata:
        for doc in docling_documents:
            doc.page_content = "\n".join([metadata, doc.page_content])
    return docling_documents

结论

在本文中,我比较了四个不同的用于读取 PDF 文件的 Python 库:PyPDF、http://unstructured.io、PyMuPDF4LLM 和 Docling。

前两个库生成纯文本输出,后两个库生成 Markdown。

通过使用 PyMuPDF4LLM 或 Docling 并将 PDF 转换为 Markdown,我们获得了更好的文本格式,减少了信息丢失,并获得了更好的表格解析。

使用 Markdown 语法,我们可以获得更好的文档分块,因为标题可以轻松指导分块过程。

使用 YAML 的 front matter 语法,我们可以向每个块添加额外的元数据。

Docling 在输出质量方面是明显的赢家。然而,Docling 的每个文档的处理时间也是最长的。

参考引用

[1] PyMuPDF (2024), RAG/LLM and PDF: Conversion to Markdown Text with PyMuPDF, Medium blog post from Apr. 10, 2024

[2] C. Auer et al. (2024), Docling Technical Report, arXiv:2408.09869, licensed under CC BY 4.0

[3] Anthropic (2024), Introducing Contextual Retrieval, Blog post from Sep. 19, 2024 on anthropic.com

[4] Unstructured Data Isn’t Just for Embeddings: Hidden Structure can Improve RAG

图片

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

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

相关文章

高速电路 PCB 设计要点一

3 高速电路 PCB 设计要点 3.1 PCB设计与信号完整性 随着电子技术的发展,电路的规模越来越大,单个器件集成的功能越来越多,速率越来越高,而器件的尺寸越来越小。由于器件尺寸的减小,器件引脚信号变化沿的速率变得越来…

【Centos】centos7内核升级-亲测有效

相关资源 通过网盘分享的文件:脚本升级 链接: https://pan.baidu.com/s/1yrCnflT-xWhAPVQRx8_YUg?pwd52xy 提取码: 52xy –来自百度网盘超级会员v5的分享 使用教程 将脚本文件上传到服务器的一个目录 执行更新命令 yum install -y linux-firmware执行脚本即可 …

Opencv计算机视觉编程攻略-第八节 检测兴趣点

目录 1.检测图像中的角点 2.快速检测特征 3.尺度不变特征的检测 4.多尺度FAST 特征的检测 在计算机视觉领域,兴趣点(也称关键点或特征点)应用包括目标识别、图像配准、视觉跟踪、三维重建等。这个概念的原理是,从图像中选取某…

基于微信小程序的医院挂号预约系统设计与实现

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大…

如何保障话费api接口的稳定性?

保障话费接口的稳定性是确保服务高效运行的关键。以下是基于最新信息的建议: 1. 选择可靠的API服务提供商 信誉和稳定性:选择有良好声誉和稳定服务记录的提供商,查看其服务水平协议(SLA)以确保高可用性。技术支持&…

video标签播放mp4格式视频只有声音没有图像的问题

video标签播放mp4格式视频只有声音没有图像的问题 这是由于视频格式是hevc(H265)编码的,这种编码格式视频video播放有问题主要是由于以下两种原因导致的: 1、浏览器没有开启硬加速模式: 开启方法(以谷歌浏览器为例)&a…

解决docker部署的容器第二天访问报错139的问题

前阵子我部署项目,把数据库放宿主机上,结果电脑一重启,Docker 直接把数据库删了个精光!我当时的表情 be like 😱:"我的数据呢???" 连备份都没来得及做&#xf…

如何对接银行卡二要素核验接口?

银行卡二要素核验接口是一种通过API(应用程序编程接口)实现对用户提供的银行卡信息进行基本身份验证的技术服务,主要用于核验银行卡号与持卡人姓名是否一致,从而确认用户身份的真实性和操作合法性。 银行卡二要素核验接口通过调用…

深度学习——深入解读各种卷积的应用场景优劣势与实现细节

前言 卷积操作在深度学习领域中占据着核心地位,其在多种神经网络架构中发挥着关键作用。然而,卷积的种类繁多,每种卷积都有其独特的定义、应用场景和优势。 对于那些对深度学习中不同卷积类型(例如 2D 卷积、3D 卷积、11 卷积、转…

Pyinstaller 打包flask_socketio为exe程序后出现:ValueError: Invalid async_mode specified

Pyinstaller 打包flask_socketio为exe程序后出现&#xff1a;ValueError: Invalid async_mode specified 一、详细描述问题描述 Traceback (most recent call last): File "app_3.py", line 22, in <module> File "flask_socketio\__init__.py"…

chromium魔改——navigator.webdriver 检测

chromium源码官网 https://source.chromium.org/chromium/chromium/src 说下修改的chromium源码思路&#xff1a; 首先在修改源码过检测之前&#xff0c;我们要知道它是怎么检测的&#xff0c;找到他通过哪个JS的API来做的检测&#xff0c;只有知道了如何检测&#xff0c;我们…

【力扣hot100题】(048)二叉树的最近公共祖先

依旧只会用递归栈。 栈记录当前遍历的节点&#xff0c;如果有一个节点已经被找到&#xff0c;则不往栈中添加新节点&#xff0c;并且每次回溯删除栈顶节点&#xff0c;每次回溯判断另一个节点有没有在栈顶节点的右边。 /*** Definition for a binary tree node.* struct Tree…

爬虫:请求头,requests库基本使用

请求方式&#xff1a;get(向服务器要资源)和post(提交资源) user-agent&#xff1a;模拟正常用户的一种方式 cookie&#xff1a;登陆保持 referer&#xff1a;表示当前这一次请求是由哪个请求过来的 抓取数据包得到的内容才是判断依据elements中的源码是渲染之后的不能作为…

[物联网iot]对比WIFI、MQTT、TCP、UDP通信协议

第一步&#xff1a;先理解最基础的关系&#xff08;类比快递&#xff09; 假设你要给朋友寄快递&#xff1a; Wi-Fi&#xff1a;相当于“公路和卡车”&#xff0c;负责把包裹从你家运到快递站。 TCP/UDP&#xff1a;相当于“快递公司的运输规则”。 TCP&#xff1a;顺丰快递&…

CSDN自动设置vip文章的解除办法

文章目录 CSDN真的会将“全部可见”文章偷偷自动设置为“VIP可读”最省事的途径&#xff1a;联系客服&#xff0c;预计1-2个工作日可以取消新版“内容管理”内手工操作 CSDN真的会将“全部可见”文章偷偷自动设置为“VIP可读” 今天无意中发现之前一些公开的文章变为仅VIP可读…

P4305 [JLOI2011] 不重复数字

使用stl中的动态数组和unordered_map #include<iostream> #include<iostream> #include<vector> #include<unordered_map> using namespace std; int t; int main(){cin>>t;while(t--){//每次处理一组数据.int n;cin>>n;vector<int&…

Joomla教程—Joomla 模块管理与Joomla 模块类型介绍

Joomla 模块管理 原文&#xff1a;Joomla 模块管理_w3cschool 模块管理&#xff0c;从文字意面上理解&#xff0c;可想而知&#xff0c;就是管理网站中所有的模块&#xff0c;模块的增、删、改、查都会在模块管理进行。这一节将简单介绍joomla后台的模块管理 1、模块管理的界…

安装gvm后普通用户模式下无法使用cd切换目录

安装gvm后普通用户模式下无法使用cd切换目录 今天装完gvm后发现无法使用cd来切换目录了。。。 1.使用type cd命令发现cd命令被定义为了函数 usrusr-pc:~$ type cd cd 是函数 cd () { if __gvm_is_function __gvm_oldcd; then__gvm_oldcd $*;fi;local dot_go_version dot_go_…

Vue中虚拟DOM创建到挂载的过程

Vue中虚拟DOM创建到挂载的过程 流程概括下来基本上就是&#xff1a;模板 → AST → render函数 → 虚拟节点 → 挂载 AST&#xff1a;抽象语法树&#xff0c;它用于记录原始代码中所有的关键信息&#xff0c;根据AST可以将代码从一种语言转化为另一种语言。 虚拟DOM创建到挂载…

选择网上购物系统要看几方面?

随着电子商务的迅猛发展&#xff0c;选择一个合适的网上购物系统已成为许多企业成功的关键。无论是初创企业还是已经成熟的公司&#xff0c;选择合适的购物系统都能显著提升用户体验、提高销售额和优化运营效率。本文将从几个重要方面探讨选择网上购物系统时需要考虑的关键因素…