最近我被一批扫描版论文折腾得够呛。连着一周,几乎每天都有同事丢来一份 PDF,说“帮我转成 Markdown,我要喂给知识库做检索”。最开始我也天真地以为 PDF 转 Markdown 不就是 OCR 一下嘛,真上手才发现,问题根本不是“认字”,而是“懂版”。就是在这个背景下,我把 MinerU 和 PaddleOCR 拼到了一起,做了一个还算顺手的智能文档解析管线。
先说结论:MinerU 擅长版面解析和结构化输出,PaddleOCR 擅长中文文字识别和图像预处理。两个一搭配,刚好补齐彼此的短板,OCR 终于从“能用”走到了“好用”。这篇文章把整个思路、踩坑过程、参数选型和常见问题都摊开讲清楚,适合正在做文档解析、知识库建设、RAG 前处理,或者单纯被 PDF 结构搞到头秃的开发者和技术爱好者参考。
1. 为什么是 MinerU + PaddleOCR:组合思路拆解
1.1 单用 OCR 卡在哪
很多项目的起点是“把图片里的字认出来”,所以第一反应是上 OCR。可等真正处理起一份完整 PDF,你会发现纯 OCR 输出的东西往往没法直接交差。
纯 OCR 的典型输出是一堆检测框和识别文本。比如“标题”“正文”“表格里的数字”在结果里全都变成了差不多的文本块,原来的层级关系、阅读顺序、段落归属全部丢失。要是文档还有双栏、页眉页脚、浮动图片、表格跨页,那基本就是一场灾难。还有一个更常见的问题:公式。OCR 模型识别公式符号容易出错,就算认出了字符,输出也是一行乱码样的文本,根本没法在 Markdown 里还原成标准数学公式。
这就像一个人认字认得很快,但他读不懂版式:不知道哪里是标题,不知道表格有几列,不知道公式从哪里开始到哪里结束。放到文档解析场景里,“认字”只是最底层的一步,之后还需要做版面分析、阅读顺序重建、结构化表示。
除开结构问题,单用通用 OCR 处理特殊文档也很吃力。扫描版合同、带水印的工程图纸、有噪声的老书页,这些场景下识别率会直线下降。于是你会开始给 OCR 调参数、做图像增强、写一堆后处理规则,最后代码量比业务逻辑还多。
1.2 MinerU 和 PaddleOCR 各自擅长的活儿
MinerU 这个项目我是在社区里偶然注意到的,它的定位是智能文档解析:输入 PDF,输出结构化的 Markdown 或 JSON。它会把文档切分成板块,识别出标题、段落、图片、表格、公式这些不同的元素,并且尽量保持阅读顺序。也就是说,它做的是“读懂版面”这件事。
MinerU 的强项在于:版面分析比较可靠,表格解析有专门的模型,公式可以转成 LaTeX,输出结果适合直接进知识库或 RAG 管线。我跑过几份排版比较规整的现代论文,默认参数下大部分版面都能重建出来,段落顺序基本正确,表格也不会乱到完全没法修复。
PaddleOCR 的强项则是“认字”,它对中文场景支持非常友好,自带方向分类、文本检测、文本识别三段式管线,可以单独调用,也可以拆开做定制化预处理。相比 MinerU 内置的 OCR 能力,PaddleOCR 在应对低质量图像、模糊背景、混合语言时往往更抗造。
而且 PaddleOCR 的模型是模块化设计的。遇到某一步效果不好,只需要替换其中一个子模型,不用整个重来。这在调优过程中会省下大量时间。
1.3 组合的真正价值:从“认得字”到“读懂版”
现在很多文档解析工具都内置了 OCR,为什么还要单独把 PaddleOCR 拿进来凑对?我在实际处理中总结了几个场景,这几个场景单靠任何一方都搞不定。
第一种是低质量扫描件。MinerU 处理清晰、规则的电子版 PDF 很稳,可遇到那种复印了好多遍、纸都泛黄的老合同,内置识别会明显吃力。这时候先用 PaddleOCR 做图像矫正、去噪、方向分类,再把处理过的图像交给 MinerU 做版面解析,效果会稳定很多。
第二种是复杂表格。MinerU 解析有线表格还可以,但是碰到无线表格、有背景色干扰、单元格合并很夸张的情况,输出的 Markdown 经常会对不齐。PaddleOCR 生态里有专门的表格结构识别能力,先用这部分把单元格坐标和结构找出来,再拿这个信息去修正 MinerU 的输出,能救回不少。
第三种是针对性识别。比如某个业务场景里只关心身份证号、发票编号、合同金额这些关键字段,没必要跑完整版面解析,直接用 PaddleOCR 检测加识别,又快又准。而 MinerU 适合做全局解析,两者不是替代关系,而是分工关系。
所以我把这两个工具拆成两个阶段来用:PaddleOCR 管“图像到文本”的质量,MinerU 管“文本到结构”的还原。谁擅长哪段就干哪段,这比幻想有一个工具能包打天下靠谱得多。
2. 环境准备与工具选型
2.1 基础环境与安装
先说环境。这两套工具都是 Python 生态,都依赖深度学习推理框架,所以环境隔离是第一课。我强烈建议用独立的虚拟环境,别直接往系统 Python 里塞依赖,不然某个依赖版本冲突能让你排查一整天。
我的基本步骤如下:
- 创建虚拟环境。当前这两套工具都对 Python 3.10 及以上支持得比较顺利。
- 安装 MinerU。它依赖的组件比较多,包含模型推理、版面分析、公式识别这些模块,安装时间会略长,耐心等就行。
- 安装 PaddleOCR。这一步会顺带安装深度学习框架,如果是 N 卡,尽量装对应的 GPU 版本。
具体安装命令以两个项目在官方文档里的说明为准,因为版本更新很快,网上搜到的老命令很可能已经失效。我自己的习惯是:新项目开工前,先去仓库 README 里看一遍最新的安装方式,这个习惯帮我避开了很多坑。
有一点特别建议:装完以后先跑一遍自带的小测试文件。许多项目下载完模型之后,通过一个简单的示例文件验证环境是否通顺,能省掉很多“代码报错但不知道是环境问题还是模型问题”的排查时间。我见过有人跳过这个步骤,最后折腾了两天才发现是依赖没装完整。
2.2 模型与参数怎么选
模型选型这块,说白了就是考虑“精度、速度、资源占用”三个维度。
PaddleOCR 这边,方向上分检测模型、方向分类模型、识别模型。对于大多数中文文档场景,我会优先选中文识别模型,并打开方向分类。如果文档以印刷体为主,默认的通用模型就能跑;如果涉及手写或者很特殊的字体,才需要考虑微调或者找专项模型。
另外,PaddleOCR 的推理参数里有一个阈值控制检测框的敏感度。阈值设太低,可能会识别出很多无关噪声;设太高,又会漏掉浅色文字。我一般在 0.3 到 0.5 之间去试,先跑几页看效果再定,而不是拿着一个万能参数用到底。
MinerU 侧,部署时有 CPU 和 GPU 的选择。我自己主要在 GPU 上跑,因为公式识别和版面模型都比较吃算力,CPU 跑一份几十页的文档要等很久。如果是偶尔处理少量文件,CPU 也能接受;但要批量处理,GPU 基本是必备的。
批处理规模也需要考虑。有的朋友喜欢一次性丢几百份 PDF 进去跑,这会遇到显存不够、进程卡死、输出文件互相覆盖等各种问题。更稳的做法是分批处理,比如每批 20 份左右,跑完一批检查输出再跑下一批。贪多嚼不烂,在文档解析这件事上体验特别明显。
3. 实操过程:把一份扫描版 PDF 变成结构化 Markdown
3.1 第一轮跑通:用默认参数“先看结果”
我不会在一开始就追求参数最优,而是选择先用默认参数把整份 PDF 跑通,输出一份结果来看看整体状态。
比如拿到一份几十页的扫描版论文,我大概会这样做:
- 把 PDF 每一页拆成清晰的图像,注意输出分辨率不要压得太低,同时避免翻拍照片那种带角度和阴影的情况。
- 用 MinerU 的默认配置跑一遍版面解析,生成初步的 Markdown 文件。
- 把 Markdown 和原始 PDF 放在一起,逐段检查标题、正文、图表、公式的位置和顺序。
这一步的重要目标不是调好效果,而是看清“烂在哪”。比如有的页面整体识别效果不错,但表格崩了;有的页面图片被错误识别成了表格;还有的公式下标全部丢失。把这些现象记录下来,后面才有针对性的调优方向。
我在第一次跑的时候,典型情况是主体段落识别得不错,但目录页、公式多的页面以及跨页表格非常难看。目录页的页码会被当成正文内容,公式里复杂的根号和分式变成了一堆没法看的符号,跨页表格干脆被拆成了两个独立表格。这个阶段不必焦虑,能做到心里有数就很好了。
3.2 低质量扫描件:用 PaddleOCR 做预处理和补齐
如果发现前面的输出里很多文字识别错了、漏了,尤其集中在扫描质量差的页面上,我就会把 PaddleOCR 拉进来做图像预处理和二次识别。
我的常见流程是:对问题页面单独用 PaddleOCR 做方向分类,先把旋转纠正过来。这个步骤看起来基础,但实际很救命。扫描件经常出现轻微倾斜或 90 度旋转,方向不对,后续一切解析都白搭。
方向纠正之后,再做必要的图像增强。我是基于页面效果手动判断:太暗就提亮度,太糊就做锐化,背景有大量黑点就做降噪。这里有个技巧,图像增强别过度。有些同事一上来就搞高反差、强锐化,结果文字边缘出现严重白边,识别率反而下降。适度永远好过用力过猛。
如果某些页面的文字已经被噪声严重破坏,默认 OCR 已经救不回来,我会改用 PaddleOCR 对整页做全量识别,拿到带坐标的文本块,再手动标注出哪些文本属于标题、正文、表格、页眉页脚。说白了就是用人的判断力补一步“版面规则”,这种页面让模型从零猜到对,实在太为难它了。
补齐之后,我会把这份带有坐标信息的文本结果作为辅助数据,结合 MinerU 的版面输出一起生成最终的 Markdown。实际操作中,这种“轻重结合”的思路比单纯换一个大模型可靠得多。
3.3 表格与公式场景的处理与调优
表格和公式是文档解析里公认的两座大山。我单独把它们拎出来说,是因为它们最能体现“从能用走向好用”的差别。
先说表格。MinerU 对格式规整的表格表现不错,但对无线表格、复杂合并单元格以及跨页表格容易出错。我的处理思路分三步:
- 先用 MinerU 把页面整体结构跑出来,定位到表格所在区域。
- 遇到结构崩坏的表格,单独把这块区域截出来,交给表格结构识别模型,拿到单元格坐标和行列关系。
- 最后根据单元格坐标,把文本按行列重组,手动生成符合 Markdown 语法的表格。
这个流程看起来麻烦,实际只对少数难搞的表格走完整流程,大部分简单表格交给 MinerU 一步到位,能省下很多工作量。另外,表格识别结果里经常出现空格错位的问题,我每次生成完都要用脚本检查一下每行的竖线数量是否一致,这能快速定位被截断的表格。
公式这边的情况更微妙。MinerU 支持公式转 LaTeX,但长公式、复杂矩阵、多层嵌套的分式仍然会出问题。我自己总结的检查点主要有三个:一是上下标是否正确,二是根号和分式的边界是否完整,三是多行公式的对齐符号有没有乱。
还有一个小坑:有些公式被误识别成图片,或者图片被误识别成公式。这会让最终 Markdown 里多出一堆奇怪的图片引用或者公式块。遇到这种情况,我会调整版面解析时的阈值,让分类更保守一些,宁可把不确定的内容标成图片,也不要硬生成错误公式,毕竟图片还能人工确认,但错公式几乎没法自动修复。
4. 常见问题与排查技巧实录
4.1 表格错位、合并单元格丢失
表格问题是最容易出现的,也是我处理数量最多的。症状表现包括:表格被拆成上下两张表、列数和行数对不上、合并单元格的内容只出现在其中一个格子里。
我的排查步骤是:
- 先确认原始 PDF 里的表格是否有完整边框。无线表格和有边框表格的处理逻辑完全不同,模型在两种场景下的表现差异也很大。
- 检查表格上方和下方是否有多余的文本块,有时候标题和表格被识别成一个大区域,导致结构混乱。
- 单独对表格区域截图再识别一次。如果第二次识别仍然错,就手动重建,不要试图靠调参解决所有问题。
实际操作中,我发现跨页表格是最难自动处理的对象之一。模型通常会把上下两页的同一张表当成两个不同表格,后续合并时要靠人工判断。我的经验是,在喂给模型之前,先把跨页表格按相同内容拼接成一张长图,再交给识别模型,效果比分别识别好得多。
还有一点,表格内文本如果包含多行,Markdown 里没有直接的换行机制。我通常会把单元格内的换行转换成<br>,这样既能在渲染时保持效果,又不会破坏表格结构。
4.2 公式乱码与上下标问题
公式识别错误往往是“看着像一回事,复制出来全是车祸现场”。比如x^2变成了x2,分式a/b变成了ab,根号直接丢失。另一个极端是公式里混入了大量空格和奇怪转义符号。
我通常的做法是:
- 把所有公式从 Markdown 里单独提取出来,逐个渲染成图片再和原文档对照。这一步虽然费时,但比在代码里盯字符串直观得多。
- 优先修复上下标。上下标可以通过 LaTeX 的
^和_重新标注,不能靠模型自动完成的就手动改。 - 根号和分式检查边界。这类问题不能只靠眼睛看,还是得逐个渲染确认。
我试过用后处理正则去自动修正,但在公式领域,正则基本只能处理一些固定的缺失模式。公式一复杂,自动化的收益就很低,不值得浪费时间。所以我的习惯是,公式特别多的文档直接降低预期,重点保证正文和表格正确,公式部分保留待人工校对。
4.3 中文标点和引号被吃
很多人容易忽略的一个问题:OCR 识别英文引号和中文引号经常混淆,中文的省略号、破折号也经常被拆成多个点或短线。这类问题不会导致解析失败,但会让最终文本显得很廉价,进知识库之后检索效果也受影响。
解决这个问题的关键不在模型,而在后处理。我会准备一份常见字符映射表,把识别结果里常见的中英文标点做统一替换。比如把半角逗号与全角逗号按上下文修正,把连续的句点合并成中文省略号,把破折号识别成--的情况还原成中文破折号。
这里提醒一句:后处理规则不能写太猛。有些规则会把合法的英文标点也改掉,尤其是涉及时差、日期、代码片段的内容。我的策略是先只改那些在文档语境里几乎不可能合法出现的组合,再人工检查一小部分样例确定规则不会误伤。
4.4 性能优化:大批量文档怎么跑
当你要处理的文档从几份变成几百份,性能问题就会浮出水面。我遇到过三个主要瓶颈:显存不足、CPU 空转、磁盘 IO 卡顿。
显存不足最直接的解决方法是缩小批大小。深度学习模型一次性吃太多图像会爆显存,我一般把批大小调小到能稳定跑完一批。还有一种做法是分页处理,把长文档按页面切开,处理完再合并结果。这个方法看起来笨,但稳定性极高。
CPU 空转通常出现在预处理阶段。有些库默认会使用所有 CPU 核,但并不代表越快越好,因为多进程会带来额外的调度开销。我习惯手动限制进程数,先设为物理核心数的一半,观察实际吞吐,再根据资源占用调整。
磁盘 IO 被很多人忽视。文件解析过程中会读写大量临时图片和中间结果,如果硬盘负载太高,速度会被拖得很明显。我把临时目录放到专门的 SSD 分区上,结果整体处理速度快了不少。这种细节平时没人提,但批量处理时经常成为隐藏瓶颈。
5. 我的几点体会与后续会怎么用
折腾完这个组合之后,我最大的感受是:没有银弹,只有合适的分工。MinerU 帮我解决“版面结构还原”,PaddleOCR 帮我兜住“低质量图像上的文字识别”。它们配合起来,比任何一方单打独斗都稳。
我现在处理新文档的流程基本固定:先跑一遍 MinerU 默认产出,再按页面难度分档,普通页面直接用结果;问题页面交给 PaddleOCR 预处理和二次识别;表格和公式单独检查;最后统一做一次后处理清洗。这套流程跑熟之后,一份几十页的扫描版论文能在一个小时内出可用的 Markdown,这在以前是我不敢想的。
再分享一个后期的小建议:解析结果不要直接当成最终答案,尽量保留原始 PDF 和中间图片。出问题的时候,能溯源、能对比,比反复调参数有用得多。文档解析本质上是个工程问题,耐心、可复现、良好的人工兜底机制,比堆叠更强大的模型更能决定交付质量。
这套方案后续我还会继续扩展,比如把 PaddleOCR 的微调能力用起来,针对特定印刷体做专项优化;或者把 MinerU 的输出 JSON 直接对接知识库的自动清洗流程。OCR 这条线从“能用”到“好用”,绝不是换个更贵的模型那么简单,而是把工程细节一点点磨到位。希望这篇记录能对你有所帮助,少走我走过的弯路。