PDF文档的解析转换与本地知识库问答

9/29/2023 MarkerChatGPT-for-Translationpy-googletransprivateGPTQAnything

# 1. 使用Marker解析PDF文档

# 1.1 Marker简介

Marker能将PDF、EPUB和MOBI转换为markdown。它比nougat快10倍,对大多数文档的准确度更高,并且幻觉风险低。

  • 支持各种PDF文档(针对书籍和科学论文进行优化)
  • 移除页眉/页脚/其他杂项
  • 将大部分方程式转换为LaTeX
  • 格式化代码块和表格
  • 支持多种语言(尽管大部分测试都是用英语完成),查看settings.py以获取语言列表
  • 可在GPU、CPU或MPS上运行

项目地址:https://github.com/VikParuchuri/marker (opens new window)

marker与nougat对比

# 1.2 运行Marker使用实例

# 1.2.1 准备运行环境

实验环境:Macbook Pro 2021,M1 pro芯片,16G内存,macOS Ventura13.2.1系统,Python3.9环境

$ git clone https://github.com/VikParuchuri/marker.git
$ pip3 install -r requirements.txt
$ pip3 install -r scripts/install/brew-requirements.txt
1
2
3

使用官方requirements安装完依赖后,我这里有各种报错,又安装了如下依赖才成功跑起来。

$ brew install tesseract-lang
$ brew install ghostscript 
$ brew install libmagic 

$ pip3 install ftfy
$ pip3 install pyspellchecker 
$ pip3 install ocrmypdf 
$ pip3 install thefuzz 
$ pip3 uninstall nougat
$ pip3 install nougat-ocr
$ pip3 install --upgrade transformers   
$ pip3 install python-magic
$ pip3 install ray

报错信息:ImportError: cannot import name 'NougatModel' from 'nougat' 
解决方案:不要安装 Nougat,只需安装 Nougat-OCR。pip3 uninstall nougat 及 pip3 install nougat-ocr

报错信息:ValueError: Non-consecutive added token '<pad>' found. Should have index 259 but has index 0 in saved vocabulary.
解决方案:更新transformers即可。pip3 install --upgrade transformers  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 1.2.2 解析PDF文档

这里以转换单个文件为例,首次执行时,会自动下载模型。官方支持对文档进行批处理,也有很多超参可以控制转换配置,详见官方README,这里就不赘述了。

$ python3 convert_single.py input/input.pdf output/output.md --parallel_factor 20
1

--parallel_factor 是批量大小和并行 OCR 工作人员增加多少。数字越大,需要的 VRAM 和 CPU 越多,但处理速度越快,默认设置为 1。

Marker解析PDF文档效果

注:这个工具只提取PDF文档里的文本,表格什么的也能解析,但对于图表,解析时会将其丢弃。

# 1.3 将Marker封装成Docker

实验环境:Debian 11 x86_64、Docker version 20.10.17、无GPU。

  • 将Marker封装成Docker,方便在其他服务器环境上迁移部署。
  • 实测安装依赖及模型需要占用大约12GB存储,运行时至少需要4GB内存,对资源的占用要求还是不小的。

requirements.txt

scikit-learn == 1.3.2
Pillow == 10.1.0
pytesseract == 0.3.10
PyMuPDF == 1.23.5
pymupdf-fonts == 1.0.5
pydantic == 2.4.2
pydantic-settings == 2.0.3
nougat-ocr == 0.1.17
transformers == 4.34.1
numpy == 1.26.1
python-dotenv == 1.0.0
torch == 2.1.1
ray == 2.7.1
tqdm == 4.66.1
tabulate == 0.9.0
thefuzz == 0.20.0
python-magic == 0.4.27
pyspellchecker == 0.7.2
ftfy == 6.1.1
nltk == 3.8.1
ocrmypdf == 15.4.0
bitsandbytes == 0.41.2.post2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Dockerfile

# 基于python3.9镜像创建新镜像
FROM python:3.9
# 创建容器内部目录
RUN mkdir /code
# 将项目复制到内部目录
ADD . /code/
# 切换到工作目录
WORKDIR /code
# 安装项目依赖
RUN pip install -r requirements.txt
# 安装vim命令
RUN apt-get update && apt-get install vim -y  
1
2
3
4
5
6
7
8
9
10
11
12

build.sh

#!/bin/bash

base_path=$(cd `dirname $0`; pwd)
input_path="${base_path}/input"
output_path="${base_path}/output"

docker build -t marker-image .                                  
docker run -itd --name marker -v ${input_path}:/code/input -v ${output_path}:/code/output marker-image:latest  
docker update marker --restart=always                           
1
2
3
4
5
6
7
8
9

新建 input 和 output 目录,执行 build.sh 脚本构建镜像与容器,之后在 input 目录上传用于测试的 PDF 文件,进入容器执行。

$ docker exec -it marker /bin/bash
$ python3 convert_single.py input/input.pdf output/output.md --parallel_factor 1
1
2

# 1.4 其他解析PDF文档的方案

pdf2docx库也可用于解析PDF文档,将其转换成word格式,适用于非扫描版的情形。

$ pip3 install pdf2docx
1

pdf2word.py

# -*- coding: utf-8 -*-

import os
from pdf2docx import Converter


def pdf2word_by_lib(pdf_file, docx_file):
    cv = Converter(pdf_file)
    cv.convert(docx_file, start=0, end=None)
    cv.close()


def remove_blank_lines_with_spaces(input_string):
    lines = input_string.split("\n")
    result_lines = []

    for line in lines:
        # 如果该行不只包含空格,就将其添加到结果中
        if line.strip() != "":
            result_lines.append(line)

    # 使用换行符将结果连接成一个字符串
    result_string = "\n".join(result_lines)
    return result_string


if __name__ == '__main__':
    src_dir = "./pdf/"
    dest_dir = "./doc"
    num = 0
    for root, _, files in os.walk(src_dir):
        for filename in files:
            if filename.lower().endswith(".pdf"):
                try:
                    src_path = os.path.join(root, filename)
                    dest_path = os.path.join(dest_dir, os.path.relpath(src_path, src_dir))
                    txt_filename = os.path.splitext(os.path.basename(dest_path))[0] + ".docx"
                    os.makedirs(os.path.dirname(dest_path), exist_ok=True)
                    if os.path.exists(os.path.join(os.path.dirname(dest_path), txt_filename)):
                        print("continue")
                        continue
                    docx = pdf2word_by_lib(src_path,os.path.join(os.path.dirname(dest_path), txt_filename))
                except Exception as e:
                    num = num + 1
                    print("文件损坏数量:", num)
                    print("error:", e)
                    continue
    print("文件损坏总数:", num)
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

# 2. 使用ChatGPT翻译文档

# 2.1 ChatGPT-for-Translation简介

项目简介:使用ChatGPT完成文件翻译。该工具接受一个文本文件(.pdf, .txt, .md, .html.rtf)或者一个包含文本的文件夹。它将生成一个直接翻译和一个双语文本文件,对于 PDF 解析做了优化。

项目地址:https://github.com/Raychanan/ChatGPT-for-Translation (opens new window)

# 2.2 ChatGPT-for-Translation使用实例

# 2.2.1 准备运行环境

实验环境:Macbook Pro 2021,M1 pro芯片,16G内存,macOS Ventura13.2.1系统,Python3.9环境

$ git clone https://github.com/Raychanan/ChatGPT-for-Translation.git
$ cd ./ChatGPT-for-Translation/
$ pip3 install -r requirements.txt
$ pip3 install tenacity
1
2
3
4

# 2.2.2 翻译PDF文档

项目使用:建议使用 gpt-3.5-turbo 模型进行翻译,经济实惠,我翻译了个200页的英文文献,耗时12分钟,花费0.23美刀。

$ python3 ChatGPT-translate.py --input_path=./your_path/your_doc.pdf --openai_key=your_openai_key
1

默认值是使用gpt-3.5-turbo模型,翻译成简体中文,支持的参数如下:

    arguments = [
        ("--input_path", {"type": str,
         "help": "input file or folder to translate"}),
        ("--openai_key", {"type": str,
         "default": "", "help": "OpenAI API key"}),
        ("--model", {"type": str, "default": "gpt-3.5-turbo",
         "help": "Model to use for translation, e.g., 'gpt-3.5-turbo' or 'gpt-4'"}),
        ("--num_threads", {"type": int, "default": 10,
         "help": "number of threads to use for translation"}),
        ("--target_language", {"type": str, "default": "Simplified Chinese",
         "help": "target language to translate to"}),
        ("--only_process_this_file_extension",
         {"type": str, "default": "", "help": "only process files with this extension"}),
        ("--use_azure", {"action": "store_true", "default": False,
         "help": "Use Azure OpenAI service instead of OpenAI platform."}),
        ("--azure_endpoint",
         {"type": str, "default": "", "help": "Endpoint URL of Azure OpenAI service. Only require when use AOAI."}),
        ("--azure_deployment_name",
         {"type": str, "default": "", "help": "Deployment of Azure OpenAI service. Only require when use AOAI."}),
    ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

翻译效果:会生成5个文档,分别是txt格式的原文提取、纯中文译文、双语对照译文,以及docx格式的纯中文译文、双语对照译文。

ChatGPT-for-Translation翻译效果

# 2.3 其他文本内容翻译的方案

# 2.3.1 破解Google翻译

破解Google翻译的 py-googletrans (opens new window) 库,使用时需要联网(被墙,国内需要设置代理)

$ pip install googletrans
1

这个库的工作原理(摘自官方说明):

- 您可能想知道为什么这个库可以正常工作,而其他方法(例如 goslate)却不起作用,因为 Google 最近使用票证机制更新了其翻译服务,以防止大量爬虫程序。
- 我最终找到了一种方法,通过对 Google 用来生成此类令牌的混淆和缩小代码进行逆向工程来生成票证,并在 Python 之上实现。 但是,这可能随时被阻止。
1
2

示例代码如下:

#-*- coding:utf-8 -*-

from googletrans import Translator
import os

os.environ["https_proxy"] = "http://127.0.0.1:7890"

translator = Translator()
result = translator.translate('hello world', dest='zh-cn').text
print(result)
1
2
3
4
5
6
7
8
9
10

注:单次请求的最大字符数为5000,超出的话可以拆分成多份,分开请求再对结果进行拼接。另外该破解方式随时可能会被阻止,如果想使用稳定的 API,建议使用 谷歌官方的翻译 API (opens new window)

# 2.3.2 LibreTranslate机器翻译

LibreTranslate是一个开源的、可以自行搭建的翻译服务,支持多种语言的互相翻译,包括中文。翻译的准确度远不如商业API,效果很一般,但它是永久免费的。服务器上部署LibreTranslate共计需要大约13GB的存储空间。

$ docker run -itd -p 5000:5000 --name libretranslate libretranslate/libretranslate
$ docker logs -f libretranslate --tail 100
1
2

注:创建容器后会自动下载语言包,这个过程会比较慢,等待它安装完毕后,浏览器访问http://ip:5000即可查看以下Web页面。

LibreTranslate

# 3. 使用privateGPT实现文档问答

# 3.1 privateGPT简介

项目简介:privateGPT 是基于 GPT4All-J 的私有化部署文档问答平台,无需联网,能 100% 保证用户的隐私不泄露。它提供了一个 API,用户可以使用自己的文档进行交互式问答和生成文本。此外,平台支持自定义训练数据和模型参数,以满足个性化需求。

# 3.2 privateGPT使用实例

# 3.2.1 准备运行环境

实验环境:Macbook Pro 2021,M1 pro芯片,16G内存,macOS Ventura13.2.1系统,Python3.10环境

[1] 必须使用Python 3.10或更高版本,早期版本的 Python 无法编译(亲测 Python 3.9 跑不起来)

$ pip3 install -r requirements.txt
1

[2] 将example.env模板文件重命名为.env,修改后的内容如下:

PERSIST_DIRECTORY=db
MODEL_TYPE=GPT4All
MODEL_PATH=models/ggml-gpt4all-j-v1.3-groovy.bin
EMBEDDINGS_MODEL_NAME=GanymedeNil/text2vec-large-chinese
MODEL_N_CTX=1000
MODEL_N_BATCH=8
TARGET_SOURCE_CHUNKS=4
1
2
3
4
5
6
7

注意事项:

  • 这里只修改了 EMBEDDINGS_MODEL_NAME 参数,默认是 all-MiniLM-L6-v2,但发现它存在中文乱码问题,以下是我亲测的4个模型:

    // 由于 langchain 加载 SentenceTransformers 嵌入的方式,第一次运行脚本时将需要互联网连接来下载嵌入模型本身。
    
    all-MiniLM-L6-v2                      // 效果一般、中文乱码
    DMetaSoul/sbert-chinese-general-v2    // 效果很差、中文不乱码
    distiluse-base-multilingual-cased-v1  // 效果很差、中文乱码
    GanymedeNil/text2vec-large-chinese    // 效果一般、中文不乱码
    
    1
    2
    3
    4
    5
    6
  • .env 参数含义:

    MODEL_TYPE: supports LlamaCpp or GPT4All
    PERSIST_DIRECTORY: is the folder you want your vectorstore in
    MODEL_PATH: Path to your GPT4All or LlamaCpp supported LLM
    MODEL_N_CTX: Maximum token limit for the LLM model
    MODEL_N_BATCH: Number of tokens in the prompt that are fed into the model at a time. Optimal value differs a lot depending on the model (8 works well for GPT4All, and 1024 is better for LlamaCpp)
    EMBEDDINGS_MODEL_NAME: SentenceTransformers embeddings model name (see https://www.sbert.net/docs/pretrained_models.html)
    TARGET_SOURCE_CHUNKS: The amount of chunks (sources) that will be used to answer a question
    
    1
    2
    3
    4
    5
    6
    7

[3] 下载 LLM 模型并将其放置在 models 目录里:

# 3.2.2 建立数据索引

将文件放入source_documents目录中,支持的格式有:.csv、.docx、.doc、.enex、.eml、.epub、.html、.md、.msg、.odt、.pdf、.pptx、.ppt、.txt。

运行以下命令来提取所有数据,首次执行时会自动联网下载 EMBEDDINGS_MODEL_NAME 处所填写的模型。

$ python3 ingest.py
1

建立完数据索引后,输出应如下所示:

Creating new vectorstore
Loading documents from source_documents
Loading new documents: 100%|██████████████████████| 1/1 [00:01<00:00,  1.73s/it]
Loaded 1 new documents from source_documents
Split into 90 chunks of text (max. 500 tokens each)
Creating embeddings. May take some minutes...
Using embedded DuckDB with persistence: data will be stored in: db
Ingestion complete! You can now run privateGPT.py to query your documents
1
2
3
4
5
6
7
8

它将创建一个包含本地矢量存储的db文件夹,建立时间具体取决于文档的大小。如果想从空数据库开始,请删除该db文件夹。

# 3.2.3 基于文档提出问题

要提出问题,请运行如下命令:

$ python3 privateGPT.py
1

注:如果没有上一步建立数据索引,会报如下错误

NoIndexException: Index not found, please create an instance before querying
1

执行成功后,脚本会交互式的等待问题输入。

> Enter a query:
1

等待一段时间之后,privateGPT会给出回复,实际执行效果如下:

基于privateGPT进行本地文档问答的效果

# 4. 搭建QAnything知识库问答系统

# 4.1 QAnything简介

网易有道开源的知识库问答引擎 QAnything,包括专门优化的自研的 embedding 和 rerank 模型,微调后的 LLM,优化后的推理代码,向量数据库,以及一个立即上手可用的前端。所有的算法模型(包括 7B 大模型+embedding/rerank+OCR)占用显存不超过 16G。

QAnything架构

# 4.2 QAnything搭建

# 4.2.1 准备运行环境

实验环境:CentOS7,512GB内存,128TB存储,NVIDIA 4090显卡(24GB显存)、CUDA11.7环境

如下是QAnything官方要求的基础环境:

System Required item Minimum Requirement Note
Linux NVIDIA GPU Memory >= 4GB (use OpenAI API) Minimum: GTX 1050Ti(use OpenAI API) Recommended: RTX 3090
NVIDIA Driver Version >= 525.105.17
Docker version >= 20.10.5 Docker install (opens new window)
docker compose version >= 2.23.3 docker compose install (opens new window)
git-lfs git-lfs install (opens new window)

[1] 准备Docker和Docker Compose环境

安装Docker环境

$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
$ sudo systemctl enable docker
$ sudo systemctl start docker
$ docker version
1
2
3
4
5

安装Docker Compose环境

$ sudo curl -L https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version
1
2
3

[2] 准备NVIDIA驱动环境

安装 nvidia-smi 及 nvidia-cuda-toolkit 工具

$ sudo yum update
$ sudo yum install nvidia-utils-520
$ sudo yum install nvidia-cuda-toolkit
1
2
3

使用 nvidia-smi 命令可以显示出GPU的所有基础信息。

$ nvidia-smi                 // 查询GPU详情
$ nvidia-smi -q              // 查询GPU型号
$ nvcc --version             // 查看CUDA驱动版本
1
2
3

配置Docker使用GPU,需要安装官方提供的nvidia-container-toolkit工具。

$ curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo  
$ sudo yum install -y nvidia-container-toolkit   
$ sudo nvidia-ctk runtime configure --runtime=docker
$ sudo systemctl restart docker   
1
2
3
4

[3] 安装git及git-lfs环境

$ sudo yum update
$ yum install git
$ wget https://packagecloud.io/github/git-lfs/packages/el/7/git-lfs-3.4.1-1.el7.x86_64.rpm/download -O git-lfs-3.4.1-1.el7.x86_64.rpm
$ sudo yum install git-lfs-3.4.1-1.el7.x86_64.rpm -y
1
2
3
4

[4] 安装其他缺失的命令

官方提供的run脚本里需要使用jq命令,未安装的话会启动失败。

$ sudo yum install jq
1

# 4.2.2 官方脚本一键安装

拉取项目代码,使用官方脚本一键安装,按照脚本提示,输入OpenAI API KEY和服务器IP地址等信息,之后会自动下载模型及环境。

$ git clone https://github.com/netease-youdao/QAnything.git
$ cd QAnything
$ chmod u+x run.sh
$ bash ./run.sh -c cloud -i 0 -b default
1
2
3
4

这个一键脚本搭建的方式,很多信息都写死了,密码也是最简单的,建议启动之前,先把 docker-compose-linux.yaml 里的配置及相应代码里的连接配置改改。

参数说明及使用方法详见:

QAnything一键脚本使用说明

当所有环境成功运行起来了之后,后台日志如下:

成功部署QAnything的日志

注意事项:如果服务无法正常使用,检查log里的日志文件即可,看看是不是有的服务没有成功启动。如果需要卸载重新搭建,执行close.sh脚本即可。

# 4.3 QAnything使用

使用 Chrome 浏览器打开后台日志打印出来的 http://xxx.xxx.xxx.xxx:5052/qanything 地址,可以看到如下界面:

QAnything主界面

之后新建一个知识库,上传一些文档,等待其完成解析。

QAnything解析文档

之后就可以输入问题,根据知识库进行问答了。

QAnything知识库问答

# 5. 参考资料

[1] cannot import name 'cached_property' from 'nougat.utils' from Github issues (opens new window)

[2] privateGPT不支持中文吗 from Github issues (opens new window)

[3] 免费且无限制的 Python 谷歌翻译 API from Github (opens new window)

[4] 网易有道开源的知识库问答引擎 QAnything from Github (opens new window)

[5] 阿里云推出企业级大模型RAG系统,几次点击即可连接PB级知识库 from 微信公众号 (opens new window)

Last Updated: 2/16/2024, 9:20:25 PM