Skip to content

Commit 26b6d7b

Browse files
authored
optimize channelprune in ac (PaddlePaddle#1564)
* optimize prune in ac
1 parent 3616d59 commit 26b6d7b

File tree

15 files changed

+824
-131
lines changed

15 files changed

+824
-131
lines changed

docs/zh_cn/api_cn/static/auto-compression/auto_compression_api.rst

+72-62
Large diffs are not rendered by default.

docs/zh_cn/api_cn/static/auto-compression/custom_function.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
1.3 自定义计算逻辑
3636
##########
3737

38-
首先需要根据 `如何基于Paddle自定义DataLoader <>`_ 章节定义测试数据集 ``test_dataloader`` 。
38+
首先需要根据 `如何基于Paddle自定义DataLoader <https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/beginner/data_load_cn.html>`_ 章节定义测试数据集 ``test_dataloader`` 。
3939

4040
```python
4141

example/auto_compression/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
PaddleSlim推出全新自动化压缩工具(Auto Compression Toolkit, ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。
2929

30+
- ACT可以自动处理常见的预测模型,如果有更特殊的改造需求,可以参考:[ACT超参配置教程](./hyperparameter_tutorial.md)来进行单独配置压缩策略。
31+
- ACT接口各个参数详细含义可以参考: [ACT API文档](../docs/zh_cn/api_cn/static/auto-compression/auto_compression_api.rst)
32+
- 一些问题以及解决方案可以参考:[FAQ](./hyperparameter_tutorial.md#12-faq)。如果FAQ不能解决您的问题,欢迎加入用户群或者通过[GitHub Issues](https://github.com/PaddlePaddle/PaddleSlim/issues)给我们提issues。
33+
3034
## **News** 📢
3135

3236
* 🔥 【**直播分享****2022.11.7 晚 20:30~21:30《PaddleSlim自动压缩CV专场》。扫码报名,进入直播技术交流群**
@@ -251,6 +255,7 @@ ac.compress()
251255
## 进阶使用
252256

253257
- ACT可以自动处理常见的预测模型,如果有更特殊的改造需求,可以参考[ACT超参配置教程](./hyperparameter_tutorial.md)来进行单独配置压缩策略。
258+
- ACT接口各个参数详细含义可以参考 [ACT API文档](../docs/zh_cn/api_cn/static/auto-compression/auto_compression_api.rst)。
254259

255260
## 社区交流
256261

example/auto_compression/hyperparameter_tutorial.md

+9-34
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ QuantAware:
1111
use_pact: false # 量化训练是否使用PACT方法
1212
weight_quantize_type: 'channel_wise_abs_max' # 权重量化方式
1313
quantize_op_types: [conv2d, depthwise_conv2d] # 量化OP列表
14-
onnx_format: false # 是否采用ONNX量化标准格式
14+
onnx_format: false # 化后的模型是否和符合ONNX量化格式标准
1515
############### 不常用,以下参数不用设置 #########################
1616
activation_bits: 8 # 激活量化比特数
1717
weight_bits: 8 # 权重量化比特数
@@ -34,7 +34,7 @@ QuantAware:
3434
from paddleslim.quant.quanter import TRANSFORM_PASS_OP_TYPES,QUANT_DEQUANT_PASS_OP_TYPES
3535
print(TRANSFORM_PASS_OP_TYPES + QUANT_DEQUANT_PASS_OP_TYPES)
3636
```
37-
- onnx_format: 是否采用ONNX量化格式标准,如果需要导出成ONNX,则需要设置为True。
37+
- onnx_format: 量化后的模型是否和符合ONNX量化格式标准,**如果需要导出成ONNX,则需要设置为True。**
3838
- activation_bits: 激活量化bit数,可选1~8。默认为8。
3939
- weight_bits: 参数量化bit数,可选1~8。默认为8。
4040
- activation_quantize_type: 激活量化方式,可选 'abs_max' , 'range_abs_max' , 'moving_average_abs_max' 。如果使用 TensorRT 加载量化后的模型来预测,请使用 'range_abs_max' 或 'moving_average_abs_max' 。默认为 'moving_average_abs_max'。
@@ -154,22 +154,7 @@ ChannelPrune:
154154
```
155155

156156
- pruned_ratio: 每个卷积层的通道数被剪裁的比例。
157-
- prune_params_name: 待剪裁的卷积层的权重名称。通过以下脚本获得推理模型中所有卷积层的权重名称:
158-
159-
```
160-
import paddle
161-
paddle.enable_static()
162-
model_dir="./inference_model"
163-
exe = paddle.static.Executor(paddle.CPUPlace())
164-
[inference_program, feed_target_names, fetch_targets] = (
165-
paddle.static.load_inference_model(model_dir, exe))
166-
for var_ in inference_program.list_vars():
167-
if var_.persistable and "conv2d" in var_.name:
168-
print(f"{var_.name}")
169-
```
170-
171-
或者,使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。
172-
157+
- prune_params_name: 待剪裁的卷积层的权重名称。如果设置为 "None", 则会按照传入的剪枝比例对所有可以裁剪的卷积层进行裁剪。或者可以参考[结构化剪枝敏感度分析工具](./prune_sensitivity_analysis/README.md)获得合适的要剪枝的参数和比例。也可以使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。默认:"None"。
173158
- criterion: 评估卷积通道重要性的指标。可选 “l1_norm” , “bn_scale” , “geometry_median”。具体定义和使用可参考[结构化稀疏API文档](https://paddleslim.readthedocs.io/zh_CN/latest/api_cn/static/prune/prune_api.html)
174159

175160
### 1.1.6 ASP半结构化稀疏
@@ -181,21 +166,7 @@ ASPPrune:
181166
- conv1_weights
182167
```
183168
184-
- prune_params_name: 待剪裁的卷积层的权重名称。通过以下脚本获得推理模型中所有卷积层的权重名称:
185-
186-
```
187-
import paddle
188-
paddle.enable_static()
189-
model_dir="./inference_model"
190-
exe = paddle.static.Executor(paddle.CPUPlace())
191-
[inference_program, feed_target_names, fetch_targets] = (
192-
paddle.static.load_inference_model(model_dir, exe))
193-
for var_ in inference_program.list_vars():
194-
if var_.persistable and "conv2d" in var_.name:
195-
print(f"{var_.name}")
196-
```
197-
198-
或者,使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。
169+
- prune_params_name: 待剪裁的卷积层的权重名称。如果设置为 "None", 则会按照传入的剪枝比例对所有可以裁剪的卷积层进行裁剪。或者,使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。
199170

200171
### 1.1.7 Transformer结构化剪枝
201172

@@ -242,7 +213,7 @@ UnstructurePrune:
242213
{'pruning_steps': int} # the total times you want to increase the ratio
243214
{'initial_ratio': float} # the initial ratio value
244215
```
245-
- prune_params_type 目前只支持None和"conv1x1_only"两个选项,前者表示稀疏化除了归一化层的参数,后者表示只稀疏化1x1卷积。
216+
- prune_params_type 目前只支持None和"conv1x1_only"两个选项,前者表示稀疏化除了归一化层的参数,后者表示只稀疏化1x1卷积。默认:"conv1x1_only".
246217
- local_sparsity 表示剪裁比例(ratio)应用的范围,仅在 'ratio' 模式生效。local_sparsity 开启时意味着每个参与剪裁的参数矩阵稀疏度均为 'ratio', 关闭时表示只保证模型整体稀疏度达到'ratio',但是每个参数矩阵的稀疏度可能存在差异。各个矩阵稀疏度保持一致时,稀疏加速更显著。
247218
- 更多非结构化稀疏的参数含义详见[非结构化稀疏API文档](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/dygraph/pruners/unstructured_pruner.rst)
248219

@@ -333,3 +304,7 @@ for var_ in inference_program.list_vars():
333304
paddle.static.save_inference_model("./infer_model", feed_vars, fetch_targets, exe, program=inference_program)
334305

335306
```
307+
308+
### 5. 量化后模型如何导出成ONNX格式
309+
310+
如果想导出ONNX格式的模型,需要在量化的时候设置 ``onnx_format=True``,而且仅支持PaddlePaddle2.4rc0 和PaddleSlim2.4rc0以上版本。

example/auto_compression/image_classification/README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@
5959
#### 3.1 准备环境
6060

6161
- python >= 3.6
62-
- PaddlePaddle >= 2.3 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装)
63-
- PaddleSlim >= 2.3
62+
- PaddlePaddle >= 2.4 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装)
63+
- PaddleSlim >= 2.4
6464

6565
安装paddlepaddle:
6666
```shell
@@ -77,7 +77,9 @@ pip install paddleslim
7777

7878
若使用`run_ppclas.py`脚本,需安装paddleclas:
7979
```shell
80-
pip install paddleclas
80+
git clone https://github.com/PaddlePaddle/PaddleClas.git -b release/2.5
81+
cd PaddleClas
82+
pip install --upgrade -r requirements.txt
8183
```
8284

8385
#### 3.2 准备数据集
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# 结构化剪枝敏感度分析
2+
3+
本示例将以自动压缩示例中MobileNetV1为例,介绍如何快速修改示例代码,进行结构化剪枝敏感度分析工具分析模型参数敏感度,从而设置合适的剪枝比例和要剪枝的参数,在保证剪枝后模型精度的前提下进行最大比例的模型剪枝。
4+
图像分类除MobileNetV1模型外其他模型的结构化剪枝敏感度分析可以直接使用 [run.py](./run.py) 脚本,替换传入的 config_path 文件为其他模型的任一压缩yaml文件,即可对其他图像分类模型进行敏感度分析。
5+
6+
## 计算通道剪枝敏感度
7+
8+
以下为示例代码每一步的含义,如果您是ACT(自动压缩工具)的用户,加粗文字表示如何把一个自动压缩示例改为一个敏感度分析示例。
9+
10+
### 1. 引入依赖
11+
12+
引入一些需要的依赖,可以直接复用以下代码,如果您需要对其他场景下模型进行敏感度分析,需要把其他场景文件下中 ``run.py`` 文件中独有的依赖也导入进来。**或者把最后一个依赖放入自动压缩示例代码中。**
13+
14+
```python
15+
import os
16+
import sys
17+
import argparse
18+
import pickle
19+
import functools
20+
from functools import partial
21+
import math
22+
from tqdm import tqdm
23+
24+
import numpy as np
25+
import paddle
26+
import paddle.nn as nn
27+
from paddle.io import DataLoader
28+
import paddleslim
29+
from imagenet_reader import ImageNetDataset
30+
from paddleslim.common import load_config as load_slim_config
31+
from paddleslim.auto_compression.analysis import analysis_prune
32+
```
33+
34+
### 2. 定义可传入参数
35+
36+
定义一些可以通过指令传入的参数。**此段代码无论您想对任何场景的模型进行分析都无需修改,复制过去替换原本的指令即可**
37+
38+
```python
39+
def argsparser():
40+
parser = argparse.ArgumentParser(description=__doc__)
41+
parser.add_argument(
42+
'--config_path',
43+
type=str,
44+
default=None,
45+
help="path of compression strategy config.",
46+
required=True)
47+
parser.add_argument(
48+
'--analysis_file',
49+
type=str,
50+
default='sensitivity_0.data',
51+
help="directory to save compressed model.")
52+
parser.add_argument(
53+
'--pruned_ratios',
54+
nargs='+',
55+
type=float,
56+
default=[0.1, 0.2, 0.3, 0.4],
57+
help="The ratios to be pruned when compute sensitivity.")
58+
parser.add_argument(
59+
'--target_loss',
60+
type=float,
61+
default=0.2,
62+
help="use the target loss to get prune ratio of each parameter")
63+
64+
return parser
65+
66+
67+
```
68+
69+
### 3. 定义eval_function
70+
71+
需要定义完整的测试流程,可以直接使用对应场景文件夹下 ``run.py`` 文件中的测试流程即可,**把自动压缩示例代码中测试回调函数中下面这一行代码:**
72+
73+
```python
74+
def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
75+
```
76+
**修改成:**
77+
```python
78+
def eval_function(compiled_test_program, exe, test_feed_names, test_fetch_list):
79+
```
80+
81+
最终的测试过程代码如下:
82+
```python
83+
def eval_reader(data_dir, batch_size, crop_size, resize_size, place=None):
84+
val_reader = ImageNetDataset(
85+
mode='val',
86+
data_dir=data_dir,
87+
crop_size=crop_size,
88+
resize_size=resize_size)
89+
val_loader = DataLoader(
90+
val_reader,
91+
places=[place] if place is not None else None,
92+
batch_size=global_config['batch_size'],
93+
shuffle=False,
94+
drop_last=False,
95+
num_workers=0)
96+
return val_loader
97+
98+
99+
def eval_function(compiled_test_program, exe, test_feed_names, test_fetch_list):
100+
val_loader = eval_reader(
101+
global_config['data_dir'],
102+
batch_size=global_config['batch_size'],
103+
crop_size=img_size,
104+
resize_size=resize_size)
105+
106+
results = []
107+
with tqdm(
108+
total=len(val_loader),
109+
bar_format='Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}',
110+
ncols=80) as t:
111+
for batch_id, (image, label) in enumerate(val_loader):
112+
# top1_acc, top5_acc
113+
if len(test_feed_names) == 1:
114+
image = np.array(image)
115+
label = np.array(label).astype('int64')
116+
pred = exe.run(compiled_test_program,
117+
feed={test_feed_names[0]: image},
118+
fetch_list=test_fetch_list)
119+
pred = np.array(pred[0])
120+
label = np.array(label)
121+
sort_array = pred.argsort(axis=1)
122+
top_1_pred = sort_array[:, -1:][:, ::-1]
123+
top_1 = np.mean(label == top_1_pred)
124+
top_5_pred = sort_array[:, -5:][:, ::-1]
125+
acc_num = 0
126+
for i in range(len(label)):
127+
if label[i][0] in top_5_pred[i]:
128+
acc_num += 1
129+
top_5 = float(acc_num) / len(label)
130+
results.append([top_1, top_5])
131+
else:
132+
# eval "eval model", which inputs are image and label, output is top1 and top5 accuracy
133+
image = np.array(image)
134+
label = np.array(label).astype('int64')
135+
result = exe.run(compiled_test_program,
136+
feed={
137+
test_feed_names[0]: image,
138+
test_feed_names[1]: label
139+
},
140+
fetch_list=test_fetch_list)
141+
result = [np.mean(r) for r in result]
142+
results.append(result)
143+
t.update()
144+
result = np.mean(np.array(results), axis=0)
145+
return result[0]
146+
```
147+
148+
### 4. 加载配置文件
149+
加载配置文件,获得文件中数据读取部分的相关配置。**使用原始的自动压缩示例代码中的即可**
150+
```python
151+
global global_config
152+
all_config = load_slim_config(args.config_path)
153+
154+
assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}"
155+
global_config = all_config["Global"]
156+
157+
global img_size, resize_size
158+
img_size = global_config['img_size'] if 'img_size' in global_config else 224
159+
resize_size = global_config[
160+
'resize_size'] if 'resize_size' in global_config else 256
161+
```
162+
163+
### 4. 进行敏感度分析
164+
165+
传入测试回调函数,配置(主要包括模型位置和模型名称等信息),分析文件保存的位置,要分析的裁剪比例和可以接受的精度目标损失。如果不传入可以接受的精度目标损失,则只返回敏感度分析情况。**把自动压缩代码中调用AutoCompression 和 ac.compress 的代码替换成以下代码即可**
166+
167+
```python
168+
analysis_prune(eval_function, global_config['model_dir'], global_config['model_filename'], global_config['params_filename'], args.analysis_file,
169+
args.pruned_ratios, args.target_loss)
170+
```

0 commit comments

Comments
 (0)