Skip to content

Commit

Permalink
optimize channelprune in ac (PaddlePaddle#1564)
Browse files Browse the repository at this point in the history
* optimize prune in ac
  • Loading branch information
ceci3 authored Dec 12, 2022
1 parent 3616d59 commit 26b6d7b
Show file tree
Hide file tree
Showing 15 changed files with 824 additions and 131 deletions.
134 changes: 72 additions & 62 deletions docs/zh_cn/api_cn/static/auto-compression/auto_compression_api.rst

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
1.3 自定义计算逻辑
##########

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

```python
Expand Down
5 changes: 5 additions & 0 deletions example/auto_compression/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

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

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

## **News** 📢

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

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

## 社区交流

Expand Down
43 changes: 9 additions & 34 deletions example/auto_compression/hyperparameter_tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ QuantAware:
use_pact: false # 量化训练是否使用PACT方法
weight_quantize_type: 'channel_wise_abs_max' # 权重量化方式
quantize_op_types: [conv2d, depthwise_conv2d] # 量化OP列表
onnx_format: false # 是否采用ONNX量化标准格式
onnx_format: false # 化后的模型是否和符合ONNX量化格式标准
############### 不常用,以下参数不用设置 #########################
activation_bits: 8 # 激活量化比特数
weight_bits: 8 # 权重量化比特数
Expand All @@ -34,7 +34,7 @@ QuantAware:
from paddleslim.quant.quanter import TRANSFORM_PASS_OP_TYPES,QUANT_DEQUANT_PASS_OP_TYPES
print(TRANSFORM_PASS_OP_TYPES + QUANT_DEQUANT_PASS_OP_TYPES)
```
- onnx_format: 是否采用ONNX量化格式标准,如果需要导出成ONNX,则需要设置为True。
- onnx_format: 量化后的模型是否和符合ONNX量化格式标准,**如果需要导出成ONNX,则需要设置为True。**
- activation_bits: 激活量化bit数,可选1~8。默认为8。
- weight_bits: 参数量化bit数,可选1~8。默认为8。
- activation_quantize_type: 激活量化方式,可选 'abs_max' , 'range_abs_max' , 'moving_average_abs_max' 。如果使用 TensorRT 加载量化后的模型来预测,请使用 'range_abs_max' 或 'moving_average_abs_max' 。默认为 'moving_average_abs_max'。
Expand Down Expand Up @@ -154,22 +154,7 @@ ChannelPrune:
```

- pruned_ratio: 每个卷积层的通道数被剪裁的比例。
- prune_params_name: 待剪裁的卷积层的权重名称。通过以下脚本获得推理模型中所有卷积层的权重名称:

```
import paddle
paddle.enable_static()
model_dir="./inference_model"
exe = paddle.static.Executor(paddle.CPUPlace())
[inference_program, feed_target_names, fetch_targets] = (
paddle.static.load_inference_model(model_dir, exe))
for var_ in inference_program.list_vars():
if var_.persistable and "conv2d" in var_.name:
print(f"{var_.name}")
```

或者,使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。

- prune_params_name: 待剪裁的卷积层的权重名称。如果设置为 "None", 则会按照传入的剪枝比例对所有可以裁剪的卷积层进行裁剪。或者可以参考[结构化剪枝敏感度分析工具](./prune_sensitivity_analysis/README.md)获得合适的要剪枝的参数和比例。也可以使用[Netron工具](https://netron.app/) 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。默认:"None"。
- criterion: 评估卷积通道重要性的指标。可选 “l1_norm” , “bn_scale” , “geometry_median”。具体定义和使用可参考[结构化稀疏API文档](https://paddleslim.readthedocs.io/zh_CN/latest/api_cn/static/prune/prune_api.html)

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

### 1.1.7 Transformer结构化剪枝

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

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

```
### 5. 量化后模型如何导出成ONNX格式
如果想导出ONNX格式的模型,需要在量化的时候设置 ``onnx_format=True``,而且仅支持PaddlePaddle2.4rc0 和PaddleSlim2.4rc0以上版本。
8 changes: 5 additions & 3 deletions example/auto_compression/image_classification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
#### 3.1 准备环境

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

安装paddlepaddle:
```shell
Expand All @@ -77,7 +77,9 @@ pip install paddleslim

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

#### 3.2 准备数据集
Expand Down
170 changes: 170 additions & 0 deletions example/auto_compression/prune_sensitivity_analysis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# 结构化剪枝敏感度分析

本示例将以自动压缩示例中MobileNetV1为例,介绍如何快速修改示例代码,进行结构化剪枝敏感度分析工具分析模型参数敏感度,从而设置合适的剪枝比例和要剪枝的参数,在保证剪枝后模型精度的前提下进行最大比例的模型剪枝。
图像分类除MobileNetV1模型外其他模型的结构化剪枝敏感度分析可以直接使用 [run.py](./run.py) 脚本,替换传入的 config_path 文件为其他模型的任一压缩yaml文件,即可对其他图像分类模型进行敏感度分析。

## 计算通道剪枝敏感度

以下为示例代码每一步的含义,如果您是ACT(自动压缩工具)的用户,加粗文字表示如何把一个自动压缩示例改为一个敏感度分析示例。

### 1. 引入依赖

引入一些需要的依赖,可以直接复用以下代码,如果您需要对其他场景下模型进行敏感度分析,需要把其他场景文件下中 ``run.py`` 文件中独有的依赖也导入进来。**或者把最后一个依赖放入自动压缩示例代码中。**

```python
import os
import sys
import argparse
import pickle
import functools
from functools import partial
import math
from tqdm import tqdm

import numpy as np
import paddle
import paddle.nn as nn
from paddle.io import DataLoader
import paddleslim
from imagenet_reader import ImageNetDataset
from paddleslim.common import load_config as load_slim_config
from paddleslim.auto_compression.analysis import analysis_prune
```

### 2. 定义可传入参数

定义一些可以通过指令传入的参数。**此段代码无论您想对任何场景的模型进行分析都无需修改,复制过去替换原本的指令即可**

```python
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(
'--analysis_file',
type=str,
default='sensitivity_0.data',
help="directory to save compressed model.")
parser.add_argument(
'--pruned_ratios',
nargs='+',
type=float,
default=[0.1, 0.2, 0.3, 0.4],
help="The ratios to be pruned when compute sensitivity.")
parser.add_argument(
'--target_loss',
type=float,
default=0.2,
help="use the target loss to get prune ratio of each parameter")

return parser


```

### 3. 定义eval_function

需要定义完整的测试流程,可以直接使用对应场景文件夹下 ``run.py`` 文件中的测试流程即可,**把自动压缩示例代码中测试回调函数中下面这一行代码:**

```python
def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
```
**修改成:**
```python
def eval_function(compiled_test_program, exe, test_feed_names, test_fetch_list):
```

最终的测试过程代码如下:
```python
def eval_reader(data_dir, batch_size, crop_size, resize_size, place=None):
val_reader = ImageNetDataset(
mode='val',
data_dir=data_dir,
crop_size=crop_size,
resize_size=resize_size)
val_loader = DataLoader(
val_reader,
places=[place] if place is not None else None,
batch_size=global_config['batch_size'],
shuffle=False,
drop_last=False,
num_workers=0)
return val_loader


def eval_function(compiled_test_program, exe, test_feed_names, test_fetch_list):
val_loader = eval_reader(
global_config['data_dir'],
batch_size=global_config['batch_size'],
crop_size=img_size,
resize_size=resize_size)

results = []
with tqdm(
total=len(val_loader),
bar_format='Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}',
ncols=80) as t:
for batch_id, (image, label) in enumerate(val_loader):
# top1_acc, top5_acc
if len(test_feed_names) == 1:
image = np.array(image)
label = np.array(label).astype('int64')
pred = exe.run(compiled_test_program,
feed={test_feed_names[0]: image},
fetch_list=test_fetch_list)
pred = np.array(pred[0])
label = np.array(label)
sort_array = pred.argsort(axis=1)
top_1_pred = sort_array[:, -1:][:, ::-1]
top_1 = np.mean(label == top_1_pred)
top_5_pred = sort_array[:, -5:][:, ::-1]
acc_num = 0
for i in range(len(label)):
if label[i][0] in top_5_pred[i]:
acc_num += 1
top_5 = float(acc_num) / len(label)
results.append([top_1, top_5])
else:
# eval "eval model", which inputs are image and label, output is top1 and top5 accuracy
image = np.array(image)
label = np.array(label).astype('int64')
result = exe.run(compiled_test_program,
feed={
test_feed_names[0]: image,
test_feed_names[1]: label
},
fetch_list=test_fetch_list)
result = [np.mean(r) for r in result]
results.append(result)
t.update()
result = np.mean(np.array(results), axis=0)
return result[0]
```

### 4. 加载配置文件
加载配置文件,获得文件中数据读取部分的相关配置。**使用原始的自动压缩示例代码中的即可**
```python
global global_config
all_config = load_slim_config(args.config_path)

assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}"
global_config = all_config["Global"]

global img_size, resize_size
img_size = global_config['img_size'] if 'img_size' in global_config else 224
resize_size = global_config[
'resize_size'] if 'resize_size' in global_config else 256
```

### 4. 进行敏感度分析

传入测试回调函数,配置(主要包括模型位置和模型名称等信息),分析文件保存的位置,要分析的裁剪比例和可以接受的精度目标损失。如果不传入可以接受的精度目标损失,则只返回敏感度分析情况。**把自动压缩代码中调用AutoCompression 和 ac.compress 的代码替换成以下代码即可**

```python
analysis_prune(eval_function, global_config['model_dir'], global_config['model_filename'], global_config['params_filename'], args.analysis_file,
args.pruned_ratios, args.target_loss)
```
Loading

0 comments on commit 26b6d7b

Please sign in to comment.