未验证 提交 407d47b3 编写于 作者: C Chang Xu 提交者: GitHub

Rearrange Some docs & Add Quant Analysis API and Demo (#1342)

上级 05c2122e
...@@ -20,7 +20,7 @@ import paddle ...@@ -20,7 +20,7 @@ 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, VOCMetric, KeyPointTopDownCOCOEval from ppdet.metrics import COCOMetric, VOCMetric, KeyPointTopDownCOCOEval
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from keypoint_utils import keypoint_post_process from keypoint_utils import keypoint_post_process
from post_process import PPYOLOEPostProcess from post_process import PPYOLOEPostProcess
......
...@@ -19,7 +19,7 @@ import argparse ...@@ -19,7 +19,7 @@ import argparse
import paddle 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 paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.quant import quant_post_static from paddleslim.quant import quant_post_static
......
...@@ -20,7 +20,7 @@ import paddle ...@@ -20,7 +20,7 @@ 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, VOCMetric, KeyPointTopDownCOCOEval from ppdet.metrics import COCOMetric, VOCMetric, KeyPointTopDownCOCOEval
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
from keypoint_utils import keypoint_post_process from keypoint_utils import keypoint_post_process
from post_process import PPYOLOEPostProcess from post_process import PPYOLOEPostProcess
...@@ -126,7 +126,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): ...@@ -126,7 +126,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
def main(): def main():
global global_config global global_config
all_config = load_slim_config(FLAGS.config_path) all_config = load_slim_config(FLAGS.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
reader_cfg = load_config(global_config['reader_config']) reader_cfg = load_config(global_config['reader_config'])
......
...@@ -23,7 +23,7 @@ import paddle ...@@ -23,7 +23,7 @@ import paddle
import paddle.nn as nn import paddle.nn as nn
from paddle.io import DataLoader from paddle.io import DataLoader
from imagenet_reader import ImageNetDataset from imagenet_reader import ImageNetDataset
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
def argsparser(): def argsparser():
......
...@@ -22,7 +22,7 @@ import yaml ...@@ -22,7 +22,7 @@ import yaml
from utils import preprocess, postprocess from utils import preprocess, postprocess
import paddle import paddle
from paddle.inference import create_predictor from paddle.inference import create_predictor
from paddleslim.auto_compression.config_helpers import load_config from paddleslim.common import load_config
def argsparser(): def argsparser():
......
...@@ -24,7 +24,7 @@ import paddle ...@@ -24,7 +24,7 @@ import paddle
import paddle.nn as nn import paddle.nn as nn
from paddle.io import DataLoader from paddle.io import DataLoader
from imagenet_reader import ImageNetDataset from imagenet_reader import ImageNetDataset
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
......
...@@ -15,7 +15,7 @@ from paddlenlp.datasets import load_dataset ...@@ -15,7 +15,7 @@ from paddlenlp.datasets import load_dataset
from paddlenlp.data import Stack, Tuple, Pad from paddlenlp.data import Stack, Tuple, Pad
from paddlenlp.data.sampler import SamplerHelper from paddlenlp.data.sampler import SamplerHelper
from paddlenlp.metrics import Mcc, PearsonAndSpearman from paddlenlp.metrics import Mcc, PearsonAndSpearman
from paddleslim.auto_compression.config_helpers import load_config from paddleslim.common import load_config
from paddleslim.auto_compression.compressor import AutoCompression from paddleslim.auto_compression.compressor import AutoCompression
......
...@@ -27,7 +27,7 @@ from paddlenlp.transformers import AutoModelForTokenClassification, AutoTokenize ...@@ -27,7 +27,7 @@ from paddlenlp.transformers import AutoModelForTokenClassification, AutoTokenize
from paddlenlp.datasets import load_dataset from paddlenlp.datasets import load_dataset
from paddlenlp.data import Stack, Tuple, Pad from paddlenlp.data import Stack, Tuple, Pad
from paddlenlp.metrics import AccuracyAndF1, Mcc, PearsonAndSpearman from paddlenlp.metrics import AccuracyAndF1, Mcc, PearsonAndSpearman
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression.compressor import AutoCompression from paddleslim.auto_compression.compressor import AutoCompression
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.common import load_onnx_model from paddleslim.common import load_onnx_model
from post_process import YOLOv5PostProcess, coco_metric from post_process import YOLOv5PostProcess, coco_metric
from dataset import COCOValDataset from dataset import COCOValDataset
......
...@@ -17,11 +17,12 @@ import sys ...@@ -17,11 +17,12 @@ import sys
import numpy as np import numpy as np
import argparse import argparse
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config from paddleslim.common import load_config
from paddleslim.common import load_onnx_model from paddleslim.common import load_onnx_model
from paddleslim.quant import quant_post_static from paddleslim.quant import quant_post_static
from dataset import COCOTrainDataset from dataset import COCOTrainDataset
def argsparser(): def argsparser():
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument( parser.add_argument(
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
from dataset import COCOValDataset, COCOTrainDataset from dataset import COCOValDataset, COCOTrainDataset
from post_process import YOLOv5PostProcess, coco_metric from post_process import YOLOv5PostProcess, coco_metric
...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): ...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
def main(): def main():
global global_config global global_config
all_config = load_slim_config(FLAGS.config_path) all_config = load_slim_config(FLAGS.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
dataset = COCOTrainDataset( dataset = COCOTrainDataset(
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.common import load_onnx_model from paddleslim.common import load_onnx_model
from post_process import YOLOv6PostProcess, coco_metric from post_process import YOLOv6PostProcess, coco_metric
from dataset import COCOValDataset from dataset import COCOValDataset
......
...@@ -17,11 +17,12 @@ import sys ...@@ -17,11 +17,12 @@ import sys
import numpy as np import numpy as np
import argparse import argparse
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config from paddleslim.common import load_config
from paddleslim.common import load_onnx_model from paddleslim.common import load_onnx_model
from paddleslim.quant import quant_post_static from paddleslim.quant import quant_post_static
from dataset import COCOTrainDataset from dataset import COCOTrainDataset
def argsparser(): def argsparser():
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument( parser.add_argument(
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
from dataset import COCOValDataset, COCOTrainDataset from dataset import COCOValDataset, COCOTrainDataset
from post_process import YOLOv6PostProcess, coco_metric from post_process import YOLOv6PostProcess, coco_metric
...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): ...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
def main(): def main():
global global_config global global_config
all_config = load_slim_config(FLAGS.config_path) all_config = load_slim_config(FLAGS.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
dataset = COCOTrainDataset( dataset = COCOTrainDataset(
......
...@@ -18,8 +18,8 @@ import numpy as np ...@@ -18,8 +18,8 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression.utils import load_inference_model from paddleslim.common import load_inference_model
from post_process import YOLOv7PostProcess, coco_metric from post_process import YOLOv7PostProcess, coco_metric
from dataset import COCOValDataset from dataset import COCOValDataset
......
...@@ -17,7 +17,7 @@ import sys ...@@ -17,7 +17,7 @@ import sys
import numpy as np import numpy as np
import argparse import argparse
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config from paddleslim.common import load_config
from paddleslim.common import load_onnx_model from paddleslim.common import load_onnx_model
from paddleslim.quant import quant_post_static from paddleslim.quant import quant_post_static
from dataset import COCOTrainDataset from dataset import COCOTrainDataset
......
...@@ -18,7 +18,7 @@ import numpy as np ...@@ -18,7 +18,7 @@ import numpy as np
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
import paddle import paddle
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
from dataset import COCOValDataset, COCOTrainDataset from dataset import COCOValDataset, COCOTrainDataset
from post_process import YOLOv7PostProcess, coco_metric from post_process import YOLOv7PostProcess, coco_metric
...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): ...@@ -75,7 +75,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
def main(): def main():
global global_config global global_config
all_config = load_slim_config(FLAGS.config_path) all_config = load_slim_config(FLAGS.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
dataset = COCOTrainDataset( dataset = COCOTrainDataset(
......
...@@ -21,7 +21,7 @@ from paddleseg.cvlibs import Config as PaddleSegDataConfig ...@@ -21,7 +21,7 @@ from paddleseg.cvlibs import Config as PaddleSegDataConfig
from paddleseg.utils import worker_init_fn from paddleseg.utils import worker_init_fn
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleseg.core.infer import reverse_transform from paddleseg.core.infer import reverse_transform
from paddleseg.utils import metrics from paddleseg.utils import metrics
......
...@@ -23,7 +23,7 @@ import paddle ...@@ -23,7 +23,7 @@ import paddle
import paddle.nn as nn import paddle.nn as nn
from paddle.io import DataLoader from paddle.io import DataLoader
from imagenet_reader import ImageNetDataset from imagenet_reader import ImageNetDataset
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
def argsparser(): def argsparser():
...@@ -93,7 +93,8 @@ def eval(): ...@@ -93,7 +93,8 @@ def eval():
def main(): def main():
global global_config global global_config
all_config = load_slim_config(args.config_path) all_config = load_slim_config(args.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
global data_dir global data_dir
data_dir = global_config['data_dir'] data_dir = global_config['data_dir']
......
...@@ -23,7 +23,7 @@ import paddle ...@@ -23,7 +23,7 @@ import paddle
import paddle.nn as nn import paddle.nn as nn
from paddle.io import DataLoader from paddle.io import DataLoader
from imagenet_reader import ImageNetDataset from imagenet_reader import ImageNetDataset
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
...@@ -107,7 +107,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): ...@@ -107,7 +107,8 @@ def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
def main(): def main():
global global_config global global_config
all_config = load_slim_config(args.config_path) all_config = load_slim_config(args.config_path)
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" assert "Global" in all_config, "Key 'Global' not found in config file. \n{}".format(
all_config)
global_config = all_config["Global"] global_config = all_config["Global"]
global data_dir global data_dir
data_dir = global_config['data_dir'] data_dir = global_config['data_dir']
......
...@@ -20,7 +20,7 @@ import paddle ...@@ -20,7 +20,7 @@ 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, VOCMetric, KeyPointTopDownCOCOEval from ppdet.metrics import COCOMetric, VOCMetric, KeyPointTopDownCOCOEval
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
def argsparser(): def argsparser():
......
...@@ -20,7 +20,7 @@ import paddle ...@@ -20,7 +20,7 @@ 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, VOCMetric, KeyPointTopDownCOCOEval from ppdet.metrics import COCOMetric, VOCMetric, KeyPointTopDownCOCOEval
from paddleslim.auto_compression.config_helpers import load_config as load_slim_config from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression import AutoCompression from paddleslim.auto_compression import AutoCompression
......
# YOLO系列离线量化示例
目录:
- [1.简介](#1简介)
- [2.Benchmark](#2Benchmark)
- [3.开始自动压缩](#离线量化流程)
- [3.1 准备环境](#31-准备环境)
- [3.2 准备数据集](#32-准备数据集)
- [3.3 准备预测模型](#33-准备预测模型)
- [3.4 离线量化并产出模型](#34-离线量化并产出模型)
- [3.5 测试模型精度](#35-测试模型精度)
- [3.6 提高离线量化精度](#36-提高离线量化精度)
- [4.预测部署](#4预测部署)
- [5.FAQ](5FAQ)
本示例将以[ultralytics/yolov5](https://github.com/ultralytics/yolov5)[meituan/YOLOv6](https://github.com/meituan/YOLOv6)[WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7) YOLO系列目标检测模型为例,将PyTorch框架产出的推理模型转换为Paddle推理模型,使用离线量化功能进行压缩,并使用敏感度分析功能提升离线量化精度。离线量化产出的模型可以用PaddleInference部署,也可以导出为ONNX格式模型文件,并用TensorRT部署。
## 2.Benchmark
| 模型 | 策略 | 输入尺寸 | mAP<sup>val<br>0.5:0.95 | 预测时延<sup><small>FP32</small><sup><br><sup>(ms) |预测时延<sup><small>FP16</small><sup><br><sup>(ms) | 预测时延<sup><small>INT8</small><sup><br><sup>(ms) | 配置文件 | Inference模型 |
| :-------- |:-------- |:--------: | :---------------------: | :----------------: | :----------------: | :---------------: | :-----------------------------: | :-----------------------------: |
| YOLOv5s | Base模型 | 640*640 | 37.4 | 5.95ms | 2.44ms | - | - | [Model](https://paddle-slim-models.bj.bcebos.com/act/yolov5s.onnx) |
| YOLOv5s | KL离线量化 | 640*640 | 36.0 | - | - | 1.87ms | - | - |
| | | | | | | | | |
| YOLOv6s | Base模型 | 640*640 | 42.4 | 9.06ms | 2.90ms | - | - | [Model](https://paddle-slim-models.bj.bcebos.com/act/yolov6s.onnx) |
| YOLOv6s | KL离线量化(量化分析前) | 640*640 | 30.3 | - | - | 1.83ms | - | - |
| YOLOv6s | KL离线量化(量化分析后) | 640*640 | 39.7 | - | - | - | - | [Infer Model](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov6s_analyzed_ptq.tar) |
| | | | | | | | | |
| YOLOv7 | Base模型 | 640*640 | 51.1 | 26.84ms | 7.44ms | - | - | [Model](https://paddle-slim-models.bj.bcebos.com/act/yolov7.onnx) |
| YOLOv7 | KL离线量化 | 640*640 | 50.2 | - | - | 4.55ms | - | - |
说明:
- mAP的指标均在COCO val2017数据集中评测得到。
## 3. 离线量化流程
#### 3.1 准备环境
- PaddlePaddle >= 2.3 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装)
- PaddleSlim > 2.3版本
- opencv-python
(1)安装paddlepaddle:
```shell
# CPU
pip install paddlepaddle
# GPU
pip install paddlepaddle-gpu
```
(2)安装paddleslim:
```shell
pip install paddleslim
```
#### 3.2 准备数据集
本示例默认以COCO数据进行自动压缩实验,可以从[MS COCO官网](https://cocodataset.org)下载[Train](http://images.cocodataset.org/zips/train2017.zip)[Val](http://images.cocodataset.org/zips/val2017.zip)[annotation](http://images.cocodataset.org/annotations/annotations_trainval2017.zip)
目录格式如下:
```
dataset/coco/
├── annotations
│ ├── instances_train2017.json
│ ├── instances_val2017.json
│ | ...
├── train2017
│ ├── 000000000009.jpg
│ ├── 000000580008.jpg
│ | ...
├── val2017
│ ├── 000000000139.jpg
│ ├── 000000000285.jpg
```
#### 3.3 准备预测模型
(1)准备ONNX模型:
**yolov5**:可通过[ultralytics/yolov5](https://github.com/ultralytics/yolov5) 官方的[导出教程](https://github.com/ultralytics/yolov5/issues/251)来准备ONNX模型。也可以下载准备好的[yolov5s.onnx](https://paddle-slim-models.bj.bcebos.com/act/yolov5s.onnx)
**yolov6**:可通过[WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7)的导出脚本来准备ONNX模型。也可以直接下载我们已经准备好的[yolov7.onnx](https://paddle-slim-models.bj.bcebos.com/act/yolov7.onnx)
**yolov7**:可通过[meituan/YOLOv6](https://github.com/meituan/YOLOv6)官方的[导出教程](https://github.com/meituan/YOLOv6/blob/main/deploy/ONNX/README.md)来准备ONNX模型。也可以下载已经准备好的[yolov6s.onnx](https://paddle-slim-models.bj.bcebos.com/act/yolov6s.onnx)
#### 3.4 离线量化并产出模型
离线量化示例通过post_quant.py脚本启动,会使用接口```paddleslim.quant.quant_post_static```对模型进行量化。配置config文件中模型路径、数据路径和量化相关的参数,配置完成后便可对模型进行离线量化。具体运行命令为:
- yolov5
```shell
python post_quant.py --config_path=./configs/yolov5s_ptq.yaml --save_dir=./yolov5s_ptq_out
```
- yolov6
```shell
python post_quant.py --config_path=./configs/yolov6s_ptq.yaml --save_dir=./yolov6s_ptq_out
```
- yolov7
```shell
python post_quant.py --config_path=./configs/yolov7s_ptq.yaml --save_dir=./yolov7s_ptq_out
```
#### 3.5 测试模型精度
修改[yolov5s_ptq.yaml](./configs/yolov5s_ptq.yaml)`model_dir`字段为模型存储路径,然后使用eval.py脚本得到模型的mAP:
```shell
export CUDA_VISIBLE_DEVICES=0
python eval.py --config_path=./configs/yolov5s_ptq.yaml
```
#### 3.6 提高离线量化精度
本节介绍如何使用量化分析工具提升离线量化精度。离线量化功能仅需使用少量数据,且使用简单、能快速得到量化模型,但往往会造成较大的精度损失。PaddleSlim提供量化分析工具,会使用接口```paddleslim.quant.AnalysisQuant```,可视化展示出不适合量化的层,通过跳过这些层,提高离线量化模型精度。由于yolov6离线量化效果较差,以yolov6为例,量化分析工具具体使用方法如下:
```shell
python analysis.py --config_path=./configs/yolov6s_analysis.yaml
```
经过分析之后,会产出模型每一层量化后的精度,和较差层的weight和activation的分布图。在进行离线量化时,可以跳过这些导致精度下降较多的层,如yolov6中,经过分析后,可跳过`conv2d_2.w_0``conv2d_11.w_0``conv2d_15.w_0``conv2d_46.w_0``conv2d_49.w_0`,可使用[yolov6s_analyzed_ptq.yaml](./configs/yolov6s_analyzed_ptq.yaml),然后再次进行离线量化。跳过这五层后,离线量化精度上升9.4个点。
```shell
python post_quant.py --config_path=./configs/yolov6s_analyzed_ptq.yaml --save_dir=./yolov6s_analyzed_ptq_out
```
注:
- 分析后,每层量化的精度会默认保存在`./analysis_results/analysis.txt`,直方分布图会默认保存在`./analysis_results/act_hist_result.pdf``./analysis_results/weight_hist_result.pdf`中。
<p align="center">
<img src="./images/sensitivity_rank.png" width=849 hspace='10'/> <br />
</p>
## 4.预测部署
## 5.FAQ
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import numpy as np
import argparse
import paddle
from tqdm import tqdm
from post_process import YOLOv6PostProcess, coco_metric
from dataset import COCOValDataset, COCOTrainDataset
from paddleslim.common import load_config, load_onnx_model
from paddleslim.quant.analysis import AnalysisQuant
def argsparser():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--config_path',
type=str,
default=None,
help="path of analysis config.",
required=True)
parser.add_argument(
'--devices',
type=str,
default='gpu',
help="which device used to compress.")
return parser
def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
bboxes_list, bbox_nums_list, image_id_list = [], [], []
with tqdm(
total=len(val_loader),
bar_format='Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}',
ncols=80) as t:
for data in val_loader:
data_all = {k: np.array(v) for k, v in data.items()}
outs = exe.run(compiled_test_program,
feed={test_feed_names[0]: data_all['image']},
fetch_list=test_fetch_list,
return_numpy=False)
res = {}
postprocess = YOLOv6PostProcess(
score_threshold=0.001, nms_threshold=0.65, multi_label=True)
res = postprocess(np.array(outs[0]), data_all['scale_factor'])
bboxes_list.append(res['bbox'])
bbox_nums_list.append(res['bbox_num'])
image_id_list.append(np.array(data_all['im_id']))
t.update()
map_res = coco_metric(anno_file, bboxes_list, bbox_nums_list, image_id_list)
return map_res[0]
def main():
global config
config = load_config(FLAGS.config_path)
dataset = COCOTrainDataset(
dataset_dir=config['dataset_dir'],
image_dir=config['val_image_dir'],
anno_path=config['val_anno_path'])
data_loader = paddle.io.DataLoader(
dataset, batch_size=1, shuffle=True, drop_last=True, num_workers=0)
global val_loader
dataset = COCOValDataset(
dataset_dir=config['dataset_dir'],
image_dir=config['val_image_dir'],
anno_path=config['val_anno_path'])
global anno_file
anno_file = dataset.ann_file
val_loader = paddle.io.DataLoader(
dataset, batch_size=1, shuffle=False, drop_last=False, num_workers=0)
load_onnx_model(config["model_dir"])
inference_model_path = config["model_dir"].rstrip().rstrip(
'.onnx') + '_infer'
analyzer = AnalysisQuant(
model_dir=inference_model_path,
model_filename='model.pdmodel',
params_filename='model.pdiparams',
eval_function=eval_function,
quantizable_op_type=config['quantizable_op_type'],
weight_quantize_type=config['weight_quantize_type'],
activation_quantize_type=config['activation_quantize_type'],
is_full_quantize=config['is_full_quantize'],
data_loader=data_loader,
batch_size=config['batch_size'],
save_dir=config['save_dir'], )
analyzer.analysis()
if __name__ == '__main__':
paddle.enable_static()
parser = argsparser()
FLAGS = parser.parse_args()
assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu']
paddle.set_device(FLAGS.devices)
main()
model_dir: ./yolov5s.onnx
dataset_dir: /dataset/coco/
train_image_dir: train2017
val_image_dir: val2017
train_anno_path: annotations/instances_train2017.json
val_anno_path: annotations/instances_val2017.json
skip_tensors: None # you can set it after analysis
model_dir: ./yolov6s.onnx
save_dir: ./analysis_results
quantizable_op_type: ["conv2d", "depthwise_conv2d"]
weight_quantize_type: 'channel_wise_abs_max'
activation_quantize_type: 'moving_average_abs_max'
is_full_quantize: False
dataset_dir: /dataset/coco/
val_image_dir: val2017
val_anno_path: annotations/instances_val2017.json
batch_size: 10
model_dir: ./yolov6s.onnx
dataset_dir: /dataset/coco/
train_image_dir: train2017
val_image_dir: val2017
train_anno_path: annotations/instances_train2017.json
val_anno_path: annotations/instances_val2017.json
skip_tensor_list: ['conv2d_2.w_0', 'conv2d_15.w_0', 'conv2d_46.w_0', 'conv2d_11.w_0', 'conv2d_49.w_0']
model_dir: ./yolov6s.onnx
dataset_dir: /dataset/coco/
train_image_dir: train2017
val_image_dir: val2017
train_anno_path: annotations/instances_train2017.json
val_anno_path: annotations/instances_val2017.json
skip_tensor_list: None
model_dir: ./yolov7s.onnx
dataset_dir: /dataset/coco/
train_image_dir: train2017
val_image_dir: val2017
train_anno_path: annotations/instances_train2017.json
val_anno_path: annotations/instances_val2017.json
from pycocotools.coco import COCO
import cv2
import os
import numpy as np
import paddle
class COCOValDataset(paddle.io.Dataset):
def __init__(self,
dataset_dir=None,
image_dir=None,
anno_path=None,
img_size=[640, 640]):
self.dataset_dir = dataset_dir
self.image_dir = image_dir
self.img_size = img_size
self.ann_file = os.path.join(dataset_dir, anno_path)
self.coco = COCO(self.ann_file)
ori_ids = list(sorted(self.coco.imgs.keys()))
# check gt bbox
clean_ids = []
for idx in ori_ids:
ins_anno_ids = self.coco.getAnnIds(imgIds=[idx], iscrowd=False)
instances = self.coco.loadAnns(ins_anno_ids)
num_bbox = 0
for inst in instances:
if inst.get('ignore', False):
continue
if 'bbox' not in inst.keys():
continue
elif not any(np.array(inst['bbox'])):
continue
else:
num_bbox += 1
if num_bbox > 0:
clean_ids.append(idx)
self.ids = clean_ids
def __getitem__(self, idx):
img_id = self.ids[idx]
img = self._get_img_data_from_img_id(img_id)
img, scale_factor = self.image_preprocess(img, self.img_size)
return {
'image': img,
'im_id': np.array([img_id]),
'scale_factor': scale_factor
}
def __len__(self):
return len(self.ids)
def _get_img_data_from_img_id(self, img_id):
img_info = self.coco.loadImgs(img_id)[0]
img_path = os.path.join(self.dataset_dir, self.image_dir,
img_info['file_name'])
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
return img
def _generate_scale(self, im, target_shape, keep_ratio=True):
"""
Args:
im (np.ndarray): image (np.ndarray)
Returns:
im_scale_x: the resize ratio of X
im_scale_y: the resize ratio of Y
"""
origin_shape = im.shape[:2]
if keep_ratio:
im_size_min = np.min(origin_shape)
im_size_max = np.max(origin_shape)
target_size_min = np.min(target_shape)
target_size_max = np.max(target_shape)
im_scale = float(target_size_min) / float(im_size_min)
if np.round(im_scale * im_size_max) > target_size_max:
im_scale = float(target_size_max) / float(im_size_max)
im_scale_x = im_scale
im_scale_y = im_scale
else:
resize_h, resize_w = target_shape
im_scale_y = resize_h / float(origin_shape[0])
im_scale_x = resize_w / float(origin_shape[1])
return im_scale_y, im_scale_x
def image_preprocess(self, img, target_shape):
# Resize image
im_scale_y, im_scale_x = self._generate_scale(img, target_shape)
img = cv2.resize(
img,
None,
None,
fx=im_scale_x,
fy=im_scale_y,
interpolation=cv2.INTER_LINEAR)
# Pad
im_h, im_w = img.shape[:2]
h, w = target_shape[:]
if h != im_h or w != im_w:
canvas = np.ones((h, w, 3), dtype=np.float32)
canvas *= np.array([114.0, 114.0, 114.0], dtype=np.float32)
canvas[0:im_h, 0:im_w, :] = img.astype(np.float32)
img = canvas
img = np.transpose(img / 255, [2, 0, 1])
scale_factor = np.array([im_scale_y, im_scale_x])
return img.astype(np.float32), scale_factor
class COCOTrainDataset(COCOValDataset):
def __getitem__(self, idx):
img_id = self.ids[idx]
img = self._get_img_data_from_img_id(img_id)
img, scale_factor = self.image_preprocess(img, self.img_size)
return {'x2paddle_image_arrays': img}
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import numpy as np
import argparse
from tqdm import tqdm
import paddle
from paddleslim.common import load_config as load_slim_config
from paddleslim.common import load_inference_model
from post_process import YOLOv6PostProcess, coco_metric
from dataset import COCOValDataset
def argsparser():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--config_path',
type=str,
default=None,
help="path of compression strategy config.",
required=True)
parser.add_argument(
'--devices',
type=str,
default='gpu',
help="which device used to compress.")
return parser
def eval():
place = paddle.CUDAPlace(0) if FLAGS.devices == 'gpu' else paddle.CPUPlace()
exe = paddle.static.Executor(place)
val_program, feed_target_names, fetch_targets = load_inference_model(
config["model_dir"], exe, "model.pdmodel", "model.pdiparams")
bboxes_list, bbox_nums_list, image_id_list = [], [], []
with tqdm(
total=len(val_loader),
bar_format='Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}',
ncols=80) as t:
for data in val_loader:
data_all = {k: np.array(v) for k, v in data.items()}
outs = exe.run(val_program,
feed={feed_target_names[0]: data_all['image']},
fetch_list=fetch_targets,
return_numpy=False)
res = {}
postprocess = YOLOv6PostProcess(
score_threshold=0.001, nms_threshold=0.65, multi_label=True)
res = postprocess(np.array(outs[0]), data_all['scale_factor'])
bboxes_list.append(res['bbox'])
bbox_nums_list.append(res['bbox_num'])
image_id_list.append(np.array(data_all['im_id']))
t.update()
coco_metric(anno_file, bboxes_list, bbox_nums_list, image_id_list)
def main():
global config
config = load_slim_config(FLAGS.config_path)
global val_loader
dataset = COCOValDataset(
dataset_dir=config['dataset_dir'],
image_dir=config['val_image_dir'],
anno_path=config['val_anno_path'])
global anno_file
anno_file = dataset.ann_file
val_loader = paddle.io.DataLoader(dataset, batch_size=1)
eval()
if __name__ == '__main__':
paddle.enable_static()
parser = argsparser()
FLAGS = parser.parse_args()
assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu']
paddle.set_device(FLAGS.devices)
main()
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np
import cv2
import json
import sys
def box_area(boxes):
"""
Args:
boxes(np.ndarray): [N, 4]
return: [N]
"""
return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])
def box_iou(box1, box2):
"""
Args:
box1(np.ndarray): [N, 4]
box2(np.ndarray): [M, 4]
return: [N, M]
"""
area1 = box_area(box1)
area2 = box_area(box2)
lt = np.maximum(box1[:, np.newaxis, :2], box2[:, :2])
rb = np.minimum(box1[:, np.newaxis, 2:], box2[:, 2:])
wh = rb - lt
wh = np.maximum(0, wh)
inter = wh[:, :, 0] * wh[:, :, 1]
iou = inter / (area1[:, np.newaxis] + area2 - inter)
return iou
def nms(boxes, scores, iou_threshold):
"""
Non Max Suppression numpy implementation.
args:
boxes(np.ndarray): [N, 4]
scores(np.ndarray): [N, 1]
iou_threshold(float): Threshold of IoU.
"""
idxs = scores.argsort()
keep = []
while idxs.size > 0:
max_score_index = idxs[-1]
max_score_box = boxes[max_score_index][None, :]
keep.append(max_score_index)
if idxs.size == 1:
break
idxs = idxs[:-1]
other_boxes = boxes[idxs]
ious = box_iou(max_score_box, other_boxes)
idxs = idxs[ious[0] <= iou_threshold]
keep = np.array(keep)
return keep
class YOLOv6PostProcess(object):
"""
Post process of YOLOv6 network.
args:
score_threshold(float): Threshold to filter out bounding boxes with low
confidence score. If not provided, consider all boxes.
nms_threshold(float): The threshold to be used in NMS.
multi_label(bool): Whether keep multi label in boxes.
keep_top_k(int): Number of total bboxes to be kept per image after NMS
step. -1 means keeping all bboxes after NMS step.
"""
def __init__(self,
score_threshold=0.25,
nms_threshold=0.5,
multi_label=False,
keep_top_k=300):
self.score_threshold = score_threshold
self.nms_threshold = nms_threshold
self.multi_label = multi_label
self.keep_top_k = keep_top_k
def _xywh2xyxy(self, x):
# Convert from [x, y, w, h] to [x1, y1, x2, y2]
y = np.copy(x)
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
return y
def _non_max_suppression(self, prediction):
max_wh = 4096 # (pixels) minimum and maximum box width and height
nms_top_k = 30000
cand_boxes = prediction[..., 4] > self.score_threshold # candidates
output = [np.zeros((0, 6))] * prediction.shape[0]
for batch_id, boxes in enumerate(prediction):
# Apply constraints
boxes = boxes[cand_boxes[batch_id]]
if not boxes.shape[0]:
continue
# Compute conf (conf = obj_conf * cls_conf)
boxes[:, 5:] *= boxes[:, 4:5]
# Box (center x, center y, width, height) to (x1, y1, x2, y2)
convert_box = self._xywh2xyxy(boxes[:, :4])
# Detections matrix nx6 (xyxy, conf, cls)
if self.multi_label:
i, j = (boxes[:, 5:] > self.score_threshold).nonzero()
boxes = np.concatenate(
(convert_box[i], boxes[i, j + 5, None],
j[:, None].astype(np.float32)),
axis=1)
else:
conf = np.max(boxes[:, 5:], axis=1)
j = np.argmax(boxes[:, 5:], axis=1)
re = np.array(conf.reshape(-1) > self.score_threshold)
conf = conf.reshape(-1, 1)
j = j.reshape(-1, 1)
boxes = np.concatenate((convert_box, conf, j), axis=1)[re]
num_box = boxes.shape[0]
if not num_box:
continue
elif num_box > nms_top_k:
boxes = boxes[boxes[:, 4].argsort()[::-1][:nms_top_k]]
# Batched NMS
c = boxes[:, 5:6] * max_wh
clean_boxes, scores = boxes[:, :4] + c, boxes[:, 4]
keep = nms(clean_boxes, scores, self.nms_threshold)
# limit detection box num
if keep.shape[0] > self.keep_top_k:
keep = keep[:self.keep_top_k]
output[batch_id] = boxes[keep]
return output
def __call__(self, outs, scale_factor):
preds = self._non_max_suppression(outs)
bboxs, box_nums = [], []
for i, pred in enumerate(preds):
if len(pred.shape) > 2:
pred = np.squeeze(pred)
if len(pred.shape) == 1:
pred = pred[np.newaxis, :]
pred_bboxes = pred[:, :4]
scale_factor = np.tile(scale_factor[i][::-1], (1, 2))
pred_bboxes /= scale_factor
bbox = np.concatenate(
[
pred[:, -1][:, np.newaxis], pred[:, -2][:, np.newaxis],
pred_bboxes
],
axis=-1)
bboxs.append(bbox)
box_num = bbox.shape[0]
box_nums.append(box_num)
bboxs = np.concatenate(bboxs, axis=0)
box_nums = np.array(box_nums)
return {'bbox': bboxs, 'bbox_num': box_nums}
def coco_metric(anno_file, bboxes_list, bbox_nums_list, image_id_list):
try:
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
except:
print(
"[ERROR] Not found pycocotools, please install by `pip install pycocotools`"
)
sys.exit(1)
coco_gt = COCO(anno_file)
cats = coco_gt.loadCats(coco_gt.getCatIds())
clsid2catid = {i: cat['id'] for i, cat in enumerate(cats)}
results = []
for bboxes, bbox_nums, image_id in zip(bboxes_list, bbox_nums_list,
image_id_list):
results += _get_det_res(bboxes, bbox_nums, image_id, clsid2catid)
output = "bbox.json"
with open(output, 'w') as f:
json.dump(results, f)
coco_dt = coco_gt.loadRes(output)
coco_eval = COCOeval(coco_gt, coco_dt, 'bbox')
coco_eval.evaluate()
coco_eval.accumulate()
coco_eval.summarize()
return coco_eval.stats
def _get_det_res(bboxes, bbox_nums, image_id, label_to_cat_id_map):
det_res = []
k = 0
for i in range(len(bbox_nums)):
cur_image_id = int(image_id[i][0])
det_nums = bbox_nums[i]
for j in range(det_nums):
dt = bboxes[k]
k = k + 1
num_id, score, xmin, ymin, xmax, ymax = dt.tolist()
if int(num_id) < 0:
continue
category_id = label_to_cat_id_map[int(num_id)]
w = xmax - xmin
h = ymax - ymin
bbox = [xmin, ymin, w, h]
dt_res = {
'image_id': cur_image_id,
'category_id': category_id,
'bbox': bbox,
'score': score
}
det_res.append(dt_res)
return det_res
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import numpy as np
import argparse
import paddle
from paddleslim.common import load_config, load_onnx_model
from paddleslim.quant import quant_post_static
from dataset import COCOTrainDataset
def argsparser():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--config_path',
type=str,
default=None,
help="path of post training quantization config.",
required=True)
parser.add_argument(
'--save_dir',
type=str,
default='ptq_out',
help="directory to save compressed model.")
parser.add_argument(
'--devices',
type=str,
default='gpu',
help="which device used to compress.")
parser.add_argument(
'--algo', type=str, default='KL', help="post quant algo.")
return parser
def main():
global config
config = load_config(FLAGS.config_path)
dataset = COCOTrainDataset(
dataset_dir=config['dataset_dir'],
image_dir=config['val_image_dir'],
anno_path=config['val_anno_path'])
train_loader = paddle.io.DataLoader(
dataset, batch_size=1, shuffle=True, drop_last=True, num_workers=0)
place = paddle.CUDAPlace(0) if FLAGS.devices == 'gpu' else paddle.CPUPlace()
exe = paddle.static.Executor(place)
# since the type pf model converted from pytorch is onnx,
# use load_onnx_model firstly and rename the model_dir
load_onnx_model(config["model_dir"])
inference_model_path = config["model_dir"].rstrip().rstrip(
'.onnx') + '_infer'
quant_post_static(
executor=exe,
model_dir=inference_model_path,
quantize_model_path=FLAGS.save_dir,
data_loader=train_loader,
model_filename='model.pdmodel',
params_filename='model.pdiparams',
batch_size=32,
batch_nums=10,
algo=FLAGS.algo,
hist_percent=0.999,
is_full_quantize=False,
bias_correction=False,
onnx_format=True)
if __name__ == '__main__':
paddle.enable_static()
parser = argsparser()
FLAGS = parser.parse_args()
assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu']
paddle.set_device(FLAGS.devices)
main()
...@@ -19,8 +19,14 @@ from .config_helpers import * ...@@ -19,8 +19,14 @@ from .config_helpers import *
from .utils import * from .utils import *
__all__ = [ __all__ = [
"AutoCompression", "Quantization", "Distillation", "AutoCompression",
"MultiTeacherDistillation", "HyperParameterOptimization", "Prune", "Quantization",
"UnstructurePrune", "ProgramInfo", "TrainConfig", "save_config", "Distillation",
"load_config", "predict_compressed_model" "MultiTeacherDistillation",
"HyperParameterOptimization",
"Prune",
"UnstructurePrune",
"ProgramInfo",
"TrainConfig",
"predict_compressed_model",
] ]
...@@ -29,13 +29,15 @@ from ..quant.quanter import convert, quant_post ...@@ -29,13 +29,15 @@ from ..quant.quanter import convert, quant_post
from ..common.recover_program import recover_inference_program from ..common.recover_program import recover_inference_program
from ..common import get_logger from ..common import get_logger
from ..common.patterns import get_patterns from ..common.patterns import get_patterns
from ..common.load_model import load_inference_model, get_model_dir
from ..common.dataloader import wrap_dataloader, get_feed_vars
from ..common.config_helper import load_config
from ..analysis import TableLatencyPredictor from ..analysis import TableLatencyPredictor
from .create_compressed_program import build_distill_program, build_quant_program, build_prune_program, remove_unused_var_nodes from .create_compressed_program import build_distill_program, build_quant_program, build_prune_program, remove_unused_var_nodes
from .strategy_config import TrainConfig, ProgramInfo, merge_config from .strategy_config import TrainConfig, ProgramInfo, merge_config
from .auto_strategy import prepare_strategy, get_final_quant_config, create_strategy_config, create_train_config from .auto_strategy import prepare_strategy, get_final_quant_config, create_strategy_config, create_train_config
from .config_helpers import load_config, extract_strategy_config, extract_train_config from .config_helpers import extract_strategy_config, extract_train_config
from .utils.predict import with_variable_shape from .utils.predict import with_variable_shape
from .utils import get_feed_vars, wrap_dataloader, load_inference_model, get_model_dir
_logger = get_logger(__name__, level=logging.INFO) _logger = get_logger(__name__, level=logging.INFO)
......
...@@ -14,42 +14,7 @@ ...@@ -14,42 +14,7 @@
import yaml import yaml
import os import os
from paddleslim.auto_compression.strategy_config import * from paddleslim.auto_compression.strategy_config import *
from ..common.config_helper import load_config
__all__ = ['save_config', 'load_config']
def print_arguments(args, level=0):
if level == 0:
print('----------- Running Arguments -----------')
for arg, value in sorted(args.items()):
if isinstance(value, dict):
print('\t' * level, '%s:' % arg)
print_arguments(value, level + 1)
else:
print('\t' * level, '%s: %s' % (arg, value))
if level == 0:
print('------------------------------------------')
def load_config(config):
"""Load configurations from yaml file into dict.
Fields validation is skipped for loading some custom information.
Args:
config(str): The path of configuration file.
Returns:
dict: A dict storing configuration information.
"""
if config is None:
return None
assert isinstance(
config,
str), f"config should be str but got type(config)={type(config)}"
assert os.path.exists(config) and os.path.isfile(
config), f"{config} not found or it is not a file."
with open(config) as f:
cfg = yaml.load(f, Loader=yaml.FullLoader)
print_arguments(cfg)
return cfg
def extract_strategy_config(config): def extract_strategy_config(config):
...@@ -101,12 +66,3 @@ def extract_train_config(config): ...@@ -101,12 +66,3 @@ def extract_train_config(config):
**value) if value is not None else TrainConfig() **value) if value is not None else TrainConfig()
# return default training config when it is not set # return default training config when it is not set
return TrainConfig() return TrainConfig()
def save_config(config, config_path):
"""
convert dict config to yaml.
"""
f = open(config_path, "w")
yaml.dump(config, f)
f.close()
...@@ -23,7 +23,7 @@ from ..dist import * ...@@ -23,7 +23,7 @@ from ..dist import *
from ..common.recover_program import recover_inference_program, _remove_fetch_node from ..common.recover_program import recover_inference_program, _remove_fetch_node
from ..common import get_logger from ..common import get_logger
from .strategy_config import ProgramInfo from .strategy_config import ProgramInfo
from .utils import load_inference_model from ..common.load_model import load_inference_model
_logger = get_logger(__name__, level=logging.INFO) _logger = get_logger(__name__, level=logging.INFO)
__all__ = [ __all__ = [
...@@ -52,7 +52,8 @@ def _create_optimizer(train_config): ...@@ -52,7 +52,8 @@ def _create_optimizer(train_config):
optimizer_builder = train_config['optimizer_builder'] optimizer_builder = train_config['optimizer_builder']
assert isinstance( assert isinstance(
optimizer_builder, dict optimizer_builder, dict
), f"Value of 'optimizer_builder' in train_config should be dict but got {type(optimizer_builder)}" ), "Value of 'optimizer_builder' in train_config should be dict but got {}".format(
type(optimizer_builder))
if 'grad_clip' in optimizer_builder: if 'grad_clip' in optimizer_builder:
g_clip_params = optimizer_builder['grad_clip'] g_clip_params = optimizer_builder['grad_clip']
g_clip_type = g_clip_params.pop('type') g_clip_type = g_clip_params.pop('type')
...@@ -444,9 +445,8 @@ def build_prune_program(executor, ...@@ -444,9 +445,8 @@ def build_prune_program(executor,
"####################channel pruning##########################") "####################channel pruning##########################")
for param in pruned_program.global_block().all_parameters(): for param in pruned_program.global_block().all_parameters():
if param.name in original_shapes: if param.name in original_shapes:
_logger.info( _logger.info("{}, from {} to {}".format(
f"{param.name}, from {original_shapes[param.name]} to {param.shape}" param.name, original_shapes[param.name], param.shape))
)
_logger.info( _logger.info(
"####################channel pruning end##########################") "####################channel pruning end##########################")
train_program_info.program = pruned_program train_program_info.program = pruned_program
......
...@@ -14,11 +14,5 @@ ...@@ -14,11 +14,5 @@
from __future__ import absolute_import from __future__ import absolute_import
from .predict import predict_compressed_model from .predict import predict_compressed_model
from .dataloader import *
from . import dataloader
from .load_model import *
from . import load_model
__all__ = ["predict_compressed_model"] __all__ = ["predict_compressed_model"]
__all__ += dataloader.__all__
__all__ += load_model.__all__
...@@ -12,7 +12,7 @@ except: ...@@ -12,7 +12,7 @@ except:
TRANSFORM_PASS_OP_TYPES = QuantizationTransformPass._supported_quantizable_op_type TRANSFORM_PASS_OP_TYPES = QuantizationTransformPass._supported_quantizable_op_type
QUANT_DEQUANT_PASS_OP_TYPES = AddQuantDequantPass._supported_quantizable_op_type QUANT_DEQUANT_PASS_OP_TYPES = AddQuantDequantPass._supported_quantizable_op_type
from .load_model import load_inference_model from ...common.load_model import load_inference_model
def post_quant_fake(executor, def post_quant_fake(executor,
......
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import paddle
from ...common import load_onnx_model
__all__ = ['load_inference_model', 'get_model_dir']
def load_inference_model(path_prefix,
executor,
model_filename=None,
params_filename=None):
# Load onnx model to Inference model.
if path_prefix.endswith('.onnx'):
inference_program, feed_target_names, fetch_targets = load_onnx_model(
path_prefix)
return [inference_program, feed_target_names, fetch_targets]
# Load Inference model.
# TODO: clean code
if model_filename is not None and model_filename.endswith('.pdmodel'):
model_name = '.'.join(model_filename.split('.')[:-1])
assert os.path.exists(
os.path.join(path_prefix, model_name + '.pdmodel')
), 'Please check {}, or fix model_filename parameter.'.format(
os.path.join(path_prefix, model_name + '.pdmodel'))
assert os.path.exists(
os.path.join(path_prefix, model_name + '.pdiparams')
), 'Please check {}, or fix params_filename parameter.'.format(
os.path.join(path_prefix, model_name + '.pdiparams'))
model_path_prefix = os.path.join(path_prefix, model_name)
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=model_path_prefix, executor=executor))
elif model_filename is not None and params_filename is not None:
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=path_prefix,
executor=executor,
model_filename=model_filename,
params_filename=params_filename))
else:
model_name = '.'.join(model_filename.split('.')
[:-1]) if model_filename is not None else 'model'
if os.path.exists(os.path.join(path_prefix, model_name + '.pdmodel')):
model_path_prefix = os.path.join(path_prefix, model_name)
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=model_path_prefix, executor=executor))
else:
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=path_prefix, executor=executor))
return [inference_program, feed_target_names, fetch_targets]
def get_model_dir(model_dir, model_filename, params_filename):
if model_dir.endswith('.onnx'):
updated_model_dir = model_dir.rstrip().rstrip('.onnx') + '_infer'
else:
updated_model_dir = model_dir.rstrip('/')
if model_filename == None:
updated_model_filename = 'model.pdmodel'
else:
updated_model_filename = model_filename
if params_filename == None:
updated_params_filename = 'model.pdiparams'
else:
updated_params_filename = params_filename
if params_filename is None and model_filename is not None:
raise NotImplementedError(
"NOT SUPPORT parameters saved in separate files. Please convert it to single binary file first."
)
return updated_model_dir, updated_model_filename, updated_params_filename
...@@ -4,7 +4,7 @@ import paddle ...@@ -4,7 +4,7 @@ import paddle
from ...analysis import TableLatencyPredictor from ...analysis import TableLatencyPredictor
from .prune_model import get_sparse_model, get_prune_model from .prune_model import get_sparse_model, get_prune_model
from .fake_ptq import post_quant_fake from .fake_ptq import post_quant_fake
from .load_model import load_inference_model from ...common.load_model import load_inference_model
def with_variable_shape(model_dir, model_filename=None, params_filename=None): def with_variable_shape(model_dir, model_filename=None, params_filename=None):
...@@ -53,7 +53,7 @@ def predict_compressed_model(executor, ...@@ -53,7 +53,7 @@ def predict_compressed_model(executor,
latency_dict(dict): The latency latency of the model under various compression strategies. latency_dict(dict): The latency latency of the model under various compression strategies.
""" """
local_rank = paddle.distributed.get_rank() local_rank = paddle.distributed.get_rank()
quant_model_path = f'quant_model_rank_{local_rank}_tmp' quant_model_path = 'quant_model_rank_{}_tmp'.format(local_rank)
prune_model_path = f'prune_model_rank_{local_rank}_tmp' prune_model_path = f'prune_model_rank_{local_rank}_tmp'
sparse_model_path = f'sparse_model_rank_{local_rank}_tmp' sparse_model_path = f'sparse_model_rank_{local_rank}_tmp'
......
...@@ -5,7 +5,7 @@ import paddle ...@@ -5,7 +5,7 @@ import paddle
import paddle.static as static import paddle.static as static
from ...prune import Pruner from ...prune import Pruner
from ...core import GraphWrapper from ...core import GraphWrapper
from .load_model import load_inference_model from ...common.load_model import load_inference_model
__all__ = ["get_sparse_model", "get_prune_model"] __all__ = ["get_sparse_model", "get_prune_model"]
...@@ -19,9 +19,10 @@ def get_sparse_model(executor, places, model_file, param_file, ratio, ...@@ -19,9 +19,10 @@ def get_sparse_model(executor, places, model_file, param_file, ratio,
ratio(float): The ratio to prune the model. ratio(float): The ratio to prune the model.
save_path(str): The save path of pruned model. save_path(str): The save path of pruned model.
""" """
assert os.path.exists(model_file), f'{model_file} does not exist.' assert os.path.exists(model_file), '{} does not exist.'.format(model_file)
assert os.path.exists( assert os.path.exists(
param_file) or param_file is None, f'{param_file} does not exist.' param_file) or param_file is None, '{} does not exist.'.format(
param_file)
paddle.enable_static() paddle.enable_static()
SKIP = ['image', 'feed', 'pool2d_0.tmp_0'] SKIP = ['image', 'feed', 'pool2d_0.tmp_0']
......
...@@ -25,12 +25,16 @@ from .analyze_helper import VarCollector ...@@ -25,12 +25,16 @@ from .analyze_helper import VarCollector
from . import wrapper_function from . import wrapper_function
from . import recover_program from . import recover_program
from . import patterns from . import patterns
from .convert_model import load_onnx_model from .load_model import load_inference_model, get_model_dir, load_onnx_model
from .dataloader import wrap_dataloader, get_feed_vars
from .config_helper import load_config, save_config
__all__ = [ __all__ = [
'EvolutionaryController', 'SAController', 'get_logger', 'ControllerServer', 'EvolutionaryController', 'SAController', 'get_logger', 'ControllerServer',
'ControllerClient', 'lock', 'unlock', 'cached_reader', 'AvgrageMeter', 'ControllerClient', 'lock', 'unlock', 'cached_reader', 'AvgrageMeter',
'Server', 'Client', 'RLBaseController', 'VarCollector', 'load_onnx_model' 'Server', 'Client', 'RLBaseController', 'VarCollector', 'load_onnx_model',
'load_inference_model', 'get_model_dir', 'wrap_dataloader', 'get_feed_vars',
'load_config', 'save_config'
] ]
__all__ += wrapper_function.__all__ __all__ += wrapper_function.__all__
......
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import yaml
import os
__all__ = ['load_config', 'save_config']
def print_arguments(args, level=0):
if level == 0:
print('----------- Running Arguments -----------')
for arg, value in sorted(args.items()):
if isinstance(value, dict):
print('\t' * level, '%s:' % arg)
print_arguments(value, level + 1)
else:
print('\t' * level, '%s: %s' % (arg, value))
if level == 0:
print('------------------------------------------')
def load_config(config):
"""Load configurations from yaml file into dict.
Fields validation is skipped for loading some custom information.
Args:
config(str): The path of configuration file.
Returns:
dict: A dict storing configuration information.
"""
if config is None:
return None
assert isinstance(
config,
str), f"config should be str but got type(config)={type(config)}"
assert os.path.exists(config) and os.path.isfile(
config), f"{config} not found or it is not a file."
with open(config) as f:
cfg = yaml.load(f, Loader=yaml.FullLoader)
print_arguments(cfg)
return cfg
def save_config(config, config_path):
"""
convert dict config to yaml.
"""
f = open(config_path, "w")
yaml.dump(config, f)
f.close()
...@@ -17,7 +17,6 @@ import logging ...@@ -17,7 +17,6 @@ import logging
import os import os
import shutil import shutil
import sys import sys
import paddle import paddle
from x2paddle.decoder.onnx_decoder import ONNXDecoder from x2paddle.decoder.onnx_decoder import ONNXDecoder
from x2paddle.op_mapper.onnx2paddle.onnx_op_mapper import ONNXOpMapper from x2paddle.op_mapper.onnx2paddle.onnx_op_mapper import ONNXOpMapper
...@@ -27,7 +26,78 @@ from x2paddle.utils import ConverterCheck ...@@ -27,7 +26,78 @@ from x2paddle.utils import ConverterCheck
from . import get_logger from . import get_logger
_logger = get_logger(__name__, level=logging.INFO) _logger = get_logger(__name__, level=logging.INFO)
__all__ = ['load_onnx_model'] __all__ = ['load_inference_model', 'get_model_dir', 'load_onnx_model']
def load_inference_model(path_prefix,
executor,
model_filename=None,
params_filename=None):
# Load onnx model to Inference model.
if path_prefix.endswith('.onnx'):
inference_program, feed_target_names, fetch_targets = load_onnx_model(
path_prefix)
return [inference_program, feed_target_names, fetch_targets]
# Load Inference model.
# TODO: clean code
if model_filename is not None and model_filename.endswith('.pdmodel'):
model_name = '.'.join(model_filename.split('.')[:-1])
assert os.path.exists(
os.path.join(path_prefix, model_name + '.pdmodel')
), 'Please check {}, or fix model_filename parameter.'.format(
os.path.join(path_prefix, model_name + '.pdmodel'))
assert os.path.exists(
os.path.join(path_prefix, model_name + '.pdiparams')
), 'Please check {}, or fix params_filename parameter.'.format(
os.path.join(path_prefix, model_name + '.pdiparams'))
model_path_prefix = os.path.join(path_prefix, model_name)
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=model_path_prefix, executor=executor))
elif model_filename is not None and params_filename is not None:
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=path_prefix,
executor=executor,
model_filename=model_filename,
params_filename=params_filename))
else:
model_name = '.'.join(model_filename.split('.')
[:-1]) if model_filename is not None else 'model'
if os.path.exists(os.path.join(path_prefix, model_name + '.pdmodel')):
model_path_prefix = os.path.join(path_prefix, model_name)
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=model_path_prefix, executor=executor))
else:
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(
path_prefix=path_prefix, executor=executor))
return [inference_program, feed_target_names, fetch_targets]
def get_model_dir(model_dir, model_filename, params_filename):
if model_dir.endswith('.onnx'):
updated_model_dir = model_dir.rstrip().rstrip('.onnx') + '_infer'
else:
updated_model_dir = model_dir.rstrip('/')
if model_filename == None:
updated_model_filename = 'model.pdmodel'
else:
updated_model_filename = model_filename
if params_filename == None:
updated_params_filename = 'model.pdiparams'
else:
updated_params_filename = params_filename
if params_filename is None and model_filename is not None:
raise NotImplementedError(
"NOT SUPPORT parameters saved in separate files. Please convert it to single binary file first."
)
return updated_model_dir, updated_model_filename, updated_params_filename
def load_onnx_model(model_path, disable_feedback=False): def load_onnx_model(model_path, disable_feedback=False):
......
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import pickle
import copy
import logging
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import numpy as np
import paddle
from paddle.fluid import core
from paddle.fluid import framework
from paddle.fluid.framework import IrGraph
from paddle.fluid.executor import global_scope
from paddle.fluid.contrib.slim.quantization import PostTrainingQuantization
from paddle.fluid.contrib.slim.quantization.utils import _get_op_input_var_names, load_variable_data
from .quanter import quant_post
from ..core import GraphWrapper
from ..common import get_logger
from ..common import get_feed_vars, wrap_dataloader, load_inference_model, get_model_dir
_logger = get_logger(__name__, level=logging.INFO)
__all__ = ["AnalysisQuant"]
class AnalysisQuant(object):
def __init__(
self,
model_dir,
model_filename=None,
params_filename=None,
eval_function=None,
quantizable_op_type=["conv2d", "depthwise_conv2d", "mul"],
weight_quantize_type='abs_max',
activation_quantize_type='moving_average_abs_max',
is_full_quantize=False,
batch_size=10,
batch_nums=10,
data_loader=None,
save_dir='analysis_results',
checkpoint_name='analysis_checkpoint.pkl',
num_histogram_plots=10, ):
"""
AnalysisQuant provides to analysis the sensitivity of each op in the model.
Args:
model_dir(str): the path of fp32 model that will be quantized
model_filename(str): the model file name of the fp32 model
params_filename(str): the parameter file name of the fp32 model
eval_function(function): eval function, define by yourself to return the metric of the inference program, can be used to judge the metric of quantized model. (TODO: optional)
quantizable_op_type(list, optional): op types that can be quantized
batch_size(int, optional): the batch size of DataLoader, default is 10
data_loader(Python Generator, Paddle.io.DataLoader, optional): the
Generator or Dataloader provides calibrate data, and it could
return a batch every time
save_dir(str, optional): the output dir that stores the analyzed information
checkpoint_name(str, optional): the name of checkpoint file that saves analyzed information and avoids break off while ananlyzing
num_histogram_plots: the number histogram plots you want to visilize, the plots will show in one PDF file in the save_dir
"""
self.model_dir = model_dir
self.model_filename = model_filename
self.params_filename = params_filename
self.batch_nums = batch_nums
self.quantizable_op_type = quantizable_op_type
self.weight_quantize_type = weight_quantize_type
self.activation_quantize_type = activation_quantize_type
self.is_full_quantize = is_full_quantize
self.histogram_bins = 1000
self.save_dir = save_dir
self.eval_function = eval_function
self.quant_layer_names = []
self.checkpoint_name = os.path.join(save_dir, checkpoint_name)
self.quant_layer_metrics = {}
self.batch_size = batch_size
self.batch_nums = batch_nums
self.num_histogram_plots = num_histogram_plots
if not os.path.exists(self.save_dir):
os.mkdir(self.save_dir)
devices = paddle.device.get_device().split(':')[0]
self.places = paddle.device._convert_to_place(devices)
executor = paddle.static.Executor(self.places)
# load model
[program, self.feed_list, self.fetch_list]= load_inference_model( \
model_dir, \
executor=executor, \
model_filename=model_filename, \
params_filename=params_filename)
# create data_loader
self.data_loader = wrap_dataloader(data_loader, self.feed_list)
# evaluate before quant
# TODO: self.eval_function can be None
if self.eval_function is not None:
self.base_metric = self.eval_function(
executor, program, self.feed_list, self.fetch_list)
_logger.info('before quantized, the accuracy of the model is: {}'.
format(self.base_metric))
# quant and evaluate after quant (skip_list = None)
post_training_quantization = PostTrainingQuantization(
executor=executor,
data_loader=self.data_loader,
model_dir=self.model_dir,
model_filename=self.model_filename,
params_filename=self.params_filename,
batch_size=self.batch_size,
batch_nums=self.batch_nums,
algo='avg', # fastest
quantizable_op_type=self.quantizable_op_type,
weight_quantize_type=self.weight_quantize_type,
activation_quantize_type=self.activation_quantize_type,
is_full_quantize=self.is_full_quantize,
skip_tensor_list=None, )
program = post_training_quantization.quantize()
self.quant_metric = self.eval_function(executor, program,
self.feed_list, self.fetch_list)
_logger.info('after quantized, the accuracy of the model is: {}'.format(
self.quant_metric))
# get quantized weight and act var name
self.quantized_weight_var_name = post_training_quantization._quantized_weight_var_name
self.quantized_act_var_name = post_training_quantization._quantized_act_var_name
executor.close()
# load tobe_analyized_layer from checkpoint
self.load_checkpoint()
self.tobe_analyized_layer = self.quantized_weight_var_name - set(
list(self.quant_layer_metrics.keys()))
self.tobe_analyized_layer = sorted(list(self.tobe_analyized_layer))
def analysis(self):
self.compute_quant_sensitivity()
self.sensitivity_ranklist = sorted(
self.quant_layer_metrics,
key=self.quant_layer_metrics.get,
reverse=False)
_logger.info('Finished computing the sensitivity of the model.')
for name in self.sensitivity_ranklist:
_logger.info("quant layer name: {}, eval metric: {}".format(
name, self.quant_layer_metrics[name]))
analysis_file = os.path.join(self.save_dir, "analysis.txt")
with open(analysis_file, "w") as analysis_ret_f:
for name in self.sensitivity_ranklist:
analysis_ret_f.write(
"quant layer name: {}, eval metric: {}\n".format(
name, self.quant_layer_metrics[name]))
_logger.info('Analysis file is saved in {}'.format(analysis_file))
self.calculate_histogram()
self.draw_pdf()
def save_checkpoint(self):
if not os.path.exists(self.save_dir):
os.makedirs(self.save_dir)
with open(self.checkpoint_name, 'wb') as f:
pickle.dump(self.quant_layer_metrics, f)
_logger.info('save checkpoint to {}'.format(self.checkpoint_name))
def load_checkpoint(self):
if not os.path.exists(self.checkpoint_name):
return False
with open(self.checkpoint_name, 'rb') as f:
self.quant_layer_metrics = pickle.load(f)
_logger.info('load checkpoint from {}'.format(self.checkpoint_name))
return True
def compute_quant_sensitivity(self):
'''
For each layer, quantize the weight op and evaluate the quantized model.
'''
for i, layer_name in enumerate(self.tobe_analyized_layer):
_logger.info('checking {}/{} quant model: quant layer {}'.format(
i + 1, len(self.tobe_analyized_layer), layer_name))
skip_list = copy.copy(list(self.quantized_weight_var_name))
skip_list.remove(layer_name)
executor = paddle.static.Executor(self.places)
post_training_quantization = PostTrainingQuantization(
executor=executor,
data_loader=self.data_loader,
model_dir=self.model_dir,
model_filename=self.model_filename,
params_filename=self.params_filename,
batch_size=self.batch_size,
batch_nums=self.batch_nums,
algo='avg', # fastest
quantizable_op_type=self.quantizable_op_type,
weight_quantize_type=self.weight_quantize_type,
activation_quantize_type=self.activation_quantize_type,
is_full_quantize=self.is_full_quantize,
skip_tensor_list=skip_list, )
program = post_training_quantization.quantize()
_logger.info('Evaluating...')
quant_metric = self.eval_function(executor, program, self.feed_list,
self.fetch_list)
executor.close()
_logger.info(
"quant layer name: {}, eval metric: {}, the loss caused by this layer: {}".
format(layer_name, quant_metric, self.base_metric -
quant_metric))
self.quant_layer_metrics[layer_name] = quant_metric
self.save_checkpoint()
def get_sensitive_ops_name(self, graph, program):
sensitive_weight_ops = self.sensitivity_ranklist[:self.
num_histogram_plots]
sensitive_act_ops = []
persistable_var_names = []
persistable_var_names = []
for var in program.list_vars():
if var.persistable:
persistable_var_names.append(var.name)
for op_name in sensitive_weight_ops:
for block_id in range(len(program.blocks)):
for op in program.blocks[block_id].ops:
var_name_list = _get_op_input_var_names(op)
if op_name in var_name_list:
for var_name in var_name_list:
if var_name not in persistable_var_names:
sensitive_act_ops.append(var_name)
return sensitive_act_ops, sensitive_weight_ops
def calculate_histogram(self):
'''
Sample histograms for the weight and corresponding act tensors
'''
devices = paddle.device.get_device().split(':')[0]
places = paddle.device._convert_to_place(devices)
executor = paddle.static.Executor(places)
[program, feed_list, fetch_list]= load_inference_model( \
self.model_dir, \
executor=executor, \
model_filename=self.model_filename, \
params_filename=self.params_filename)
scope = global_scope()
graph = IrGraph(core.Graph(program.desc), for_test=False)
self.sensitive_act_ops, self.sensitive_weight_ops = self.get_sensitive_ops_name(
graph, program)
for var in program.list_vars():
if var.name in self.quantized_act_var_name:
var.persistable = True
batch_id = 0
for data in self.data_loader():
executor.run(program=program,
feed=data,
fetch_list=fetch_list,
return_numpy=False,
scope=scope)
batch_id += 1
if batch_id >= self.batch_nums:
break
self.weight_histogram = {}
self.act_histogram = {}
for var_name in self.sensitive_act_ops:
var_tensor = load_variable_data(scope, var_name)
var_tensor = np.array(var_tensor)
min_v = float(np.min(var_tensor))
max_v = float(np.max(var_tensor))
var_tensor = var_tensor.flatten()
_, hist_edges = np.histogram(
var_tensor.copy(),
bins=self.histogram_bins,
range=(min_v, max_v))
self.act_histogram[var_name] = [var_tensor, hist_edges]
for var_name in self.sensitive_weight_ops:
var_tensor = load_variable_data(scope, var_name)
var_tensor = np.array(var_tensor)
min_v = float(np.min(var_tensor))
max_v = float(np.max(var_tensor))
var_tensor = var_tensor.flatten()
_, hist_edges = np.histogram(
var_tensor.copy(),
bins=self.histogram_bins,
range=(min_v, max_v))
self.weight_histogram[var_name] = [var_tensor, hist_edges]
def draw_pdf(self):
pdf_path_a = os.path.join(self.save_dir, 'act_hist_result.pdf')
pdf_path_w = os.path.join(self.save_dir, 'weight_hist_result.pdf')
with PdfPages(pdf_path_a) as pdf:
for name in self.act_histogram:
plt.hist(
self.act_histogram[name][0],
bins=self.act_histogram[name][1])
plt.xlabel(name)
plt.ylabel("frequency")
plt.title("Hist of variable {}".format(name))
plt.show()
pdf.savefig()
plt.close()
with PdfPages(pdf_path_w) as pdf:
for name in self.weight_histogram:
plt.hist(
self.weight_histogram[name][0],
bins=self.weight_histogram[name][1])
plt.xlabel(name)
plt.ylabel("frequency")
plt.title("Hist of variable {}".format(name))
plt.show()
pdf.savefig()
plt.close()
_logger.info('Histogram plots are saved in {} and {}'.format(
pdf_path_a, pdf_path_w))
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册