PaddleOCR 模型裁剪实战:基于 PaddleSlim FPGM 剪枝的检测模型压缩与部署全流程
PaddleOCR 模型裁剪实战:基于 PaddleSlim FPGM 剪枝的检测模型压缩与部署全流程
模型裁剪(Pruning)是模型压缩的重要手段:复杂模型在任务上表现更好,但往往存在冗余,通过移除网络中冗余的"子模型"(卷积滤波器),可以在几乎不损失精度的前提下显著降低计算复杂度、提升推理性能。本文以 PaddleOCR 仓库中的 deploy/slim/prune 目录为核心,完整讲解如何使用飞桨模型压缩库 PaddleSlim 对 OCR 模型(以 DB 检测模型为例)进行敏感度分析、裁剪训练、推理模型导出与部署。读完本文,你将掌握"安装 PaddleSlim → 准备预训练模型 → 敏感度分析 → 裁剪微调 → 导出部署"的一整套可复现实操方案,并理解脚本背后的剪枝原理与参数决策逻辑。
一、模型裁剪的基本原理与 PaddleSlim
为什么要裁剪? 更复杂的模型通常能取得更好的精度,但同时也会带来参数冗余。模型裁剪通过移除神经网络模型中的子模型(例如卷积层中不重要的滤波器/卷积核)来减少这种冗余,从而降低模型计算复杂度、提高模型推理性能——这在移动端、嵌入式等资源受限场景下尤为关键。
用什么工具裁剪? 本教程基于 PaddleSlim。PaddleSlim 是飞桨官方开源的模型压缩库,集成了模型剪枝(Pruning)、量化(包括量化训练与离线量化)、知识蒸馏和神经网络搜索等多种业界常用且领先的模型压缩功能。PaddleOCR 仓库中的裁剪示例正是通过 PaddleSlim 提供的剪枝 API 来压缩 OCR 模型。
在开始本教程之前,建议先了解以下前置知识:
- PaddleOCR 模型的训练方法:理解配置文件结构、训练命令与 checkpoint/pretrained_model 的区别,这是理解裁剪脚本的前提;
- 模型剪枝的基本概念:滤波器(filter)剪枝原理、敏感度(sensitivity)分析的含义。
二、快速开始:裁剪的四大步骤
PaddleOCR 模型裁剪的整体流程分为四步:
- 安装 PaddleSlim;
- 准备训练好的模型(加载预训练模型);
- 敏感度分析 + 裁剪训练:先分析各网络层的冗余度,决定每层的裁剪比例,再执行裁剪并微调训练恢复精度;
- 导出模型、预测部署:将裁剪后的模型导出为 inference model,用于 Python / C++ 端推理。
下文逐一展开。
1. 安装 PaddleSlim
在 PaddleOCR 仓库根目录下,通过源码方式安装 PaddleSlim(develop 分支):
git clone https://github.com/PaddlePaddle/PaddleSlim.git
cd PaddleSlim
git checkout develop
python3 setup.py install
安装完成后,脚本中 from paddleslim.dygraph import FPGMFilterPruner 即可正常导入。
2. 准备预训练模型
模型裁剪需要加载事先训练好的模型作为起点。PaddleOCR 提供了一系列官方预训练模型,开发者可以根据需求自行选择,也可以使用自己训练好的模型。
从实现上看,预训练模型的加载由 ppocr/utils/save_load.py 中的 load_model(config, model, optimizer) 完成:脚本会优先读取配置中 Global.checkpoints 指定的断点(.pdparams 文件)恢复训练状态(含 optimizer 状态),否则读取 Global.pretrained_model 指定的预训练权重加载模型参数。因此运行裁剪命令时,通过 -o Global.pretrained_model=... 覆盖配置项即可指定要裁剪的模型。
3. 敏感度分析:理解每个网络层的冗余度
敏感度分析的含义:加载预训练模型后,对模型中的每一个网络层逐一进行敏感度分析——即对某一层按不同比例(如 10%、20%、30%…)裁剪卷积核,观察模型在验证集上精度的下降幅度,从而量化"这一层有多冗余、能裁多少"。分析结果保存在敏感度文件 sen.pickle 中,之后可以通过 PaddleSlim 提供的方法加载该文件,根据允许的精度损失自动确定每个网络层的裁剪比例。
敏感度文件的数据格式如下:
sen.pickle(Dict){
'layer_weight_name_0': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
'layer_weight_name_1': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
}
示例(来自仓库文档):
{
'conv10_expand_weights': {0.1: 0.006509952684312718, 0.2: 0.01827734339798862, 0.3: 0.014528405644659832, 0.6: 0.06536008804270439, 0.8: 0.11798612250664964, 0.7: 0.12391408417493704, 0.4: 0.030615754498018757, 0.5: 0.047105205602406594}
'conv10_linear_weights': {0.1: 0.05113190831455035, 0.2: 0.07705573833558801, 0.3: 0.12096721757739311, 0.6: 0.5135061352930738, 0.8: 0.7908166677143281, 0.7: 0.7272187676899062, 0.4: 0.1819252083008504, 0.5: 0.3728054727792405}
}
如何解读这份数据? 加载敏感度文件后会返回一个字典:外层字典的 key 是网络中各层参数的名字(layer_weight_name),value 是内层字典,记录了该网络层在不同裁剪比例(pruning_ratio,取值为 0.1~0.9 的浮点数)下的精度损失(acc_loss)。以示例中的 conv10_expand_weights 为例:当该层被裁掉 10% 的卷积核后,模型性能相较原模型下降约 0.65%(0.1: 0.006509952684312718);而 conv10_linear_weights 在同样 10% 裁剪比例下精度损失约 5.1%,说明该层对裁剪更敏感、冗余度更低。对比各层数据即可直观判断"哪层冗余多可多裁、哪层敏感需少裁"。
运行敏感度分析命令:进入 PaddleOCR 根目录,执行:
python3 deploy/slim/prune/sensitivity_anal.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model="your trained model" Global.save_model_dir=./output/prune_model/
其中:
-c指定模型配置文件,示例使用 DB 检测模型配置 configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml;-o Global.pretrained_model="your trained model"指定要裁剪的预训练模型路径(替换为实际模型);Global.save_model_dir=./output/prune_model/指定裁剪训练后模型的保存目录。
4. 导出推理模型并部署
裁剪训练完成后,将裁剪后的模型导出为可用于部署的 inference model:
python3 deploy/slim/prune/export_prune_model.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model=./output/det_db/best_accuracy Global.save_inference_dir=./prune/prune_inference_model
其中 Global.pretrained_model 指向裁剪训练得到的最佳模型(best_accuracy),Global.save_inference_dir 指定推理模型的导出目录。导出后的 inference model 预测与部署请参考:
三、源码级解析:敏感度分析脚本 sensitivity_anal.py
deploy/slim/prune/sensitivity_anal.py 是整个裁剪流程的核心脚本,其关键实现逻辑如下:
1. 可剪枝参数筛选:get_pruned_params() 定义了哪些参数参与剪枝——要求参数 shape 为 4 维(即卷积核权重),并排除 depthwise(深度可分离卷积)、transpose(转置卷积)、conv2d_56 与 conv2d_57 等特殊层。这保证了剪枝操作只作用于常规卷积滤波器。
2. 模型构建与 FLOPs 统计:脚本通过 build_model 按配置构建网络,并依据 Architecture.model_type 决定统计 FLOPs 的输入尺寸(det 模型为 [1, 3, 640, 640],rec 模型为 [1, 3, 32, 320]),在剪枝前后分别调用 paddle.flops 输出 FLOPs 对比,直观展示裁剪带来的计算量下降。
3. 剪枝器初始化:脚本实例化 FPGMFilterPruner(Filter Pruning via Geometric Median,基于几何中位数的滤波器剪枝算法),并将模型置于 train() 模式。FPGM 的核心思想是:几何中位数附近的滤波器与其他滤波器高度相似、信息冗余,删除它们不会显著影响特征表达能力。
4. 敏感度分析开关:脚本中定义了 run_sensitive_analysis 标志,两种模式:
run_sensitive_analysis=True(自动分析模式):调用pruner.sensitive(eval_func=eval_fn, sen_file="./deploy/slim/prune/sen.pickle", skip_vars=[...]),在验证集上自动计算每个可剪枝卷积层在不同裁剪比例下的精度损失,结果写入sen.pickle;随后调用pruner._get_ratios_by_loss(params_sensitive, loss=0.02),根据"允许精度损失不超过 0.02(即 2%)"的约束,自动为每个网络层计算合适的裁剪比例并输出日志。run_sensitive_analysis=False(固定比例模式):跳过transpose、linear类参数后,将所有可剪枝参数统一设置为 0.1(即每层裁剪 10%)。裁剪比例越大,被裁掉的卷积权重越多,计算量下降越明显,但精度损失风险也越高。
5. 执行裁剪与微调训练:pruner.prune_vars(params_sensitive, <a href="https://link.gitcode.com/i/0c4bb68262feb471e607bf7bb3975674" target="_blank">0]) 依据各层裁剪比例生成裁剪计划并实际执行剪枝;随后调用 [tools/program.py 中的 program.train(...) 启动裁剪后的微调(fine-tune)训练,通过训练让模型恢复因裁剪损失的精度。eval_fn 中根据 model_type 选择评估指标:det 模型使用 hmean(检测任务综合指标),rec 模型使用 acc(识别准确率)。
四、配置文件与关键参数说明
示例命令中的配置文件 configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml 是一个完整的 DB 文本检测模型训练配置,其关键段落与裁剪流程的关联如下:
| 配置段 | 关键参数 | 作用与裁剪关联 |
|---|---|---|
Global |
epoch_num、save_model_dir、pretrained_model、save_inference_dir、eval_batch_step |
训练轮数、模型保存目录、预训练权重路径、推理模型导出目录等全局控制项 |
Architecture |
model_type: det、algorithm: DB、Backbone: MobileNetV3(scale: 0.5)、Neck: DBFPN(out_channels: 96)、Head: DBHead(k: 50) |
网络结构定义。model_type 决定敏感度分析输入尺寸与评估指标 |
Loss |
name: DBLoss、main_loss_type: DiceLoss |
训练损失,裁剪微调阶段沿用 |
Optimizer |
name: Adam、lr: Cosine(learning_rate: 0.001) |
优化器与学习率调度,裁剪微调训练使用 |
PostProcess |
name: DBPostProcess、thresh: 0.3、box_thresh: 0.6、unclip_ratio: 1.5 |
检测后处理参数,推理阶段同样生效 |
Metric |
name: DetMetric、main_indicator: hmean |
评估指标,敏感度分析中的 eval_fn 返回 hmean 作为精度度量 |
Train / Eval |
SimpleDataSet 数据集与 loader 配置 |
裁剪微调训练与敏感度分析验证所需的数据 |
裁剪实际执行的参数在配置之上通过命令行 -o 覆盖注入(如 Global.pretrained_model、Global.save_model_dir),无需改动配置文件本身。
五、源码级解析:推理模型导出脚本 export_prune_model.py
deploy/slim/prune/export_prune_model.py 负责把裁剪后的模型导出为推理模型,其流程为:
- 重建模型并执行敏感度分析:构建与训练一致的网络与验证集 dataloader,调用
pruner.sensitive(eval_func=eval_fn, sen_file="./sen.pickle", skip_vars=[...])生成敏感度文件,再用_get_ratios_by_loss(params_sensitive, loss=0.02)按 2% 精度损失预算计算各层裁剪比例,最后pruner.prune_vars(...)执行裁剪——即导出脚本会重新执行一次剪枝计划,与训练时保持一致的裁剪结构; - 加载裁剪后的模型权重:通过
load_model(config, model)加载裁剪微调训练保存的best_accuracy权重(即Global.pretrained_model指向的模型); - 评估验证:调用
program.eval在验证集上评估裁剪模型的最终指标(det 看hmean,rec 看acc),便于确认裁剪后精度; - 动转静导出:使用
paddle.jit.to_static将动态图模型转换为静态图,并通过paddle.jit.save保存到Global.save_inference_dir/inference。这里有几个值得注意的细节:- 检测模型的导出输入 shape 为
[3, -1, -1](动态宽高); - 识别模型的输入 shape 为
[3, 32, -1],高度必须固定为 32; - 若网络包含 TPS(薄板样条变换)模块,则不支持变长输入,导出时输入宽度会被固定为训练时的尺寸(代码中固定为 100)。
- 检测模型的导出输入 shape 为
导出完成后,save_inference_dir 目录下将生成 inference.pdmodel、inference.pdiparams 等推理模型文件,可直接对接 PaddleOCR 的Python 推理或 C++ 推理流程。
六、通过 TIPC 自动化验证裁剪流程
在 PaddleOCR 的测试体系中,裁剪流程已被集成进 TIPC(Test Infrastructure and Pipeline for CI)自动化测试。以 test_tipc/configs/ch_ppocr_mobile_v2_0_det_FPGM/train_infer_python.txt 为例:
fpgm_train:deploy/slim/prune/sensitivity_anal.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model=./pretrain_models/det_mv3_db_v2.0_train/best_accuracy
fpgm_export:deploy/slim/prune/export_prune_model.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o
该配置将 FPGM 剪枝的训练(fpgm_train)与导出(fpgm_export)环节纳入 CI 测试,配合 inference:tools/infer/predict_det.py 完成裁剪模型的推理验证。这说明裁剪流程在仓库中被视为一条标准、可回归测试的模型压缩流水线,开发者可以参照该配置自行复现与扩展。
七、注意事项与总结
- 预训练模型是前提:裁剪必须加载一个已收敛的、精度达标的模型作为起点,否则敏感度分析结果不可靠;
- 敏感度分析成本较高:自动分析模式需要在验证集上对每个卷积层逐一按多档比例裁剪并评估,耗时较长;若只做快速验证,可将脚本中
run_sensitive_analysis设为False使用固定 10% 裁剪比例; - 裁剪比例与精度的权衡:
_get_ratios_by_loss(..., loss=0.02)中的loss=0.02即允许的精度损失预算(约 2%),预算越小、每层裁剪比例越保守;敏感度示例中 10% 裁剪仅损失 0.65% 精度的层属于低敏感度层,可承受更高裁剪比例; - 裁剪后必须微调:剪枝会破坏模型精度,
sensitivity_anal.py在prune_vars之后调用program.train进行微调训练以恢复精度,这是裁剪流程不可或缺的一环; - 部署阶段:导出后的 inference model 在 Python 端可通过
tools/infer/predict_det.py等工具进行预测,在资源受限场景(移动端、嵌入式)可进一步叠加量化压缩等方案获得更极致的推理加速。
综上所述,PaddleOCR 的模型裁剪方案以 PaddleSlim 的 FPGM 滤波器剪枝为核心,通过"敏感度分析确定各层裁剪比例 → 执行剪枝 → 微调恢复精度 → 动转静导出"的完整链路,为 OCR 检测/识别模型在保持精度的前提下降低计算量、提升推理性能提供了开箱即用的工具与可复现的实操路径。