ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

昇腾910B部署MinerU 2.5:PDF解析的NPU迁移实战

昇腾910B部署MinerU 2.5:PDF解析的NPU迁移实战 第一台Atlas 800T A2送进机房那天我本来准备用它跑大模型推理结果被数据团队拉去先解决PDF解析。知识库那边每天要灌几百份论文、合同和扫描件原来的解析方案碰到多栏版面就乱公式表格更是没法看。开源圈里论解析质量MinerU 2.5确实能打但问题也很现实它官方只做了CUDA适配昇腾910B上并没有现成的安装包。这次部署前前后后折腾了大概一周从环境适配到算子排查再到现在能稳定批量解析踩了不少坑。这篇文章把完整过程整理出来给同样拿着昇腾卡、又想跑MinerU 2.5做PDF解析的工程师一条可复现的路径。文章既聊部署决策也聊实际踩坑重点是三件事怎么把MinerU 2.5从CUDA搬到NPU上跑起来过程中哪些算子会翻车以及真正接入知识库时要注意什么。适合两类人看一类是要在国产算力卡上跑AI解析工具的部署工程师另一类是负责RAG知识库、想搞清楚MinerU输出怎么对接检索流程的后端同学。1. 项目思路与部署选型分析1.1 为什么是MinerU 2.5选型的时候其实对比过好几套方案。PyMuPDF这类传统库胜在轻量但面对扫描版PDF基本等于盲人摸象公式、表格、多栏版面还原全都不靠谱。unstructured、pdfplumber对简单文档还能应付一旦页面里混了图片、公式、页眉页脚抽出来的结构就乱了根本没法直接作为向量化切块的输入。MinerU的优势在于它把整个解析流程做成了流水线版面检测、阅读顺序还原、OCR、公式识别、表格结构化最后统一输出Markdown和JSON。2.5版本把模型权重和推理逻辑拆分得更干净还优化了中文多栏版面的处理效果。对我们团队来说最看重的是它能把PDF直接转成Markdown省去中间很多脏活知识库侧拿到Markdown之后直接按标题和段落切块就能进向量库整个链路缩短了一大截。另外MinerU 2.x还支持Word、图片、网页等格式虽然标题写的是PDF解析工具但实际知识库场景里Word文档也不少这一点在后面对接时省了不少事。1.2 昇腾910B上部署的难点在哪昇腾910B本身不缺算力单卡FP16算力在300T以上HBM容量也很可观理论上来讲跑MinerU这种场景完全没问题。真正的难点在于软件生态。MinerU底层依赖PyTorch而PyTorch官方并不直接支持NPU必须靠torch_npu这个适配层把设备操作映射到昇腾CANN上。这就是第一道坎MinerU代码里大量直接用torch.device(cuda)、.cuda()这类调用在昇腾环境里会直接报设备不存在。更深一层的问题在于算子兼容性MinerU里的版面检测、公式识别模型用到了Transformer结构里面的一些Attention算子、FlashAttention加速逻辑在NPU上不一定有对应实现踩到不支持的算子就得想办法绕行。还有一个隐性问题是模型分发。MinerU的模型权重托管在HuggingFace和ModelScope上很多内网环境的昇腾服务器既不能直接访问外网也没有预置模型目录这看起来是小事实际部署时却经常卡住大半天。1.3 部署形态与适配路线怎么定部署形态上我当时在Docker和源码环境之间犹豫过。官方镜像opendatalab/mineru:2.5默认是按CUDA环境封装的在昇腾上直接拉下来既没有驱动映射也没有CANN环境除非自己再套一层昇腾容器运行时否则没法用。后来评估了一下团队实际使用场景最终决定放弃容器化直接用物理机上的conda虚拟环境来部署。原因也很简单MinerU的适配改造过程中需要反复改代码、跑测试、看日志裸环境调试成本最低而且昇腾服务器的驱动、CANN已经由基础设施团队装好了我在Python层面做适配就够了没必要再叠加容器这层变量。适配路线上采用了“主模型走NPU、OCR兜底走CPU”的混合方案。PaddleOCR这套依赖在昇腾上适配比较麻烦而MinerU的OCR环节对整体吞吐影响比重不小强行迁到NPU反而会引入一堆不确定的算子编译问题页面里跑得动的那部分模型统一放到910B上做加速OCR单独留在CPU上跑整套流程不至于被一个环节拖死。这个决策在后面验证阶段确实起作用了。2. 环境准备与版本组合2.1 硬件识别与固件检查上手第一步先确认服务器和卡的真实状态别上来就装软件。Atlas 800T A2这个机型有多种卡型配置910B有32GB和64GB显存版本命令输出里对应的是910B3和910B4这类型号两者能塞下的模型规模和并发策略都不一样。# 查看NPU卡信息 npu-smi info # 更详细的单卡信息 npu-smi info -t board -i 0正常输出里能看到芯片型号、HBM容量、固件版本。顺便检查一下操作系统和内核昇腾CANN对OS版本有明确的兼容要求Ubuntu 20.04/22.04、openEuler、CentOS这些主流的都在支持列表里但版本太老的系统容易在后续编译算子时出问题。还要确认固件和驱动是否配套。昇腾的固件、驱动、CANN这三者的版本耦合非常紧跨大版本混用经常导致设备初始化失败。我的建议是在基础设施团队装好的基础上用npu-smi info里的固件版本反查对应的CANN版本不要盲目装最新CANN。2.2 版本矩阵CANN、PyTorch与torch_npu怎么匹配昇腾环境最容易翻车的就是版本对齐。PyTorch、torch_npu、CANN三者必须互相匹配不能从pip上随便拉一个最新版。torch_npu的发布页面会给出它对应的PyTorch主版本和CANN版本范围安装之前一定要先核对清楚。这次部署所用的组合是CANN toolkit: 8.0.RC3 Python: 3.10 PyTorch: 2.1.0 torch_npu: 2.1.0.post6之所以选PyTorch 2.1是因为torch_npu对2.1这一代的适配相对成熟MinerU 2.5的依赖约束也允许这个版本。用更新的PyTorch 2.3或2.5也并不是不行但配套的torch_npu版本较新踩到未知算子时排查难度会更高对部署工期不友好。先用成熟组合跑通全流程再逐步升级是更稳妥的做法。CANN的安装路径固定安装在/usr/local/Ascend下装完一定要手动source环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh这一步漏掉的话后面torch_npu初始化时会报找不到libruntime.so之类的错误。2.3 创建Python虚拟环境并完成基础软件安装项目环境用conda隔离Python版本固定在3.10。需要说明的是MinerU的依赖非常多像paddleocr、paddlepaddle、transformers、tokenizers这些都有版本要求直接用系统Python拆出一份干净环境反而省心。conda create -n mineru python3.10 -y conda activate mineru # 先装CPU版PyTorch防止pip自动拉到带CUDA的版本 pip install torch2.1.0 --index-url https://download.pytorch.org/whl/cpu # 再装torch_npu版本必须和torch严格对应 pip install torch-npu2.1.0.post6装完先别急着往下走立刻做一个冒烟测试python -c import torch; import torch_npu; print(torch.npu.device_count())如果输出卡的编号而不是抛异常说明NPU接入PyTorch成功。通常还可以顺手创建几个tensor在NPU上做个简单加法确认计算图能正常下发。这一步测试通过后面适配MinerU时心里就有底了。2.4 MinerU 2.5源码获取与依赖安装MinerU分成pip包和源码工程两种使用方式。在昇腾场景下强烈建议走源码安装因为后面需要打的补丁集中在代码层pip包改起来不方便。直接clone官方仓库切到2.5分支或对应release tag。git clone https://github.com/opendatalab/MinerU.git cd MinerU git tag # 查看版本 git checkout 2.5 # 或git checkout对应release tag pip install -e .[full]full依赖会把OCR、表格识别、公式识别对应的模型依赖都装进来缺了哪个都会在运行时才暴露。安装过程中如果看到paddlepaddle自动安装了GPU版本要立刻卸载重装CPU版因为这台机器上根本没有NVIDIA驱动GPU版Paddle在初始化阶段就会抛CUDA相关错误。3. 昇腾适配改造把MinerU从CUDA搬到NPU3.1 定位代码里的“CUDA锚点”MinerU官网对CUDA环境是开箱即用但在NPU环境下首先要把整个代码库里所有和CUDA/device相关的硬编码找出来。第一阶段先在工程目录里做一次全局检索grep -rn torch.device --include*.py | grep -i cuda grep -rn \.cuda() --include*.py检索结果基本集中在magic_pdf/model/、magic_pdf/lib/、magic_pdf/pipe/这几个目录。主要分三类问题一是torch.device(cuda)这种显式指定设备的调用二是模型实例化后用.to(cuda)或者.cuda()做搬运的调用三是在推理循环里用device参数透传的地方。这里有个经验不要机械地把所有cuda字符串全局替换成npu因为有些地方是字符串匹配逻辑比如判断某个算子是否在GPU上运行改成npU之后可能造成误判。比较稳妥的办法是只修改真正的设备创建和tensor搬运位置。3.2 设备迁移与推理后端修改MinerU在配置文件中通常有一个device-mode之类的字段标准版本一般只接受cpu和cuda两个取值。昇腾场景下要把它改成npu同时让代码识别到这个字段。根据源码里设备读取的集中度可以用比较直接的方式改在magic_pdf/model/model.py这类统一加载模型的文件里把从配置读出来的device值做一次映射。# 在读取device配置后增加映射 if device npu: import torch_npu device npu然后在tensor搬运的代码里统一改用to(device)。MinerU内部大多数的模型封装已经在用.to(device)传参所以只要把device来源改成npu大部分设备迁移就完成了。真正要动手术的是那些直接写死torch.device(cuda)的地方按检索结果逐个修改即可。修改完再做一次单元验证直接用MinerU的模型加载脚本初始化全部模型如果加载过程没有报设备不存在的错误说明设备切换这层已经通了。3.3 模型加载与精度策略昇腾910B的FP16算力远高于FP32跑AI推理时用半精度几乎是最优解。MinerU的模型权重下载下来默认是FP32的PyTorch加载后直接跑推理也能跑通但速度上不去显存占用还偏高。我采用的方案是在推理入口处统一对模型做半精度转换并为每个模型单独指定设备model model.half().to(device)这里有一个容易忽略的坑不是所有算子在FP16下都能保持数值稳定。特别是表格结构识别的输出涉及坐标回归如果出现坐标偏移导致输出乱排就要考虑把那个特定模型保留FP32其他模型继续用FP16。实际部署时我是先全量FP16跑了一遍测试集发现版面检测输出有小幅坐标偏差最后把layout模型单独拉回FP32其余模型保持半精度。3.4 算子不兼容问题的排查与绕行算子不兼容是昇腾适配里最耗时的部分也是最难提前预测的部分。910B的CANN对常用CNN算子支持很好但Transformer结构里的某些Attention算子和自定义算子就未必全覆盖了。部署中遇到两种典型报错第一种是运行时抛出Op not supported或Unsupported kernel类型的错误。这种情况下先看报错信息里提到的是哪个算子再去源码里找到对应的模型配置。MinerU的公式识别模型默认可能开着FlashAttention之类的加速开关这个在NPU上不一定有实现处理方式是把相关开关关掉回退到普通Attention。代码里一般会有if hasattr(model.config, use_flash_attention): model.config.use_flash_attention False第二种是图编译阶段卡死或内存越界。这类问题多数和算子融合策略有关可以先关闭CANN的图优化模式export ASCEND_LAUNCH_BLOCKING1这个环境变量能让算子逐一下发而不是整图下发虽然会慢一点但错误信息会精确到具体算子排查起来效率提高很多。等定位到问题算子并绕过之后再把这个变量去掉恢复全速推理。还有一类要特别注意的是PaddleOCR相关依赖。PaddlePaddle在昇腾上不是原生适配一旦启动OCR模型就会抛No such device之类的错。这里就是前面说的“OCR走CPU”兜底方案发挥作用的时候了在配置里把OCR的执行设备固定为cpu让PaddlePaddle的推理始终在CPU侧执行其余模型照常用NPU。4. 实操部署与功能验证4.1 模型权重与配置文件准备昇腾服务器通常部署在内网模型权重获取是个现实问题。MinerU第一次运行时会尝试从HuggingFace或ModelScope下载模型如果网络不通就直接卡死。解决办法是提前在能访问外网的机器上把整个模型目录下载好打包后传到910B服务器放到固定目录再在配置里指定模型路径。用ModelScope下载比较方便modelscope download --model 模型仓库名 --local_dir /data/mineru/modelsMinerU的配置路径和字段在不同小版本上有差异部署时要对照仓库里的config模板逐项检查。核心配置项包括模型目录路径models-dir、设备类型device-mode、OCR后端选择、表格识别开关等。我这边最终使用的关键配置参考如下{ models-dir: /data/mineru/models, device-mode: npu, enable-formula: true, enable-table: true, lang: ch, ocr: { backend: paddle, device: cpu } }注意device-mode设成npu后对应代码支持要跟上这一点在3.2节已经完成了。4.2 跑通第一份PDF解析配置完成后先别一上来跑整个PDF目录。拿一份有代表性的单页PDF做验证页面里最好同时包含标题、正文、多栏排版和一个公式这样能把主要模型全都触发一遍。source /usr/local/Ascend/ascend-toolkit/set_env.sh conda activate mineru magic-pdf -p /data/test/test.pdf -o /data/test/output -m auto如果解析过程没有报错查看输出目录find /data/test/output -type f正常会生成Markdown文件、JSON中间结果、图片目录和模型推理的细节文件。打开Markdown确认版面顺序是否正确、公式有没有被识别出来、表格结构是否完整。首次跑通的意义不只是验证功能更重要的是确认整条推理链路在NPU上是通的。这时候可以顺便用一个监控窗口观察显卡状态npu-smi info watch如果推理过程中卡利用率有明显波动说明NPU确实在参与计算如果一直为0那大概率是设备切换没生效所有模型其实还跑在CPU上。4.3 封装成解析服务命令行工具验证没问题之后还要考虑供团队使用。数据团队不可能每个人都在服务器上敲命令行所以基于FastAPI包一个轻量解析服务接收文件上传返回Markdown和JSON。from fastapi import FastAPI, UploadFile from magic_pdf.pipe.UNIPipe import UNIPipe app FastAPI() app.post(/parse) async def parse_pdf(file: UploadFile): with open(/tmp/input.pdf, wb) as f: f.write(await file.read()) pipe UNIPipe(pdf_path/tmp/input.pdf, model_json_path/data/mineru/models) pipe.pipe_classify() pipe.pipe_parse() md_content pipe.get_markdown() return {markdown: md_content}实际使用时要做并发控制。910B单卡虽然显存很大但MinerU的推理链路是串行的多个请求同时进来会导致NPU资源争抢。我当时在服务层加了一个全局锁把并发解析请求排成队列一次只处理一个PDF稳定性好很多。4.4 解析结果接入知识库的注意点把MinerU输出接入知识库时有几个细节值得提醒。MinerU生成的Markdown中每个章节标题、段落、表格和公式都有相对清晰的边界但直接按固定字数切块会切坏表格和公式。我自己实践下来先按Markdown的标题结构做一级分段再把大段落按200-400字区间切块同时保留表格和公式的完整区块检索效果明显比纯按字数硬切好。另外MinerU的JSON输出比Markdown的信息量更大包含每个版面元素的位置和类型。如果知识库后续要做文档溯源或高亮定位建议把JSON和Markdown一起入库检索命中的时候可以直接映射回原文坐标。5. 性能表现与资源监控5.1 910B上的基准测试怎么测才靠谱适配完成后最好先做一个量化的基准测试一是验证加速效果二是给后面的并发策略提供依据。我当时的测试方式是抽取100页不同类型的PDF做样本包括单栏文字版、双栏扫描版、带密集公式的论文页、带复杂表格的财报页统计单页平均耗时。在NPU半精度推理、OCR走CPU的配置下单页平均耗时大约在2秒上下纯文字版能到1秒以内公式密集的多栏页面可能要3到4秒。同一批PDF在纯CPU上跑单页平均耗时在40秒以上。910B的加速效果非常明显尤其在公式和表格识别这两个环节NPU算力的优势发挥得最充分。如果跑不到这个量级优先检查设备是否真的切到了NPU再确认模型有没有成功转成FP16。大多数性能异常都出在这两个环节。5.2 CPU与NPU的混合调度前面说过OCR放在CPU上跑这在单次解析里会增加一些延迟因为主流程在NPU完成版面、公式识别后要把文本区域交给CPU做OCR再把结果合并回来。但从系统整体稳定性看这个取舍很值得。刚开始尝试把PaddleOCR强推上NPU结果不仅要处理PaddlePaddle的昇腾适配还要面对自定义算子的编译问题折腾了两天没完全跑通。切到CPU之后虽然OCR这个子环节不是全速状态但整个流程稳定可用出问题的概率大幅下降。对于每天几百份PDF的解析量级CPU吃OCR这部分压力完全没问题。更需要注意的是CPU和NPU真的可以同时干活。PaddleOCR的CPU推理和MinerU主模型的NPU推理在时间上存在重叠实测下来整体吞吐并没有被CPU拖垮。5.3 长时间运行的稳定性批量解析跑几个小时很容易暴露内存增长、句柄泄漏这类问题。建议在正式启用前连续跑一批上百份PDF观察两个指标一是系统内存占用是否持续增长二是NPU显存有没有泄漏。如果内存持续增长优先排查是否有循环内缓存未释放的问题。MinerU的pipeline在做批量处理时有些中间结果会常驻内存批量模式下要记得定期清理或者直接把解析服务设计成一次一个进程、解析完自动释放重来的模式。显存方面910B的64GB大显存让MinerU跑得很轻松单次解析显存占用大约6到10GB即使同时开几个并发进程也不会碰到显存天花板。这也是当初选择910B做这个场景的重要原因。6. 常见问题排查与避坑实录6.1 环境类问题速查整个部署过程踩过的问题里环境类占了一大半而且很多是重复性的。整理成速查表方便后续团队排查现象可能原因排查与解决import torch_npu报错找不到CANN库没有source环境变量执行source /usr/local/Ascend/ascend-toolkit/set_env.shtorch.npu.device_count()返回0没有设置可见设备设置export ASCEND_RT_VISIBLE_DEVICES0PaddleOCR初始化报CUDA错误装成了GPU版PaddlePaddle卸载后重装CPU版pip install paddlepaddle2.6.x解析时CPU很高但NPU利用率为0设备切换未生效模型跑在CPU上检查config里的device-mode以及代码里的device映射算子编译报错CANN版本与PyTorch版本不匹配核对torch_npu官方版本矩阵重新安装对齐版本容器内看不到NPU设备没有使用昇腾容器运行时裸环境部署或安装ascend-docker-runtime6.2 模型与推理异常速查模型层面的问题比环境问题更有代表性这里列几个我印象深刻的现象可能原因排查与解决模型加载时报KeyError模型权重与代码版本不一致重新下载对应2.5版本的模型权重输出Markdown里公式全乱码公式识别模型精度异常检查该模型是否被强制转成FP16必要时保留FP32表格识别输出列错乱表格模型在NPU上坐标回归有偏差表格模型改用FP32或单独CPU推理排除算子精度问题解析过程偶发卡死CANN图编译在某类页面触发问题开启ASCEND_LAUNCH_BLOCKING1定位具体算子多页PDF解析到某页崩溃个别页面触发不支持的算子路径先记录崩溃页码用单页模式跑通该页做专项处理有一个排查技巧比较关键MinerU在执行时会把每个阶段的中间结果写到目录里报错时不要只看终端日志去中间结果目录里找对应的JSON和图片切片能快速定位是版面检测挂了、OCR挂了还是表格识别挂了排查效率能提升一半。注意部署期间对源码所做的任何修改都要在仓库里留好diff记录或独立patch文件。昇腾适配的改动虽然不大但官方仓库一升级就会全被覆盖留好补丁才能快速在新版本上重新应用。7. 写在最后一点个人实践体会昇腾910B跑MinerU这件事本质上不是MinerU本身有多难部署而是把一套成熟的开源工具从CUDA生态迁移到NPU生态的过程中生态差异带来的一系列摩擦。作为使用者我们能做的不是等官方适配而是把PyTorch这一层的基础打通把设备映射、精度策略、算子绕行这几件事做扎实剩下的就水到渠成。这套适配思路不止适用于MinerU其他基于PyTorch的开源推理项目到了昇腾上也基本是同样的套路先查环境版本再改设备映射然后逐个解决算子兼容性最后做性能验证。模板是通用的具体细节因项目而异。最后再分享一个小技巧910B这种国产加速卡虽然生态不比CUDA顺手但单卡显存和算力是真的能打。真正入手之前别被社区里一些悲观言论劝退先跑通一个demo再判断值不值得投入很多时候结果会比你预想的要好。
返回列表