PDF解析与基于LLM的本地知识库问答

9/29/2023 MarkerQAnythingOllamaAnythingLLM本地知识库

# 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. 搭建QAnything知识库问答系统

# 3.1 QAnything简介

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

QAnything架构

# 3.2 QAnything搭建

# 3.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

# 3.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脚本即可。

# 3.3 QAnything使用

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

QAnything主界面

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

QAnything解析文档

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

QAnything知识库问答

# 4. 基于Ollama及AnythingLLM构建知识库问答

# 4.1 Ollama及AnythingLLM简介

# 4.1.1 Ollama基本介绍

Ollama是一个开源的大型语言模型服务工具,它帮助用户快速在本地运行大模型。

Ollama

# 4.1.2 AnythingLLM基本介绍

AnythingLLM 是一个开源的、可定制的、功能丰富的文档聊天机器人。这是一个全栈应用程序,它能够将任何文档、资源或内容片段转化为LLM在聊天中可以利用的相关上下文。该应用允许用户选择并配置要使用的LLM或向量数据库,并支持多用户管理及权限控制,以确保数据安全和高效协作。

AnythingLLM

# 4.2 Ollama及AnythingLLM安装

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

# 4.2.1 安装Ollama并下载模型

从官网下载Ollama对应系统的客户端进行安装即可,之后在终端里输入 ollama 命令,即可查看帮助。

$ ollama
1

查看ollama帮助

然后使用 ollama run 命令启动模型(模型不存在的话会自动下载),这里我使用qwen:0.5b进行测试,支持的模型可在这里查看:https://ollama.com/library (opens new window)

$ ollama run qwen:0.5b
1

ollama启动模型服务

模型服务启动之后,打开 http://127.0.0.1:11434 地址,会告知“Ollama is running”,在终端里可以直接进行问答。

使用ollama在终端进行问答

注:如果想要添加 Ollama 未支持的模型或者自己微调的模型,可以使用 llama.cpp (opens new window) 工具将模型转换成 GGUF 格式,然后导入 Ollama,详见:Import a model (opens new window)

# 4.2.2 安装AnythingLLM并配置

从官网下载AnythingLLM对应系统的客户端进行安装即可,首次启动需要进行一些配置。

Step1:LLM模型选择Ollama(参数直接使用默认的)

AnythingLLM配置Ollama服务

Step2:Embedding模型选择Ollama(参数直接使用默认的)

AnythingLLM配置Embedding模型

Step3:向量数据库选择LanceDB(这个是本地的,比较省事)

Step4:确认AnythingLLM的配置,下一步的邮箱及用途可以随便填,再下一步创建一个Workspace。

AnythingLLM确认配置

Step5:全部配置完成之后,可以进入到AnythingLLM的主界面

AnythingLLM主界面

# 4.3 在AnythingLLM里进行本地知识库问答

# 4.3.1 构建本地知识库

点击工作空间旁边的上传按钮,上传一些文档,将其加入右侧的工作空间,保存Embedding。

上传文档构建本地知识库

# 4.3.2 本地知识库问答

在 Send a message 处输入要问的问题,大模型很快便可给予回复,点击 Show Citations 可以查看参考了什么文档,这个文档是可以点开来看的。

AnythingLLM本地知识库问答

注:这里的效果不好是因为选用的是qwen:0.5b模型,下面将其换成GPT-4的模型效果就好多了。

# 4.3.3 将LLM服务换成GPT4

点击左侧底部的“扳手”图标,即可进入到 AnythingLLM设置页,在这里可以对之前的初始化配置进行重新设置。

AnythingLLM将LLM服务换成GPT4

需要注意的是,国内用户使用GPT-4 别选择那个OpenAI(没有配置代理的地方,国内网络访问不通)。而是应该选择Local Al,这里去自定义配置,相当于使用BaseURL的方式。

同理,Embedding Model(选择text-embedding-ada-002)与 Vector Database (还是选择LanceDB)也重新配置。然后删掉之间的工作空间,重新上传文档构建本地知识库,再重新进行问答即可。

AnythingLLM更换GPT4大模型服务后的问答效果

注:因为Vector Database 还是使用LanceDB,一开始我并没有将其重配,但是问答时报错 LanceDBError: No vector column found to create index,我将之前的Vector Database、工作空间、本地知识库全部删了重新配置即可正常使用。

# 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)

[6] Ollama + AnythingLLM,有手就行本地知识库+LLM 组合拳 from Bilibili (opens new window)

[7] Ollama导入 GGUF、PyTorch 或 Safetensors 模型 from Github (opens new window)

[8] llama.cpp是在本地以最少硬件配置和最先进的性能实现 LLM 推理 from Github (opens new window)

Last Updated: 4/2/2024, 10:08:41 AM