Skip to content

Commit

Permalink
add ppdet auto compression demo (PaddlePaddle#1039)
Browse files Browse the repository at this point in the history
  • Loading branch information
yghstill committed Apr 8, 2022
1 parent 3cf6116 commit 380bce6
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 60 deletions.
63 changes: 63 additions & 0 deletions demo/auto-compression/configs/PaddleDet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 使用预测模型进行量化训练示例

预测模型保存接口:
动态图使用``paddle.jit.save``保存;
静态图使用``paddle.static.save_inference_model``保存。

本示例将介绍如何使用PaddleDetection中预测模型进行蒸馏量化训练。

## 模型量化蒸馏训练流程

### 1. 准备COCO格式数据

参考[COCO数据准备文档](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.4/docs/tutorials/PrepareDataSet.md#coco%E6%95%B0%E6%8D%AE)

### 2. 准备需要量化的环境

- PaddlePaddle >= 2.2
- PaddleDet >= 2.3

```shell
pip install paddledet
```

#### 3 准备待量化模型
- 下载代码
```
git clone https://github.com/PaddlePaddle/PaddleDetection.git
```
- 导出预测模型
```shell
python tools/export_model.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml -o weights=https://paddledet.bj.bcebos.com/models/yolov3_mobilenet_v1_270e_coco.pdparams
```
或直接下载:
```shell
wget https://paddledet.bj.bcebos.com/models/slim/yolov3_mobilenet_v1_270e_coco.tar
tar -xf yolov3_mobilenet_v1_270e_coco.tar
```

#### 2.4 测试模型精度
拷贝``yolov3_mobilenet_v1_270e_coco``文件夹到``PaddleSlim/demo/auto-compression/``文件夹。
```
cd PaddleSlim/demo/auto-compression/
```
使用[demo_coco.py](../demo_coco.py)脚本得到模型的分类精度:
```
python3.7 ../demo_coco.py --model_dir=../yolov3_mobilenet_v1_270e_coco/ --model_filename=model.pdmodel --params_filename=model.pdiparams --eval=True
```

### 3. 进行多策略融合压缩

每一个小章节代表一种多策略融合压缩,不代表需要串行执行。

### 3.1 进行量化蒸馏压缩
蒸馏量化训练示例脚本为[demo_coco.py](../demo_coco.py),使用接口``paddleslim.auto_compression.AutoCompression``对模型进行量化训练。运行命令为:
```
python ../demo_coco.py \
--model_dir='infermodel_mobilenetv2' \
--model_filename='model.pdmodel' \
--params_filename='./model.pdiparams' \
--save_dir='./output/' \
--devices='gpu' \
--config_path='./yolov3_mbv1_qat_dis.yaml'
```
5 changes: 2 additions & 3 deletions demo/auto-compression/configs/PaddleDet/coco_dataset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ TrainDataset:
!COCODataSet
image_dir: train2017
anno_path: annotations/instances_train2017.json
dataset_dir: dataset/coco
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']
dataset_dir: dataset/coco/

EvalDataset:
!COCODataSet
image_dir: val2017
anno_path: annotations/instances_val2017.json
dataset_dir: dataset/coco
dataset_dir: dataset/coco/

TestDataset:
!ImageFolder
Expand Down
40 changes: 0 additions & 40 deletions demo/auto-compression/configs/PaddleDet/ppyoloe_reader.yml

This file was deleted.

15 changes: 15 additions & 0 deletions demo/auto-compression/configs/PaddleDet/yolo_reader.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
_BASE_: [
'./coco_dataset.yml',
]

worker_num: 8

TestReader:
inputs_def:
image_shape: [3, 640, 640]
sample_transforms:
- Decode: {}
- Resize: {target_size: [640, 640], keep_ratio: False, interp: 2}
- NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
- Permute: {}
batch_size: 4
32 changes: 32 additions & 0 deletions demo/auto-compression/configs/PaddleDet/yolov3_mbv1_qat_dis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Distillation:
distill_lambda: 1.0
distill_loss: l2_loss
distill_node_pair:
- teacher_conv2d_84.tmp_0
- conv2d_84.tmp_0
- teacher_conv2d_85.tmp_0
- conv2d_85.tmp_0
- teacher_conv2d_86.tmp_0
- conv2d_86.tmp_0
merge_feed: true
teacher_model_dir: ./yolov3_mobilenet_v1_270e_coco/
teacher_model_filename: model.pdmodel
teacher_params_filename: model.pdiparams
Quantization:
activation_bits: 8
is_full_quantize: false
not_quant_pattern:
- skip_quant
quantize_op_types:
- conv2d
- depthwise_conv2d
weight_bits: 8
TrainConfig:
epochs: 1
eval_iter: 1000
learning_rate: 0.0001
optimizer: SGD
optim_args:
weight_decay: 4.0e-05
#origin_metric: 0.289

153 changes: 147 additions & 6 deletions demo/auto-compression/demo_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,156 @@
import sys
sys.path[0] = os.path.join(os.path.dirname("__file__"), os.path.pardir)
import argparse

import functools
from functools import partial
import numpy as np
import paddle
from ppdet.core.workspace import load_config, merge_config
from ppdet.core.workspace import create
from ppdet.metrics import COCOMetric
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression

paddle.enable_static()

from utility import add_arguments, print_arguments

parser = argparse.ArgumentParser(description=__doc__)
add_arg = functools.partial(add_arguments, argparser=parser)

# yapf: disable
add_arg('model_dir', str, None, "inference model directory.")
add_arg('model_filename', str, None, "inference model filename.")
add_arg('params_filename', str, None, "inference params filename.")
add_arg('save_dir', str, 'output', "directory to save compressed model.")
add_arg('devices', str, 'gpu', "which device used to compress.")
add_arg('batch_size', int, 1, "train batch size.")
add_arg('config_path', str, None, "path of compression strategy config.")
add_arg('eval', bool, False, "whether to run evaluation.")
# yapf: enable


def reader_wrapper(reader):
def gen():
for data in reader:
yield {
"image": data['image'],
'im_shape': data['im_shape'],
'scale_factor': data['scale_factor']
}

return gen


def eval():
dataset = reader_cfg['EvalDataset']
val_loader = create('TestReader')(dataset,
reader_cfg['worker_num'],
return_list=True)

place = paddle.CUDAPlace(0) if args.devices == 'gpu' else paddle.CPUPlace()
exe = paddle.static.Executor(place)

val_program, feed_target_names, fetch_targets = paddle.fluid.io.load_inference_model(
args.model_dir,
exe,
model_filename=args.model_filename,
params_filename=args.params_filename)
clsid2catid = {v: k for k, v in dataset.catid2clsid.items()}

anno_file = dataset.get_anno()
metric = COCOMetric(
anno_file=anno_file, clsid2catid=clsid2catid, bias=0, IouType='bbox')
for batch_id, data in enumerate(val_loader):
data_new = {k: np.array(v) for k, v in data.items()}
outs = exe.run(val_program,
feed={
'image': data['image'],
'im_shape': data['im_shape'],
'scale_factor': data['scale_factor']
},
fetch_list=fetch_targets,
return_numpy=False)
res = {}
for out in outs:
v = np.array(out)
if len(v.shape) > 1:
res['bbox'] = v
else:
res['bbox_num'] = v

metric.update(data_new, res)
if batch_id % 100 == 0:
print('Eval iter:', batch_id)
metric.accumulate()
metric.log()
metric.reset()


def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
clsid2catid = {v: k for k, v in dataset.catid2clsid.items()}

anno_file = dataset.get_anno()
metric = COCOMetric(
anno_file=anno_file, clsid2catid=clsid2catid, bias=1, IouType='bbox')
for batch_id, data in enumerate(val_loader):
data_new = {k: np.array(v) for k, v in data.items()}
outs = exe.run(compiled_test_program,
feed={
'image': data['image'],
'im_shape': data['im_shape'],
'scale_factor': data['scale_factor']
},
fetch_list=test_fetch_list,
return_numpy=False)
res = {}
for out in outs:
v = np.array(out)
if len(v.shape) > 1:
res['bbox'] = v
else:
res['bbox_num'] = v

metric.update(data_new, res)
if batch_id % 100 == 0:
print('Eval iter:', batch_id)
metric.accumulate()
metric.log()
map_res = metric.get_results()
metric.reset()
return map_res['bbox'][0]


if __name__ == '__main__':
args = parser.parse_args()
print_arguments(args)
paddle.enable_static()
reader_cfg = load_config('./configs/PaddleDet/yolo_reader.yml')
if args.eval:
eval()
sys.exit(0)

compress_config, train_config = load_slim_config(args.config_path)

cfg = load_config('./configs/PaddleDet/ppyoloe_reader.yml')
train_loader = create('TestReader')(reader_cfg['TrainDataset'],
reader_cfg['worker_num'],
return_list=True)
dataset = reader_cfg['EvalDataset']
val_loader = create('TestReader')(reader_cfg['EvalDataset'],
reader_cfg['worker_num'],
return_list=True)

print(cfg)
train_dataloader = reader_wrapper(train_loader)

coco_loader = create('TestReader')(cfg['TrainDataset'], cfg['worker_num'])
ac = AutoCompression(
model_dir=args.model_dir,
model_filename=args.model_filename,
params_filename=args.params_filename,
save_dir=args.save_dir,
strategy_config=compress_config,
train_config=train_config,
train_dataloader=train_dataloader,
eval_callback=eval_function,
devices=args.devices)

for data in coco_loader:
print(data.keys())
ac.compress()
3 changes: 2 additions & 1 deletion paddleslim/auto_compression/compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ def _start_train(self, train_program_info, test_program_info):
_logger.info("epoch: {}, batch: {}, loss: {}".format(
epoch_id, batch_id, np_probs_float))

if batch_id % int(self.train_config.eval_iter) == 0:
if batch_id % int(
self.train_config.eval_iter) == 0 and batch_id != 0:
if self.eval_function is not None:

# GMP pruner step 3: update params before summrizing sparsity, saving model or evaluation.
Expand Down
18 changes: 8 additions & 10 deletions paddleslim/quant/quanter.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ def quant_aware(program,
optimizer_func=None,
executor=None,
onnx_format=False,
return_program=False):
return_program=False,
draw_graph=False):
"""Add quantization and dequantization operators to "program"
for quantization training or testing.
Expand Down Expand Up @@ -241,6 +242,8 @@ def quant_aware(program,
initialization. Default is None.
return_program(bool): If user want return value is a Program rather than Compiled Program, This argument should be set True.
Default is False.
draw_graph(bool): whether to draw graph when quantization is initialized. In order to prevent cycle,
the ERNIE model needs to be set to True. Default is False.
Returns:
paddle.static.CompiledProgram | paddle.static.Program: Program with quantization and dequantization ``operators``
"""
Expand Down Expand Up @@ -308,15 +311,10 @@ def quant_aware(program,
VARS_MAPPING_TABLE))
save_dict(main_graph.out_node_mapping_table)

main_graph.draw('./', 'graph.pdf')
#remove_ctr_vars = set()
#from paddle.fluid.framework import IrVarNode
#all_var_nodes = {IrVarNode(node) for node in main_graph.nodes() if node.is_var()}
#for node in all_var_nodes:
# print("node: ", node)
# if node.is_ctrl_var():
# remove_ctr_vars.add(node)
#self.safe_remove_nodes(remove_ctr_vars)
# TDOD: remove it.
if draw_graph:
main_graph.draw('./', 'graph.pdf')

if for_test or return_program:
quant_program = main_graph.to_program()
else:
Expand Down

0 comments on commit 380bce6

Please sign in to comment.