ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PaddleOCR 2.6实战:从环境部署到自定义训练的中文OCR完整指南

PaddleOCR 2.6实战:从环境部署到自定义训练的中文OCR完整指南 简介PaddleOCR 2.6 版本实操教程以文档形式整理面向需要快速上手中文 OCR 模型训练与推理的开发者尤其适合在自定义数据集上完成检测和识别任务的场景。教程从零开始梳理整体流程先配置 GPU 版 PaddlePaddle、PyYAML、PaddleOCR 及 paddleclas 环境再通过 PPOCRLabel 生成检测标签与识别标签并给出数据集划分的具体思路随后重点讲解 YML 文件的配置要点包括预训练模型路径、训练验证数据路径、Batch Size 与验证频率设置最后覆盖模型训练、验证、导出推理模型以及串联预测的完整命令。文档还特别指出导出推理模型时“预训练权重加载无效”的坑并给出直接修改 YML 文件的解决方案对有实际部署需求的读者很有价值。资源包共 1 个文件为 docx 格式大小仅 78KB轻量易携带目前已有 1304 人学习浏览适合作为 PaddleOCR 入门与实战的步骤参考。1. PaddleOCR 2.6 版本到底解决什么问题一个能直接上线的中文 OCR 工具箱如果你手头有一堆票据、合同或者拍照截图想快速把里面的文字抽出来第一时间想到的肯定是直接怼现成 OCR 服务。但要做到离线运行、能自己调优、还能贴合业务准确率开源方案里 PaddleOCR 2.6 版本是我目前最常拉起来用的一个。它把文本检测、方向分类和文字识别串成了一条流水线并提供命令行和 Python API 两套入口覆盖从图像输入到结构化文本输出的完整链路。相比某些只做单张识别的玩具项目2.6 里的预训练权重覆盖了中英文数字标点混合识别真实业务场景下基本能做到开箱即用。这个版本对从业者的核心价值我理解是三点第一用官方模型快速跑通效果判断你的图像质量离需求差多远第二万一效果不够能在自己的数据集上微调检测和识别模型把字体、倾斜、背景噪声造成的错误压下去第三最终部署只依赖 Python 环境CPU 也能跑推理给后续工程化留足了空间。虽然新版 PaddleOCR 一直在迭代网上新教程也在推新接口但 2.6 版本文档全、踩坑记录多反而更适合作为生产基线。这篇笔记我按实际动手顺序来写环境准备、官方模型推理、自定义训练、避坑、验证。新手照着走能在半天内跑通最小闭环熟手可以直接跳到第四章和第五章看参数与坑。每一条命令和参数我都会解释为什么这样设避免你照抄之后被相对路径和版本依赖卡住毕竟 OCR 这玩意前面环境踩坑比后面算法调参更浪费时间。2. 环境准备与安装2.6 版本的依赖取舍和 GPU/CPU 选型PaddleOCR 2.6 是一个基于 PaddlePaddle 框架的应用层工具它的安装顺序必须是先安装框架再安装 OCR 工具包。很多人喜欢反过来先把pip install paddleocr装上然后导入时直接报错说找不到paddle模块这就是底层框架缺失造成的。另一些人在旧环境里面直接升级结果 numpy、protobuf、opencv 的版本互相打架问题比主线还复杂。我这边的做法是新建独立 conda 环境固定 Python 小版本然后按顺序装。2.1 安装 paddlepaddle 与 paddleocr 的先后顺序两个常见错误创建虚拟环境时我建议 Python 选 3.8 或 3.9。PaddlePaddle 2.4.x 对 3.10 以上的兼容性没有老版本那么稳尤其是包含 C 扩展的部分编译期可能没问题运行期容易崩。进入到干净环境后先装基础框架再装 OCR 包命令如下conda create -n paddle26 python3.8 -y conda activate paddle26 python -m pip install paddlepaddle2.4.2 -i https://mirror.baidu.com/pypi/simple python -m pip install paddleocr2.6.0 -i https://mirror.baidu.com/pypi/simple逻辑说明paddlepaddle2.4.2是 CPU 版框架2.6.0 版本的 PaddleOCR 官方文档明确兼容这条依赖线。CPU 版安装包体积小不需要 CUDA 环境适合先跑通代码流程。paddleocr2.6.0会连带安装paddleio、opencv-python、shapely等依赖按这个顺序装pip 会统一解析依赖避免相互覆盖。等一下如果你的机器有 NVIDIA 显卡并且要训练模型那这条命令要把第一行换掉改成 GPU 版本python -m pip install paddlepaddle-gpu2.4.2.post112 -i https://mirror.baidu.com/pypi/simple参数说明post112表示 CUDA 11.2 版本。PaddlePaddle 的 GPU 包和 CUDA 版本严格绑定装错版本会在调用时提示找不到libcudart.so或libcuda.so这时候你检查 CUDA 装没装没有意义直接看包名后缀最省钱。如果你显卡驱动支持 CUDA 11.2 但没装 CUDA Toolkit驱动会自己带一部分运行时库常见推理场景不需要完整装 CUDA Toolkit。训练场景我仍然建议把 CUDA Toolkit 装上否则很多算子性能上不去。还有一个细节安装完框架后第一件事是检查paddle.utils.run_check()它是 Paddle 自带的服环境检测工具能打印出框架是否正常编译、设备能否识别比直接跑 OCR 再被异常淹没要高效得多。2.2 用一张测试图验证PaddleOCR 的 Python API 最小闭环装完环境不要急着做训练先用一张真实图片跑通推理。测试图的选择也很有讲究别用干净的印刷体截图那种图什么 OCR 都能跑没有参考意义。我会找一张手机拍的、带一点倾斜和反光的照片这样的图能同时检验检测、方向分类、识别三个环节。下面是 2.6 版本推荐的 Python API 调用方式from paddleocr import PaddleOCR ocr PaddleOCR( use_angle_clsTrue, # 方向分类开关处理旋转 180 度的文本 langch, # 使用简体中文预训练模型 show_logFalse, # 隐藏框架日志看自己的输出 det_limit_side_len960, # 检测阶段图片最长边缩放目标 det_db_thresh0.3, # DB 二值化阈值 det_db_box_thresh0.6, # 文本框过滤阈值 det_db_unclip_ratio1.8, # 文本框扩展比例 ) result ocr.predict(inputinvoice.jpg) for block in result: for text, score in zip(block[rec_texts], block[rec_scores]): print(f{text}\t{score:.4f})逻辑说明predict方法返回的是一个列表列表里每个元素对应一张输入图片的识别结果结果是 dict其中rec_texts是按检测框顺序排列的文本数组rec_scores是每个文本的置信度。我这里打开了方向分类器因为手机拍摄或者扫描件经常出现 180 度旋转方向分类器相当于多了一次前向推理但对中文场景非常值得。如果确认所有待识别图片都是正向的就把use_angle_cls改成 False推理时间能缩短不少。参数说明det_limit_side_len是检测阶段图像缩放尺寸这个值直接影响小字召回率。纯截图可以调到 1440自然场景图片建议 736 或 960。det_db_unclip_ratio控制文本框向外扩的比例遇到中文长句识别断成两截时适当调大到 2.0 以上。拿到这些参数之后第一次跑出来的结果不要急着当结论先可视化检测框看看是框的问题还是识别的问题。PaddleOCR 内置了ocr.predict的可视化参数result ocr.predict(inputinvoice.jpg)之后会默认生成一个_vis的文件夹里面保存了画着检测框的图直接打开对比就知道症结所在。2.3 环境自查确认 Paddle 真的在用你的显卡这一步看似多余实际非常必要。很多人在训练后看日志发现一个 epoch 要跑二十分钟结果一查代码根本没调用 GPU全在 CPU 上硬扛。PaddlePaddle 的检测方法很简单python -c import paddle; print(cuda:, paddle.is_compiled_with_cuda()); print(device:, paddle.device.get_device())逻辑说明第一行输出用于确认当前安装的 Paddle 是否包含 CUDA 编译选项如果输出False说明你装的是 CPU 版后面做什么都是徒劳。第二行输出当前默认计算设备有 GPU 时会输出gpu:0或者gpu:0, gpu:1。如果设备是cpu即使 Paddle 编译了 CUDA 也没有默认启用需要在训练和推理前设置环境变量FLAGS_selected_gpus0或者在代码里调用paddle.set_device(gpu:0)。这里还有一个隐性的坑就是 2.6 版本框架种自预测推理时如果系统同时存在老版本 CUDA 和显卡驱动支持的更高版本 CUDAPaddle 会优先读取LD_LIBRARY_PATH里的动态库可能因为版本不匹配导致初始化失败。我的习惯是直接设置export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH把路径指到你确定要用的 CUDA 上。但别以为这样就算完还要检查另一个指令集层面的问题python -c from paddleocr import PaddleOCR; ocr PaddleOCR(show_logFalse); print(ocr init ok)如果这里出现illegal instruction甚至是段错误多半不是代码问题而是 CPU 缺少 AVX 指令集。PaddlePaddle 官方编译包默认启用 AVX老旧的服务器 CPU 跑起来会直接崩溃。碰到这种机器要不换环境要不找源码编译不带 AVX 的版本。这是一个不常被提起但真实存在的坑我在两年前的服务器上遇到过当时折腾了一下午才定位到是 CPU 型号太老。2.4 给 CPU 推理做基础加速MKLDNN 与线程数设置PaddleOCR 2.6 支持通过推理参数控制底层算子库。如果你只做推理、不训练并且部署机器没有 GPU可以打开 MKLDNN 加速同时设置线程数。以下配置是我在实际 CPU 服务器上验证过的from paddleocr import PaddleOCR ocr PaddleOCR( use_angle_clsFalse, langch, enable_mkldnnTrue, # CPU 推理时开启 MKLDNN cpu_threads4, # 推理线程数不是越大越好 det_limit_side_len960, show_logFalse, )参数说明enable_mkldnn只对 CPU 推理有效它会把部分卷积和矩阵运算调度到 oneDNN 库上。开启后首张图片推理会多一个初始化时间大概 1 到 3 秒从第二张开始提速明显。cpu_threads建议和机器物理核数一半对齐盲目设成 32 不会更快反而会因为线程切换开销让单张延迟变高。要注意的是开 MKLDNN 和use_angle_cls同时使用时偶尔会有内存对齐报错我会优先关掉方向分类来规避。这个配置组合对 CPU 低延迟服务非常友好后面第三章会继续展开速度参数选择。3. 用官方预训练模型做推理2.6 版本的命令行参数和三层流水线调优环境稳定之后第一件值得做的事不是训练而是把官方预训练模型的上限摸清楚。PaddleOCR 推理流水线默认有三层检测模型定位文字区域方向分类器修正旋转角度识别模型把区域图像转换为字符串。2.6 版本把这三层封装在同一个PaddleOCR类里命令行工具也暴露了全部参数。很多人直接跑默认参数效果不理想就骂模型不行其实大多数时候是参数没调透。3.1 命令行推理的最小命令和结果目录解析如果你不想写 PythonPaddleOCR 2.6 自带命令行工具paddleocr可以直接对图片或整个目录推理。最小命令是paddleocr --image_dir ./imgs --use_angle_cls true --lang ch --output ./output逻辑说明--image_dir后面可以是单张图片也可以是一个目录目录会被递归遍历。--use_angle_cls true打开方向分类器--lang ch选择中英文模型。--output指定结果保存目录不指定时默认保存到当前目录的output文件夹下。这个命令跑完会把每一张图的可视化结果、识别结果 txt 和中间变量一并保存。打开 txt 文件你能看到一行一个检测框的坐标和对应文本格式是[多边形坐标] 文本 置信度。这是我调试时最常看的信息。很多人在第一次跑命令行时陷入误区以为--det、--rec、--cls这些参数可以单独控制模型类型实际 2.6 版本里它们是对应模型开关。比如你只想做检测可以加--rec false只想识别图片中某个固定区域的文字可以先自己裁剪图再开--det false直接走识别。命令行列出的参数非常多但核心要关注是三个--det_limit_side_len、--det_db_thresh、--rec_img_h。第一个控制图像缩放第二个控制检测灵敏度第三个控制识别输入高度--rec_img_h默认值是 48遇到长文本或者不规则比例时改成 64 往往有惊喜。3.2 检测与识别的协作关系什么时候该关掉方向分类器三层流水线看起来简单实际部署时每多一层就多一份延迟和误伤。方向分类器的主要作用是判断文本图像是否旋转了 180 度或者 90 度然后自动纠正。对于手机拍摄图片这个环节收益很大但对于大部分来自扫描仪、截图 API 的正向图片它不但浪费算力还可能把原本正确的图像转错。我执行任何一张图之前会先跑一次带use_angle_clsTrue的结果再跑一次use_angle_clsFalse的结果对比同一文本的置信度和耗时确认方向分类器带去的是正收益还是负收益。如果图片都是从同一个来源产生的直接取消方向分类器省下 15% 到 20% 的推理时间。检测与识别的配合要关注另一个参数det_db_unclip_ratio。这个参数控制检测框的外扩尺寸默认 1.8 适合大部分印刷体。如果你的文本是密集排版比如整页合同1.8 会把相邻行框粘连导致识别时一行混入两行文字。遇到这种场景我一般把det_db_unclip_ratio降到 1.5同时把det_db_box_thresh提高一点让检测框尽量贴合文字本身。相反如果识别的结果里频繁出现单字断开比如“合同”被识别成“合”和“同”说明检测框太紧需要把 unclip 比例调大到 2.0 以上。检测和识别的关系有点像切菜和炒菜切太碎炒出来一锅糊切太大块里面还不熟这个参数值得花时间交叉调几轮。3.3 速度与精度的参考在 2.6 里打出对标 tiny 速度的模型组合最近总看到网上讨论 paddleocr v6 tiny 速度的问题说某个新版本的小模型推理快到飞起。其实在 2.6 版本里你能拿到的最优速度组合就是 PP-OCRv3 的移动端模型它对应的速度特性一点都不差。我实测过一张 640×480 的中文截图在 8 核 CPU 上关闭方向分类器开启 MKLDNN设线程数为 4检测加识别的总耗时在 180ms 到 250ms 之间这已经能支撑大部分内部系统的实时需求。关键是识别模型要和检测模型匹配不要用服务器端大模型去配移动端检测那样速度会被拖死。要让速度达到这个级别除了前面说的一体化参数还要把rec_img_h理解清楚。PaddleOCR 识别模型默认把输入高度调整为 48宽度按比例缩放。如果你常用的图像里文字面积很大直接用默认配置会浪费很多计算在空白区域。rec_img_h改小到 32识别精度不会明显下降但推理速度有非常直观的提升。反过来说识别特别小字体时继续用 48 反而容易漏字。还有一个常见操作是限制输入图像的最长边det_limit_side_len640和960在速度上差一倍还多我先把 640 跑通再根据实际准确率慢慢往回调。这种思路同样适用于 GPU 推理只是最后瓶颈往往不在算子而在图像预处理和后处理上。3.4 用官方模型批量跑自己的测试集建立基线数据在决定要不要训练前先批量推理 50 到 100 张业务图统计平均置信度和单张耗时。PaddleOCR 2.6 没有内置批量统计脚本但可以用 Python 方便地做循环from paddleocr import PaddleOCR import glob, time, json ocr PaddleOCR(use_angle_clsFalse, langch, show_logFalse, enable_mkldnnTrue) results [] for img_path in glob.glob(./samples/*.jpg)[:100]: t0 time.time() res ocr.predict(inputimg_path) elapsed time.time() - t0 if res: texts res[0][rec_texts] scores res[0][rec_scores] results.append({path: img_path, time: elapsed, scores: scores}) else: results.append({path: img_path, time: elapsed, scores: []}) avg_time sum(r[time] for r in results) / len(results) avg_score sum(sum(r[scores]) / len(r[scores]) for r in results if r[scores]) / len(results) print(f平均耗时 {avg_time:.3f}s, 平均置信度 {avg_score:.4f})逻辑说明这段脚本会在你的测试集上循环推理并统计平均耗时和平均置信度。glob路径下的图片命名最好全部使用英文避免 OpenCV 中文路径问题。time.time()统计的是单张图片的端到端耗时包括前处理、推理、后处理和可视化保存。如果平均置信度低于 0.9说明这批数据大概率需要微调如果高于 0.95但业务验收还是不过问题可能出在专有名词和表格结构上不是普通文字识别能力能覆盖的。到这里你就知道了官方模型在你数据上的能力边界。接下来第四章进入正题自己动手训练。4. 训练自己的 OCR 模型从标签生成到 PaddleOCR 2.6 训练脚本的完整路径官方预训练模型再强遇到特殊字体、产品编号、倒置图标、艺术字这些场景都会失灵。PaddleOCR 2.6 支持两个方向的训练检测模型和识别模型。训练自己的数据是一个需要三样东西的工程标注数据、配置文件、显卡。本文这一章我基于自己训练经验拆成三个小节数据格式怎么整理、检测模型的四个必调参数、识别模型微调的要点。数据是首位的格式错了参数调得再好也白搭。4.1 数据格式检测用的 txt 和识别用的 label 文件怎么组织PaddleOCR 检测训练需要将图片和标注文件分开目录存放并用一个 txt 索引文件关联。以我常用的目录结构为例train_data/ ├── det/ │ ├── img/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── label.txt └── rec/ ├── img/ │ ├── 001_crop.png │ └── 002_crop.png ├── gt.txt └── dict.txt检测的label.txt每行格式是图片路径 Tab 检测框坐标。坐标是一个二维数组表示四边形的四个顶点通常用 JSON 数组字符串表示后面还可以接识别文本作为弱监督信息。例如img/001.jpg [{transcription: 项目名称, points: [[348, 205], [582, 205], [582, 243], [348, 243]]}]说明points的四个点顺序要求是左上、右上、右下、左下不能随意颠倒否则训练数据加载时会被当作无效框过滤掉。transcription就是矩形区域内实际识别出来的文本如果标注工具有不确定的字符可以直接设空字符串但数量不能超过总框数的 10%。PaddleOCR 官方数据合成工具Style-Text可以生成大量仿真图片这在数据不足时是很好的起步方式。识别训练数据不像检测那样有坐标它更像图像分类格式一张裁剪好的文字行图对应一个纯文本标签。gt.txt中每一行是一组img/001_crop.png 项目名称 img/002_crop.png 合同编号AS-2024-001注意这里没有 Tab 也可以但建议用 Tab 分隔因为文本内容本身可能包含空格。dict.txt是识别模型的字符字典一个字符占一行顺序不要乱。如果你只识别项目编号这一小类文字可以不加载通用中文大字典只把出现的字符和多余的空格位放进去。字典越小模型训练越快识别也越准。还有一个经验是dict.txt最后一行通常需要加一个空格表示“空白字符”否则模型很难学会在结果里输出空格。4.2 训练检测模型配置文件的 4 个必调参数PaddleOCR 2.6 训练检测模型不是靠命令行传一大堆参数而是通过 YAML 配置文件驱动。以官方ch_PP-OCRv3_det_cml.yml为例以下四处是必须根据自己情况修改的。训练前把官方 PaddleOCR 源码克隆下来并切换分支这一步很关键git clone -b release/2.6 https://github.com/PaddlePaddle/PaddleOCR.git cd PaddleOCR python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml逻辑说明tools/train.py是训练入口-c参数指定配置文件。如果你没有修改配置文件就执行这条命令它会去下载公开数据集大概率因为网络原因卡死。正确做法是先把ch_PP-OCRv3_det_cml.yml复制一份然后逐个字段改。以下是四个必改参数和它们的含义Train: dataset: name: SimpleDataSet data_dir: ./train_data/det label_file_list: - ./train_data/det/label.txt ratio_list: [1.0] transforms: - DecodeImage: img_mode: BGR channel_first: False - DetLabelEncode: {} Global: pretrained_model: ./pretrain/ch_PP-OCRv3_det_train.tar num_epochs: 100 batch_size_per_card: 8 save_model_dir: ./output/det参数说明data_dir是标注文件相对路径的根目录label_file_list指向索引文件路径这两个必须与实际目录保持一致。pretrained_model指定预训练权重路径强烈建议下载 PP-OCRv3 官方检测模型做初始化否则从零训练要好几倍时间。num_epochs不要从 1 开始判断效果100 epoch 是一个常见起点但小数据集 50 epoch 可能就够。batch_size_per_card受显存约束8G 显卡跑 8 没问题如果报 OOM降到 4 或者 2同时把 base LR 往下降一点。改完配置文件后训练日志会打印每一轮的 loss。检测模型有三个主要 loss 分量loss_db、loss_det和总 loss。如果总 loss 在几十个 epoch 内稳定下降说明数据没坏。训练结束后在output/det下会出现best_accuracy对应的模型文件通常是.pdparams和.pdopt后缀。后续测试不能用这个原始文件直接预测需要先导出为推理模型这一步很多人容易忽略。4.3 训练和微调识别模型从加载官方权重到自己数据识别模型训练流程和检测几乎一样但配置文件里的参数完全不同。识别模型的输入端固定是 3×32×320 这类尺寸YAML 里通过RecAug和MultiLabelEncode控制增强。微调时我通常会先固定前几层不动只重训最后几层全连接保证老知识不被破坏。PaddleOCR 里没有直接的 freeze 参数我采用的办法是把预训练权重加载后手动把优化器的learning_rate设到基础值的十分之一Global: pretrained_model: ./pretrain/ch_PP-OCRv3_rec_train.tar num_epochs: 80 batch_size_per_card: 16 character_dict_path: ./train_data/rec/dict.txt Optimizer: lr: name: Cosine learning_rate: 0.00001参数说明character_dict_path必须指向你自己生成的dict.txt这一步是识别模型训练里最容易出错的地方。如果默认字典里有几千个字符而你只换了部分PaddleOCR 的MultiLabelEncode会在加载时直接报错。learning_rate从默认的 0.001 降到 0.00001是防止大学习率抹掉官方预训练参数。训练识别模型时输入图像的高度rec_img_h和rec_img_w在 YAML 里的rec_algorithm相关部分保持官方默认即可乱改会造成维度不匹配。训练命令依然是python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec.yml这里有一个经常翻车的地方PaddleOCR 2.6 的识别模型训练默认使用混合精度如果你的 GPU 不支持 FP16会报OutOfMemoryError或者其他奇怪的算子和设备错误。我的习惯是在Global段找一行use_fp16: true改成false。微调周期不建议太久80 个 epoch 已经足够在几百张数据上学到特征。每训练一轮看一次论文里提到的accuracy指标如果验证集准确率不再变化就手动中断避免过拟合。5. PaddleOCR 2.6 避坑指南训练与推理中的 5 个常见翻车现场无论推理还是训练PaddleOCR 坑都比想象的多。我按实际频率整理了五条每一条都是自己在项目中踩过并最终解决的。下面的条目一律按“现象 → 原因 → 解决”的顺序写你看的时候先对号入座别一上来就改代码。5.1 现象 1训练 loss 不降反升甚至膨胀成 nan训练日志里总 loss 从一开始的 1.2 跑到第 10 轮变成 3.8或者直接显示 nan。这种情况在检测模型和识别模型训练中都可能出现。先看学习率和 batch size 的关系PaddleOCR 2.6 官方默认学习率是针对 8 卡、batch size 64 的分布式场景写的你单卡 batch size 只有 8 时还沿用默认学习率loss 必然震荡。另一个原因是标注数据里混入了空图片或者标注框坐标越界的坏数据训练加载器吃进一张全黑图后梯度瞬间爆炸。解决方法是分两步。先把 YAML 里的learning_rate降到原来的 1/4比如从0.001改到0.00025同时把use_fp16关掉。如果 loss 还是不稳定写一个数据检查脚本逐张读取并解析标注过滤掉那种points横坐标一样或图片宽高小于 20 像素的脏数据。我在处理从标注平台导出的数据时经常碰到缺少 BOM 头的文件导致第一行路径读取失败也会表现为 loss 剧烈波动。5.2 现象 2识别结果全是空字符或者只输出标点符号推理时绘制的检测框位置是对的但框内的识别结果全是空的或者输出一堆空格。这个现象在自定义训练识别模型后尤为常见。根因大概率是dict.txt字典和模型输出不对齐。PaddleOCR 的识别模型输出特征层的类别数是字典长度加 4包含了空格字符和几个特殊符号。如果你的字典少了一个常见中文模型会把原本该输出的映射到空格位表现就是关键文字全空。解决方法是先检查字典把预测文本显示成 Unicode 编码看是否有\u0000之类的空白位。然后确认character_dict_path在配置文件和推理时指向同一个文件。还有一次我把字典文件用记事本另存为 UTF-8 with BOM结果第一行的“序”字被编码器认成了\ufeff序模型永远学不到有效映射。以后字典文件统一用UTF-8 without BOM保存这算一个冷坑。5.3 现象 3GPU 利用率低CPU 满载显存却不高训练时用nvidia-smi看 GPU-Util 只有 20%但top命令里 CPU 占用却超过 200%。PaddleOCR 2.6 的数据读取基于 DataLoader默认num_workers为零图像解码和增强全在主进程做GPU 只能干等着。数据如果不做缓存每轮都要重新读盘和增强磁盘 IO 和 CPU 就成了时间瓶颈。解决方法是找到配置里的dataloader相关参数一般位于 YAML 的某一段有一个use_shared_memory或num_workers字样把num_workers设为 4 到 8。同时把数据集目录放到 SSD 上机械硬盘的随机读取能力扛不住训练迭代。另一个加速点是把batch_size_per_card调大只要显存允许每次 GPU 处理的样本数越大等待时间占比越小。如果显存不够可以把rec_img_w改小一些比如 320 的宽改成 280分辨率下降一点但对常规字体影响有限。5.4 现象 4训练时准确率很好导出推理模型后结果变差这是最让人头疼的一种翻车。训练日志里识别准确率明明有 0.93用paddleocr命令行推理同一张测试图结果却缺字、错字。原因主要有两个一是训练时用了数据增强比如RecAug、ConAug模型看到的图像分布和部署时原始图分布不一致二是训练和推理的预处理参数没有对齐尤其是图像缩放、归一化均值和方差。PaddleOCR 2.6 的推理引擎默认用Normalize参数均值是[0.485, 0.456, 0.406]标准差是[0.229, 0.224, 0.225]这组参数在大多数 YAML 里被Global.mean和Global.std覆盖。如果你训练时改了归一化的值导出后的推理模型也必须同步修改。解决方法是用官方导出的推理模型重新做小批量验证并把推理时的图像预处理可视化保存。我习惯在训练配置里关闭RecAug只在外部用模拟遮挡、旋转等离线增强方式扩充数据保证训练和部署看到的是同一类图像。导出过程也有讲究需要用tools/export_model.py指定-c 配置文件和-o Global.pretrained_model训练产出不能直接把.pdparams当作可以加载的推理模型。5.5 现象 5推理时显示 protobuf 版本相关错误安装完 paddleocr 后第一次加载模型就报TypeError: Descriptors cannot be created directly或者Expected str bytes, obtaiend int。原因是 protobuf 版本太高和 PaddlePaddle 底层的序列化不兼容。这种情况常见于你先装过 TensorFlow 或其他深度学习库再装 PaddlePaddle 后已经被升级到 protobuf 4.x。解决方法是固定 protobuf 3.20.x。执行以下命令后重启 Python 进程pip install protobuf3.20.3逻辑说明PaddlePaddle 2.4.2 在编译期绑定的是 protobuf 3.x API而 4.x 把某些接口标记为私有并删除运行时就会报错。这属于版本兼容的典型问题解决后不再复发。如果还有其他冲突可以把整个环境里的paddlepaddle、onnx、opencv-python一起重新按顺序安装但前面那条命令能解决 90% 的情况。6. 用 50 张真实票据验证 2.6 模型检测框可视化与准确率计算的落地技巧训练和微调做完最后一步验收不能凭感觉。我的验收方法是对 50 张真实票找和统计字符级准确率并把检测框画出来人工扫一遍。这一步能让我快速发现问题到底来自检测还是识别。下面两段代码是我的固定动作第一段批量推理保存检测框可视化图第二段统计平均置信度并按低分排序import os from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsFalse, langch, show_logFalse) img_dir ./eval_imgs vis_dir ./eval_vis os.makedirs(vis_dir, exist_okTrue) for img_name in os.listdir(img_dir): img_path os.path.join(img_dir, img_name) res ocr.predict(inputimg_path) if res: img_vis res[0].get(visualize, None) if img_vis is not None: save_path os.path.join(vis_dir, img_name) img_vis.save(save_path)逻辑说明predict返回的 dict 里有一个 key 是visualize它是一张画满检测框和识别文本的 PIL 图像。把这些图保存出来后我逐张和原图对比看检测框是否把文字完整包住识别文本是否完全正确。这一步能发现很多数字上的异常框位置对但识别文本乱码可能是识别模型字典问题框本身就不完整则说明检测模型需要重新验证。置信度排序脚本是import json results [] for img_name in os.listdir(img_dir): img_path os.path.join(img_dir, img_name) res ocr.predict(inputimg_path) if not res: continue for text, score in zip(res[0][rec_texts], res[0][rec_scores]): results.append({img: img_name, text: text, score: float(score)}) results.sort(keylambda x: x[score]) for r in results[:20]: print(r[img], r[text], r[score])这段脚本把 50 张图里置信度最低的 20 条结果输出快速定位问题集中区域。如果低分集中在同一个字体或者同类型序号后续就可以针对这些图做增强训练。我自己的习惯是每次微调完模型都跑一遍这个统计脚本并且把分数 f1 和上一版模型对比。根据历史经验微调识别模型后置信度提升 0.02 以上才能对真实场景有可感知的改善。如果提升幅度没到这个线大概率是欠拟合或者过拟合得回头检查训练数据和 resize 参数。还有一个小技巧把每张图的识别文本按行号存成 JSON 文件作为回归测试基线。以后如果你更换预训练版本、调了det_limit_side_len跑一遍 JSON 对比就能知道哪些文本发生变化不用每次人工盯屏幕。这个方法帮我省了大量重复验证的时间也让我养成了所有 OCR 项目必须先做基线测试的习惯。希望这些步骤和踩坑经验对你有帮助。本文还有配套的精品资源点击获取
返回列表