ARTICLE DETAIL

资讯详情

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

麒麟系统离线部署PaddleOCR实战:依赖打包、模型量化与CPU推理加速

麒麟系统离线部署PaddleOCR实战:依赖打包、模型量化与CPU推理加速 前一阵子接了个活儿要在客户的麒麟服务器上做离线OCR用来识别合同和票据。机器是银河麒麟V10网络彻底隔离连pip源都连不上模型下载更是想都别想。折腾了几天总算把PaddleOCR跑通顺带把识别速度优化了不少。这篇文章就把整个落地过程完整写一遍包括方案选型、离线依赖、推理模型、量化加速、服务化封装以及麒麟系统下最常见的那些坑。如果你也在国产化环境下做类似的事情这篇应该能帮你省下不少时间。我自己也是从零开始摸的中间踩了很多“一看就会、一跑就挂”的坑尤其是离线环境下的依赖收集和模型下载看着简单实操起来全是细节。文章里我会把当时的具体做法、命令、参数都贴出来也把为什么这么做的原因讲清楚方便你根据实际情况调整。1. 部署前的整体设计与方案选型1.1 为什么选PaddleOCR而不是其他OCR方案做OCR选型的时候市面上主要有几条路开源的Tesseract、EasyOCR商业OCR服务以及PaddleOCR。Tesseract我最早试过中文识别效果确实一般对复杂版面、倾斜文本、低分辨率拍照图的表现不太行而且它的模型训练和调参体系也比较老想在生产项目里做到可靠识别投入产出比太低。EasyOCR的识别效果比Tesseract好一些底子是基于PyTorch的但问题在于依赖PyTorch一套离线部署时整个依赖体积会非常大而且PyTorch在ARM架构的麒麟系统上安装也是个麻烦事。商业OCR服务比如云厂商提供的接口效果好是好但在涉密或内网项目里根本没法用数据不能出网这一条就直接否掉了。PaddleOCR的优势恰好是这几点的交集中文识别效果好模型轻量纯离线可跑支持CPU推理而且百度开源比较早社区文档和踩坑案例都多。对于信创环境下“数据不出内网”的需求来说这是目前最合适的方案之一。另外在国产CPU或者无GPU的机器上PaddleOCR的CPU推理性能也可以接受识别一张普通票据文本大概几百毫秒到1秒出头做到批量处理没问题。1.2 离线部署的三个核心难点离线部署和在线环境最大的区别在于你没有办法“缺什么装什么”一切都要提前准备好一次拷进去。实际操作中核心难点有三个第一是依赖包的收集。PaddleOCR加载OCR模型需要PaddlePaddle框架还需要opencv、shapely、pyclipper、numpy、Pillow等一堆依赖。这些包本身的依赖又层层嵌套离线环境一旦缺少某个传递依赖pip install 就会直接报错。所以要在一台有外网的机器上把所有wheel包都下载好再搬到内网去用--no-index方式安装。第二是硬件架构和系统版本的匹配。麒麟系统有x86_64架构也有ARM64架构有的机器是飞腾、鲲鹏CPU有的是兆芯、海光。你的Python版本、PaddlePaddle版本、opencv版本都必须对应架构否则装了也跑不起来。第三是模型文件的准备。PaddleOCR在线安装后首次运行会自动从网上下载模型但在离线环境里这个下载过程必然失败。你必须提前把检测、方向分类、识别这三个模型下载好放到固定目录里然后在初始化时显式指定路径。1.3 技术选型版本与架构对照表这里把我当时使用的一套版本组合列出来你可以直接抄作业组件版本选型说明操作系统银河麒麟V10 SP1x86_64部分机器是SP2的aarch64版本后续命令需对应调整Python3.8.xPaddlePaddle 2.5/2.6 系列对Python 3.8支持最稳PaddlePaddle2.6.1CPU版如需GPU版需另配CUDA对应版本本文以CPU为主PaddleOCR2.7.0.3API相对稳定社区资料最多适合生产落地检测模型ch_PP-OCRv4_det_infer中英文检测server版精度更高方向分类模型ch_ppocr_mobile_v2.0_cls_infer用于识别旋转文本识别模型ch_PP-OCRv4_rec_infer中英文识别支持常见文档字体为什么没有直接用PaddleOCR 3.x因为3.x的API改动比较大推理接口从ocr.ocr()变成了ocr.predict()网上的教程也还没有2.x那么齐全。如果你的项目是新起步不依赖老接口用3.x也行但要把下面代码里的调用方式对应改掉。我自己是为了稳定和省事选了2.7.x线。2. 离线环境准备先搞定一个能用的Python2.1 确认系统基础状态拿到机器第一件事不是急着装东西而是把系统底细摸清楚。我当时先跑了这几条命令cat /etc/os-release uname -m python3 -V ldd --version | head -n 1麒麟V10的/etc/os-release里会显示系统版本和ID可能是kylin也可能是ubuntu因为银河麒麟的服务器版是基于Ubuntu/Debian体系做的很多包管理行为跟Ubuntu一致。uname -m用来确认架构x86_64和aarch64在wheel包选择上完全是两条路线。还有一条命令建议顺手跑一下apt list --installed | grep python3看系统自带的Python版本。很多麒麟服务器默认自带的是Python 3.6或3.7而这个版本可能和你要安装的PaddlePaddle不兼容。如果版本太老你可能需要自己离线编译一个更高版本的Python这块后面单独说。2.2 在外网机器上准备wheel包离线安装最关键的一步是提前准备好所有依赖的wheel包。我在一台外网Ubuntu 20.04机器上严格按照下面这个流程收集依赖。先建目录然后用pip下载PaddleOCR的依赖列表mkdir wheels pip download paddleocr2.7.0.3 -d wheels这条命令会把PaddleOCR以及它的所有依赖包下载到wheels目录。如果你已经写好requirements.txt也可以直接pip download -r requirements.txt -d wheels注意如果目标机器是ARM架构你需要在下载时指定平台参数否则下载到的可能是x86_64的包。ARM版的下载命令大致是pip download paddleocr2.7.0.3 -d wheels \ --platform manylinux2014_aarch64 \ --only-binary:all:但如果你只是在一台普通的x86_64服务器上下载然后把包拷到同为x86_64的麒麟机器上情况会简单很多。我当时的做法是找一台和麒麟基础库最接近的外网Ubuntu机器用普通的pip download下载然后拷贝过去用pip install --no-index --find-linkswheels直接离线安装。这里有个隐藏坑需要注意有些包如果源码里有编译过程下载的可能是tar.gz源码包而不是编译好的wheel。比如pyclipper和shapely如果在目标机器上来不及编译会非常痛苦。为了确保拿到编译好的二进制包建议加上--only-binary:all:参数pip download paddleocr2.7.0.3 -d wheels --only-binary:all:如果有包只提供源码这个命令会报错提示哪个包没有对应的二进制wheel。遇到这种情况要么换版本要么手动去下载对应平台编译好的wheel。2.3 离线安装与依赖精简把wheels目录拷贝到麒麟服务器后开始安装pip install --no-index --find-linkswheels paddleocr2.7.0.3如果系统里已经有pip可以直接用如果没有可以先通过系统自带的Python 3和ensurepip模块装一个基础pip。这一步通常不会太麻烦因为麒麟系统虽然精简但python3-pip这个基础包一般会带。安装完PaddleOCR后它会把PaddlePaddle作为依赖一起装。如果你的wheels目录里没有paddlepaddle这个包pip就会报错找不到依赖。所以我在下载依赖时会单独确认一下paddlepaddle是否在列表里ls wheels | grep paddle如果没有就先把paddlepaddle下载进来pip download paddlepaddle2.6.1 -d wheels注意PaddleOCR在安装时可能不会默认把paddlepaddle作为强依赖因为它允许你预装GPU版的paddlepaddle-gpu。所以在离线机器上安装PaddleOCR之后一定要手动确认PaddlePaddle已经装好。依赖精简方面实际推理时有一些包其实用不到比如visualdl、fasttext这些它们更多是训练和可视化用的。但在离线环境里我建议还是按照全部依赖一起装不要手动排除因为PaddleOCR源码里有大量import语句缺一个启动就报错。为了省那几十MB搞出一堆运行错误不划算。2.4 Python解释器版本太老怎么办如果你遇到麒麟系统自带Python只有3.6且PaddlePaddle没有对应cp36的wheel就需要换一个Python版本。我当时是直接编译了一个Python 3.8。在离线机器上编译Python需要提前准备好编译工具链和几个系统库sudo apt install -y build-essential zlib1g-dev libffi-dev libssl-dev然后用源码包继续tar -xf Python-3.8.18.tgz cd Python-3.8.18 ./configure --prefix/usr/local/python38 --enable-optimizations make -j$(nproc) sudo make install--enable-optimizations会让编译时间变长但生成的Python运行效率更好。编译过程中如果有缺少库的报错看提示补对应依赖包就行。编译完成后用绝对路径使用新的Python/usr/local/python38/bin/python3 -m pip install --no-index --find-linkswheels paddleocr2.7.0.3这里提醒一下千万不要因为系统自带的Python版本不够就去升级系统glibc或者直接替换系统Python。麒麟的很多底层系统管理工具依赖自带Python一旦换掉整个系统都可能出问题。3. PaddleOCP安装与推理模型准备3.1 安装PaddlePaddleCPU与GPU的差异化处理PaddlePaddle是整个OCR系统中最重的依赖安装时CPU和GPU路线完全不一样。在麒麟系统上绝大多数时候是CPU环境原因很简单GPU驱动的安装和适配在国产系统上本身就是一件费劲的事。CPU版安装很简单pip install --no-index --find-linkswheels paddlepaddle2.6.1安装完验证一下python -c import paddle; paddle.utils.run_check()如果输出结果里有“PaddlePaddle is installed successfully”之类的信息就说明框架层没问题。GPU版的情况要复杂很多。首先你得确认这台机器上有可用的NVIDIA显卡其次麒麟系统的内核和显卡驱动要匹配然后还要保证CUDA版本和paddlepaddle-gpu版本对应。PaddlePaddle 2.6.1对应CUDA 11.2/11.6/11.7都有不同的包名。如果项目确实要求GPU加速建议让厂商或系统管理员先把显卡驱动装好再通过nvidia-smi确认CUDA版本然后选择对应的paddlepaddle-gpu安装包。我的经验是在信创项目里先按CPU方案设计把模型优化做好。如果后续确需GPU再在同样的代码基础上切换推理引擎。CPU优化做得好很多时候性能并没有想象中那么差。3.2 安装PaddleOCR并准备推理模型PaddleOCR本体安装倒是很简单pip install --no-index --find-linkswheels paddleocr2.7.0.3装完后你会得到一个PaddleOCR的命令行工具和Python库。但真实项目里一般直接用Python API调用很少用命令行。离线部署最大的问题在模型文件。PaddleOCR运行需要三个推理模型文本检测模型、方向分类模型、文本识别模型。这几个模型文件需要在有外网的环境提前下载好。下载地址在PaddleOCR的GitHub Release页面通常是一个tar包解压后是inference.pdmodel和inference.pdiparams两个核心文件。我当时的模型目录结构是这样组织的/mnt/ocr/models/ ├── ch_PP-OCRv4_det_infer/ │ ├── inference.pdmodel │ └── inference.pdiparams ├── ch_ppocr_mobile_v2.0_cls_infer/ │ ├── inference.pdmodel │ └── inference.pdiparams └── ch_PP-OCRv4_rec_infer/ ├── inference.pdmodel └── inference.pdiparams需要注意的是在离线环境里如果你使用PaddleOCR类时不指定模型路径它会在初始化时尝试从网络下载模型然后卡在下载阶段直到超时。前期我遇到过一次这个情况还以为程序死循环了。解决方法很简单在初始化时通过det_model_dir、cls_model_dir、rec_model_dir三个参数指定本地模型路径。3.3 离线环境下模型获取的自救方案如果你所在的团队没有外网或者是所有机器都完全隔离那获取模型文件的常规办法就断了。这里说几个可行的获取途径。最方便的是在内网的软件资产库或离线包管理系统中提前提交模型文件走公司内部的审批和分发流程。很多政企客户内部都有这种资源池把PaddleOCR、模型包、Python解释器一起提交进去后续在任意内网机器上都能快速拉取。如果没有内部资源池那就在一台有外网的机器上把模型tar包下载好用移动介质或者内网共享目录拷进麒麟服务器。我个人的习惯是下载完先本地解压确认tar包结构完整避免拷贝到现场才发现文件损坏。还有一个多数人不知道的细节PaddleOCR模型会自动缓存到~/.paddleocr/目录。如果你在一台可联网的机器上成功运行过一次那么模型就会被缓存下来。部署时直接把整个~/.paddleocr/目录拷到目标机器放到对应用户的家目录下也能解决问题。这个方式尤其适合那些你不想手动指定模型路径的场景。3.4 首次推理验证环境准备齐了以后写一个最简单的脚本验证整条链路from paddleocr import PaddleOCR ocr PaddleOCR( use_angle_clsTrue, langch, det_model_dir./models/ch_PP-OCRv4_det_infer, cls_model_dir./models/ch_ppocr_mobile_v2.0_cls_infer, rec_model_dir./models/ch_PP-OCRv4_rec_infer, show_logFalse ) result ocr.ocr(./test.jpg, clsTrue) for line in result[0]: text line[1][0] score line[1][1] print(f{text}\t{score})如果你的PaddleOCR是3.x版本接口会不太一样初始化参数大同小异但推理要改成result ocr.predict(./test.jpg)第一次运行会做模型加载和PaddlePaddle初始化耗时会长一些只要不报错就说明链路已经通了。4. 模型优化从“能跑”到“跑得更快”4.1 推理参数调优先别急着换模型很多人在模型优化上第一个想到的就是换更大的模型或者上GPU但实际在CPU离线环境下最先应该做的是把推理参数吃透。PaddleOCR的检测和识别参数对速度影响非常大。我当时重点调整了这几个参数det_limit_side_len文本检测时输入图像的长边尺寸默认是960。如果图片内容不那么密集可以调到640或736检测耗时能明显下降。代价是特别长的文本行或密集排列的小字可能会漏检测。det_db_thresh检测二值化阈值默认0.3。调高到0.4左右能过滤掉一些模糊的背景区域减少误检。det_db_box_thresh检测框阈值默认0.6。调高这个值可以丢掉低置信度的检测框减少后续识别的无效计算。drop_score识别结果置信度过滤默认0.5。在正式场景中我会调到0.7能过滤掉很多乱识别出来的字。rec_batch_num识别批次大小默认6。在CPU上批量大小对速度的影响不是线性的不是越大越好。我实测在4核CPU上batch为4的时候速度最理想再大会增加内存和计算压力速度反而下降。use_angle_cls是否需要方向分类。如果图片基本都是正立的建议关掉省一次推理过程。初始化时把这些参数传进去ocr PaddleOCR( use_angle_clsTrue, langch, det_limit_side_len736, det_db_thresh0.4, det_db_box_thresh0.6, rec_batch_num4, drop_score0.7, det_model_dir./models/ch_PP-OCRv4_det_infer, cls_model_dir./models/ch_ppocr_mobile_v2.0_cls_infer, rec_model_dir./models/ch_PP-OCRv4_rec_infer, show_logFalse )注意rec_batch_num这个参数它在内部识别阶段是把多个文本行拼在一起做batch推理。批量识别比逐个识别单行文本要快很多但batch过大会导致单次推理时间变长而且在CPU上更容易触发线程竞争所以一定要实测调参不要照搬默认值。4.2 模型量化小体积、快推理的关键手段PaddleOCR官方基于PaddleSlim提供了一整套模型压缩工具包括量化、剪枝、蒸馏。其中对CPU部署最友好、效果最直接的就是量化。量化的核心思路是把模型的权重和激活从FP32精度降到INT8精度。模型体积直接缩到四分之一推理速度在CPU上通常能提升30%-50%左右精度损失一般控制在1%-2%以内。具体操作分为两步第一步准备量化数据集第二步执行量化并导出模型。量化数据集不需要太多准备几百张有代表性的图片就行覆盖你实际业务中会遇到的版式类型。然后使用PaddleOCR源码包里的slim工具脚本以离线方式执行量化导出python deploy/slim/quantization/quant.py \ --det_model_dir ./models/ch_PP-OCRv4_det_infer \ --rec_model_dir ./models/ch_PP-OCRv4_rec_infer \ --image_dir ./quant_images \ --save_dir ./quant_model脚本会读取./quant_images目录里的图片作为校准数据分析模型每层激活值的分布范围生成INT8量化后的新模型输出到./quant_model目录。这期间如果PaddleSlim没有安装需要先离线安装paddleslim包。量化后推理代码里直接指定量化模型路径就行ocr PaddleOCR( use_angle_clsTrue, langch, det_model_dir./quant_model/ch_PP-OCRv4_det_infer, cls_model_dir./quant_model/ch_ppocr_mobile_v2.0_cls_infer, rec_model_dir./quant_model/ch_PP-OCRv4_rec_infer, show_logFalse )有一个实际经验是量化后最好准备一个测试集把量化前后的识别结果逐条对比一遍。因为某些极端字符、生僻字或者模糊图片在INT8精度下可能识别出错。如果发现量化模型在某类场景上效果下降明显可以只对检测模型量化识别模型保持FP32因为识别模型的精度更敏感。4.3 推理引擎加速开启MKLDNN与多核调度PaddlePaddle在CPU上有专门的优化加速库MKLDNN默认可能是关闭的。开启后在支持AVX指令集的CPU上推理速度提升非常明显。在PaddleOCR初始化时有两个参数可以直接开启这个能力ocr PaddleOCR( use_angle_clsTrue, langch, enable_mkldnnTrue, cpu_threads8, det_model_dir./models/ch_PP-OCRv4_det_infer, cls_model_dir./models/ch_ppocr_mobile_v2.0_cls_infer, rec_model_dir./models/ch_PP-OCRv4_rec_infer, show_logFalse )enable_mkldnnTrue开启MKLDNN加速cpu_threads8让PaddlePaddle使用8个线程进行推理。线程数的设置要根据CPU核数来不用盲目设得很大设太大会造成线程频繁切换性能反而下降。我一般在4核机器上设48核机器上设816核及以上机器设12就好。在部分老旧的CPU上如果MKLDNN优化出错可以在初始化时关闭它毕竟兼容性永远优先于性能。另外PaddleOCR在2.5版本以后对MKLDNN的适配已经比较成熟2.4及以前版本不建议使用。4.4 大图预处理影响速度的前置环节模型推理只是耗时的一部分图像前处理同样可能成为瓶颈。举个例子一张手机拍摄的3000x4000像素票据原图如果不做任何预处理直接丢给检测模型图像缩放、通道转换、归一化的耗时可能比模型推理还高。我当时的做法是在调用OCR之前增加一个统一的预处理函数把图片最长边限制到2000像素内超过则等比缩放把图片转换为RGB三通道去掉Alpha通道把图像转为numpy数组的BGR格式保持和OpenCV一致如果图片方向固定可以先做一次旋转校正减少方向分类的负担。缩放这一步特别重要。大图在缩放后文字区域虽然变小了但对于常见文档和票据来说2000像素以内的分辨率足够识别。这个预处理可以让整体耗时直接减少一半以上。5. 服务化封装让OCR变成可调用的接口5.1 “初始化一次”原则不能每次请求都new一个OCR对象我在做接口封装时踩过一个典型的坑第一版代码在每次请求进来时都执行PaddleOCR()初始化结果首次请求超时直接打满。原因很简单PaddleOCR初始化时要加载三个模型、创建PaddlePaddle推理引擎这个过程非常耗时动辄好几秒甚至十几秒。初始化一次没关系但如果每次请求都重复这个过程系统根本没法用。正确的做法是在服务进程启动时初始化一个全局的OCR对象之后所有请求复用这一个实例。5.2 基于Flask封装一个最简OCR接口下面是我在实际项目中用过的接口代码结构很简单能直接跑import base64 import uuid from io import BytesIO from flask import Flask, request, jsonify from paddleocr import PaddleOCR from PIL import Image import numpy as np app Flask(__name__) # 全局只初始化一次 ocr PaddleOCR( use_angle_clsTrue, langch, enable_mkldnnTrue, cpu_threads8, det_limit_side_len960, rec_batch_num4, drop_score0.7, det_model_dir./models/ch_PP-OCRv4_det_infer, cls_model_dir./models/ch_ppocr_mobile_v2.0_cls_infer, rec_model_dir./models/ch_PP-OCRv4_rec_infer, show_logFalse ) def decode_base64_to_image(base64_str): img_data base64.b64decode(base64_str) img Image.open(BytesIO(img_data)) img img.convert(RGB) return np.array(img)[:, :, ::-1] # RGB转BGR app.route(/ocr, methods[POST]) def ocr_api(): try: data request.get_json() image_base64 data.get(image) if not image_base64: return jsonify({code: 1, msg: 缺少image字段}) image decode_base64_to_image(image_base64) result ocr.ocr(image, clsTrue) lines [] if result and result[0]: for item in result[0]: box item[0] text item[1][0] score item[1][1] lines.append({ text: text, score: float(score), box: [list(map(float, point)) for point in box] }) return jsonify({code: 0, data: lines}) except Exception as e: return jsonify({code: 1, msg: str(e)}) if __name__ __main__: app.run(host0.0.0.0, port5000, threadedFalse)这段代码里有两个容易被忽略的细节一是threadedFalse。PaddleOCR对象并不是线程安全的多个线程同时调用ocr.ocr()可能导致推理出错甚至进程崩溃。如果要用多线程必须自己加锁或者使用多进程模型。二是PaddleOCR的ocr.ocr()支持直接传入numpy数组作为输入不一定要先保存成临时文件再读取。这样可以避免大量的临时文件读写开销在批量场景下性能差距很明显。5.3 并发扩展用多进程而不是多线程上面提到PaddleOCR本身不是线程安全的那如果并发量上来了怎么办我当时用的方案是多进程部署。用gunicorn启动多个worker进程每个worker进程里有一份独立的OCR实例gunicorn -w 4 -b 0.0.0.0:5000 app:app-w 4表示启动4个worker进程每个进程都有自己独立的内存空间和OCR实例互不干扰。这样能充分利用多核CPU也规避了线程安全问题。但这种方式有一个代价内存占用会随着进程数线性增长。每个OCR进程大约占用1到1.5GB内存具体看模型大小和图片缓存所以4个worker大概需要4到6GB内存。如果机器内存不够就要适当降低worker数量。还有一个优化空间是使用消息队列做异步处理。把图片先提交到一个待处理队列然后由固定数量的OCR worker消费队列。这样即使前端请求量很大也不会把OCR服务打垮适合对实时性要求不高、但批量很大的场景。5.4 批量文件识别实践接口场景适合单张上传但很多后台项目是定时批量跑一批图片。批量场景下我习惯直接用Python脚本遍历目录然后用batch的方式处理import os from paddleocr import PaddleOCR ocr PaddleOCR( use_angle_clsTrue, langch, enable_mkldnnTrue, cpu_threads8 ) img_dir ./input_images out_txt ./output_result.txt with open(out_txt, w, encodingutf-8) as f: for img_name in os.listdir(img_dir): img_path os.path.join(img_dir, img_name) if not os.path.isfile(img_path): continue result ocr.ocr(img_path, clsTrue) f.write(f {img_name} \n) if result and result[0]: for item in result[0]: text item[1][0] score item[1][1] f.write(f{text}\t{score:.4f}\n) f.flush()批量任务中要注意内存释放。如果处理完一张大图后图片数据仍然被变量引用内存占用会持续累积。可以在每轮循环末尾显式释放del result另外如果某个目录里包含超长文本图片或者异常图片可以单独捕获异常避免一个文件导致整个批次中断try: result ocr.ocr(img_path, clsTrue) except Exception as e: f.write(f[ERROR] {img_name}: {e}\n) continue6. 麒麟系统下那些绕不开的坑6.1 ImportError: libGL.so.1: cannot open shared object file这是我第一次在麒麟机器上跑PaddleOCR时遇到的第一个报错。原因是opencv-python这个包依赖libGL这个图形库而麒麟系统的精简环境里没有装。很多云镜像或服务器版系统都不会默认带这个图形库。解决方式有两种。第一种是安装系统库sudo apt update sudo apt install -y libgl1第二种是换用opencv-python-headlesspip uninstall opencv-python -y pip install --no-index --find-linkswheels opencv-python-headless4.8.1.78headless版本的OpenCV不依赖GUI相关库专门给服务器环境用的功能上不影响PaddleOCR的图片编解码和图像处理。我在后续项目里都直接改用headless版本彻底避开了这个坑。类似还有libgthread-2.0.so.0缺失的报错这个需要安装libglib2.0-0sudo apt install -y libglib2.0-06.2 GLIBC版本不满足千万不要升级系统glibc离线安装wheel包时如果提示类似version GLIBC_2.29 not found说明某个包编译时依赖的glibc版本比系统自带的要高。银河麒麟V10在不同架构和版本下glibc版本会有差异比如2.28、2.31都有可能出现。这时候千万不要尝试去升级系统glibc。glibc是整个Linux系统最底层的运行库升级不当会导致几乎所有命令失效系统直接废掉。正确的思路是寻找与当前glibc兼容的软件包版本。比如某个新版本的numpy要求GLIBC_2.29而系统只有2.28那就找一个较旧版本的numpy或者找一个专门为当前系统编译的Python发行版。总体来说麒麟系统的软件生态更新节奏比Ubuntu/Debian慢选择软件版本时尽量和系统基础库对齐不要一味追求最新。6.3 第一次推理特别慢甚至像卡死PaddleOCR首次推理比后续推理慢很多这是正常的因为模型第一次加载时需要做算子选择、内存分配、线程池初始化等一堆工作。但如果首次推理卡了几分钟都没反应就要怀疑是不是在尝试联网下载模型。我遇到过的情况是在初始化时只写了langch没有指定det_model_dir等路径PaddleOCR检查本地没有模型就会尝试从GitHub下载。离线环境下下载必然失败而且默认超时时间很长看起来就像死循环。解决方案很简单只要显式指定了模型路径就不会触发自动下载。另外还可以把show_logFalse关掉日志减少干扰。6.4 中文乱码和字体文件缺失OCR识别出来的文字是正常的但程序打印到终端或写进日志时出现乱码这通常是终端编码问题。在Linux环境下Python的输出编码跟随系统语言环境如果系统locale不是UTF-8就会乱码。可以设置环境变量强制UTF-8输出export PYTHONIOENCODINGutf-8 export LANGzh_CN.UTF-8如果程序中需要生成图片并在图片上绘制中文比如在检测框上标注识别结果则需要系统中安装中文字体。麒麟系统默认可能没有中文字体需要手动安装sudo apt install -y fonts-noto-cjk或者安装文泉驿微米黑sudo apt install -y fonts-wqy-microhei这个坑通常出现在后续做结果可视化的时候代码本身没问题但画出来的图全是方框多半就是字体没装。6.5 GPU明明可用但PaddleOCR跑的还是CPU在麒麟系统上即使机器有NVIDIA GPU也不代表PaddlePaddle能直接用。先检查驱动是否装好nvidia-smi如果这个命令报错说明驱动没装好PaddlePaddle自然用不了GPU。如果驱动正常再确认安装的是paddlepaddle-gpu而不是paddlepaddle。还有一个容易忽略的点PaddleOCR在初始化时默认使用GPU但如果你传入了某些CPU相关的参数比如enable_mkldnnTrue那么它会自动降级为CPU推理。所以如果确定要用GPU就不要开MKLDNN这两个是互斥的。6.6 离线安装时的权限和路径问题麒麟系统的用户目录权限管理比较严格安装到系统目录时经常遇到权限不足。建议统一使用虚拟环境避免污染系统Python也方便后续迁移python3 -m venv /opt/ocr_env source /opt/ocr_env/bin/activate在虚拟环境里安装所有依赖之后整个/opt/ocr_env目录可以被整体打包拷到其他机器。这也是离线部署里比较优雅的方案依赖都在虚拟环境里不用反复处理系统级冲突。6.7 模型文件拷贝后格式损坏有段时间我遇到一个诡异问题模型文件在开发机上跑正常拷到麒麟服务器后PaddleOCR报参数文件解析错误。后来排查发现是拷贝过程中使用FTP的文本模式传输导致二进制文件被转换了。模型文件是二进制文件必须使用二进制模式传输FTP也好、U盘拷贝也好都要确保字节一致。一个快速校验方法是计算文件的MD5md5sum inference.pdmodel拷贝前后对比一下校验值如果一致就说明文件没问题。写在最后的一些体会整套方案跑通之后再回头看最大的感受是在麒麟系统这种国产化环境里做AI应用部署真正的难点从来不在模型本身而在于基础环境的兼容性。Python版本、glibc版本、wheel包的平台标签、系统底层库的缺失这些看起来不起眼的小问题每一个都可能让你卡上一整天。我自己总结出一套比较实用的流程基本可以复用到类似的离线部署场景先花时间把系统底细和CPU架构摸清楚再在可联网的机器上提前准备好所有依赖包和模型文件然后按“基础环境、PaddlePaddle、PaddleOCR、模型验证、性能优化、服务化封装”的顺序逐层推进。每一步都先用最简脚本验证确认通过后再进入下一步不要一口气把所有东西都装完再调试那样出了问题很难定位。PaddleOCR本身的文档已经相当完善但在麒麟系统这种非标准环境下还是有不少需要自己摸索的地方。希望这篇文章能帮你少走一些弯路。如果你在部署过程中遇到了我这里没提到的问题也欢迎多交流国产化环境下大家互相补位路才会越走越宽。
返回列表