NLP(八十九)PDF文档智能问答入门

本文将会介绍如何使用fitz模块处理PDF文档,以及如何使用OpenAI大模型对PDF文档进行问答。

在文章NLP(六十一)使用Baichuan-13B-Chat模型构建智能文档中笔者介绍如何使用大模型来构建智能文档问答助手,其中包括PDF文档。

在文本中,笔者将会介绍如何使用fitz模块来处理PDF文档,并基于此,再使用大模型对PDF文档内容进行智能问答,提升问答效果。

fitz模块介绍

PyMuPDFFitz是用于在Python中处理PDF文件的相关模块。FitzPyMuPDF的子模块,提供了一个简化和封装版本的PyMuPDF功能。

  1. 关系:
  • PyMuPDF:是一个强大灵活的Python库,用于处理PDF,不依赖其它Python包。
  • Fitz:是PyMuPDF库的一个包装器,专门设计用于在Python中处理PDF文件。
  1. 用途:
  • PyMuPDF:提供了广泛的功能,用于操作PDF文档,包括方便的高级函数和底层操作。
  • Fitz:简化和封装了PyMuPDF的功能,使在Python中处理PDF文件更加简单。
  1. 安装:
  • 要使用Fitz,需要安装Fitz和PyMuPDF。如果未安装其中任何一个模块,可能会出现类似"ModuleNotFoundError: No module named 'frontend'"的错误。
  • 可以使用pip进行安装,例如从清华镜像源安装PyMuPDF。

总之,虽然PyMuPDF是一个全面的用于在Python中操作PDF文档的库,但Fitz是一个子模块,简化和封装了PyMuPDF的功能,使其更适合特定涉及PDF文件的任务,并且更易于使用。

PDF文档处理

PyMuPDF模块的官方教程为:https://pymupdf.readthedocs.io/en/latest/tutorial.html 。

以下将会介绍如何使用fitz模块来获取PDF文档的基本信息、文本、图片、表格以及如何分割PDF文档。

本文所使用的PDF文档为:

  • demo1.pdf
demo1.pdf
  • demo2.pdf
demo2.pdf

获取PDF文档基本信息

Python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: utf-8 -*-
# PyMuPDF==1.23.26
import fitz

pdf_path = "../data/demo1.pdf"
doc = fitz.open(pdf_path)

# basic PDF info
title = doc.metadata["title"]
author = doc.metadata["author"]
create_date = doc.metadata["creationDate"]
num_pages = doc.page_count
page = doc.load_page(0)
page_height = page.bound().height
page_width = page.bound().width
print(f"title: {title}\nauthor: {author}\ncreate_date: {create_date}\nnum_pages: {num_pages}\n"
f"page_height: {page_height}\npage_width: {page_width}\n")

输出结果为:

1
2
3
4
5
6
title: 
author:
create_date: D:20240301052229+00'00'
num_pages: 1
page_height: 841.9199829101562
page_width: 594.9599609375

获取文本

Python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding: utf-8 -*-
# PyMuPDF==1.23.26
import fitz

pdf_path = "../data/demo1.pdf"
doc = fitz.open(pdf_path)
num_pages = doc.page_count

# Text info of PDF
for page_index in range(num_pages):
page = doc.load_page(page_index)
text = page.get_text()
print(f"第{page_index + 1}页的文本内容为:\n{text}\n")

输出结果为:

1
2
3
4
5
6
7
1页的文本内容为:
2024/3/1 13:22
md2pdf - Markdown to PDF
https://md2pdf.netlify.app
1/1
This is a demo pdf.
Today is Friday, 1st Match, 2024.

获取图片

获取PDF文档中的图片的Python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
# PyMuPDF==1.23.26
import fitz

pdf_path = "../data/demo1.pdf"
doc = fitz.open(pdf_path)
num_pages = doc.page_count

# Image info of PDF
for page_index in range(num_pages):
page = doc.load_page(page_index)
image_list = page.get_images()
print(image_list)
for img in image_list:
xref = img[0]
pix = fitz.Pixmap(doc, xref)
print(pix.colorspace, '-->', fitz.csRGB)
img_path = f'../output/image{page_index + 1}_{xref}.png'
pix.save(img_path)

# 关闭文件
doc.close()

获取表格

从demo2.pdf文档中提取表格数据的Python代码如下:

1
2
3
4
5
6
7
8
9
10
# -*- coding: utf-8 -*-
import fitz

doc = fitz.open('../data/demo2.pdf')
page = doc[0] # 提取第1页中的所有表格
tables = page.find_tables()
print(f"tables: {tables}")
for i, table in enumerate(tables):
df = tables[0].to_pandas()
df.to_csv(f'../output/table_pg_{1}_{i + 1}.csv', index=False)

提取的表格数据将会保存为csv格式文件。

分割PDF

处理book1.pdf文档(扫描版PDF文件),将其前三页分割形成新的PDF文档book_split.pdf,示例PDF代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
import fitz


pdf_document = fitz.open("../data/book1.pdf")

# 构建输出文件名,以页数命名
output_pdf = f"../data/book_split.pdf"

# 创建一个新的Document对象,包含当前页面
new_pdf = fitz.open()
new_pdf.insert_pdf(pdf_document, from_page=0, to_page=2)

# 保存单独的PDF文件
new_pdf.save(output_pdf)
new_pdf.close()

pdf_document.close()

其中分割后PDF文档的第二页截图如下:

分割后PDF文档第二页截图

PDF文档问答

基于上述的PDF文档处理,我们将结合大模型(OpenAI)对PDF文档进行回答。

文字版PDF

文字版PDF可使用fitz轻松获取PDF文档中的纯文字内容,再使用大模型进行问答(简化版RAG)。

示例Python代码如下(使用PDF文件为oppo_n3_flip.pdf):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# -*- coding: utf-8 -*-
import os
import fitz
from openai import OpenAI


def get_pdf_content(pdf_path: str) -> str:
doc = fitz.open(pdf_path)
num_pages = doc.page_count
bg_content_list = []

# Full Text of PDF
for page_index in range(num_pages):
page = doc.load_page(page_index)
text = page.get_text()
bg_content_list.append(text)

return ''.join(bg_content_list)


def get_answer(pdf_content: str, query: str) -> str:
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"The full text of PDF file is: {pdf_content}"},
{"role": "user", "content": query}
],
max_tokens=1000
)

answer = response.choices[0].message.content

return answer


if __name__ == '__main__':
content = get_pdf_content("../data/oppo_n3_flip.pdf")
query1 = "OPPO Find N3 Flip的价格?"
print(get_answer(pdf_content=content, query=query1))

query2 = "蚂蚁集团发布的大模型叫什么?"
print(get_answer(pdf_content=content, query=query2))

query3 = "混元大模型是什么时候发布的?"
print(get_answer(pdf_content=content, query=query3))

输出结果如下:

1
2
3
OPPO Find N3 Flip有两个版本可选,分别是12GB+256GB12GB+512GB。起售价为6799元人民币。
蚂蚁集团发布的大模型叫做"大图模型"Large Graph Model,简称LGM)。
混元大模型是在202397日,在2023腾讯全球数字生态大会上正式对外亮相的。

扫描版PDF

而扫描版PDF(也称为影印版PDF)想要使用fitz来获取PDF中的文字内容是困难的,因此,需要先将每一页PDF转化为图片,再使用OCR技术获取图片中的文字,这样就能获取扫描版PDF中的文字,当然,获取文本的质量主要取决于PDF文档的质量及OCR识别效果。

可以使用fitz模块将每一页PDF转化为图片,同时,本文中采用PaddlePaddle的PaddleOCR模型进行文字识别。

示例的PDF文档为book_split.pdf,我们仅使用其中的第二页,Python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# -*- coding: utf-8 -*-
import fitz
from PIL import Image
import os
import cv2
import json
import base64
import requests
from openai import OpenAI


def convert_pdf_2_img(pdf_file: str, pages: int) -> None:
pdf_document = fitz.open(pdf_file)

# Iterate through each page and convert to an image
for page_number in range(pages):
# Get the page
page = pdf_document[page_number]
# Convert the page to an image
pix = page.get_pixmap()
# Create a Pillow Image object from the pixmap
image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
# Save the image
image.save(f"../output/book1_{page_number + 1}.png")

# Close the PDF file
pdf_document.close()


def cv2_to_base64(img):
data = cv2.imencode('.jpg', img)[1]
return base64.b64encode(data.tobytes()).decode('utf8')


def image_ocr(image_path):
# get ocr result
data = {'images': [cv2_to_base64(cv2.imread(image_path))]}
headers = {"Content-type": "application/json"}
url = "http://localhost:50076/predict/ch_pp-ocrv3"
r = requests.post(url=url, headers=headers, data=json.dumps(data))
if r.json()["results"]:
return "\n".join([ocr_record["text"].strip() for ocr_record in r.json()["results"][0]["data"]])
else:
return ""


def get_answer(pdf_content: str, query: str) -> str:
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"The full text of PDF file is: {pdf_content}"},
{"role": "user", "content": query}
],
max_tokens=1000
)

answer = response.choices[0].message.content

return answer


if __name__ == '__main__':
test_pdf_file = "../data/book1.pdf"
convert_pdf_2_img(pdf_file=test_pdf_file, pages=2)
page1_ocr_result = image_ocr("../output/book1_2.png")
print(f"识别文字内容: {page1_ocr_result}")

query1 = "破浪理论的创始人是谁,他的出生年月?"
predict_answer = get_answer(pdf_content=page1_ocr_result, query=query1)
print("回答:", predict_answer)

query2 = "这本书的作者是谁?"
predict_answer = get_answer(pdf_content=page1_ocr_result, query=query2)
print("回答:", predict_answer)

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
识别文字内容: 作者简介
R.N.艾略特(1871-1948),波浪理论的创始人,
曾当过会计师,就职于铁路公司、餐饮等多种行业。
人们对他的身世了解不多,1934年出版《波浪原理》,
为这一重要理论奠定了坚实的基础。他的理论由于
艰深难懂而被人们长期忽视。1978年,他的思想的
直接继承者帕御特出版了与人合著的《波浪理论》一
书,并在期权交易竞赛中取得四个月获得400%以上
的轿人成绩,从而使波浪原理迅速传播。现在的任
何一部股市理论教科书中波浪理论都占有越来越大
的篇幅。
ee more oleasevisit:htuos/nomeoto
回答: 破浪理论的创始人是R.N.艾略特,他出生于1871年。
回答: 这本书的作者是 R.N.艾略特,他是波浪理论的创始人。

可以看到,此时的回答效果是非常好的。

总结

本文主要介绍了如何使用fitz模块来处理PDF文档,以及如何结合大模型对文字版、扫描版PDF文档进行智能回答。

本文的亮点之一在于可使用OCR技术获得扫描版PDF文本内容,在大模型加持下回答效果表现很好。

在后续的文章中,我们将会处理PDF文档中的多模态部分,比如表格、图片等,这将会是一项很有挑战性并且很有趣的事情。

本文给出的Python代码均已开源,可访问Github网址:https://github.com/percent4/pdf-llm_series .

欢迎关注我的公众号NLP奇幻之旅,原创技术文章第一时间推送。

欢迎关注我的知识星球“自然语言处理奇幻之旅”,笔者正在努力构建自己的技术社区。


NLP(八十九)PDF文档智能问答入门
https://percent4.github.io/NLP(八十九)PDF文档智能问答入门/
作者
Jclian91
发布于
2024年4月3日
许可协议