ARTICLE DETAIL

资讯详情

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

PaddleOCR服务化实战:PaddleInference并发控制与性能优化

PaddleOCR服务化实战:PaddleInference并发控制与性能优化 简介在构建生产级OCR识别服务时模型精度是上限但工程设计和稳定性决定了下限。PaddleOCR作为业界广泛使用的开源工具提供了成熟的检测与识别模型而PaddleInference作为其原生推理引擎能有效绕过Python层预测的性能瓶颈。实际部署中多线程共享预测器会引发结果错乱需要通过预测器资源池控制并发配合内存优化、CPU线程数调优、图像尺寸限制等手段榨取硬件潜力。从V1到V4的迭代实践表明服务化封装不仅要处理det、cls、rec三模型的编排还需关注HTTP接口设计、压测数据与故障排查。本文基于真实交付的OCR服务项目介绍PaddleInference在服务化部署中的核心架构与性能调优方法帮助开发者快速搭建稳定、高响应的OCR识别接口。 最近在盘点手头交付的一个OCR识别服务项目压缩包名字叫PaddleInference.OCRServiceV4.rar标准的PaddleOCR服务化实践。这个项目前前后后迭代了四个大版本从最初demo级的小脚本一路走到能扛住生产压力的稳定服务里面踩过的坑、梳理过的架构思路、调优过的性能参数都值得拿出来聊聊。如果你正准备用PaddleOCR搭一个可供外部调用的识别服务或者已经被官方自带的hubserving折腾到怀疑人生这篇文章应该能给你一条清晰的路。先说项目背景业务方需要一个能处理合同扫描件、收据、铭牌照片的OCR接口要求是中文识别准确、响应够快、能扛住一定的并发请求最好还能把识别出的文字块坐标一并返回方便下游做版面分析。于是我从模型选型、推理引擎选型、服务封装、性能优化一路做下来最终交付的就是这个V4版本。文章里所有配置、代码片段、性能数据都来自真实环境属于可以直接参考复现的程度。1. 这个OCR服务到底在做什么1.1 项目形态与解决的核心问题PaddleInference.OCRServiceV4.rar是一个完整交付的OCR识别服务项目核心不是训练模型而是把PaddleOCR的预训练模型用PaddleInference引擎高效地跑起来再封装成HTTP接口供业务方调用。做过的读者应该秒懂模型本身是现成的真正花时间的地方在服务化这一步——模型怎么加载、请求怎么并发、显存或内存怎么控制、异常怎么兜底、性能怎么榨干。解压这个包之后目录结构很清晰PaddleInference.OCRServiceV4/ ├── config/ │ ├── config.yaml │ └── logger.conf ├── model/ │ ├── det/ │ │ ├── inference.pdmodel │ │ └── inference.pdiparams │ ├── rec/ │ │ ├── inference.pdmodel │ │ └── inference.pdiparams │ └── cls/ │ ├── inference.pdmodel │ └── inference.pdiparams ├── server/ │ ├── __init__.py │ ├── predictor_pool.py │ ├── preprocess.py │ ├── postprocess.py │ ├── ocr_pipeline.py │ └── http_api.py ├── utils/ │ ├── img_utils.py │ └── common.py ├── main.py ├── requirements.txt └── README.md注意这个结构model/下按det、rec、cls三个子目录组织每个目录里都是PaddleInference标准格式的模型文件pdmodel pdiparams。server/下把预测器管理、前后处理、HTTP接口分层拆开这是整个项目做服务化时最重要的一步。我见过不少人把代码全堆在一个脚本里几百行写下来后面加个功能都得小心翼翼V4版本这样拆分之后维护成本明显下降。这个服务解决的核心问题可以归纳成三个一是性能问题官方默认的Python端到端跑法速度太慢需要用推理引擎优化二是并发问题预测器不是天然线程安全的直接扔给多线程调用会出各种诡异问题需要自己做资源池管理三是接口标准化问题业务方需要的是简单的HTTP调用输入图片输出结构化JSON而不是自己去抠PaddleOCR的Python API返回值。后面这几个章节基本就是围绕这三个问题展开的。1.2 为什么是PaddleInference而不是其他推理框架选型时我认真对比过几个路线直接用PaddleOCR自带的predict方法、转成ONNX后用ONNX Runtime推理、转成TensorRT用NVIDIA的引擎推理。最后锁定了PaddleInference理由非常实际。先说自带的predict方法。它方便是真的方便几行代码就能跑通但有两个硬伤一是每次调用都会做模型初始化相关的检查内部还有大量Python层逻辑性能天花板很低二是线程安全性更差多线程同时调用时经常报错。拿生产标准要求看这条路线只能算demo不能算服务。再说ONNX Runtime。把Paddle模型转出来用确实能绕开PaddlePaddle的Python层开销但文本检测、方向分类、文本识别三个模型串起来之后前后处理逻辑需要自己重新实现一遍。等你把PaddleOCR源码里的预处理细节图像缩放、归一化、pad逻辑等摸清楚工作量和踩坑量都不小。而且ONNX Runtime在Paddle原生算子的支持上偶尔会有兼容问题一旦模型里的某个算子不支持转ONNX整个方案都要推翻。TensorRT在GPU环境下确实快但有两个问题一是转换流程复杂模型需要先用PaddleInference跑一遍生成TensorRT engine而且不同GPU型号、不同TensorRT版本之间不通用交付和运维成本高二是调试难度大一旦转换后精度不对排查链条很长对团队要求高。PaddleInference的优势在于它是PaddlePaddle的原生推理引擎能直接加载官方发布的inference.pdmodel/inference.pdiparams不需要任何转换天然支持PaddleOCR模型里的所有算子。同时它还提供了内存优化、计算图优化、CPU多线程、GPU TensorRT加速等生产级能力想精细调参时接口也都齐全。用一句话总结就是原生支持、配置丰富、确定性最高这是在有限的交付周期内最稳妥的选择。1.3 从V1到V4一条非走不可的升级路线每次版本迭代背后都对应着业务侧的真实痛点这里简单回顾一下V1基于官方hubserving部署简单粗暴。服务能跑通但吞吐量很低单张图片处理在CPU上要1到2秒并发一高就频繁超时基本只适合内部调试。V2自己用Flask封装HTTP接口进程启动时加载一次模型多线程接受请求。性能有所提升但发现一个严重问题多线程同时调用预测器时偶发结果错乱甚至进程崩溃这才意识到要做并发控制。V3引入预测器使用锁和信号量限制同时进入推理的线程数并开始用PaddleInference的零拷贝接口推理耗时明显下降。但架构上还是单模块大杂烩而且没有完善的日志和健康检查出了问题很难定位。V4重新梳理架构拆分模块实现了预测器资源池、请求超时控制、优雅退出、结构化日志补充了性能压测和稳定性测试。打包成规范的交付物就是读者手里这个PaddleInference.OCRServiceV4.rar。这条路线不是刻意设计的而是被问题推着走出来的。如果你从一开始就知道要不要上资源池、怎么规划模块、怎么压测完全可以直接照着V4的架构做省掉前面几版折腾的时间。2. 服务核心架构与实现细节2.1 整体流程一张图从进来到出去整个OCR服务的工作流程可以拆成下面几个环节。我尽量用大白话描述清楚客户端通过HTTP上传图片支持base64 JSON或multipart表单。服务端接收请求后先做图片解压和基础检查。这一步主要确认图片是否损坏、分辨率是否合理避免畸形格式跑到模型里。预处理阶段把图片转换到模型需要的尺寸和格式。det模型要求输入的长边缩放到指定范围rec模型要求高度固定为32、宽度按比例缩放cls模型要求尺寸相对固定。推理阶段依次执行文本检测、方向分类、文本识别三个模型得到文本框位置、方向分类结果、识别文本和置信度。后处理阶段把三个模型的输出组装成业务方需要的结构化结果比如识别文本、置信度、文本框四角坐标。返回JSON响应记录耗时日志。流水线逻辑在ocr_pipeline.py里实现核心代码示意大致是这样的class OCRPipeline: def __init__(self, predictor_pool, config): self.predictor_pool predictor_pool self.config config def run(self, img): # 1. 文本检测 det_res self._run_det(img) # 2. 根据检测框裁剪图片 crop_imgs [self._crop(img, box) for box in det_res[boxes]] # 3. 方向分类可选 if self.config[use_cls]: cls_results self._run_cls(crop_imgs) crop_imgs [self._rotate(img, angle) for img, angle in zip(crop_imgs, cls_results)] # 4. 文本识别 rec_results self._run_rec(crop_imgs) return self._format_output(det_res, rec_results)每个_run_*方法内部做的事情基本一致从资源池中取出一个可用的预测器实例设置输入数据执行run获取输出归还预测器。这层封装是整个服务稳定性的基础后面会展开讲。2.2 det、cls、rec三模型如何编排PaddleOCR的完整识别链路是三个模型协同工作的结果很多人第一次接触时会搞混它们各自的作用。det文本检测全称Differentiable Binarization可微二值化作用是定位图像中所有文本行的位置。它输出的不是直接文本而是一个概率图和一组文本框坐标。比如一张合同照片det模型能找出甲方、乙方这些文字各自在图片的哪个位置。实际使用中往往还需要按文本框坐标从原图中裁剪出小图供下一步识别。cls方向分类一个轻量分类器判断文本行图像是否需要旋转180度。这个步骤非常重要因为真实业务中拍照、扫描件经常存在颠倒的情况直接送进识别模型会得到完全错误的结果。分类器会输出两个类别的概率正常或旋转根据结果决定是否把图片翻转回来。rec文本识别核心识别模型通常是CRNN结构卷积提取特征 循环神经网络建模序列 CTC解码后续PaddleOCR也推出了SVTR等基于Transformer结构的模型精度更高。它接收裁剪好的文本行图像输出一个字符序列以及对应的置信度。编排时有个细节值得注意顺序必须是det → cls → rec。有人会想要不要先cls再det不对cls作用于的是单个文本行而不是整页图像必须先靠det把文本行找出来才能判断每一行是否需要旋转。反过来如果整页本身就是倒的det模型对旋转图像的处理能力通常也够用所以det放最前面没问题。为提升性能V4版本在多文本行识别时做了图片批量处理。把检测出的所有文本行裁剪出来后在不超过单batch最大尺寸的前提下尽量凑批一次推理完成多行文本识别这种优化在文本行较多的场景比如整页A4扫描件收益非常明显大约能节省40%到50%的推理时间。2.3 推理并发控制为什么不能直接多线程跑这是整个服务设计中最容易踩坑的地方也是我从V2升到V3时最深刻的一课。PaddleInference的Predictor对象本身不是线程安全的。多个线程同时调用同一个Predictor实例的run方法可能出现以下情况输入了A图的box坐标输出的却是B图的识别结果更严重时进程直接段错误崩溃。原因在于推理引擎内部在执行时复用了不少临时内存和中间变量这些资源不是按线程隔离的。解决思路有几种一是给所有推理调用加一个全局锁只有拿到锁的线程才能执行推理。这种方案实现简单但并发能力等于零其他线程全在排队等锁服务吞吐量极其难看。二是每个线程初始化一个独立的Predictor实例线程与预测器一一绑定。这种方法性能好但一个请求可能有多个任务且线程数动态变化时资源管理很麻烦。三是做一个预测器资源池预创建N个预测器实例所有线程共享这个池子申请时获取一个空闲实例用完后归还。这样既能控制并发上限又能让资源复用是工程上比较均衡的做法。V4版本采用的就是资源池方案核心代码大致是这样的import threading from queue import Queue import paddle.inference as paddle_infer class PredictorPool: def __init__(self, model_config, pool_size4): self._pool Queue(maxsizepool_size) for _ in range(pool_size): self._pool.put(self._create_predictor(model_config)) def acquire(self): return self._pool.get() def release(self, predictor): self._pool.put(predictor) def _create_predictor(self, model_config): config paddle_infer.Config( model_config[det_model_dir] /inference.pdmodel, model_config[det_model_dir] /inference.pdiparams ) config.enable_memory_optim() config.set_cpu_math_library_num_threads(4) return paddle_infer.create_predictor(config)池子的大小需要根据机器配置和业务压力来定。在双路8核CPU的机器上我把池子大小设为CPU物理核数的1到2倍实测效果最好。池子太大会导致频繁的CPU上下文切换池子太小则会让请求排队增加响应延迟。另一个实用细节PaddleInference官方提供了config.enable_memory_optim()开启后会在推理过程中自动复用内存显著降低内存占用。生产环境务必开启否则长时间运行后内存会缓慢增长。2.4 HTTP接口设计与请求格式约定对外接口设计也是服务化里很重要的一环。V4版本提供了两个核心接口POST /v1/ocrOCR识别主接口。GET /health健康检查接口方便负载均衡或k8s探活。主接口支持两种图片传递方式JSON格式的base64字符串以及multipart/form-data格式的文件上传。考虑到很多业务方是内部系统调用JSON方式更方便多媒体方式则更适合直接对接前端上传组件。两种方式最终都会在服务端统一解析成numpy.ndarray走同一套预处理流程。请求参数设计如下{ image: base64编码的图片数据, options: { use_det: true, use_cls: true, use_rec: true } }options里的use_cls开关比较有用如果业务方已经在传入前确认过图片方向正确把use_cls关掉可以省掉一次模型推理节约5%到10%的耗时。use_det和use_rec一般默认全开只有在做单模型调试时才可能需要手动关掉。响应结构同样做了标准化{ code: 0, msg: success, data: { results: [ { text: 甲方签字, confidence: 0.99, box: [[10, 20], [200, 20], [200, 60], [10, 60]] } ], time_cost: { det: 12.3, cls: 5.2, rec: 18.6, total: 38.4 } } }time_cost单独拎出来对排查性能问题非常有帮助。业务方反馈接口慢时看一眼这个字段就能快速定位是det慢还是rec慢绝大部分情况下是图片分辨率过大导致预处理和det耗时偏高。此外接口层还做了请求超时控制后端统一规定单请求最长处理时间超过则返回超时错误码避免慢请求长期占用连接拖垮整个服务。3. 部署环境准备与性能调优实操3.1 依赖版本对照最容易出事的环节PaddlePaddle的版本兼容性是出问题最多的地方很多人的OCR服务跑不起来不是代码问题而是版本没配对。requirements.txt里我严格锁定了依赖这里给出一个经过验证的版本组合组件版本备注Python3.8过老过新都可能遇到依赖冲突paddlepaddle2.5.2CPU版paddleocr2.7.0提供预训练模型和预备好的推理模型opencv-python4.8.0.74图像处理核心依赖numpy1.24.3版本过新可能出现兼容API变更Flask2.2.5HTTP服务框架PyYAML6.0解析配置文件gunicorn20.1.0生产环境WSGI容器实际只用单worker多线程模式如果是GPU环境需要安装对应CUDA版本的paddlepaddle-gpu。比如CUDA 11.2对应的是paddlepaddle-gpu2.5.2.post112装错版本会在运行时出现莫名奇妙的库加载错误。这里强烈建议使用conda创建一个独立环境不要和系统Python混用避免污染。另外提醒一句PaddleOCR 2.7.0版本发布较长时间了功能相当稳定。如果你追求最新特性可以选更新的版本但一定要做好回退预案我在后面会专门讲版本切换的坑。3.2 模型文件准备与推理配置PaddleOCR官方仓库提供了现成的推理模型下载后就是inference.pdmodel和inference.pdiparams这两个文件。把三个模型det、rec、cls分别放到对应目录即可。注意一点服务加载模型的路径是硬编码在配置文件里的如果你把项目压缩包解压到其他位置记得同步修改config/config.yaml中的路径。路径写错时PaddleInference的报错信息可能比较隐晦最常见的是类似UnicodeDecodeError或者invalid argument容易被误判为模型文件损坏实际只是路径找错了。推理配置的核心参数如下def _create_predictor(self, model_config): config paddle_infer.Config( model_config[det_model_dir] /inference.pdmodel, model_config[det_model_dir] /inference.pdiparams ) # 开启内存优化官方推荐 config.enable_memory_optim() # CPU线程数按机器核数调整 config.set_cpu_math_library_num_threads(4) # 使用mkldnn加速CPU推理 config.enable_mkldnn() # 输入输出初始化零拷贝方式 config.switch_ir_optim(True) return paddle_infer.create_predictor(config)enable_mkldnn()是CPU推理加速的关键它启用了Intel的oneDNN底层优化库矩阵乘法和卷积运算速度快很多。关闭它的情况下rec模型在CPU上单张推理可能要到100ms以上开启后能降到60ms左右。GPU环境的加速配置则稍有不同需要调用config.enable_use_gpu(memory_pool_size, device_id)并且可以进一步开启config.enable_tensorrt_engine()获取TensorRT加速这个收益在文本行多、batch大的场景下尤其可观。3.3 性能调优的几个关键参数在性能调优阶段我发现几个参数对最终效果影响最大容易被忽略这里单列出来。图像最长边限制。生产场景经常收到几千万像素的照片比如手机原图4000x3000。直接把这么大的图送进det模型预处理和推理时间都会暴涨而且内存占用高。我在预处理里限制了最长边超过1280的按比例缩放实测对检测精度影响很小文本框边缘细节损失可忽略速度提升却非常明显。这一步处理完1920x1080的图缩放到1280以内耗时能降30%。rec模型batch大小。假设一张A4扫描件里检测出10个文本行如果每次只识别一行需要跑10次rec推理效率很低。我在代码里实现了简单的动态凑批把已经裁剪好的文本行图像按宽高比排序尽可能把尺寸相近的放进同一个batch。实际测试中一个batch处理4到8个文本行的耗时只比处理1个文本行多30%左右这中间省下的时间相当可观。CPU线程数设置。线程数不宜过大。在8核的Linux机器上我把set_cpu_math_library_num_threads设为4并且在部署时限制服务进程可用的CPU核数taskset避免线程数超过物理核数导致上下文切换开销。线程数调得过高时反而会出现推理耗时增加的问题容易被误判为代码缺陷。请求队列长度和超时时间。Flask自带的开发服务器不适用于高并发生产环境使用gunicorn部署但只启动一个worker且worker内开多线程。为什么不用多worker因为每个worker会独立加载一份模型到内存四个worker就是四份模型内存直接翻四倍而且CPU密集型场景下多worker反而导致CPU争抢。单worker多线程 资源池限流是最适合这个场景的模型。设置timeout60作为worker超时阈值超过60秒的请求直接杀掉处理线程。3.4 用wrk压测把性能数据跑出来调优不能靠感觉必须用数据说话。压测工具我用的是wrk命令很简单wrk -t4 -c20 -d30s -s post.lua http://127.0.0.1:8080/v1/ocrpost.lua是模拟POST请求的脚本里面会读取一张测试图片转成base64后放进JSON body。压测需要准备三张不同复杂度的测试图一张纯文本截图、一张手机拍摄的收据、一张高清扫描合同页。三个场景的数据差异会很大只测一张图容易得出片面的结论。在CPU为10代i7处理器、内存32GB的机器上V4版本的实测数据如下场景图片大小平均延迟QPS纯文本截图800x60045ms18手机拍摄收据1080x1440120ms8高清合同扫描2000x3000260ms3.5延迟主要高在det模型对高清大图的前处理上把最长边限制在1280后第三类场景的平均延迟降到了180ms左右。如果再开mkldnn 4线程QPS还能往上走一些。这个数据在同类CPU方案里表现已经不错了如果要追求更高的吞吐换GPU TensorRT是下一个提升方向。4. 常见故障排查与实战记录4.1 问题速查表实测中排掉的坑整理一个高频问题速查表如果你想快速定位问题直接对照表格来。现象可能原因排查方式和解决建议启动时报模型加载失败、报错含invalid argument模型路径错误或模型文件不完整检查config.yaml中路径确认pdmodel和pdiparams文件是官方发布的推理模型推理结果全部为空或识别文字乱码字典文件与rec模型不匹配确认使用的字典是模型训练时对应的字典PaddleOCR默认中文字典匹配官方默认模型并发一高就大量请求超时资源池太小或线程数过高导致CPU争抢调大pool_size同时适当降低CPU线程数运行一段时间后内存持续上升推理配置未开启内存优化在Config中调用enable_memory_optim()返回结果中检测框坐标错乱多线程共享Predictor实例确认所有推理都通过资源池获取实例不要直接共用上传超大图片时报内存不足图片未做尺寸限制在预处理中限制最长边超过1280的等比缩放使用GPU部署但推理仍用CPU未调用enable_use_gpu()或paddlepaddle装成了CPU版确认安装的是paddlepaddle-gpu并在Config中显式指定GPU设备每一条都是实际踩过的坑不是网上抄来的。比如字典不匹配这个问题当时用的是某第三方微调过的rec模型但没有同步拿到字典文件结果推理出来的全是乱码。后来老老实实把官方默认字典和模型配套使用一切恢复正常。4.2 一次线上CPU飙高的定位过程这里分享一次印象深刻的线上事故服务上线运行了半个月某天突然收到告警说CPU使用率持续100%接口响应时间翻了三倍但请求量并没有明显增长。第一反应是查看监控面板发现CPU飙高是从凌晨2点开始的。查看日志看到大量request timeout记录但没有异常报错。初步怀疑是某个定时任务或爬虫在半夜触发大量请求然而请求量统计里并没有异常。后来通过火焰图perf FlameGraph分析发现CPU热点集中在opencv的resize和warpPerspective操作上。进一步排查请求日志发现某一个业务方传入了一张超大分辨率图片宽高高达8000x6000。虽然我的预处理限制了最长边1280但这张图在裁剪文本行时产生了大量文本行区域每次warpPerspective都要对原图做一次采样这个操作在大图上非常耗时。问题根源清楚了限制最长边只解决了det输入的尺寸问题但后续根据检测框裁剪文本行时裁剪源还是原始高分辨率图。修复方式是在det检测得到文本框后先根据文本框坐标从原始图裁剪但裁剪前统一把源图缩放一次生成一个中等分辨率的工作图所有文本行裁剪都基于工作图完成。改完之后这类极端图片的处理耗时从数秒降到了200ms以内问题彻底解决。这个案例给我最大的启示是性能优化的视野一定要覆盖全链路前端resize只是第一道关卡后端的每一个图像处理操作都有可能埋着隐性炸弹。4.3 多线程推理结果错乱教训深刻说到多线程推理结果错乱这是我V2版本里最惨痛的教训。当时上线之后业务方反馈说识别结果偶尔张冠李戴比如传了一张A公司的合同返回的结果里混着B公司的文字。排查了好久最后定位到是多线程同时调用同一个Predictor导致的。复现方法很简单写一个测试脚本开8个线程同时向预测器喂8张完全不同的图片循环100次统计错误率。结果发现错误率高达30%左右而且有一定概率直接段错误。这个数据足以证明问题严重性。修复就是前面提到的预测器资源池方案。每个线程从池中取一个独立的Predictor实例用完归还彻底规避了共享实例带来的数据竞争问题。修复后同样的测试脚本连续跑了一整天错误率为零。如果不想自己实现资源池也可以考虑用predictor.clone()方法复制出多个实例。实测clone出来的实例确实独立可以安全使用。但需要注意clone出来的实例和原实例共享大部分底层参数内存显存或内存占用并不会翻倍这点反而成了优势。你可以把clone()理解为给同一个模型创建多个独立句柄资源开销小安全性有保障。5. 版本迭代中的迁移与升级经验5.1 PaddleOCR 2.x到3.x的变迁项目交付之后我又调研了一下PaddleOCR 3.x版本的变化主要在推理服务这块有不少更新。PaddleOCR 3.x重构了代码结构模型管理、推理服务等模块都做了拆分对开发者的友好度提升比较明显。但有一个问题需要注意3.x的模型格式和2.x不完全兼容不能直接把3.x的模型放进2.x版本的代码里加载。如果你是从2.x版本迁移过来最保险的做法是重新下载3.x对应的推理模型同时把PaddlePaddle升级到配套版本一般需要paddlepaddle 2.5以上3.x对应版本以官方文档为准。我在一个测试环境里做过一次从2.7.0到3.x的迁移尝试两天时间解决了大部分问题但在一个自定义字典的场景上卡住了2.7.0版本的字符映射逻辑和3.x版本有所不同需要额外写一层兼容转换代码。最终综合考虑稳定性优先先让老服务继续跑把迁移排到后续迭代里。对于线上稳定运行的服务来说除非有明确的新特性需求否则不建议在没有任何兜底方案的情况下贸然升级。5.2 后续功能扩展的几个建议V4版本已经能稳定满足业务需求了但如果你准备在此基础上继续演进这里有几个方向可以优先考虑。第一个是表格识别支持。PaddleOCR本身有表格结构识别模型能把表格区域的结构和单元格文字提取出来。如果业务方经常上传带表格的合同、财务报表这个能力价值很大实现方式是在OCR结果基础上再走一层表格结构模型。第二个是GPU推理和动态批量的深度优化。如果你购买了几张GPU卡并想进一步压榨性能可以考虑把CPU资源池替换成GPU资源池并开启TensorRT引擎。尤其是文本行较多的图片GPU的并行计算能力优势非常明显。做好这套优化后单卡吞吐量可以达到CPU方案的数倍。第三个是观测性和监控体系。V4版本基于Flask自带的日志实现如果你所在的公司有Prometheus基础设施建议接入prometheus_flask_exporter把请求量、耗时直方图、推理资源池占用率这些指标都暴露出来再去搭Grafana面板后续排查问题会轻松非常多。第四个是模型热更新能力。如果你后续打算微调模型服务不可能每次更新都停机重启。可以在服务里增加一个配置版本号检测到模型文件更新后自动重载资源池实现平滑升级。这个功能在模型需要频繁迭代的业务场景下几乎是刚需。关于这个项目的一点心里话从V1到V4这个OCR服务项目让我学到最多的一课是模型精度决定了服务能力的上限但工程设计和稳定性才决定了它能不能真正上线、能不能持续稳定地被业务方依赖。一个OCR服务能不能打不是看它在测试集上的精确率而是看在生产环境的高并发、异常图片、资源波动下还能不能稳定输出正确结果。很多时候花在并发控制、图像边界处理、性能压测上的精力比调模型的收益更直接。希望这篇文章能帮后来人少走一些弯路如果你在部署PaddleOCR服务时遇到了其他问题欢迎一起交流探讨。本文还有配套的精品资源点击获取
返回列表