本文将会介绍如何使用fitz模块处理PDF文档,以及如何使用OpenAI大模型对PDF文档进行问答。
在文章NLP(六十一)使用Baichuan-13B-Chat模型构建智能文档 中笔者介绍如何使用大模型来构建智能文档问答助手,其中包括PDF文档。
在文本中,笔者将会介绍如何使用fitz
模块来处理PDF文档,并基于此,再使用大模型对PDF文档内容进行智能问答,提升问答效果。
fitz模块介绍
PyMuPDF
和Fitz
是用于在Python中处理PDF文件的相关模块。Fitz
是PyMuPDF
的子模块,提供了一个简化和封装版本的PyMuPDF
功能。
关系:
PyMuPDF:是一个强大灵活的Python库,用于处理PDF,不依赖其它Python包。
Fitz:是PyMuPDF库的一个包装器,专门设计用于在Python中处理PDF文件。
用途:
PyMuPDF:提供了广泛的功能,用于操作PDF文档,包括方便的高级函数和底层操作。
Fitz:简化和封装了PyMuPDF的功能,使在Python中处理PDF文件更加简单。
安装:
要使用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
demo2.pdf
获取PDF文档基本信息
Python代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import fitz pdf_path = "../data/demo1.pdf" doc = fitz.open (pdf_path) 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().widthprint (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 import fitz pdf_path = "../data/demo1.pdf" doc = fitz.open (pdf_path) num_pages = doc.page_countfor 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://m d2pdf.netlify.app1 /1 This is a demo pdf. Today is Friday, 1 st 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 import fitz pdf_path = "../data/demo1.pdf" doc = fitz.open (pdf_path) num_pages = doc.page_countfor 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 import fitz doc = fitz.open ('../data/demo2.pdf' ) page = doc[0 ] 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 import fitz pdf_document = fitz.open ("../data/book1.pdf" ) output_pdf = f"../data/book_split.pdf" new_pdf = fitz.open () new_pdf.insert_pdf(pdf_document, from_page=0 , to_page=2 ) 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 import osimport fitzfrom openai import OpenAIdef get_pdf_content (pdf_path: str ) -> str : doc = fitz.open (pdf_path) num_pages = doc.page_count bg_content_list = [] 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 answerif __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 有两个版本可选,分别是12 GB + 256 GB 和12 GB + 512 GB 。起售价为6799 元人民币。 蚂蚁集团发布的大模型叫做"大图模型" (Large Graph Model ,简称LGM )。 混元大模型是在2023 年9 月7 日,在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 import fitzfrom PIL import Imageimport osimport cv2import jsonimport base64import requestsfrom openai import OpenAIdef convert_pdf_2_img (pdf_file: str , pages: int ) -> None : pdf_document = fitz.open (pdf_file) for page_number in range (pages): page = pdf_document[page_number] pix = page.get_pixmap() image = Image.frombytes("RGB" , [pix.width, pix.height], pix.samples) image.save(f"../output/book1_{page_number + 1 } .png" ) 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 ): 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 answerif __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奇幻之旅 ,原创技术文章第一时间推送。
欢迎关注我的知识星球“自然语言处理奇幻之旅 ”,笔者正在努力构建自己的技术社区。