From 0db9a28e91fdc239e2f1d95a98bf4c4141809ae8 Mon Sep 17 00:00:00 2001 From: Guanghua Yu <742925032@qq.com> Date: Wed, 18 May 2022 19:37:46 +0800 Subject: [PATCH] update ACT quick start demo (#1116) --- demo/auto_compression/README.md | 72 ++++++++++++++----- .../image_classification/README.md | 20 +++--- ...mobilev1.yaml => mobilenetv1_qat_dis.yaml} | 0 paddleslim/auto_compression/auto_strategy.py | 40 ++++++++--- paddleslim/auto_compression/compressor.py | 72 +++++++++++++++---- 5 files changed, 153 insertions(+), 51 deletions(-) rename demo/auto_compression/image_classification/configs/{mobilev1.yaml => mobilenetv1_qat_dis.yaml} (100%) diff --git a/demo/auto_compression/README.md b/demo/auto_compression/README.md index 6c1c4bb74dbda..7d22b02482c77 100644 --- a/demo/auto_compression/README.md +++ b/demo/auto_compression/README.md @@ -1,7 +1,7 @@ -# 自动压缩工具ACT(Auto Compression Tookit) +# 自动化压缩工具ACT(Auto Compression Tookit) ## 简介 -PaddleSlim推出全新自动压缩工具(ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。ACT自动压缩工具主要特性如下: +PaddleSlim推出全新自动化压缩工具(ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。ACT自动化压缩工具主要特性如下: - **『更便捷』**:开发者无需了解或修改模型源码,直接使用导出的预测模型进行压缩; - **『更智能』**:开发者简单配置即可启动压缩,ACT工具会自动优化得到最好预测模型; - **『更丰富』**:ACT中提供了量化训练、蒸馏、结构化剪枝、非结构化剪枝、多种离线量化方法及超参搜索等等,可任意搭配使用。 @@ -14,32 +14,68 @@ PaddleSlim推出全新自动压缩工具(ACT),旨在通过Source-Free的 ## 快速上手 +- 1.准备模型及数据集 + +```shell +# 下载MobileNet预测模型 +wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar +tar -xf MobileNetV1_infer.tar +# 下载ImageNet小型数据集 +wget https://sys-p0.bj.bcebos.com/slim_ci/ILSVRC2012_data_demo.tar.gz +tar xf ILSVRC2012_data_demo.tar.gz +``` + +- 2.运行 + ```python # 导入依赖包 -from paddleslim.auto_compression.config_helpers import load_config +import paddle +from PIL import Image +from paddle.vision.datasets import DatasetFolder +from paddle.vision.transforms import transforms from paddleslim.auto_compression import AutoCompression -from paddleslim.common.imagenet_reader import reader -# 加载配置文件 -compress_config, train_config = load_slim_config("./image_classification/mobilenetv1_qat_dis.yaml") +paddle.enable_static() +# 定义DataSet +class ImageNetDataset(DatasetFolder): + def __init__(self, path, image_size=224): + super(ImageNetDataset, self).__init__(path) + normalize = transforms.Normalize( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.120, 57.375]) + self.transform = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(image_size), transforms.Transpose(), + normalize + ]) + + def __getitem__(self, idx): + img_path, _ = self.samples[idx] + return self.transform(Image.open(img_path).convert('RGB')) + + def __len__(self): + return len(self.samples) + # 定义DataLoader -train_loader = reader(mode='test') # DataLoader +train_dataset = ImageNetDataset("./ILSVRC2012_data_demo/ILSVRC2012/train/") +image = paddle.static.data( + name='inputs', shape=[None] + [3, 224, 224], dtype='float32') +train_loader = paddle.io.DataLoader(train_dataset, feed_list=[image], batch_size=32) # 开始自动压缩 ac = AutoCompression( - model_dir="./mobilenetv1_infer", - model_filename="model.pdmodel", - params_filename="model.pdiparams", + model_dir="./MobileNetV1_infer/", + model_filename="inference.pdmodel", + params_filename="inference.pdiparams", save_dir="output", - strategy_config=compress_config, - train_config=train_config, + strategy_config=None, + train_config=None, train_dataloader=train_loader, - eval_callback=None) # eval_function to verify accuracy + eval_dataloader=train_loader) # eval_function to verify accuracy ac.compress() ``` **提示:** - DataLoader传入的数据集是待压缩模型所用的数据集,DataLoader继承自`paddle.io.DataLoader`。 -- 如无需验证自动压缩过程中模型的精度,`eval_callback`可不传入function,程序会自动根据损失来选择最优模型。 -- 自动压缩Config中定义量化、蒸馏、剪枝等压缩算法会合并执行,压缩策略有:量化+蒸馏,剪枝+蒸馏等等。 +- 如无需验证自动化压缩过程中模型的精度,`eval_callback`可不传入function,程序会自动根据损失来选择最优模型。 +- 自动化压缩Config中定义量化、蒸馏、剪枝等压缩算法会合并执行,压缩策略有:量化+蒸馏,剪枝+蒸馏等等。 ## 应用示例 @@ -52,11 +88,11 @@ ac.compress() #### [NLP](./nlp) #### 即将发布 -- [ ] 更多自动压缩应用示例 -- [ ] X2Paddle模型自动压缩示例 +- [ ] 更多自动化压缩应用示例 +- [ ] X2Paddle模型自动化压缩示例 ## 其他 - ACT可以自动处理常见的预测模型,如果有更特殊的改造需求,可以参考[ACT超参配置教程](./hyperparameter_tutorial.md)来进行单独配置压缩策略。 -- 如果你发现任何关于ACT自动压缩工具的问题或者是建议, 欢迎通过[GitHub Issues](https://github.com/PaddlePaddle/PaddleSlim/issues)给我们提issues。同时欢迎贡献更多优秀模型,共建开源生态。 +- 如果你发现任何关于ACT自动化压缩工具的问题或者是建议, 欢迎通过[GitHub Issues](https://github.com/PaddlePaddle/PaddleSlim/issues)给我们提issues。同时欢迎贡献更多优秀模型,共建开源生态。 diff --git a/demo/auto_compression/image_classification/README.md b/demo/auto_compression/image_classification/README.md index 00f66b2fd29db..b625410d81a20 100644 --- a/demo/auto_compression/image_classification/README.md +++ b/demo/auto_compression/image_classification/README.md @@ -18,9 +18,9 @@ ## 2. Benchmark - MobileNetV1模型 -| 模型 | 策略 | Top-1 Acc | 耗时(ms) threads=4 | +| 模型 | 策略 | Top-1 Acc | 耗时(ms) threads=4 | |:------:|:------:|:------:|:------:| -| MobileNetV1 | Base模型 | 70.90 | 39.041 | +| MobileNetV1 | Base模型 | 70.90 | 39.041 | | MobileNetV1 | 量化+蒸馏 | 70.49 | 29.238| - 测试环境:`SDM710 2*A75(2.2GHz) 6*A55(1.7GHz)` @@ -59,7 +59,7 @@ pip install paddleslim ```shell wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar -tar -zxvf MobileNetV1_infer.tar +tar -xf MobileNetV1_infer.tar ``` 也可根据[PaddleClas文档](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/inference_deployment/export_model.md)导出Inference模型。 @@ -73,20 +73,20 @@ python run.py \ --model_dir='MobileNetV1_infer' \ --model_filename='inference.pdmodel' \ --params_filename='inference.pdiparams' \ - --save_dir='./save_quant_mobilev1/' \ + --save_dir='./output' \ --batch_size=128 \ - --config_path='./configs/mobilev1.yaml'\ + --config_path='./configs/mobilenetv1_qat_dis.yaml'\ --data_dir='ILSVRC2012' - + # 多卡启动 python -m paddle.distributed.launch run.py \ --model_dir='MobileNetV1_infer' \ --model_filename='inference.pdmodel' \ --params_filename='inference.pdiparams' \ - --save_dir='./save_quant_mobilev1/' \ + --save_dir='./output' \ --batch_size=128 \ - --config_path='./configs/mobilev1.yaml'\ - --data_dir='ILSVRC2012' + --config_path='./configs/mobilenetv1_qat_dis.yaml'\ + --data_dir='ILSVRC2012' ``` @@ -96,4 +96,4 @@ python -m paddle.distributed.launch run.py \ - [Paddle Inference C++部署](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.5/docs/deployment/inference/cpp_inference.md) - [Paddle Lite部署](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.5/docs/deployment/lite/lite.md) -## 5.FAQ \ No newline at end of file +## 5.FAQ diff --git a/demo/auto_compression/image_classification/configs/mobilev1.yaml b/demo/auto_compression/image_classification/configs/mobilenetv1_qat_dis.yaml similarity index 100% rename from demo/auto_compression/image_classification/configs/mobilev1.yaml rename to demo/auto_compression/image_classification/configs/mobilenetv1_qat_dis.yaml diff --git a/paddleslim/auto_compression/auto_strategy.py b/paddleslim/auto_compression/auto_strategy.py index 2e8ef96acd365..373f1222df211 100644 --- a/paddleslim/auto_compression/auto_strategy.py +++ b/paddleslim/auto_compression/auto_strategy.py @@ -25,16 +25,16 @@ "prepare_strategy", "create_strategy_config", "get_final_quant_config" ] -### config tester to test the loss of quant_post +# config tester to test the loss of quant_post hpo_config_tester = { - "ptq_algo": ["avg", "mse", "KL"], + "ptq_algo": ["avg"], "weight_quantize_type": ['channel_wise_abs_max', 'abs_max'], "bias_correct": [False], - "batch_num": [2, 3], + "batch_num": [5], "max_quant_count": 1, } -### default hpo config +# default hpo config default_hpo_config = { "ptq_algo": ["KL", "hist", "avg", "mse"], "weight_quantize_type": ['channel_wise_abs_max', 'abs_max'], @@ -44,11 +44,26 @@ "max_quant_count": 20, } -### default quant config, can be used by ptq&hpo and qat&distillation +# default quant config, can be used by ptq&hpo and qat&distillation default_quant_config = { 'quantize_op_types': ['conv2d', 'depthwise_conv2d', 'mul', 'matmul'], 'weight_bits': 8, - 'activation_bits': 8 + 'activation_bits': 8, + "is_full_quantize": False, + "activation_quantize_type": 'range_abs_max', + "weight_quantize_type": 'abs_max', + "not_quant_pattern": ["skip_quant"], +} + +# default train config +DefaultTrainConfig = { + "epochs": 1, + "eval_iter": 500, + "learning_rate": 0.0001, + "optimizer": "Momentum", + "optim_args": { + "weight_decay": 4.0e-05 + }, } EXPERIENCE_STRATEGY_WITHOUT_LOSS = [ @@ -118,6 +133,12 @@ def create_strategy_config(strategy_str, model_type): return configs +def create_train_config(strategy_str, model_type): + # TDOD: support more strategy and model_type + train_config = TrainConfig(**DefaultTrainConfig) + return train_config + + def prepare_strategy(model_dir, model_filename, params_filename, @@ -203,9 +224,9 @@ def prepare_strategy(model_dir, return strategy_config -def get_final_quant_config(ptq_loss): +def get_final_quant_config(ptq_loss, mode='DistilQuant'): """ transform quantization tester config to real quantization config """ - if ptq_loss <= MAGIC_EMD_DISTANCE: + if mode == 'HPO': quant_config = Quantization(**default_quant_config) hpo_config = HyperParameterOptimization(**default_hpo_config) configs = [{ @@ -213,10 +234,11 @@ def get_final_quant_config(ptq_loss): 'HyperParameterOptimization': hpo_config }] - else: + if mode == 'DistilQuant': quant_config = Quantization(**default_quant_config) dis_config = Distillation() configs = [{'Quantization': quant_config, 'Distillation': dis_config}] + _logger.info("Start Quantization and Distillation Training.") return configs diff --git a/paddleslim/auto_compression/compressor.py b/paddleslim/auto_compression/compressor.py index 1cf4d7a9c1bed..300770948f3cc 100644 --- a/paddleslim/auto_compression/compressor.py +++ b/paddleslim/auto_compression/compressor.py @@ -24,14 +24,14 @@ import paddle.distributed.fleet as fleet if platform.system().lower() == 'linux': from ..quant import quant_post_hpo -from ..quant.quanter import convert +from ..quant.quanter import convert, quant_post from ..common.recover_program import recover_inference_program from ..common import get_logger from ..common.patterns import get_patterns from ..analysis import TableLatencyPredictor from .create_compressed_program import build_distill_program, build_quant_program, build_prune_program, remove_unused_var_nodes from .strategy_config import ProgramInfo, merge_config -from .auto_strategy import prepare_strategy, get_final_quant_config, create_strategy_config +from .auto_strategy import prepare_strategy, get_final_quant_config, create_strategy_config, create_train_config _logger = get_logger(__name__, level=logging.INFO) @@ -143,6 +143,11 @@ def __init__(self, self._strategy, self._config = self._prepare_strategy( self.strategy_config) + # If train_config is None, set default train_config + if self.train_config is None: + self.train_config = create_train_config(self.strategy_config, + self.model_type) + def _prepare_envs(self): devices = paddle.device.get_device().split(':')[0] places = paddle.device._convert_to_place(devices) @@ -248,8 +253,9 @@ def _prepare_program(self, program, feed_target_names, fetch_targets, feed_target_names, fetch_targets) config_dict = dict(config._asdict()) - if config_dict["prune_strategy"] == "gmp" and config_dict[ - 'gmp_config'] is None: + if "prune_strategy" in config_dict and config_dict[ + "prune_strategy"] == "gmp" and config_dict[ + 'gmp_config'] is None: _logger.info( "Calculating the iterations per epoch……(It will take some time)") # NOTE:XXX: This way of calculating the iters needs to be improved. @@ -351,20 +357,57 @@ def compress(self): ).lower() == 'linux': ptq_loss = quant_post_hpo.g_min_emd_loss - final_quant_config = get_final_quant_config(ptq_loss) + final_quant_config = get_final_quant_config( + ptq_loss, mode='DistilQuant') quant_strategy, quant_config = self._prepare_strategy( final_quant_config) self.single_strategy_compress(quant_strategy[0], quant_config[0], strategy_idx) - old_model_path = os.path.join( + tmp_model_path = os.path.join( self.save_dir, 'strategy_{}'.format(str(strategy_idx + 1))) final_model_path = os.path.join(self.final_dir) - shutil.move(old_model_path, final_model_path) + if not os.path.exists(final_model_path): + os.makedirs(final_model_path) + tmp_model_file = os.path.join(tmp_model_path, 'model.pdmodel') + tmp_params_file = os.path.join(tmp_model_path, 'model.pdiparams') + final_model_file = os.path.join(final_model_path, 'model.pdmodel') + final_params_file = os.path.join(final_model_path, 'model.pdiparams') + shutil.move(tmp_model_file, final_model_file) + shutil.move(tmp_params_file, final_params_file) + _logger.info( + "==> Finished the ACT process and the final model is saved in:{}". + format(final_model_path)) os._exit(0) def single_strategy_compress(self, strategy, config, strategy_idx): - ### start compress, including train/eval model - if strategy == 'ptq_hpo': + # start compress, including train/eval model + # TODO: add the emd loss of evaluation model. + if strategy == 'quant_post': + quant_post( + self._exe, + model_dir=self.model_dir, + quantize_model_path=os.path.join( + self.save_dir, 'strategy_{}'.format(str(strategy_idx + 1))), + data_loader=self.train_dataloader, + model_filename=self.model_filename, + params_filename=self.params_filename, + save_model_filename=self.model_filename, + save_params_filename=self.params_filename, + batch_size=1, + batch_nums=config.batch_num, + algo=config.ptq_algo, + round_type='round', + bias_correct=config.bias_correct, + hist_percent=config.hist_percent, + quantizable_op_type=config.quantize_op_types, + is_full_quantize=config.is_full_quantize, + weight_bits=config.weight_bits, + activation_bits=config.activation_bits, + activation_quantize_type='range_abs_max', + weight_quantize_type=config.weight_quantize_type, + onnx_format=False) + + elif strategy == 'ptq_hpo': if platform.system().lower() != 'linux': raise NotImplementedError( "post-quant-hpo is not support in system other than linux") @@ -503,11 +546,12 @@ def _save_model(self, test_program_info, strategy, strategy_idx): test_program_info.program, paddle.static.CompiledProgram) else test_program_info.program - paddle.static.load(test_program, - os.path.join(self.save_dir, 'best_model')) - os.remove(os.path.join(self.save_dir, 'best_model.pdmodel')) - os.remove(os.path.join(self.save_dir, 'best_model.pdopt')) - os.remove(os.path.join(self.save_dir, 'best_model.pdparams')) + if os.path.exists(os.path.join(self.save_dir, 'best_model.pdparams')): + paddle.static.load(test_program, + os.path.join(self.save_dir, 'best_model')) + os.remove(os.path.join(self.save_dir, 'best_model.pdmodel')) + os.remove(os.path.join(self.save_dir, 'best_model.pdopt')) + os.remove(os.path.join(self.save_dir, 'best_model.pdparams')) if 'qat' in strategy: float_program, int8_program = convert(test_program_info.program._program, self._places, self._quant_config, \