未验证 提交 380bce65 编写于 作者: G Guanghua Yu 提交者: GitHub

add ppdet auto compression demo (#1039)

上级 3cf61169
# 使用预测模型进行量化训练示例
预测模型保存接口:
动态图使用``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,14 +5,13 @@ TrainDataset: ...@@ -5,14 +5,13 @@ TrainDataset:
!COCODataSet !COCODataSet
image_dir: train2017 image_dir: train2017
anno_path: annotations/instances_train2017.json anno_path: annotations/instances_train2017.json
dataset_dir: dataset/coco dataset_dir: dataset/coco/
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']
EvalDataset: EvalDataset:
!COCODataSet !COCODataSet
image_dir: val2017 image_dir: val2017
anno_path: annotations/instances_val2017.json anno_path: annotations/instances_val2017.json
dataset_dir: dataset/coco dataset_dir: dataset/coco/
TestDataset: TestDataset:
!ImageFolder !ImageFolder
......
_BASE_: [
'./coco_dataset.yml',
]
worker_num: 8
TrainReader:
sample_transforms:
- Decode: {}
- RandomDistort: {}
- RandomExpand: {fill_value: [123.675, 116.28, 103.53]}
- RandomCrop: {}
- RandomFlip: {}
batch_transforms:
- BatchRandomResize: {target_size: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768], random_size: True, random_interp: True, keep_ratio: False}
- NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
- Permute: {}
- PadGT: {}
batch_size: 24
shuffle: true
drop_last: true
use_shared_memory: true
collate_batch: true
EvalReader:
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
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: 1
_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
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
...@@ -2,15 +2,156 @@ import os ...@@ -2,15 +2,156 @@ import os
import sys import sys
sys.path[0] = os.path.join(os.path.dirname("__file__"), os.path.pardir) sys.path[0] = os.path.join(os.path.dirname("__file__"), os.path.pardir)
import argparse 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 load_config, merge_config
from ppdet.core.workspace import create 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: ac.compress()
print(data.keys())
...@@ -315,7 +315,8 @@ class AutoCompression: ...@@ -315,7 +315,8 @@ class AutoCompression:
_logger.info("epoch: {}, batch: {}, loss: {}".format( _logger.info("epoch: {}, batch: {}, loss: {}".format(
epoch_id, batch_id, np_probs_float)) 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: if self.eval_function is not None:
# GMP pruner step 3: update params before summrizing sparsity, saving model or evaluation. # GMP pruner step 3: update params before summrizing sparsity, saving model or evaluation.
......
...@@ -198,7 +198,8 @@ def quant_aware(program, ...@@ -198,7 +198,8 @@ def quant_aware(program,
optimizer_func=None, optimizer_func=None,
executor=None, executor=None,
onnx_format=False, onnx_format=False,
return_program=False): return_program=False,
draw_graph=False):
"""Add quantization and dequantization operators to "program" """Add quantization and dequantization operators to "program"
for quantization training or testing. for quantization training or testing.
...@@ -241,6 +242,8 @@ def quant_aware(program, ...@@ -241,6 +242,8 @@ def quant_aware(program,
initialization. Default is None. 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. return_program(bool): If user want return value is a Program rather than Compiled Program, This argument should be set True.
Default is False. 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: Returns:
paddle.static.CompiledProgram | paddle.static.Program: Program with quantization and dequantization ``operators`` paddle.static.CompiledProgram | paddle.static.Program: Program with quantization and dequantization ``operators``
""" """
...@@ -308,15 +311,10 @@ def quant_aware(program, ...@@ -308,15 +311,10 @@ def quant_aware(program,
VARS_MAPPING_TABLE)) VARS_MAPPING_TABLE))
save_dict(main_graph.out_node_mapping_table) save_dict(main_graph.out_node_mapping_table)
main_graph.draw('./', 'graph.pdf') # TDOD: remove it.
#remove_ctr_vars = set() if draw_graph:
#from paddle.fluid.framework import IrVarNode main_graph.draw('./', 'graph.pdf')
#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)
if for_test or return_program: if for_test or return_program:
quant_program = main_graph.to_program() quant_program = main_graph.to_program()
else: else:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册