Skip to content

Commit

Permalink
update ACT quick start demo (PaddlePaddle#1116)
Browse files Browse the repository at this point in the history
  • Loading branch information
yghstill authored May 18, 2022
1 parent bdffa0f commit 0db9a28
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 51 deletions.
72 changes: 54 additions & 18 deletions demo/auto_compression/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 自动压缩工具ACT(Auto Compression Tookit)
# 自动化压缩工具ACT(Auto Compression Tookit)

## 简介
PaddleSlim推出全新自动压缩工具(ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。ACT自动压缩工具主要特性如下
PaddleSlim推出全新自动化压缩工具(ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。ACT自动化压缩工具主要特性如下
- **『更便捷』**:开发者无需了解或修改模型源码,直接使用导出的预测模型进行压缩;
- **『更智能』**:开发者简单配置即可启动压缩,ACT工具会自动优化得到最好预测模型;
- **『更丰富』**:ACT中提供了量化训练、蒸馏、结构化剪枝、非结构化剪枝、多种离线量化方法及超参搜索等等,可任意搭配使用。
Expand All @@ -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中定义量化、蒸馏、剪枝等压缩算法会合并执行,压缩策略有:量化+蒸馏,剪枝+蒸馏等等。

## 应用示例

Expand All @@ -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。同时欢迎贡献更多优秀模型,共建开源生态。
20 changes: 10 additions & 10 deletions demo/auto_compression/image_classification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)`
Expand Down Expand Up @@ -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模型。

Expand All @@ -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'
```


Expand All @@ -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
## 5.FAQ
40 changes: 31 additions & 9 deletions paddleslim/auto_compression/auto_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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 = [
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -203,20 +224,21 @@ 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 = [{
'Quantization': quant_config,
'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

Expand Down
72 changes: 58 additions & 14 deletions paddleslim/auto_compression/compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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, \
Expand Down

0 comments on commit 0db9a28

Please sign in to comment.