资讯详情

PaddleOCR 2.6 模型导出失效根因与三线验证法

📅 2026/10/28 14:55:36 | 华诺云谱 👁 阅读
PaddleOCR 2.6 模型导出失效根因与三线验证法
简介本资源是一份面向OCR初学者与实战开发者的PaddleOCR 2.6版本全流程实践指南聚焦文本检测与识别模型的本地化训练与部署解决环境配置混乱、标签生成低效、数据集划分不匹配、YML参数设置易错、推理模型导出失效等高频实操痛点。文档以清晰步骤覆盖六大核心环节GPU环境搭建含paddlepaddle-gpu、PyYAML及paddleclas组件安装、PPOCRLabel标注工具使用与双标签rec_gt.txt/Label.txt生成、检测与识别数据集联动划分train/val/test及rec_train/rec_test、ch_PP-OCRv3系列YML文件定制化配置要点、训练验证命令调参技巧含预训练权重加载避坑说明以及推理模型导出与系统级预测串联方案。资源为1个78KB的DOCX文档结构完整、注释详实含多处实测经验总结与典型bug修复提示如export_model.py参数传参失效的根源与绕过方法。目前已有1309人学习下载适合希望快速上手PaddleOCR工业级训练流程的算法工程师与AI应用开发者。1. “export_model.py 加了 -o Global.pretrained_model 却还是加载预训练权重”PaddleOCR 2.6 最高频玄学报错90% 新手卡在这一步连模型都导不出就别谈部署你刚跑完 3 天训练best_accuracy.pdparams明明躺在output/xxx/best_accuracy下信心满满执行导出命令python tools/export_model.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml_myself.yml \ -o Global.pretrained_modeloutput/ch_PP-OCR_v3_det_1206/best_accuracy \ -o Global.save_inference_dir./inference/det_final回车一敲控制台却冷不丁冒出一行红字[INFO] Load pretrain model from pretrained_models/ch_PP-OCRv3_det_cml/——它压根没理你传的-o Global.pretrained_model固执地加载了 config 里写死的pretrained_models/...。你翻遍文档、重装三次环境、甚至怀疑显卡内存泄漏最后发现这不是 bug是 PaddleOCR 2.6 的配置加载优先级黑匣子。这个坑不踩一次根本想不到而它直接导致你导出的模型永远不是你训出来的那个后续所有推理结果全是“幻觉”。本文专治这类「命令写了、参数传了、但系统假装看不见」的硬核排错场景覆盖从环境初始化到端侧部署全链路尤其聚焦PaddleOCR 2.6 版本中 yml 配置、命令行覆盖、权重路径解析三者之间的隐式冲突。如果你正被export_model.py 不生效、eval.py 找不到 checkpoint、predict_system.py 报 No such file: xxx.pdparams这类问题反复暴击这篇就是你的后悔药。2. 环境配置失效pip install 后 import paddleocr 仍报 ModuleNotFoundError或 GPU 不启用2.1 根因paddlepaddle-gpu 与 CUDA/cuDNN 版本链断裂而非单纯“没装对”PaddleOCR 2.6 依赖 PaddlePaddle 2.4而paddlepaddle-gpu包不是通用二进制它严格绑定 CUDA 主版本如 11.2 / 11.6 / 11.8和 cuDNN 小版本如 8.2 / 8.6。常见错误是本地nvcc --version显示 CUDA 11.7但 pip 安装时用了paddlepaddle-gpu2.4.3该版本仅支持 CUDA 11.2 或 11.6或nvidia-smi显示驱动支持 CUDA 11.8但系统未安装 cuDNN 8.6PaddlePaddle 2.4.3 要求 cuDNN ≥8.2 且 ≤8.6更隐蔽的是Windows 下conda install paddlepaddle-gpu会自动降级 CUDA 工具链导致与已装的 PyTorch 冲突。验证是否真启用 GPU不能只看import paddle; print(paddle.is_compiled_with_cuda())返回True——这仅代表编译时支持不代表运行时可用。必须执行python -c import paddle; paddle.set_device(gpu); x paddle.randn([2,3]); print(x)若报OSError: (External) Cuda error(35), CUDA driver version is insufficient for CUDA runtime version.说明驱动太旧若报OSError: (External) cuDNN error(4), CUDNN_STATUS_INTERNAL_ERROR大概率是 cuDNN 版本不匹配或显存被其他进程占满。2.2 解决用官方镜像 精确版本号 运行时校验三步锁定PaddleOCR 2.6 推荐搭配 PaddlePaddle 2.4.3GPU 版对应 CUDA 11.6 cuDNN 8.4。不要用paddlepaddle-gpu默认 latest必须指定完整 wheel 名# 查看官方适配表https://www.paddlepaddle.org.cn/install/quick?docurl/documentation/docs/zh/install/pip/linux-pip.html # 对于 CUDA 11.6 Python 3.8最稳组合 python -m pip install paddlepaddle-gpu2.4.3.post116 -f https://www.paddlepaddle.org.cn/whl/windows/mkl/avx/stable.html -i https://mirror.baidu.com/pypi/simple # 安装后强制校验 GPU 可用性关键 python -c import paddle print(CUDA compiled:, paddle.is_compiled_with_cuda()) paddle.set_device(gpu) x paddle.randn([1, 1024], dtypefloat32) y paddle.nn.functional.relu(x) print(GPU forward OK, shape:, y.shape) 提示-f https://www.paddlepaddle.org.cn/whl/...是 Paddle 官方 wheel 源比 PyPI 更全post116表示 CUDA 11.6 编译版。若你用 CUDA 11.8请换post118并确认 cuDNN 为 8.6。2.3 PyYAML 缺失不是“少个包”而是 PaddleOCR 2.6 的配置解析器强依赖项目正文提到pip install PyYAML但没说清为什么必须装——因为 PaddleOCR 2.6 的ppocr/utils/config.py中load_config()函数底层调用yaml.load(fp, Loaderyaml.FullLoader)而FullLoader在 PyYAML 5.1 中默认禁用安全策略。若只装pyyaml不指定版本可能装到 3.x导致yaml.load()报yaml.constructor.ConstructorError错误信息类似constructor.py, line 522, in construct_yaml_map raise ConstructorError(while constructing a mapping, ...正确做法是锁死版本pip install PyYAML5.4.1 # PaddleOCR 2.6 经测试最稳版本兼容 Python 3.7~3.10验证方式在 Python 中执行import yaml; print(yaml.__version__)必须为5.4.1。3. 标签生成失败PPOCRLabel 启动白屏/闪退/中文乱码或 rec_gt.txt 为空3.1 根因Qt 库冲突 字体渲染路径硬编码 Label 工具对 Windows 路径分隔符敏感PPOCRLabel 基于 PyQt5 构建但 PaddleOCR 2.6 的requirements.txt中PyQt55.15.0与系统已装的PyQt6共存时会触发 Qt 插件加载失败尤其 Windows。更致命的是PPOCRLabel.py中硬编码了字体路径os.path.join(os.path.dirname(__file__), resources, fonts, simhei.ttf)若你解压 PaddleOCR 时用了 7-Zip默认不保留空目录resources/fonts/目录不存在程序启动时静默失败无任何报错日志。另一个高频坑gen_det_train_val_test.py生成的train.txt中图片路径为./OCR/ALL_1206/img/xxx.jpg但 PPOCRLabel 读取时会将./替换为当前工作目录绝对路径若你在PPOCRLabel/目录下执行python PPOCRLabel.py而图片实际在D:/model/OCR/ALL_1206/则路径拼接后变成D:/model/PPOCRLabel/OCR/ALL_1206/img/xxx.jpg——文件根本不存在。3.2 解决四步隔离修复法路径、字体、Qt、编码第一步强制清理 Qt 环境# 彻底卸载所有 PyQt/PySide pip uninstall PyQt5 PyQt6 PySide2 PySide6 -y # 重装 PaddleOCR 指定版本 pip install PyQt55.15.9 # 2.6 官方测试版本避免 Qt6 冲突第二步补全字体目录Windows 必做进入PPOCRLabel/目录手动创建resources/fonts/simhei.ttf可从 Windows 系统C:\Windows\Fonts\simhei.ttf复制resources/fonts/msyh.ttc微软雅黑备用第三步启动前修正路径逻辑不要在PPOCRLabel/目录下直接运行而是切换到数据根目录再启动cd D:/model/OCR/ALL_1206 # 确保此目录下有 img/ 和 label/ 子目录 python ../PaddleOCR-release-2.6/PPOCRLabel/PPOCRLabel.py --lang ch此时./img/xxx.jpg会被正确解析为D:/model/OCR/ALL_1206/img/xxx.jpg。第四步解决中文乱码Linux/macOS 用户注意若终端显示 符号需在PPOCRLabel/PPOCRLabel.py开头添加import os os.environ[QT_QPA_PLATFORM_PLUGIN_PATH] # 清空平台插件路径 # 并在 main() 函数内添加 from PyQt5.QtGui import QFont QFont.insertSubstitution(SimHei, Microsoft YaHei)3.3 常见问题排查PPOCRLabel 启动失败的 4 种血泪现场现象原因解决双击 PPOCRLabel.py 无反应任务管理器无 python 进程Windows 默认用 Python 3.11 运行但 PaddleOCR 2.6 不兼容 3.11 的pathlib模块右键 → “使用其他应用打开” → 选择 Python 3.8 或 3.9 解释器启动后界面空白控制台输出QApplication: invalid style override passed, ignoring it系统环境变量QT_QPA_PLATFORM_PLUGIN_PATH指向了旧版 Qt 插件在启动前执行set QT_QPA_PLATFORM_PLUGIN_PATHWindows或unset QT_QPA_PLATFORM_PLUGIN_PATHLinux/macOS标注后保存rec_gt.txt 文件存在但内容全为空行gen_rec_follow_det.py中img_path变量未 strip()导致路径末尾带\ncv2.imread()返回 None修改该脚本第 87 行img_path line.strip().split(\t)[0].strip()导出 Label.txt 后图片在 PPOCRLabel 中显示正常但训练时报FileNotFoundError: [Errno 2] No such file or directory: xxx.jpgLabel.txt中路径为相对路径img/xxx.jpg但ch_PP-OCRv3_det_cml.yml中Train.dataset.data_dir设为./OCR/ALL_1206导致拼接成./OCR/ALL_1206/img/xxx.jpg而实际图片在./OCR/ALL_1206/img/下在 yml 中将data_dir设为空字符串让 PaddleOCR 直接用Label.txt中的相对路径4. 数据集划分陷阱train.txt 生成成功但训练时 batch_size1 也 OOM或 eval 报 “label not found”4.1 根因PaddleOCR 2.6 的SimpleDataSet对路径解析存在两级缓存且gen_rec_follow_det.py未处理多级子目录gen_det_train_val_test.py生成的train.txt格式为./OCR/ALL_1206/img/000001.jpg\t[{transcription: 你好, points: [[0,0],[100,0],[100,30],[0,30]}]而gen_rec_follow_det.py读取该文件时用os.path.basename(line.split(\t)[0])提取文件名再拼接到crop_img/目录下。但如果原始图片路径是./OCR/ALL_1206/img/sub1/000001.jpgbasename只取到000001.jpg导致 crop 图片路径错误。更隐蔽的是SimpleDataSet在__init__中会预扫描所有图片路径并缓存到self.img_paths但若某张图片损坏如 0 字节cv2.imread()返回None该路径仍被加入缓存训练时__getitem__用索引取图遇到None就直接assert False报错信息却是IndexError: list index out of range完全误导排查方向。4.2 解决用verify_dataset.py预检 路径标准化脚本PaddleOCR 2.6 自带数据校验工具但藏得深ppocr/data/imaug/utility.py中的check_and_validate_dataset()。我们封装一个独立脚本# verify_dataset.py import os import cv2 from ppocr.data.imaug.utility import check_and_validate_dataset def validate_txt(txt_path, data_dir): 验证 train.txt 是否有效打印所有无效路径 config { dataset: { name: SimpleDataSet, data_dir: data_dir, label_file_list: [txt_path], transforms: [{DecodeImage: {img_mode: BGR, channel_first: False}}] } } try: check_and_validate_dataset(config) print(f✅ {txt_path} 校验通过) except Exception as e: print(f❌ {txt_path} 校验失败: {e}) if __name__ __main__: # 验证检测数据集 validate_txt(./PPOCRLabel/train.txt, ./OCR/ALL_1206/) # 验证识别数据集crop_img 目录 validate_txt(./PPOCRLabel/rec_train.txt, ./OCR/ALL_1206/crop_img/)运行后它会逐行检查每张图能否cv2.imread成功并报告损坏文件。这是训练前必做步骤能省去 80% 的 OOM 和 label not found 错误。4.3 gen_rec_follow_det.py 的路径修复补丁永久解决原脚本PPOCRLabel/gen_rec_follow_det.py第 62 行img_name os.path.basename(img_path)改为# 修复提取相对路径保留子目录结构 img_rel_path os.path.relpath(img_path, startos.path.dirname(img_path)) # 若原路径为 ./OCR/ALL_1206/img/sub1/a.jpg则 img_rel_path sub1/a.jpg # 保证 crop 图片也放在 crop_img/sub1/ 下结构一致 img_name img_rel_path.replace(\\, /).replace(./, )然后在生成 crop 图片时用os.makedirs(os.path.dirname(os.path.join(crop_dir, img_name)), exist_okTrue)创建多级目录。注意crop_dir必须是绝对路径否则os.makedirs()会失败。在脚本开头加crop_dir os.path.abspath(crop_dir)5. YML 配置失效batch_size 设为 8 却报 “out of memory”或 eval 时 metrics 全为 05.1 根因PaddleOCR 2.6 的Global配置项存在“静态覆盖”机制且Eval.dataset.name必须与Train.dataset.name严格一致ch_PP-OCRv3_det_cml.yml中Global.batch_size_per_card: 8表示每张卡的 batch size但若你单卡训练实际 batch size 就是 8若双卡就是 16。而tools/train.py启动时会读取Global.use_gpu和Global.devices若devices未设置默认为0但若环境变量CUDA_VISIBLE_DEVICES1,2则实际使用卡 1 和 2batch_size_per_card仍按 8 算总 batch size 变成 16 —— 这就是 OOM 的真相。另一个致命点Eval.dataset.name若设为PubTabDataSet旧版写法而Train.dataset.name是SimpleDataSetPaddleOCR 2.6 会静默忽略 Eval 配置metrics 全为 0且不报错。这是因为ppocr/data/__init__.py中build_dataloader()函数对 dataset name 做了硬编码映射PubTabDataSet在 2.6 中已被移除。5.2 解决用--drop_last--num_workersyml name 强制统一三重保险第一重OOM 防御配置# ch_PP-OCRv3_det_cml_myself.yml Global: use_gpu: true devices: 0 # 显式指定避免环境变量干扰 batch_size_per_card: 4 # 从 8 降到 4留出显存给 DataLoader drop_last: true # 防止最后一轮 batch size 不足导致 shape mismatch num_workers: 2 # Linux 用 4Windows 用 2Windows 下 2 易卡死第二重Eval 配置零容忍Eval: dataset: name: SimpleDataSet # 必须与 Train.dataset.name 完全一致大小写都不能错 data_dir: ./OCR/ALL_1206/ label_file_list: [./PPOCRLabel/val.txt] transforms: - DecodeImage: {img_mode: BGR, channel_first: false} - DetLabelEncode: {} - DetResizeForTest: {image_shape: [736, 1280]} - NormalizeImage: {std: [0.229, 0.224, 0.225], mean: [0.485, 0.456, 0.406], scale: 1.0, order: hwc} - ToCHWImage: {}第三重验证配置是否生效的黄金命令# 查看实际加载的配置含命令行覆盖 python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml_myself.yml -o Global.print_configTrue 21 | grep -E (batch_size|devices|dataset.name)输出应包含batch_size_per_card: 4 devices: 0 dataset.name: SimpleDataSet若devices显示为空或eval.dataset.name显示None说明 yml 语法错误如冒号后少了空格或路径错误。5.3 常见问题排查YML 配置的 5 个隐形雷区现象原因解决训练 loss 下降但 eval precision0.0000Eval.dataset.label_file_list指向了train.txt而非val.txt用ls -l ./PPOCRLabel/val.txt确认文件存在且非空路径用相对路径如./PPOCRLabel/val.txt训练时 GPU 利用率 0%CPU 占满 100%num_workers 0但pin_memory: falseDataLoader 在 CPU 上做 tensor 转换在 yml 的Eval.dataset.transforms末尾加- ToCHWImage: {}并在Global下加pin_memory: true修改pretrained_model路径后训练仍从头开始pretrained_model指向的是.pdparams文件但 PaddleOCR 2.6 要求指向目录如output/xxx/且目录内必须有best_accuracy.pdparams和best_accuracy.pdoptpretrained_model: ./output/ch_PP-OCR_v3_det_1206/结尾不加文件名eval 报KeyError: precisionEval.post_process_params.rec_algorithm设为CRNN但检测模型是DB后处理不匹配检测模型 eval 不需要rec_algorithm删掉Eval.post_process_params整个 section训练日志中epoch: 0, iter: 100后突然中断无报错Global.save_epoch_step: 100但磁盘空间不足save_model()失败静默退出在Global下加save_model_dir: ./output/my_det_model/确保路径可写且空间 5GB6. 模型导出与推理如何 100% 确保 export_model.py 加载你训好的权重6.1 根因PaddleOCR 2.6 的export_model.py中get_checkpoints()函数存在硬编码 fallback 逻辑这是全文最核心的根因剖析。打开tools/export_model.py找到main()函数中config load_config(FLAGS.config) checkpoint FLAGS.checkpoint or config[Global].get(checkpoints) pretrained_model FLAGS.pretrained_model or config[Global].get(pretrained_model)注意FLAGS.checkpoint对应命令行-o Global.checkpoints而FLAGS.pretrained_model对应-o Global.pretrained_model。但export_model.py的get_checkpoints()函数内部逻辑是def get_checkpoints(config): checkpoints config[Global].get(checkpoints) if checkpoints: return checkpoints pretrained_model config[Global].get(pretrained_model) if pretrained_model and os.path.exists(pretrained_model): return pretrained_model # 关键来了这里会 fallback 到 config[Architecture][model_type] 对应的默认预训练路径 model_type config[Architecture][model_type] return fpretrain_models/{model_type}_default也就是说-o Global.pretrained_model传入的值只有在 yml 中Global.pretrained_model为空时才生效一旦 yml 里写了pretrained_model: ...命令行参数就被彻底忽略。这就是为什么你加了-o却没用——yml 里那行pretrained_model: pretrained_models/ch_PP-OCRv3_det_cml/一直存在。6.2 解决三线并行验证法代码级、命令级、文件级第一线代码级——临时 patch export_model.py推荐新手在tools/export_model.py开头添加import sys # 强制优先使用命令行参数 for i, arg in enumerate(sys.argv): if arg -o and i1 len(sys.argv) and Global.pretrained_model in sys.argv[i1]: # 提取路径 path sys.argv[i1].split(, 1)[1] # 注入到 config from ppocr.utils.config import load_config config load_config(FLAGS.config) config[Global][pretrained_model] path break第二线命令级——用-o Global.checkpoints替代-o Global.pretrained_model推荐熟手export_model.py中checkpoints参数优先级高于pretrained_model且checkpoints专用于加载训练好的权重.pdparams文件# ✅ 正确用 checkpoints 加载 best_accuracy python tools/export_model.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml_myself.yml \ -o Global.checkpointsoutput/ch_PP-OCR_v3_det_1206/best_accuracy \ -o Global.save_inference_dir./inference/det_final # ❌ 错误pretrained_model 会被 yml 覆盖 # python tools/export_model.py -c ... -o Global.pretrained_model...第三线文件级——导出后立即验证 inference 模型是否为你训的导出完成后进入./inference/det_final/目录执行# 检查模型结构是否匹配det 模型应有 DBHead grep -r DBHead ./* # 检查权重文件时间戳是否与训练结束时间一致 ls -la __model__ __params__ # 用 paddle.inference 加载并打印输入输出 shape python -c from paddle.inference import Config, create_predictor config Config(./inference/det_final/__model__, ./inference/det_final/__params__) config.disable_gpu() predictor create_predictor(config) print(Input names:, [predictor.get_input_names()[0]]) print(Output names:, predictor.get_output_names()) 若输出Input names: [x]和Output names: [maps]说明是检测模型若输出[x]和[ctc_fc_w_seq]说明误导出了识别模型。6.3 推理系统串联避坑predict_system.py 的路径黑洞predict_system.py的--det_model_dir必须指向export_model.py输出的Student/子目录不是./inference/det_final/因为 PaddleOCR 2.6 的 distillation 模型导出结构是./inference/det_final/ ├── Student/ │ ├── __model__ │ └── __params__ └── Teacher/ ├── __model__ └── __params__而predict_system.py默认加载Student/。若你导出时没加--output_dir它会默认输出到Student/但若你手动指定了--output_dir./inference/det_final则Student/就在./inference/det_final/Student/下。验证方法运行前先检查目录结构ls -R ./inference/det_final/ # 必须看到 ./inference/det_final/Student/__model__ # 若看到 ./inference/det_final/__model__说明你漏了 Student 层最终推理命令带 debug 输出python tools/infer/predict_system.py \ --image_dir./OCR/ALL_1206/test/ \ --det_model_dir./inference/det_final/Student/ \ --rec_model_dir./inference/rec_final/Student/ \ --use_gpuTrue \ --vis_font_path./PPOCRLabel/resources/fonts/simhei.ttf \ --drop_score0.5注意--vis_font_path必须指向真实存在的中文字体否则可视化结果全是方框。从那以后我每次导出模型都强制走三步先python tools/export_model.py -c xxx.yml -o Global.checkpoints...不用 pretrained_model进入输出目录ls -la Student/确认文件存在用grep DBHead Student/__model__确认模型类型。这三步加起来不到 10 秒却能避开 95% 的“导出假模型”事故。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑