上一篇《MinerU-利用专用LLM模型提取PDF内容的工具实测》中MinerU的效果基本满足需求。今天来看看另一个文档处理专用模型Dolphin:
Dolphin是字节跳动开源的一款轻量级(322M参数)但高性能的文档解析模型,采用 “先解析结构后解析内容” 两阶段范式:
- 布局分析:首先生成文档元素的自然阅读顺序序列(如标题、表格、公式等),构建结构化骨架;
- 并行内容解析:基于元素类型和特定提示词(如表格用HTML、公式用LaTeX)并行提取内容,避免传统OCR的错误累积和效率瓶颈。
其优势包括:
-
高精度:在纯文本和混合元素文档上的编辑距离优于GPT-4.1、Mistral-OCR等模型;
-
高效率:解析速度达0.1729 FPS,比Mathpix快近2倍;
-
灵活输出:支持JSON和Markdown格式,适用于学术论文、财务报表等场景。
基本信息
- 论文:https://arxiv.org/abs/2505.14059
- 源码: https://github.com/bytedance/Dolphin
- 模型地址:https://huggingface.co/ByteDance/Dolphin
- 在线Demo: http://115.190.42.15:8888/dolphin
下面我们来试用下
nohup huggingface-cli download ByteDance/Dolphin --local-dir ByteDance/Dolphin > huggingface-cli.log 2>&1 &
tail -f huggingface-cli.log
下载完成后查看模型内容, 以我的模型根目录 /data/ai/models 为例:
# du -sh /data/ai/models/ByteDance/Dolphin/*
8.0K /data/ai/models/ByteDance/Dolphin/config.json
4.0K /data/ai/models/ByteDance/Dolphin/generation_config.json
759M /data/ai/models/ByteDance/Dolphin/model.safetensors
4.0K /data/ai/models/ByteDance/Dolphin/preprocessor_config.json
4.0K /data/ai/models/ByteDance/Dolphin/README.md
4.0K /data/ai/models/ByteDance/Dolphin/special_tokens_map.json
3.9M /data/ai/models/ByteDance/Dolphin/tokenizer_config.json
7.5M /data/ai/models/ByteDance/Dolphin/tokenizer.json
模型权重文件大小只有759M
在干净目录下执行如下命令:
mkdir -p pkg
git clone --depth 1 https://github.com/bytedance/Dolphin.git pkg/Dolphin
将如下内容:
FROM pytorch/pytorch:2.5.1-cuda12.1-cudnn9-devel
COPY pkg /home
WORKDIR /home/Dolphin
# 设置pip源为阿里云pip源
ENV PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/
# 安装基础工具
RUN --mount=type=cache,target=/var/cache/apt \
apt update && \
apt install -y apt-utils cmake build-essential && \
apt install -y vim net-tools telnet curl wget netcat lsof git && \
apt install -y libgl1 libglib2.0-0
# 安装 dolphin 及其依赖
RUN --mount=type=cache,target=/root/.cache/pip ls -l /root/.cache/pip && \
pip install -r requirements.txt
# 清空父镜像的 ENTRYPOINT
ENTRYPOINT []
CMD []
保存为 dolphin.dockerfile
执行构建命令:
nohup docker build -f dolphin.dockerfile -t dolphin:250526_cu121 --push . > build.log 2>&1 &
tail -100f build.log
其中 250526 是我当时拉取 dolphin 代码的最后一次 commit 时间
构建成功后,启动容器:
docker run --name dolphin -itd --gpus '"device=0"' \
-e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 \
-v /data/ai/models:/models \
-v /data/ai/workspace/dolphin:/workspace \
dolphin:250526_cu121 bash
docker exec -it dolphin bash
root@c6c0d5b0a02b:/home/Dolphin#
Dolphin模型只接受图片输入。所以解析 pdf 文档时,需要先将其转换为图片。一小段python代码即可实现。将如下内容:
import argparse
import os
import fitz # PyMuPDF: pip install pymupdf
from pathlib import Path
def pdf_to_images(pdf_path, output_folder, dpi=200):
"""将PDF每页转换为PNG图片
Args:
pdf_path (str): PDF文件路径
output_folder (str): 输出目录
dpi (int): 图片分辨率(默认200)
"""
# 创建输出目录(如果不存在)
os.makedirs(output_folder, exist_ok=True)
doc = fitz.open(pdf_path)
total_pages = len(doc) # 提前获取总页数
try:
for page_num in range(total_pages):
page = doc.load_page(page_num)
pix = page.get_pixmap(matrix=fitz.Matrix(dpi/72, dpi/72))
output_path = os.path.join(output_folder, f"page_{page_num+1}.png")
pix.save(output_path)
print(f"已转换第 {page_num+1}/{total_pages} 页 -> {output_path}")
finally:
# 确保文档总是被关闭
doc.close()
print(f"转换完成! 共转换 {total_pages} 页")
def find_pdf_files(input_dir, max_depth=2):
"""递归查找目录中的PDF文件
Args:
input_dir (str): 输入目录
max_depth (int): 最大递归深度(默认2层)
Returns:
list: 找到的PDF文件路径列表
"""
pdf_files = []
input_dir = os.path.abspath(input_dir)
base_depth = input_dir.count(os.sep)
for root, dirs, files in os.walk(input_dir):
current_depth = root.count(os.sep) - base_depth
if current_depth > max_depth:
continue
for file in files:
if file.lower().endswith('.pdf'):
pdf_files.append(os.path.join(root, file))
return pdf_files
def main():
# 创建命令行参数解析器
parser = argparse.ArgumentParser(
description="PDF转图片工具:将PDF每页转换为PNG图片(支持批量处理)",
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
# 参数定义
parser.add_argument("input_path", type=str, help="PDF文件路径或包含PDF的目录")
parser.add_argument("--output", type=str, default=None, help="图片输出根目录(默认使用输入目录)")
parser.add_argument("--dpi", type=int, default=200, help="图片分辨率(DPI)")
parser.add_argument("--depth", type=int, default=2, help="递归查找深度(默认2层)")
# 解析参数
args = parser.parse_args()
# 确定输出目录
output_root = args.output if args.output else args.input_path
# 处理单个文件
if os.path.isfile(args.input_path) and args.input_path.lower().endswith('.pdf'):
pdf_name = Path(args.input_path).stem
output_folder = os.path.join(output_root, pdf_name)
pdf_to_images(args.input_path, output_folder, args.dpi)
return
# 处理目录
pdf_files = find_pdf_files(args.input_path, args.depth)
if not pdf_files:
print(f"在目录 {args.input_path} 中未找到PDF文件")
return
print(f"找到 {len(pdf_files)} 个PDF文件")
for pdf_path in pdf_files:
# 计算相对路径以保持目录结构
relative_path = os.path.relpath(pdf_path, args.input_path)
pdf_name = Path(pdf_path).stem
output_folder = os.path.join(
output_root, os.path.dirname(relative_path), pdf_name)
print(f"\n正在处理: {pdf_path}")
pdf_to_images(pdf_path, output_folder, args.dpi)
if __name__ == "__main__":
main()
保存为 pdf2img.py
安装依赖:
pip install pymupdf
将 pdf 文档批量转化为图片,每页对应一张图片:
# python pdf2img.py -h
usage: pdf2img.py [-h] [--output OUTPUT] [--dpi DPI] [--depth DEPTH] input_path
PDF转图片工具:将PDF每页转换为PNG图片(支持批量处理)
positional arguments:
input_path PDF文件路径或包含PDF的目录
options:
-h, --help show this help message and exit
--output OUTPUT 图片输出根目录(默认使用输入目录) (default: None)
--dpi DPI 图片分辨率(DPI) (default: 200)
--depth DEPTH 递归查找深度(默认2层) (default: 2)
命令样例:
python pdf2img.py GAOKAO-2025-CME --output GAOKAO-2025-CME-dolphin
其中 GAOKAO-2025-CME 目录放置了2025高考一卷的语数英试卷
然后调用 Dolphin 模型来解析上一步中 PDF 文件转换成的图片,命令样例:
# python demo_page_hf.py --model_path /models/ByteDance/Dolphin \
--input_path "/workspace/GAOKAO-2025-CME-dolphin/images/" \
--save_dir "/workspace/GAOKAO-2025-CME-dolphin/images/"
执行成功后图片相同目录下,生成2个文件夹:
- markdown/ 放置每个图片对应的解析后的 Markdown 文本
- recognition_json/ 放置每个图片对应的布局结构和位置信息,以及文本内容
recognition_json 下的文件样例如下:
[ { "label": "sec", "bbox": [ 693, 466, 2307, 551 ],
"text": "2025 年普通高等学校招生全国统一考试 (新高考 I 卷)",
"reading_order": 0
},
{
"label": "para",
"bbox": [
1458,
594,
1542,
650
],
"text": "数学",
"reading_order": 1
},
。。。
这个数据是包含了原始位置信息的完备数据,可以根据需要,用来做进一步处理。
如果不关心原始位置信息,那么markdown下面的md文件就可以直接使用了。
我们来看下这个数学卷例子中前3道题的效果:
## 2025 年普通高等学校招生全国统一考试 (新高考 I 卷)
数学
一、选择题:本题共8小题,每小题5分,共40分.在每小题给出的四个选项中,只有一项是符合题目要求的。
1.(1+5i)i 的虚部为 (大正)
- 4.
—1
- B.0
- C.1
- D. 6
- 答案(1+5i)i=-5+i,故虚部为1.故选择:C
2. 已知集合 $U=\{x \mid x$ 是小于 9 的正整数 $\}, A=\{1,3,5\}$, 则 $\int_{U} A$ 中元素个数为 (
- A. 2
- B. 3
- C.5
- D. 8
- 答案]$\int_{U}A=\{2,4,6,7,8\}$,5个元素.故选择:
3. 双曲线虚轴长是实轴长的 $\sqrt{7}$ 倍, 则离心率为 ( )
- A. $\sqrt{2}$
- B.2
- C. $\sqrt{7}$
- D. $2 \sqrt{2}$
- 答案) b = ${\sqrt {7}}a$ ⇒ $b^{2}$ = $7a^{2}$ ⇒ e^{2}$ = ${\frac {c^{2}}{a^{2}}}$ = 8 ⇒ e = 2 {\sqrt {2}}$. 故选择:D
Markdown渲染后的效果:
基本正确
另一个pdf 解析结果的样例:
## 2025年普通高等学校招生全国统一考试(新1卷)
$\star$ 祝大家学习生活愉快
注意事项
1.答卷前,考生务必用黑色字迹的钢笔或签字笔将自己的姓名和考生号,试室号,座位号填写在答题卡上.用2B铅笔将试卷类型和考生号填涂在答题卡相应位置上。
2.选择题每小题选出答案后,用2B铅笔把答题卡上对应的题目选项的答案信息点涂黑:如需改动,用橡皮擦干净后,再填涂其他答案.答案不能答在试卷上。
3.非选择题必须用黑色字迹的钢笔或签字笔作答,答案必须写在答题卡各题目指定区域内相应位置上:如需改动,先划掉原来的答案,然后再写上新的答案,不准使用铅笔和涂改液.不按以上要求作答的答案无效.
一、选择题:本大题共8小题,每小题5分,共计40分.每小题给出的四个选项中,只有一个选项是正确的.请把正确的选项填涂在答题卡相应的位置上.
1. $(1+5 \mathrm{i}) \mathrm{i}_{的虚部为(\quad)}$
A. $-1$
B.
C.1
D.6
【答案】C
【解析】
【分析】根据复数代数形式的运算法则以及虚部的定义即可求出
【详解】因为 $(1+5 \mathrm{i}) \mathrm{i}=\mathrm{i}+5 \mathrm{i}^{2}=-5+\mathrm{i}$, 所以其虚部为 1 ,
故选:C.
2. 设全集 $U=\left\{x \mid x \text { 罂租谗 } \boldsymbol{q} \boldsymbol{\square} \boldsymbol{\square} \boldsymbol{窭窦}\right\}$, 集合 $A=\{1,3,5\}$, 则 ${ }_{U} A$ 中元素个数为()
A. 0
B.
C. 5
D. 8
【答案】C
【解析】
【分析】根据补集的定义即可求出,
【详解】因为 $U=\{1,2,3,4,5,6,7,8\}$, 所以 ${ }_{U} A=\{2,4,6,7,8\}, \quad{ }_{U} A$ 中的元素个数为 5 故选: C.
3. 若双曲线 $C$ 的虚轴长为实轴长的 $\sqrt{7}$ 倍, 则 $C$ 的离心率为()
A. $\sqrt{2}$
B.2
C. $\sqrt{7}$
D. $2 \sqrt{2}$
【答案】D
## (解析)
【分析】由题可知双曲线中 $a, b$ 的关系, 结合 $a^{2}+b^{2}=c^{2}$ 和离心率公式求解
【详解】设双曲线的实轴,虚轴,焦距分别为2a,2b,2c, 由题知,$\quad b={\sqrt {7}}a$, 于是$a^{2}+b^{2}=c^{2}=a^{2}+7a^{2}=8a^{2}$,则$c=2{\sqrt {2}}a$, 即$e={\frac {c}{a}}=2{\sqrt {2}}$.
资源峰值:
|=========================================+======================+======================|
| 4 NVIDIA GeForce RTX 4090 On | 00000000:81:00.0 Off | Off |
| 30% 46C P2 130W / 450W | 5710MiB / 24564MiB | 42% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+
实测只需要6G显存就能跑起来了。
玩得愉快。