pdf 版面分析与优化策略

102 篇文章 8 订阅

1. 简介

版面分析作为RAG的第一步工作,其效果对于下游工作至关重要。
在这里插入图片描述

前常见的 PDF 解析方法包括三种

  • 基于规则:根据 PDF 的组织特征确定每个部分的规则(风格和内容)缺点:不通用(PDF格式不固定)
  • 基于深度学习:目标检查和 OCR 结合的流行解决方案
  • 基于多模态大模型:对复杂的结构进行Pasing或提取PDF中的关键信息

1.1 基于规则的pdf解析

代表框架:

  • pdfplumber
  • PyMuPDF
  • pypdf
1.1.1 pdfplumber

pdfplumber 按照每一页处理 pdf,获得 pdf 的页面文字提取表格,最终将 pdf 文件生成对应的 txt 文件。
缺点:按页进行处理有时候一段上下文连续的文字,分散到两页并不好进行处理。

## 纯文本
file_name = './example_data/焦点科技__2019年__年度报告.pdf'
output_file = './example_data/焦点科技__2019年__年度报告.txt'
with pdfplumber.open(file_name) as pdf, open(output_file, 'wt', encoding='utf-8') as f:
	for page in pdf.pages: # pdf.pages是⼀个Page对象的列表
		text = page.extract_text() # 调⽤Page对象的extract_text()⽅法提取⽂本
		f.write(text)
		f.write('\n')
		
## 表格
with pdfplumber.open(file_name) as pdf, open(output_file, 'wt', encoding='utf-8') as f:
	for page in pdf.pages: # pdf.pages是⼀个Page对象的列表
		text = page.extract_table() # 提取每⻚的表格⽂字信息
		# page.extract_tables() 提取多个表格
		print(text)
		if text:
			f.write(str(text))
			f.write('\n')
			
## 表格保存为excel
from openpyxl import Workbook
with pdfplumber.open(file_name) as pdf:
	for i in pdf.pages:
		table = i.extract_table()
		if table:
			workbook = Workbook() # 创建⼀个⼯作簿
			sheet = workbook.active # 获取当前活跃的sheet,默认是第⼀个sheet
			for row in table:
				sheet.append(row)
				workbook.save(output_file) # 保存⼯作簿
				break

## 提取图片保存到本地
with pdfplumber.open(file_name) as pdf:
	for i in pdf.pages:
	print('⻚码:', i.page_number)
	print('⻚宽:', i.width)
	print('⻚⾼:', i.height)
	print('⻚⽂本:', i.extract_text())
	print('⻚表格:', i.extract_table())
	print('⻚图⽚:', i.images)
	for img in i.images:
		# 获取图⽚的⼆进制流
		img_data = img['stream'].get_data()
		with open(output_file, 'wb') as f:
			f.write(img_data)
	break # 解析第⼀⻚
1.1.2 pymupdf

pymupdf 支持将文字修改写入 pdf 的界面,支持提取所有类型的文档,支持的功能特别多,基本上各种文件都可以处理

import fitz
pdf_path = '/example_data/安靠智电__2019年__年度报告.pdf'
doc = fitz.open(pdf_path)

# 基本的pdf信息
title = doc.metadata['title'] # 标题
author = doc.metadata['author'] # 作者
create_time = doc.metadata['creationDate'] # 创建时间
num_pages = doc.page_count # ⻚数
page = doc.load_page(0) # 加载第⼀⻚
page_width = page.rect.width # ⻚宽
page_height = page.rect.height # ⻚⾼
page_text = page.get_text() # ⻚⽂本
page_images = page.get_images(full=True) # ⻚图⽚
print('标题:', title)
print('作者:', author)
print('创建时间:', create_time)
print('⻚数:', num_pages)
print('⻚码:', 1)
print('⻚宽:', page_width)
print('⻚⾼:', page_height)
print('⻚⽂本:', page_text)
print('⻚图⽚:', page_images)


num_pages = doc.page_count # ⻚数
for page_index in range(num_pages):
	page = doc.load_page(page_id=page_index)
	page_text = page.get_text() # ⻚⽂本
	print(page_text)
	break
	
for page_index in range(num_pages):
	page = doc.load_page(page_id=page_index)
	image_list = page.get_images() # 图⽚
	print(image_list)
	# 两种⽅式
	# 1
	for img in image_list:
		xref = img[0]
		base_image = doc.extract_image(xref)
		image_bytes = base_image["image"] # 图⽚⼆进制数据
		image_ext = base_image["ext"] # 图⽚格式
		# image_path = f'./example_data/__焦点科技__2019年__年度报告__page_{page_index}__image_{xref}.{image_ext}'
		# with open(image_path, 'wb') as f:
		# f.write(image_bytes)
	# 2
	pix = fitz.Pixmap(doc, xref)
	pix.save(f'./example_data/焦点科技__2019年__年度报告__page_{page_index}__image_{xref}.png')
	break
	
## 获取表格
for page_index in range(num_pages):
	page = doc.load_page(page_id=page_index)
	tables = page.find_tables() # 表格
	print(tables)
	# 提取表格数据并存储为csv⽂件
	for table in tables:
		df = table.to_pandas()
		print(df.head())
		df.to_csv(
					f'./example_data/焦点科技__2019年__年度报告_page_{page_index}__table_{table}.csv',
				index=False)
		break
		
## PDF文档分割
output_dir = './example_data/{0}.pdf'
for page_index in range(num_pages):
	print(page_index)
	# 创建⼀个新的Document对象,包含当前⻚⾯
	new_pdf = fitz.open()
	new_pdf.insert_pdf(doc, from_page=page_index, to_page=page_index)
	# 保存为单独的pdf⽂件
	new_pdf.save(output_dir.format(page_index))
	new_pdf.close()
	break
doc.close()
1.1.3 pypdf

pypdf 就是一种基于规侧广泛使用的解析器,也是 LangChain 和 Llamalndex 中解析 PDF文件的标准方法。

pip install PyPDF2

在这里插入图片描述

import PyPDF2
filename = "./example_data/焦点科技_2019年__年度报告.pdf"
pdf_file = open(filename, 'rb')
reader = PyPDF2.PdfReader(pdf_file)
page_num = 0
page = reader.pages[page_num]
text = page.extract_text()
print('--------------------------------------------------')
print(text)
pdf_file.close()
'''
焦点科技股份有限公司 2019年年度报告全⽂ 
1 
焦点科技股份有限公司 
Focus Technology Co., Ltd. 
(南京江北新区星⽕路软件⼤厦 A 座12F)
⼆〇⼀九年年度报告 
⼆〇⼆〇年⼆⽉
'''

1.2 基于深度学习的pdf解析

基于深度学习模型的方法能够准确地识别整个文档的布局,包括表格和段落。它甚至可以理解表中的结构。这意味着它可以将文档划分为定义明确、完整的信息单元。同时保留预期的含义和结构。
代表性的开源框架:

  • Unstructured: 已经集成到langchain中。使用hi_res策略设置infer_table_structure=True 可以很好的识别表格信息。然而 fast策略因为不使用目标检测模型,在识别图像和表格方面表现比较差。
  • Layout-parser:如果需要识别复杂的结构化PDF ,建议使用最大的模型以获得更高的精度,但是会比较慢。此外,Layout解析器的模型在过去几年中似乎没有更新。
  • PP-StructureV2: 可以组合各种模型用于文档分析,性能高于平均水平。
Unstructured
from unstructured.partition.pdf import partition_pdf
filename = "../paper/bert.pdf"
# infer_table_structure=True automatically selects hi_res strategy
elements = partition_pdf(filename=filename, infer_table_structure=True)
tables = [el for el in elements if el.category == "Table"]
print(tables[0].text)
print('--------------------------------------------------')
print(tables[0].metadata.text_as_html)

1.3 基于多模态的pdf解析(复杂结构)

LlamaIndex例子:检索相关图像(PDF页面)并将其发送到GPT4-V 以响应查询。

  1. 将每个PDF页面视为一个图像,让GPT4-V 对每个页面进行图像推理,为图像推理构建文本矢量存储索引,根据图像矢量存储查询答案。
  2. 使用Table Transformer从检索到的图像中裁剪表信息,然后将这些裁剪的图像发送到GPT4-V 进行查询相应。
  3. 对裁剪的表图像应用OCR,并将数据发送到GPT4 以回答查询。

经测试确定第三种方法最有效。
此外可以试用多模态模型从图像中提取或者总结关键信息(PDF文件可以很容易转换为图像)

1.3.1 解析pdf文档存在哪些挑战

挑战在于准确提取整个页面的布局,并将表格、标题、段落和图片在内的内容翻译成文档的文本表示。这个过程涉及到处理文本提取、图像识别中的不确定之处,以及表中行-列关系的混乱。

1.3.1.1 挑战一:如何从表格和图像中提取数据问题?

使用unstructured框架作为示例,检测到的表格数据可以直接导出为HTML:
在这里插入图片描述
复制HTML标记并将其另存为HTML文件,使用Chrome打开如下:
在这里插入图片描述

1.3.1.2 挑战二:如何重新排列检测到的块?特别对于双列PDF?

在这里插入图片描述
在确定布局后,unstructured框架会将每个页面划分成几个矩形块:

在这里插入图片描述
每个矩形块的详细信息可以通过以下格式获取:
在这里插入图片描述
其中(x1, y1)是左上顶点的坐标,(x2, y2)是右下顶点的坐标
在这里插入图片描述
此时,可以选择重新调整页面的阅读顺序。unstructured 有一个内置排序算法。但是实际排序结果不是很满意。
因此有必要设计一种排序算法。最简单的方法就是先按左上角顶点的水平座标排序,如果水平座标相同,则按垂直坐标排序。伪代码如下:

layout.sort(key=lambda z:(z.bbox.x1, z.bbox.y1, z.bbox.x2, z.bbox.y2))

然而还发现,即使同一列中的块,其水平坐标也可能发生变化。如下图,提取到的紫色线条块的水平坐标bbox.x1 实际更靠左。排序时他将位于路线块之前,显然违反了阅读顺序。

在这里插入图片描述
在这种情况下使用一种可能的算法:

  • 首先,对左上角所有x坐标x1 进行排序,可以得到X1_min
  • 然后,对所有右下角x坐标x2进行排序,可以得到x2_max
  • 接下来,将页面中心线的x坐标确定x1_min = min([el.bbox.x1 for el in layout]) ; x2_max = max([el.bbox.x2 for el in layout]); mid_line_x_cooedinate = (x2_max + x1_min)/2
  • 接下来,如果bbox.x1< mid_line_x_cordinate ,则块被分类为左列的一部分。否则被视为右边的一部分。
  • 分类完成后,根据列中的y坐标对每个块进行排序。
  • 最后,将右侧列连接到左侧列的右侧
left_column, right_column = [], []
for el in layout:
	if el.bbox.x1 < mid_line_x_cooedinate:
		left_column.append(el)
	else:
		right_column.append(el)

left_column.sort(key = lambda z: z.bbox.y1)
right_column.sort(key = lambda z: z.bbox.y1)
sorted_layout = left_column + right_column

值得一提的是这种改进也与单列pdf兼容.

1.3.1.3 挑战三:如何提取多级标题?

提取多级标题的目的是提高LLM答案的准确性。
该算法依然依赖与上面提到的布局快。我们可以设置type=‘Section-hearder’的块,并计算高度差(bbox.y2-bbox.y1)。高度差最大的块对应第一级标题,其次是第二级标题…

补充:PPT类文档解析问题

  • 难点:如何对PPT 中的大量的流程图,架构图进行提取,因为这些图多以形状元素在PPT中呈现,如果只提取文字,大量潜藏的信息会大量丢失。
  • 解决办法:将PPT转为pdf形式,然后用户上述处理PDF 的形式进行解析。

参考:
版面分析–优化策略篇
【知识库构建——知识文本分块】
【知识库构建——文档切分优化策略篇】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值