diff --git a/slim/quantization/README.md b/slim/quantization/README.md index 5c53d275830d0244bfc4a281ed3052a24d3955a8..b044778e1f7db1eb989b39b69b1016cf7f82bb2b 100644 --- a/slim/quantization/README.md +++ b/slim/quantization/README.md @@ -37,7 +37,23 @@ - config: 检测库的配置,其中配置了训练超参数、数据集信息等。 - slim_file: PaddleSlim的配置文件,参见[配置文件说明](#配置文件说明)。 -您可以通过运行脚本`run.sh`运行该示例,请确保已正确下载[pretrained model](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#%E5%B7%B2%E5%8F%91%E5%B8%83%E6%A8%A1%E5%9E%8B%E5%8F%8A%E5%85%B6%E6%80%A7%E8%83%BD)。 +您可以通过运行以下命令运行该示例,请确保已正确下载[pretrained model](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#%E5%B7%B2%E5%8F%91%E5%B8%83%E6%A8%A1%E5%9E%8B%E5%8F%8A%E5%85%B6%E6%80%A7%E8%83%BD)。 + +step1: 开启显存优化策略 +``` +export FLAGS_fast_eager_deletion_mode=1 +export FLAGS_eager_delete_tensor_gb=0.0 +``` +step2: 设置gpu卡 +``` +export CUDA_VISIBLE_DEVICES=0 +``` +step3: 开始训练 +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c 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)。 @@ -58,23 +74,12 @@ PaddlePaddle框架中有四个和量化相关的IrPass, 分别是QuantizationTra >注意:配置文件中的信息不会保存在断点中,重启前对配置文件的修改将会生效。 -## 评估 - -如果在配置文件中设置了`checkpoint_path`,则每个epoch会保存一个量化后的用于评估的模型, -该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__`和`__params__`两个文件。 -其中,`__model__`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。模型结构和训练时一样。 - -如果不需要保存评估模型,可以在定义Compressor对象时,将`save_eval_model`选项设置为False(默认为True)。 - -脚本slim/quantization/eval.py中为使用该模型在评估数据集上做评估的示例。 - - -## 预测 +### 保存评估和预测模型 如果在配置文件的量化策略中设置了`float_model_save_path`, `int8_model_save_path`, `mobile_model_save_path`, 在训练结束后,会保存模型量化压缩之后用于预测的模型。接下来介绍这三种预测模型的区别。 -### float预测模型 -在介绍量化训练时的模型结构时介绍了PaddlePaddle框架中有四个和量化相关的IrPass, 分别是QuantizationTransformPass、QuantizationFreezePass、ConvertToInt8Pass以及TransformForMobilePass。float预测模型是在应用QuantizationFreezePass并删除eval_program中多余的operators之后,保存的模型。 +#### FP32模型 +在介绍量化训练时的模型结构时介绍了PaddlePaddle框架中有四个和量化相关的IrPass, 分别是QuantizationTransformPass、QuantizationFreezePass、ConvertToInt8Pass以及TransformForMobilePass。FP32模型是在应用QuantizationFreezePass并删除eval_program中多余的operators之后,保存的模型。 QuantizationFreezePass主要用于改变IrGraph中量化op和反量化op的顺序,即将类似图1中的量化op和反量化op顺序改变为图2中的布局。除此之外,QuantizationFreezePass还会将`conv2d`、`depthwise_conv2d`、`mul`等算子的权重离线量化为int8_t范围内的值(但数据类型仍为float32),以减少预测过程中对权重的量化操作,示例如图2: @@ -83,7 +88,7 @@ QuantizationFreezePass主要用于改变IrGraph中量化op和反量化op的顺 图2:应用QuantizationFreezePass后的结果

-### int8预测模型 +#### 8-bit模型 在对训练网络进行QuantizationFreezePass之后,执行ConvertToInt8Pass, 其主要目的是将执行完QuantizationFreezePass后输出的权重类型由`FP32`更改为`INT8`。换言之,用户可以选择将量化后的权重保存为float32类型(不执行ConvertToInt8Pass)或者int8_t类型(执行ConvertToInt8Pass),示例如图3: @@ -92,7 +97,7 @@ QuantizationFreezePass主要用于改变IrGraph中量化op和反量化op的顺 图3:应用ConvertToInt8Pass后的结果

-### mobile预测模型 +#### mobile模型 经TransformForMobilePass转换后,用户可得到兼容[paddle-lite](https://github.com/PaddlePaddle/Paddle-Lite)移动端预测库的量化模型。paddle-mobile中的量化op和反量化op的名称分别为`quantize`和`dequantize`。`quantize`算子和PaddlePaddle框架中的`fake_quantize_abs_max`算子簇的功能类似,`dequantize` 算子和PaddlePaddle框架中的`fake_dequantize_max_abs`算子簇的功能相同。若选择paddle-mobile执行量化训练输出的模型,则需要将`fake_quantize_abs_max`等算子改为`quantize`算子以及将`fake_dequantize_max_abs`等算子改为`dequantize`算子,示例如图4:

@@ -100,18 +105,43 @@ QuantizationFreezePass主要用于改变IrGraph中量化op和反量化op的顺 图4:应用TransformForMobilePass后的结果

-### python预测 +## 评估 +### 每个epoch保存的评估模型 +因为量化的最终模型只有在end_epoch时保存一次,不能保证保存的模型是最好的,因此 +如果在配置文件中设置了`checkpoint_path`,则每个epoch会保存一个量化后的用于评估的模型, +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__`和`__params__`两个文件。 +其中,`__model__`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。模型结构和训练时一样。 -### PaddleLite预测 -float预测模型可使用PaddleLite进行加载预测,可参见教程[Paddle-Lite如何加载运行量化模型](https://github.com/PaddlePaddle/Paddle-Lite/wiki/model_quantization) +如果不需要保存评估模型,可以在定义Compressor对象时,将`save_eval_model`选项设置为False(默认为True)。 + +脚本slim/quantization/eval.py中为使用该模型在评估数据集上做评估的示例。 +运行命令为: +``` +python eval.py --model_path ${checkpoint_path}/${epoch_id}/eval_model/ --model_name __model__ --params_name __params__ -c yolov3_mobilenet_v1_voc.yml +``` -## 从评估模型保存预测模型 -从[配置文件说明](#配置文件说明)中可以看到,在 `end_epoch` 时将保存可用于预测的 `float`, `int8`, `mobile`模型,但是在训练之前不能准确地保存结果最好的epoch的结果,因此,提供了从`${checkpoint_path}/${epoch_id}/eval_model/`下保存的评估模型转化为预测模型的接口 `freeze.py `, 需要配置的参数为: +在评估之后,选取效果最好的epoch的模型,可使用脚本 slim/quantization/freeze.py将该模型转化为以上介绍的三种模型:float模型,int8模型,mobile模型,需要配置的参数为: - model_path, 加载的模型路径,`为${checkpoint_path}/${epoch_id}/eval_model/` - weight_quant_type 模型参数的量化方式,和配置文件中的类型保持一致 -- save_path `float`, `int8`, `mobile`模型的保存路径,分别为 `${save_path}/float/`, `${save_path}/int8/`, `${save_path}/mobile/` +- save_path `FP32`, `8-bit`, `mobile`模型的保存路径,分别为 `${save_path}/float/`, `${save_path}/int8/`, `${save_path}/mobile/` + +### 最终评估模型 +最终使用的评估模型是float模型,使用脚本slim/quantization/eval.py中为使用该模型在评估数据集上做评估的示例。 +运行命令为: +``` +python eval.py --model_path ${float_model_path} --model_name model --params_name params -c yolov3_mobilenet_v1_voc.yml +``` + +## 预测 + +### python预测 + + +### PaddleLite预测 +FP32模型可使用PaddleLite进行加载预测,可参见教程[Paddle-Lite如何加载运行量化模型](https://github.com/PaddlePaddle/Paddle-Lite/wiki/model_quantization) + ## 示例结果 @@ -128,3 +158,5 @@ float预测模型可使用PaddleLite进行加载预测,可参见教程[Paddle- ## FAQ + + diff --git a/slim/quantization/compress.py b/slim/quantization/compress.py index ef4319e14bef027f985be35bec10c5865c846aff..70166f1400310f74f59916c6a54d4ba8fc93c596 100644 --- a/slim/quantization/compress.py +++ b/slim/quantization/compress.py @@ -198,7 +198,8 @@ def main(): exe.run(startup_prog) start_iter = 0 - checkpoint.load_pretrain(exe, train_prog, cfg.pretrain_weights) + + checkpoint.load_params(exe, train_prog, cfg.pretrain_weights) def eval_func(program, scope): diff --git a/slim/quantization/eval.py b/slim/quantization/eval.py index df817a6622142fcb20b1b1bace2e094457521a7a..e4f162a5b37114d20752be4dc27b10e6358a6d0a 100644 --- a/slim/quantization/eval.py +++ b/slim/quantization/eval.py @@ -142,8 +142,8 @@ def main(): assert os.path.exists(FLAGS.model_path) infer_prog, feed_names, fetch_targets = fluid.io.load_inference_model( dirname=FLAGS.model_path, executor=exe, - model_filename='model', - params_filename='params') + model_filename=FLAGS.model_name, + params_filename=FLAGS.params_name) eval_keys = ['bbox', 'gt_box', 'gt_label', 'is_difficult'] eval_values = ['multiclass_nms_0.tmp_0', 'gt_box', 'gt_label', 'is_difficult'] @@ -179,6 +179,16 @@ if __name__ == '__main__': default=None, type=str, help="Dataset path, same as DataFeed.dataset.dataset_dir") + parser.add_argument( + "--model_name", + default='model', + type=str, + help="model file name to load_inference_model") + parser.add_argument( + "--params_name", + default='params', + type=str, + help="params file name to load_inference_model") FLAGS = parser.parse_args() main() diff --git a/slim/quantization/images/ConvertToInt8Pass.png b/slim/quantization/images/ConvertToInt8Pass.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5849819c0bc8e592dc8f864d8945330df85ab1 Binary files /dev/null and b/slim/quantization/images/ConvertToInt8Pass.png differ diff --git a/slim/quantization/images/FreezePass.png b/slim/quantization/images/FreezePass.png new file mode 100644 index 0000000000000000000000000000000000000000..acd2b0a890a8af85bec6eecdb22e47ad386a178c Binary files /dev/null and b/slim/quantization/images/FreezePass.png differ diff --git a/slim/quantization/images/TransformForMobilePass.png b/slim/quantization/images/TransformForMobilePass.png new file mode 100644 index 0000000000000000000000000000000000000000..4104cacc67af0be1c7bc152696e2ae544127aace Binary files /dev/null and b/slim/quantization/images/TransformForMobilePass.png differ diff --git a/slim/quantization/images/TransformPass.png b/slim/quantization/images/TransformPass.png new file mode 100644 index 0000000000000000000000000000000000000000..f29ab62753e0e6ddf28d0c1dda7139705fc24b18 Binary files /dev/null and b/slim/quantization/images/TransformPass.png differ diff --git a/slim/quantization/yolov3_mobilenet_v1_voc.yml b/slim/quantization/yolov3_mobilenet_v1_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..2c7c8060a234a458dafc65a0395d8e74fe60d6aa --- /dev/null +++ b/slim/quantization/yolov3_mobilenet_v1_voc.yml @@ -0,0 +1,87 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 70000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: VOC +map_type: 11point +pretrain_weights: http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar +weights: output/yolov3_mobilenet_v1_voc/model_final +num_classes: 20 + +YOLOv3: + backbone: MobileNet + yolo_head: YOLOv3Head + +MobileNet: + norm_type: sync_bn + norm_decay: 0. + conv_group_scale: 1 + with_extra_blocks: false + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.0001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 2000 + - 8000 +#- !LinearWarmup +#start_factor: 0. +#steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: ../../dataset/voc + annotation: VOCdevkit/VOC_all/ImageSets/Main/train.txt + image_dir: VOCdevkit/VOC_all/JPEGImages + use_default_label: true + num_workers: 8 + bufsize: 128 + use_process: true + mixup_epoch: 250 + +YoloEvalFeed: + batch_size: 8 + image_shape: [3, 608, 608] + dataset: + dataset_dir: ../../dataset/voc + annotation: VOCdevkit/VOC_all/ImageSets/Main/val.txt + image_dir: VOCdevkit/VOC_all/JPEGImages + use_default_label: true + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + use_default_label: true