>运行该示例前请安装Paddle1.6或更高版本 # 检测模型量化压缩示例 ## 概述 该示例使用PaddleSlim提供的[量化压缩策略](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#1-quantization-aware-training%E9%87%8F%E5%8C%96%E4%BB%8B%E7%BB%8D)对检测模型进行压缩。 在阅读该示例前,建议您先了解以下内容: - [检测模型的常规训练方法](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection) - [PaddleSlim使用文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md) ## 配置文件说明 关于配置文件如何编写您可以参考: - [PaddleSlim配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#122-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8) - [量化策略配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#21-%E9%87%8F%E5%8C%96%E8%AE%AD%E7%BB%83) 其中save_out_nodes需要得到检测结果的Variable的名称,下面介绍如何确定save_out_nodes的参数 以MobileNet V1为例,可在compress.py中构建好网络之后,直接打印Variable得到Variable的名称信息。 代码示例: ``` eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, extra_keys) # print(eval_values) ``` 根据运行结果可看到Variable的名字为:`multiclass_nms_0.tmp_0`。 ## 训练 根据 [PaddleCV/PaddleDetection/tools/train.py](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/tools/train.py) 编写压缩脚本compress.py。 在该脚本中定义了Compressor对象,用于执行压缩任务。 通过`python compress.py --help`查看可配置参数,简述如下: - config: 检测库的配置,其中配置了训练超参数、数据集信息等。 - slim_file: PaddleSlim的配置文件,参见[配置文件说明](#配置文件说明)。 您可以通过运行以下命令运行该示例。 step1: 设置gpu卡 ``` export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 ``` step2: 开始训练 使用PaddleDetection提供的配置文件用8卡进行训练: ``` python compress.py \ -s yolov3_mobilenet_v1_slim.yaml \ -c ../../configs/yolov3_mobilenet_v1_voc.yml \ -d "../../dataset/voc" \ -o max_iters=258 \ LearningRate.base_lr=0.0001 \ LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [258, 516]}]" \ pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ YoloTrainFeed.batch_size=64 ``` >通过命令行覆盖设置max_iters选项,因为PaddleDetection中训练是以`batch`为单位迭代的,并没有涉及`epoch`的概念,但是PaddleSlim需要知道当前训练进行到第几个`epoch`, 所以需要将`max_iters`设置为一个`epoch`内的`batch`的数量。 如果要调整训练卡数,需要调整配置文件`yolov3_mobilenet_v1_voc.yml`中的以下参数: - **max_iters:** 一个`epoch`中batch的数量,需要设置为`total_num / batch_size`, 其中`total_num`为训练样本总数量,`batch_size`为多卡上总的batch size. - **YoloTrainFeed.batch_size:** 当使用DataLoader时,表示单张卡上的batch size; 当使用普通reader时,则表示多卡上的总的batch_size。batch_size受限于显存大小。 - **LeaningRate.base_lr:** 根据多卡的总`batch_size`调整`base_lr`,两者大小正相关,可以简单的按比例进行调整。 - **LearningRate.schedulers.PiecewiseDecay.milestones:** 请根据batch size的变化对其调整。 - **LearningRate.schedulers.PiecewiseDecay.LinearWarmup.steps:** 请根据batch size的变化对其进行调整。 以下为4卡训练示例,通过命令行覆盖`yolov3_mobilenet_v1_voc.yml`中的参数: ``` python compress.py \ -s yolov3_mobilenet_v1_slim.yaml \ -c ../../configs/yolov3_mobilenet_v1_voc.yml \ -d "../../dataset/voc" \ -o max_iters=258 \ LearningRate.base_lr=0.0001 \ LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [258, 516]}]" \ pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ YoloTrainFeed.batch_size=64 ``` 以下为2卡训练示例,受显存所制,单卡`batch_size`不变, 总`batch_size`减小,`base_lr`减小,一个epoch内batch数量增加,同时需要调整学习率相关参数,如下: ``` python compress.py \ -s yolov3_mobilenet_v1_slim.yaml \ -c ../../configs/yolov3_mobilenet_v1_voc.yml \ -d "../../dataset/voc" \ -o max_iters=516 \ LearningRate.base_lr=0.00005 \ LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [516, 1012]}]" \ pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ YoloTrainFeed.batch_size=32 ``` 通过`python compress.py --help`查看可配置参数。 通过`python ../../tools/configure.py ${option_name} help`查看如何通过命令行覆盖配置文件`yolov3_mobilenet_v1_voc.yml`中的参数。 ### 训练时的模型结构 这部分介绍来源于[量化low-level API介绍](https://github.com/PaddlePaddle/models/tree/develop/PaddleSlim/quant_low_level_api#1-%E9%87%8F%E5%8C%96%E8%AE%AD%E7%BB%83low-level-apis%E4%BB%8B%E7%BB%8D)。 PaddlePaddle框架中和量化相关的IrPass, 分别有QuantizationTransformPass、QuantizationFreezePass、ConvertToInt8Pass。在训练时,对网络应用了QuantizationTransformPass,作用是在网络中的conv2d、depthwise_conv2d、mul等算子的各个输入前插入连续的量化op和反量化op,并改变相应反向算子的某些输入。示例图如下:
图1:应用QuantizationTransformPass后的结果
图2:应用QuantizationFreezePass后的结果
图3:应用ConvertToInt8Pass后的结果