未验证 提交 9b5c5202 编写于 作者: C ceci3 提交者: GitHub

[cherry pick] optimize channelprune in ac (#1598)

上级 5030efd6
......@@ -12,29 +12,34 @@ AutoCompression
**参数: **
- **model_dir(str)** - 需要压缩的推理模型所在的目录。
- **train_dataloader(paddle.io.DataLoader)** - 训练数据迭代器。注意:如果选择离线量化超参搜索策略的话, ``train_dataloader`` 和 ``eval_callback`` 设置相同的数据读取即可。
- **model_filename(str)** - 需要压缩的推理模型文件名称。
- **params_filename(str)** - 需要压缩的推理模型参数文件名称。
- **train_dataloader(paddle.io.DataLoader)** - 训练数据迭代器。注意:如果选择离线量化超参搜索策略的话, ``train_dataloader`` 和 ``eval_dataloader`` 设置相同的数据读取即可。
- **model_filename(str)** - 需要压缩的推理模型文件名称。如果压缩的是onnx模型,则本参数设置为 ``None`` 即可。
- **params_filename(str)** - 需要压缩的推理模型参数文件名称。如果压缩的是onnx模型,则本参数设置为 ``None`` 即可。
- **save_dir(str)** - 压缩后模型的所保存的目录。
- **train_config(dict)** - 训练配置。可以配置的参数请参考: `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L103>`_ 。注意:如果选择离线量化超参搜索策略的话, ``train_config`` 直接设置为 ``None`` 即可。
- **input_shapes(dict|tuple|list)** - 如果模型除 ``batch size`` 维度外还有可变维度(某一维度为-1意味着当前维度是可变维度),则需要设置此参数在压缩前固定下来。如果设置的是dict类型,则关键字为输入的名字,对应的值为每个输入的具体shape,例如模型中输入 ``X`` 的形状为 ``[-1, 3, -1, -1]`` 意味着 ``batch size`` 维度、 ``hight`` 维度和 ``width`` 维度都是变化的, ``input_shape`` 可以设置为 ``{"X": [-1, 3, 512, 512]}`` 。如果 ``input_shapes`` 设置为list或者tuple形式的话,模型只能有一个输入,并且输入的形状会设置成 ``input_shapes`` 的形状。设置为 ``None`` 的话,就保持原始形状不变,可能会跳过搜索压缩策略的过程。默认: ``None`` 。
- **train_config(dict)** - 训练配置。可以配置的参数请参考: `TrainConfig <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L103>`_ 。注意:如果选择离线量化超参搜索策略的话, ``train_config`` 直接设置为 ``None`` 即可。
- **strategy_config(dict, list(dict), 可选)** - 使用的压缩策略,可以通过设置多个单种策略来并行使用这些压缩方式。字典的关键字必须在:
``Quantization`` (量化配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L24>`_ ),
``Distillation`` (蒸馏配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L39>`_),
``MultiTeacherDistillation`` (多teacher蒸馏配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L56>`_),
``HyperParameterOptimization`` (超参搜索配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L73>`_),
``Prune`` (剪枝配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L82>`_),
``UnstructurePrune`` (非结构化稀疏配置, 可配置的参数参考 `<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L91>`_) 之间选择。
``QuantAware`` (量化训练配置, 可配置的参数参考 `QuantAware <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L55>`_ ),
``QuantPost`` (离线量化配置, 可配置的参数参考 `QuantPost <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L187>`_ ),
``Distillation`` (蒸馏配置, 可配置的参数参考 `Distillation <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L107>`_),
``MultiTeacherDistillation`` (多teacher蒸馏配置, 可配置的参数参考 `MultiTeacherDistillation <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L134>`_),
``HyperParameterOptimization`` (超参搜索配置, 可配置的参数参考 `HyperParameterOptimization <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L160>`_),
``ChannelPrune`` (结构化稀疏配置, 可配置的参数参考 `ChannelPrune <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L254>`_),
``UnstructurePrune`` (非结构化稀疏配置, 可配置的参数参考 `UnstructurePrune <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L288>`_) 之间选择。
``ASPPrune`` (ASP半结构化结构化稀疏配置, 可配置的参数参考 `ASPPrune <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L268>`_) 之间选择。
``TransformerPrune`` (Transformer结构化稀疏配置, 只针对Transformer-encoder结构进行剪枝,可配置的参数参考 `TransformerPrune <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/auto_compression/strategy_config.py#L278>`_) 之间选择。
目前关键字只支持以下几种组合策略或者单策略配置:
1) ``Quantization`` & ``HyperParameterOptimization``: 离线量化超参搜索策略;
2) ``Quantization`` & ``Distillation``: 量化训练和蒸馏的策略;
3) ``ChannelPrune`` & ``Distillation``: 结构化剪枝和蒸馏的策略;
4) ``ASPPrune`` & ``Distillation``: ASP结构化剪枝和蒸馏的策略;
5) ``TransformerPrune`` & ``Distillation``: Transformer结构化剪枝和蒸馏的策略;
1) ``QuantPost`` & ``HyperParameterOptimization``: 离线量化超参搜索策略;
2) ``QuantAware`` & ``Distillation``: 量化训练和蒸馏的策略;
3) ``ChannelPrune`` & ``Distillation``: 结构化稀疏和蒸馏的策略;
4) ``ASPPrune`` & ``Distillation``: ASP半结构化稀疏和蒸馏的策略;
5) ``TransformerPrune`` & ``Distillation``: Transformer结构化稀疏和蒸馏的策略;
6) ``UnstructurePrune`` & ``Distillation``: 非结构化稀疏和蒸馏的策略;
7) ``Distillation``: 单独单蒸馏策略;
7) ``Distillation``: 单独单teacher蒸馏策略;
8) ``MultiTeacherDistillation``: 多teacher蒸馏策略。
设置为None的话会自动的选择策略去做压缩。默认:None。
- **eval_callback(function, 可选)** - eval回调函数,使用回调函数判断模型训练情况, 回调函数的写法参考: `<//github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/static/auto-compression/custom_function.rst>`_ 。 ``eval_callback`` 和 ``eval_dataloader`` 不能都设置为None。默认:None。
- **target_speedup(float, 可选)** - 目标加速比例,在支持硬件延时表的设备上会根据预估的加速进行压缩策略选择;在硬件延时表不支持的设备上会默认量化相比 ``float32`` 加速70%,剩下的加速比会等价设置成剪枝的比例(压缩后模型实测的加速情况和预计差别可能较大,暂时不太推荐在硬件延时表不支持的设备上使用本参数)。默认: ``None`` 。
- **eval_callback(function, 可选)** - eval回调函数,使用回调函数判断模型训练情况, 回调函数的写法参考: `custom_function <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/static/auto-compression/custom_function.rst>`_ 。 ``eval_callback`` 和 ``eval_dataloader`` 不能都设置为None。默认:None。
- **eval_dataloader(paddle.io.Dataloader, 可选)** - 如果传入测试数据迭代器,则使用 ``EMD`` 距离判断压缩前后模型之间的差别,目前仅支持离线量化超参搜索使用这种方式判断压缩前后模型的压缩。
- **deploy_hardware(str, 可选)** - 压缩后模型的部署硬件。默认: ``gpu`` 。
......@@ -42,55 +47,30 @@ AutoCompression
**示例代码:**
```shell
.. code-block:: shell
import paddle
from paddleslim.auto_compression import AutoCompression
default_qat_config = {
"quantize_op_types": ["conv2d", "depthwise_conv2d", "mul"],
"weight_bits": 8,
"activation_bits": 8,
"is_full_quantize": False,
"not_quant_pattern": ["skip_quant"],
}
default_distill_config = {
"loss": args.loss,
"node": args.node,
"alpha": args.alpha,
"teacher_model_dir": args.teacher_model_dir,
"teacher_model_filename": args.teacher_model_filename,
"teacher_params_filename": args.teacher_params_filename,
}
train_dataloader = Cifar10(mode='train')
eval_dataloader = Cifar10(mode='eval')
ac = AutoCompression(model_path, train_dataloader, model_filename, params_filename, save_dir, \
strategy_config="Quantization": Quantization(**default_ptq_config),
strategy_config="QuantPost": QuantPost(**default_ptq_config),
"Distillation": HyperParameterOptimization(**default_distill_config)}, \
train_config=None, eval_callback=eval_dataloader,devices='gpu')
```
train_config=None, eval_dataloader=eval_dataloader,devices='gpu')
.. py:method:: paddleslim.auto_compression.AutoCompression.compress()
......@@ -130,19 +110,33 @@ TrainConfig
- **sharding_config(dict, optional)** - 使用fleet api的前提下可以使用sharding 策略。参数按照fleet 接口中所描述的进行配置: `sharding_configs <https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/distributed/fleet/DistributedStrategy_cn.html#sharding_configs>`_ 。
- **sparse_model(bool, optional)** - 设置 ``sparse_model`` 为 True, 可以移出非结构化稀疏产出的模型中多余的mask tensor的变量,默认: False.
Quantization
QuantAware
----------
量化配置。
量化训练配置。
**参数:**
- **quantize_op_types(list[str])** - 需要进行量化的 op 类型。
- **weight_quantize_type(str)** - 参数量化方式,可选: ['channel_wise_abs_max', 'abs_max']。
- **weight_bits(int)** - 参数量化bit数。
- **activation_bits(int)** - 激活量化bit数。
- **is_full_quantize(bool)** - 是否量化所有可支持op类型。
- **not_quant_pattern(str|list[str])** - 所有 ``name_scope`` 包含 ``'not_quant_pattern'`` 字符串的 op 都不量化, 设置方式请参考 `fluid.name_scope <https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/name_scope_cn.html#name-scope>`_ 。
- **use_pact(bool)** - 是否开启PACT。一般情况下,开启PACT后,量化产出的模型精度会更高。算法原理请参考: `PACT: Parameterized Clipping Activation for Quantized Neural Networks <https://arxiv.org/abs/1805.06085>`_
- **weight_quantize_type(str)** - 参数量化方式,可选: ['channel_wise_abs_max', 'abs_max', 'moving_average_abs_max', 'range_abs_max']。如果使用 TensorRT 加载量化后的模型来预测,请使用 'channel_wise_abs_max' 。 默认 'channel_wise_abs_max' 。
- **quantize_op_types(list[str])** - 需要进行量化的 op 类型。通过以下代码输出所有支持量化的OP类型:
.. code-block:: shell
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(bool)** - 量化后的模型是否和符合ONNX量化格式标准, **如果需要导出成ONNX,则需要设置为True。** 默认:False。
- **weight_bits(int)** - 参数量化bit数。默认:8.
- **activation_bits(int)** - 激活量化bit数。默认:8。
- **activation_quantize_type(str)** - 激活量化方式,可选 'abs_max' , 'range_abs_max' , 'moving_average_abs_max' 。如果使用 TensorRT 加载量化后的模型来预测,请使用 'range_abs_max' 或 'moving_average_abs_max' 。默认为 'moving_average_abs_max'。
- **not_quant_pattern(str|list[str])** - 所有 ``name_scope`` 包含 ``'not_quant_pattern'`` 字符串的 op 都不量化, 设置方式请参考 `fluid.name_scope <https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/name_scope_cn.html#name-scope>`_ 。默认:'skip_quant'.
- **window_size(int)** - 'range_abs_max' 量化方式的 window size ,默认10000。
- **moving_rate(float)** - 'moving_average_abs_max' 量化方式的衰减系数,默认 0.9。
- **for_tensorrt(bool)** - 量化后的模型是否使用 TensorRT 进行预测。默认值为False. 通过以下代码,输出for_tensorrt=True时会量化到的OP:
.. code-block:: shell
from paddleslim.quant.quanter import TENSORRT_OP_TYPES
print(TENSORRT_OP_TYPES)
- **is_full_quantize(bool)** - 是否量化所有可支持op类型。默认:False。
Distillation
----------
......@@ -151,7 +145,7 @@ Distillation
**参数:**
- **loss(str|list[str])** - 蒸馏损失名字,可以设置的损失类型为paddleslim中支持的蒸馏损失,可选的损失函数有: ``fsp``, ``l2``, ``soft_label`` 。如果您需要其他损失函数,可以暂时通过向 `蒸馏损失文件<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/dist/single_distiller.py>`_ z中添加相应的损失函数计算,或者通过提issue的方式我们来协助解决。
- **loss(str|list[str])** - 蒸馏损失名字,可以设置的损失类型为paddleslim中支持的蒸馏损失,可选的损失函数有: ``fsp``, ``l2``, ``soft_label`` 。如果您需要其他损失函数,可以暂时通过向 `蒸馏损失文件 <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/dist/single_distiller.py>`_ 中添加相应的损失函数计算,或者通过提issue的方式我们来协助解决。
- **node(list[str])** - 蒸馏节点名字列表,可以选择:1. 使用自蒸馏的话,蒸馏结点仅包含学生网络节点即可, 支持多节点蒸馏; 2. 使用其他蒸馏的话,蒸馏节点需要包含教师网络节点和对应的学生网络节点, 每两个节点组成一对,分别属于教师模型和学生模型。
- **alpha(float|list[float])** - 每一个蒸馏损失的权重,长度需要和 ``loss`` 的长度保持一致。
......@@ -167,7 +161,7 @@ MultiTeacherDistillation
**参数:**
- **loss(list[str])** - 蒸馏损失名字,可以设置的损失类型为paddleslim中支持的蒸馏损失,可选的损失函数有: ``fsp``, ``l2``, ``soft_label`` 。如果您需要其他损失函数,可以暂时通过向 `蒸馏损失文件<https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/dist/single_distiller.py>`_ z中添加相应的损失函数计算,或者通过提issue的方式我们来协助解决。
- **loss(list[str])** - 蒸馏损失名字,可以设置的损失类型为paddleslim中支持的蒸馏损失,可选的损失函数有: ``fsp``, ``l2``, ``soft_label`` 。如果您需要其他损失函数,可以暂时通过向 `蒸馏损失文件 <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/dist/single_distiller.py>`_ 中添加相应的损失函数计算,或者通过提issue的方式我们来协助解决。
- **node(list[list[str]])** - 蒸馏节点名字嵌套列表,教师模型的个数和外部列表的长度需要保持一致。每一个列表代表一个教师模型和学生模型直接的蒸馏节点,其中每两个节点组成一对,分别属于教师模型和学生模型。
- **alpha(list[float])** - 每一个蒸馏损失的权重,长度需要和 ``distill_loss`` 的长度保持一致。
......@@ -194,17 +188,33 @@ HyperParameterOptimization
- **batch_num(int|list[int])** - 迭代次数, 设置类型为列表的话,列表中的最大最小值会作为上下界,在上下界范围内进行均匀采样。
- **max_quant_count(int)** - 超参搜索运行的最大轮数, 默认:20。
PruneConfig
ChannelPrune
----------
裁剪配置。
结构化稀疏配置。
**参数:**
- **prune_algo(str)** - 裁剪算法,可设置为: ``prune`` 或者 ``asp`` 。 ``prune`` 暂时只支持对视觉模型进行压缩, ``asp`` 裁剪暂时只支持对 ``FC`` 进行压缩。
- **pruned_ratio(float)** - 裁剪比例。
- **prune_params_name(list[str])** - 参与裁剪的参数的名字。
- **criterion(str)** - 裁剪算法设置为 ``prune`` 时,评估一个卷积层内通道重要性所参考的指标。目前支持 ``l1_norm``, ``bn_scale``, ``geometry_median`` 。
- **pruned_ratio(float)** - 每个卷积层的通道数被剪裁的比例。
- **prune_params_name(list[str])** - 参与裁剪的参数的名字。如果设置为 ``None`` , 则会按照传入的剪枝比例对所有可以裁剪的卷积层进行裁剪。合适的卷积层可以通过计算每一层的敏感度来选择,敏感度可以通过 `敏感度计算工具 <../../../../../example/auto_compression/prune_sensitivity_analysis/>`_ 来获得每层的敏感度信息,然后设置合适的裁剪的卷积层名字。也可以使用 `Netron工具 <https://netron.app/`_ 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。默认: ``None`` 。
- **criterion(str)** - 评估一个卷积层内通道重要性所参考的指标。目前支持 ``l1_norm``, ``bn_scale``, ``geometry_median`` 。具体定义和使用可参考 `结构化稀疏API文档 <https://paddleslim.readthedocs.io/zh_CN/latest/api_cn/static/prune/prune_api.html`_ 。
ASPPrune
----------
ASP半结构化稀疏配置
**参数:**
- **prune_params_name(list[str])** - 待剪裁的卷积层的权重名称。如果设置为 ``None``, 则会按照传入的剪枝比例对所有可以裁剪的卷积层进行裁剪。或者,使用 `Netron工具 <https://netron.app/>`_ 可视化`*.pdmodel`模型文件,选择合适的卷积层进行剪裁。默认: ``None`` 。
TransformerPrune
----------
针对Transformer结构的结构化剪枝参数
- **pruned_ratio(float)** - 每个全链接层的被剪裁的比例。
UnstructurePrune
----------
......@@ -221,5 +231,5 @@ UnstructurePrune
``prune_steps(int)`` - 迭代训练多少iteration后,改变稀疏比例。
``initial_ratio(float)`` - 初始的稀疏比例。
其它配置可以参考非结构化稀疏接口中 `configs参数 <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/static/prune/unstructured_prune_api.rst#gmpunstrucuturedpruner>`_ 的配置。
- **prune_params_type(str)** - 用以指定哪些类型的参数参与稀疏。目前只支持 ``None`` 和 ``conv1x1_only`` 两个选项,后者表示只稀疏化1x1卷积。而前者表示稀疏化除了归一化的参数。
- **prune_params_type(str)** - 用以指定哪些类型的参数参与稀疏。目前只支持 ``None`` 和 ``conv1x1_only`` 两个选项,后者表示只稀疏化1x1卷积。而前者表示稀疏化除了归一化的参数。默认: ``conv1x1_only`` 。
- **local_sparsity(bool)** - 剪裁比例(ratio)应用的范围: ``local_sparsity`` 开启时意味着每个参与剪裁的参数矩阵稀疏度均为 ``ratio`` , 关闭时表示只保证模型整体稀疏度达到 ``ratio`` ,但是每个参数矩阵的稀疏度可能存在差异。
......@@ -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
......
......@@ -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.8.22** [**PaddleSlim v2.3.3**](https://github.com/PaddlePaddle/PaddleSlim/releases/tag/v2.3.3)全新发布!目前已经在图像分类、目标检测、图像分割、NLP等20多个模型验证正向效果。
......@@ -69,25 +73,39 @@ ACT相比传统的模型压缩方法,
<font size=0.5>
| 模型类型 | model name | 压缩前<br/>精度(Top1 Acc %) | 压缩后<br/>精度(Top1 Acc %) | 压缩前<br/>推理时延(ms) | 压缩后<br/>推理时延(ms) | 推理<br/>加速比 | 芯片 |
| ------------------------------- | ---------------------------- | ---------------------- | ---------------------- | ---------------- | ---------------- | ---------- | ----------------- |
| ------------------------------- | ----------------------------- | ---------------------- | ---------------------- | ---------------- | ---------------- | ---------- | --------------- |
| [图像分类](./image_classification) | MobileNetV1 | 70.90 | 70.57 | 33.15 | 13.64 | **2.43** | SDM865(骁龙865) |
| [图像分类](./image_classification) | MobileNetV3_large_x1_0 | 75.32 | 74.04 | 16.62 | 9.85 | **1.69** | SDM865(骁龙865) |
| [图像分类](./image_classification) | MobileNetV3_large_x1_0_ssld | 78.96 | 77.17 | 16.62 | 9.85 | **1.69** | SDM865(骁龙865) |
| [图像分类](./image_classification) | ShuffleNetV2_x1_0 | 68.65 | 68.32 | 10.43 | 5.51 | **1.89** | SDM865(骁龙865) |
| [图像分类](./image_classification) | SqueezeNet1_0_infer | 59.60 | 59.45 | 35.98 | 16.96 | **2.12** | SDM865(骁龙865) |
| [图像分类](./image_classification) | PPLCNetV2_base | 76.86 | 76.43 | 36.50 | 15.79 | **2.31** | SDM865(骁龙865) |
| [图像分类](./image_classification) | PPLCNetV2_base | 76.86 | 76.39 | 36.50 | 15.79 | **2.31** | SDM865(骁龙865) |
| [图像分类](./image_classification) | ResNet50_vd | 79.12 | 78.74 | 3.19 | 0.92 | **3.47** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | PPHGNet_tiny | 79.59 | 79.20 | 2.82 | 0.98 | **2.88** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | PPHGNet_tiny | 79.59 | 79.20 | 2.82 | 0.98 | **2.88** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | InceptionV3 | 79.14 | 78.32 | 4.79 | 1.47 | **3.26** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | EfficientNetB0 | 77.02 | 74.27 | 1.95 | 1.44 | **1.35** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | GhostNet_x1_0 | 74.02 | 72.62 | 2.93 | 1.03 | **2.84** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | ViT_base_patch16_224 | 81.89 | 82.05 | 367.17 | 51.70 | **7.10** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | PP-HumanSeg-Lite | 92.87 | 92.35 | 56.36 | 37.71 | **1.49** | SDM710 |
| [语义分割](./semantic_segmentation) | PP-LiteSeg | 77.04 | 76.93 | 1.43 | 1.16 | **1.23** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | HRNet | 78.97 | 78.90 | 8.19 | 5.81 | **1.41** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | HRNet | 78.97 | 78.90 | 8.188 | 5.812 | **1.41** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | UNet | 65.00 | 64.93 | 15.29 | 10.23 | **1.49** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | Deeplabv3-ResNet50 | 79.90 | 79.26 | 12.766 | 8.839 | **1.44** | NVIDIA Tesla T4 |
| [语义分割](./semantic_segmentation) | BiSeNetV2 | 73.17 | 73.20 | 35.61 | 15.94 | **2.23** | NVIDIA Tesla T4 |
| [NLP](./nlp) | PP-MiniLM | 72.81 | 72.44 | 128.01 | 17.97 | **7.12** | NVIDIA Tesla T4 |
| [NLP](./nlp) | ERNIE 3.0-Medium | 73.09 | 72.40 | 29.25(fp16) | 19.61 | **1.49** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv5s<br/>(PyTorch) | 37.40 | 36.9 | 5.95 | 1.87 | **3.18** | NVIDIA Tesla T4 |
| [NLP](./nlp) | ERNIE 3.0-Medium | 73.09 | 72.16 | 29.25(fp16) | 19.61 | **1.49** | NVIDIA Tesla T4 |
| [NLP](./pytorch_huggingface) | bert-base-cased(Hugging-Face) | 81.35 | 81.51 | 11.60 | 4.83 | **2.40** | NVIDIA Tesla T4 |
| [目标检测](./detection) | SSD-MobileNetv1 | 73.8(voc) | 73.52 | 4.0 | 1.7 | **2.35** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv5s<br/>(PyTorch) | 37.4 | 36.9 | 5.95 | 1.87 | **3.18** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv6s<br/>(PyTorch) | 42.4 | 41.3 | 9.06 | 1.83 | **4.95** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv6s_v2(PyTorch) | 43.4 | 43.0 | 9.06 | 1.83 | **4.95** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv7-Tiny(PyTorch) | 37.3 | 37.0 | 5.06 | 1.68 | **3.01** | NVIDIA Tesla T4 |
| [目标检测](./pytorch_yolo_series) | YOLOv7<br/>(PyTorch) | 51.1 | 50.8 | 26.84 | 4.55 | **5.89** | NVIDIA Tesla T4 |
| [目标检测](./detection) | PP-YOLOE-l | 50.9 | 50.6 | 11.2 | 6.7 | **1.67** | NVIDIA Tesla T4 |
| [目标检测](./detection) | PP-YOLOE-s | 43.1 | 42.6 | 6.51 | 2.12 | **3.07** | NVIDIA Tesla T4 |
| [图像分类](./image_classification) | MobileNetV1<br/>(TensorFlow) | 71.0 | 70.22 | 30.45 | 15.86 | **1.92** | SDMM865(骁龙865) |
- 备注:目标检测精度指标为mAP(0.5:0.95)精度测量结果。图像分割精度指标为IoU精度测量结果。
- 更多飞桨模型应用示例及Benchmark可以参考:[图像分类](./image_classification)[目标检测](./detection)[语义分割](./semantic_segmentation)[自然语言处理](./nlp)
- 更多其它框架应用示例及Benchmark可以参考:[YOLOv5(PyTorch)](./pytorch_yolo_series)[YOLOv6(PyTorch)](./pytorch_yolo_series)[YOLOv7(PyTorch)](./pytorch_yolo_series)[HuggingFace(PyTorch)](./pytorch_huggingface)[MobileNet(TensorFlow)](./tensorflow_mobilenet)
......@@ -237,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)
## 社区交流
......
......@@ -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 # 权重量化比特数
......@@ -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'。
......@@ -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半结构化稀疏
......@@ -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结构化剪枝
......@@ -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)
......@@ -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以上版本。
......@@ -45,6 +45,8 @@
| MobileNetV3_large_x1_0 | 量化+蒸馏 | 74.04 | - | 9.85 | [Config](./configs/MobileNetV3_large_x1_0/qat_dis.yaml) | [Model](https://paddle-slim-models.bj.bcebos.com/act/MobileNetV3_large_x1_0_QAT.tar) |
| MobileNetV3_large_x1_0_ssld | Baseline | 78.96 | - | 16.62 | - | [Model](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV3_large_x1_0_ssld_infer.tar) |
| MobileNetV3_large_x1_0_ssld | 量化+蒸馏 | 77.17 | - | 9.85 | [Config](./configs/MobileNetV3_large_x1_0/qat_dis.yaml) | [Model](https://paddle-slim-models.bj.bcebos.com/act/MobileNetV3_large_x1_0_ssld_QAT.tar) |
| ViT_base_patch16_224 | Baseline | 81.89 | 367.17(batch_size=40) | - | - | [Model](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/ViT_base_patch16_224_infer.tar) |
| ViT_base_patch16_224 | 量化+蒸馏 | 82.05 | 51.70(batch_size=40) | - | [Config](./configs/VIT/qat_dis.yaml) | [Model](https://bj.bcebos.com/v1/paddle-slim-models/act/ViT_base_patch16_224_QAT.tar) |
- ARM CPU 测试环境:`SDM865(4xA77+4xA55)`
- Nvidia GPU 测试环境:
......@@ -57,8 +59,8 @@
#### 3.1 准备环境
- python >= 3.6
- PaddlePaddle >= 2.4.0 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装)
- PaddleSlim >= 2.4.0
- 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
......@@ -73,6 +75,13 @@ pip install paddlepaddle_gpu
pip install paddleslim
```
若使用`run_ppclas.py`脚本,需安装paddleclas:
```shell
git clone https://github.com/PaddlePaddle/PaddleClas.git -b release/2.5
cd PaddleClas
pip install --upgrade -r requirements.txt
```
#### 3.2 准备数据集
本案例默认以ImageNet1k数据进行自动压缩实验,如数据集为非ImageNet1k格式数据, 请参考[PaddleClas数据准备文档](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/data_preparation/classification_dataset.md)。将下载好的数据集放在当前目录下`./ILSVRC2012`
......
......@@ -30,6 +30,13 @@
| ERNIE 3.0-Medium | Base模型| 75.35 | 57.45 | 60.17 | 81.16 | 77.19 | 80.59 | 79.70 | 73.09 |
| ERNIE 3.0-Medium | 剪枝+量化训练| 74.17 | 56.84 | 59.75 | 80.54 | 76.03 | 76.97 | 80.80 | 72.16 |
| 模型 | 策略 | 报销工单数据 |
|:------:|:------:|:------:|
| UIE-base | Base模型 | [91.83](https://bj.bcebos.com/v1/paddle-slim-models/act/uie_base.tar) |
| UIE-base | 量化训练 | [95.80](https://bj.bcebos.com/v1/paddle-slim-models/act/uie_base_qat_model.tar) |
注:UIE模型精度为在5-shot(每个类别包含5条标注数据)数据集上进行模型微调的结果,压缩后精度更高可能原因是过拟合在当前数据集。
模型在不同任务上平均精度以及加速对比如下:
| 模型 |策略| Accuracy(avg) | 预测时延<sup><small>FP32</small><sup><br><sup> | 预测时延<sup><small>FP16</small><sup><br><sup> | 预测时延<sup><small>INT8</small><sup><br><sup> | 加速比 |
|:-------:|:--------:|:----------:|:------------:|:------:|:------:|:------:|
......
# 结构化剪枝敏感度分析
本示例将以自动压缩示例中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)
```
# 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 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
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
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]
def main():
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
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)
if __name__ == '__main__':
paddle.enable_static()
parser = argsparser()
args = parser.parse_args()
main()
......@@ -17,17 +17,11 @@ from .compressor import *
from .strategy_config import *
from .config_helpers import *
from .utils import *
from .analysis import *
__all__ = [
"AutoCompression",
"QuantAware",
"QuantPost",
"Distillation",
"MultiTeacherDistillation",
"HyperParameterOptimization",
"Prune",
"UnstructurePrune",
"ProgramInfo",
"TrainConfig",
"predict_compressed_model",
"AutoCompression", "QuantAware", "QuantPost", "Distillation",
"MultiTeacherDistillation", "HyperParameterOptimization", "Prune",
"UnstructurePrune", "ProgramInfo", "TrainConfig",
"predict_compressed_model", "analysis_prune"
]
import sys
import pickle
import logging
import paddle
from ..common import get_logger
from ..common.load_model import load_inference_model
from ..prune import sensitivity, get_ratios_by_loss
_logger = get_logger(__name__, level=logging.INFO)
__all__ = ['analysis_prune']
def get_prune_params(program):
params = []
for block in program.blocks:
for op in block.ops:
if op.type == 'conv2d' and op.attr('groups') == 1:
for inp_name in op.input_arg_names:
if block.var(inp_name).persistable is True:
params.append(inp_name)
return params
def analysis_prune(eval_function,
model_dir,
model_filename,
params_filename,
analysis_file,
pruned_ratios,
target_loss=None):
devices = paddle.device.get_device().split(':')[0]
places = paddle.device._convert_to_place(devices)
exe = paddle.static.Executor(places)
[eval_program, feed_target_names, fetch_targets] = (load_inference_model(
model_dir,
model_filename=model_filename,
params_filename=params_filename,
executor=exe))
params = get_prune_params(eval_program)
_logger.info("start analysis")
sens_0 = sensitivity(
eval_program,
places,
params,
eval_function,
sensitivities_file=analysis_file,
eval_args=[exe, feed_target_names, fetch_targets],
pruned_ratios=pruned_ratios)
with open(analysis_file, 'rb') as f:
if sys.version_info < (3, 0):
sensitivities = pickle.load(f)
else:
sensitivities = pickle.load(f, encoding='bytes')
_logger.info("finish analysis: {}".format(sensitivities))
ratios = {}
if target_loss is not None:
ratios = get_ratios_by_loss(sensitivities, target_loss)
_logger.info("you can set prune_params_name: {} in ChannelPrune".format(
ratios.keys()))
_logger.info("you can set pruned_ratio: {} in ChannelPrune".format(
ratios.values()))
return ratios
......@@ -399,6 +399,33 @@ def _get_label_info(dataloader, feed_target_names):
return label_info
def _get_chn_prune_params(program):
params = []
original_shapes = {}
for block in program.blocks:
for op in block.ops:
if op.type == 'conv2d' and op.attr('groups') == 1:
for inp_name in op.input_arg_names:
var_ = block.var(inp_name)
if var_.persistable is True:
params.append(inp_name)
original_shapes[inp_name] = var_.shape
return params, original_shapes
def _get_asp_prune_params(program):
params = []
for block in program.blocks:
for op in block.ops:
if (op.type == 'conv2d' and op.attr('groups') == 1
) or op.type == 'mul' or op.type == 'matmul_v2':
for inp_name in op.input_arg_names:
var_ = block.var(inp_name)
if var_.persistable is True:
params.append(inp_name)
return params
def build_prune_program(executor,
place,
config,
......@@ -428,11 +455,16 @@ def build_prune_program(executor,
elif strategy.startswith('channel_prune'):
from ..prune import Pruner
pruner = Pruner(config["criterion"])
if config['prune_params_name'] is None:
params, original_shapes = _get_chn_prune_params(
train_program_info.program)
else:
params = []
original_shapes = {}
### TODO(ceci3): set default prune weight
for param in train_program_info.program.global_block().all_parameters():
if config['prune_params_name'] is not None and param.name in config[
for param in train_program_info.program.global_block(
).all_parameters():
if config[
'prune_params_name'] is not None and param.name in config[
'prune_params_name']:
params.append(param.name)
original_shapes[param.name] = param.shape
......@@ -441,7 +473,9 @@ def build_prune_program(executor,
train_program_info.program,
paddle.static.global_scope(),
params=params,
ratios=[config['pruned_ratio']] * len(params),
ratios=[config['pruned_ratio']] * len(params)
if isinstance(config['pruned_ratio'], float) else
config['pruned_ratio'],
place=place)
_logger.info(
"####################channel pruning##########################")
......@@ -457,7 +491,10 @@ def build_prune_program(executor,
from paddle.static import sparsity
pruner = sparsity
excluded_params_name = []
### TODO(ceci3): set default prune weight
if config['prune_params_name'] is None:
config['prune_params_name'] = _get_asp_prune_params(
train_program_info.program)
for param in train_program_info.program.global_block().all_parameters():
if config['prune_params_name'] is not None:
if param.name not in config['prune_params_name']:
......
......@@ -252,11 +252,14 @@ class QuantPost(BaseStrategy):
class ChannelPrune:
def __init__(self, pruned_ratio, prune_params_name, criterion='l1_norm'):
def __init__(self,
pruned_ratio,
prune_params_name=None,
criterion='l1_norm'):
"""
ChannelPrune Config.
Args:
pruned_ratio(float): The ratios to be pruned.
pruned_ratio(float|list[float]): The ratios to be pruned.
prune_params_name(list(str)): A list of parameter names to be pruned.
criterion(str|function): the criterion used to sort channels for pruning, can be choose from ['l1_norm', 'bn_scale', 'geometry_median']. Default: 'l1_norm'.
"""
......@@ -266,7 +269,7 @@ class ChannelPrune:
class ASPPrune:
def __init__(self, prune_params_name):
def __init__(self, prune_params_name=None):
"""
ASPPrune Config.
Args:
......@@ -292,7 +295,7 @@ class UnstructurePrune:
threshold=0.01,
ratio=0.55,
gmp_config=None,
prune_params_type=None,
prune_params_type='conv1x1_only',
local_sparsity=False):
"""
UnstructurePrune Config.
......
......@@ -188,6 +188,16 @@ class Pruner():
for idx in src:
idx = idx * repeat
target.extend(range(idx, idx + repeat))
elif "stride" in trans:
stride = trans['stride']
target = src.repeat(stride) if stride > 1 else src
elif "squeeze" in trans:
repeat = trans['repeat']
targets_set = set()
for idx in src:
targets_set.add(idx / repeat)
target = list(targets_set)
src = target
ret.append((name, axis, src))
......
......@@ -87,7 +87,7 @@ def sensitivity(program,
if eval_args is None:
baseline = eval_func(graph.program)
else:
baseline = eval_func(eval_args)
baseline = eval_func(graph.program, *eval_args)
pruner = Pruner(criterion=criterion)
_logger.info("sensitive - param: {}; ratios: {}".format(name,
......@@ -104,7 +104,7 @@ def sensitivity(program,
if eval_args is None:
pruned_metric = eval_func(pruned_program)
else:
pruned_metric = eval_func(eval_args)
pruned_metric = eval_func(pruned_program, *eval_args)
loss = (baseline - pruned_metric) / baseline
_logger.info("pruned param: {}; {}; loss={}".format(name, ratio,
loss))
......
import os
import sys
import numpy as np
from tqdm import tqdm
import unittest
sys.path.append("../../")
import paddle
from PIL import Image
from paddle.vision.datasets import DatasetFolder
from paddle.vision.transforms import transforms
from paddleslim.auto_compression import AutoCompression
from paddleslim.auto_compression.analysis import analysis_prune
paddle.enable_static()
class ImageNetDataset(DatasetFolder):
def __init__(self, data_dir, image_size=224, mode='train'):
super(ImageNetDataset, self).__init__(data_dir)
self.data_dir = data_dir
normalize = transforms.Normalize(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.120, 57.375])
self.transform = transforms.Compose([
transforms.Resize(256), transforms.CenterCrop(image_size),
transforms.Transpose(), normalize
])
self.mode = mode
train_file_list = os.path.join(data_dir, 'train_list.txt')
val_file_list = os.path.join(data_dir, 'val_list.txt')
self.mode = mode
if mode == 'train':
with open(train_file_list) as flist:
full_lines = [line.strip() for line in flist]
np.random.shuffle(full_lines)
lines = full_lines
self.samples = [line.split() for line in lines]
else:
with open(val_file_list) as flist:
lines = [line.strip() for line in flist]
self.samples = [line.split() for line in lines]
def __getitem__(self, idx):
img_path, label = self.samples[idx]
if self.mode == 'train':
return self.transform(
Image.open(os.path.join(self.data_dir, img_path)).convert(
'RGB'))
else:
return self.transform(
Image.open(os.path.join(self.data_dir, img_path)).convert(
'RGB')), np.array([label]).astype('int64')
def __len__(self):
return len(self.samples)
def eval_func(program, exe, feed_names, fetch_list, dataloader):
results = []
with tqdm(
total=len(dataloader),
bar_format='Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}',
ncols=80) as t:
for batch_id, data in enumerate(dataloader):
image = data[0]['inputs']
label = data[0]['labels']
# top1_acc, top5_acc
if len(feed_names) == 1:
image = np.array(image)
label = np.array(label).astype('int64')
pred = exe.run(program,
feed={feed_names[0]: image},
fetch_list=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:
image = np.array(image)
label = np.array(label).astype('int64')
result = exe.run(
program,
feed={feed_names[0]: image,
feed_names[1]: label},
fetch_list=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]
class ACTChannelPrune(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(ACTChannelPrune, self).__init__(*args, **kwargs)
if not os.path.exists('MobileNetV1_infer'):
os.system(
'wget -q https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar'
)
os.system('tar -xf MobileNetV1_infer.tar')
if not os.path.exists('ILSVRC2012_data_demo'):
os.system(
'wget -q https://sys-p0.bj.bcebos.com/slim_ci/ILSVRC2012_data_demo.tar.gz'
)
os.system('tar -xf ILSVRC2012_data_demo.tar.gz')
self.train_dataloader, self.eval_dataloader = self.create_dataloader()
def create_dataloader(self):
train_dataset = ImageNetDataset("./ILSVRC2012_data_demo/ILSVRC2012/")
image = paddle.static.data(
name='inputs', shape=[None] + [3, 224, 224], dtype='float32')
label = paddle.static.data(
name='labels', shape=[None] + [1], dtype='float32')
train_dataloader = paddle.io.DataLoader(
train_dataset,
feed_list=[image],
batch_size=32,
shuffle=True,
num_workers=0,
return_list=False)
def eval_reader(data_dir,
batch_size,
crop_size,
resize_size,
place=None):
val_dataset = ImageNetDataset(
"./ILSVRC2012_data_demo/ILSVRC2012/", mode='val')
val_loader = paddle.io.DataLoader(
val_dataset,
feed_list=[image, label],
batch_size=batch_size,
shuffle=False,
drop_last=False,
num_workers=0,
return_list=False)
return val_loader
val_loader = eval_reader(
'./ILSVRC2012_data_demo/ILSVRC2012/',
batch_size=32,
crop_size=224,
resize_size=256)
return train_dataloader, val_loader
def get_analysis(self):
def eval_function(compiled_test_program, exe, test_feed_names,
test_fetch_list):
res = eval_func(compiled_test_program, exe, test_feed_names,
test_fetch_list, self.eval_dataloader)
return res
ratios = analysis_prune(eval_function, './MobileNetV1_infer',
'inference.pdmodel', 'inference.pdiparams',
'senti.data', [0.1], 0.05)
return ratios
def test_ac_prune_name_is_None(self):
def eval_function(exe, compiled_test_program, test_feed_names,
test_fetch_list):
res = eval_func(compiled_test_program, exe, test_feed_names,
test_fetch_list, self.eval_dataloader)
return res
configs = {
'Distillation': {},
'ChannelPrune': {
'pruned_ratio': 0.1
},
'TrainConfig': {
'epochs': 1,
'eval_iter': 1000,
'learning_rate': 5.0e-03,
'optimizer_builder': {
'optimizer': {
'type': 'SGD'
},
"weight_decay": 0.0005,
}
}
}
ac = AutoCompression(
model_dir='./MobileNetV1_infer',
model_filename="inference.pdmodel",
params_filename="inference.pdiparams",
save_dir="prune_output",
config=configs,
train_dataloader=self.train_dataloader,
eval_callback=eval_function) # eval_function to verify accuracy
ac.compress()
os.system('rm -rf prune_output')
def test_ac_prune(self):
ratios = self.get_analysis()
def eval_function(exe, compiled_test_program, test_feed_names,
test_fetch_list):
res = eval_func(compiled_test_program, exe, test_feed_names,
test_fetch_list, self.eval_dataloader)
return res
configs = {
'Distillation': {},
'TrainConfig': {
'epochs': 1,
'eval_iter': 1000,
'learning_rate': 5.0e-03,
'optimizer_builder': {
'optimizer': {
'type': 'SGD'
},
"weight_decay": 0.0005,
}
}
}
configs.update({
'ChannelPrune': {
'prune_params_name': list(ratios.keys())
}
})
configs['ChannelPrune'].update({'pruned_ratio': list(ratios.values())})
ac = AutoCompression(
model_dir='./MobileNetV1_infer',
model_filename="inference.pdmodel",
params_filename="inference.pdiparams",
save_dir="prune_output",
config=configs,
train_dataloader=self.train_dataloader,
eval_callback=eval_function) # eval_function to verify accuracy
ac.compress()
os.system('rm -rf prune_output')
def test_ac_sparse(self):
def eval_function(exe, compiled_test_program, test_feed_names,
test_fetch_list):
res = eval_func(compiled_test_program, exe, test_feed_names,
test_fetch_list, self.eval_dataloader)
return res
configs = {
'Distillation': {},
'ASPPrune': {},
'TrainConfig': {
'epochs': 1,
'eval_iter': 1000,
'learning_rate': 5.0e-03,
'optimizer_builder': {
'optimizer': {
'type': 'SGD'
},
"weight_decay": 0.0005,
}
}
}
ac = AutoCompression(
model_dir='./MobileNetV1_infer',
model_filename="inference.pdmodel",
params_filename="inference.pdiparams",
save_dir="asp_output",
config=configs,
train_dataloader=self.train_dataloader,
eval_callback=eval_function) # eval_function to verify accuracy
ac.compress()
os.system('rm -rf asp_output')
if __name__ == '__main__':
unittest.main()
......@@ -61,10 +61,10 @@ class TestSensitivity(StaticCase):
print("acc_val_mean: {}".format(acc_val_mean))
return acc_val_mean
def eval_func_for_args(args):
program = args[0]
feeder = fluid.DataFeeder(
feed_list=['image', 'label'], place=place, program=program)
def eval_func_for_args(program, feed_list):
feeder = paddle.fluid.DataFeeder(
feed_list=feed_list, place=place, program=program)
acc_set = []
for data in val_reader():
acc_np = exe.run(program=program,
......@@ -93,7 +93,7 @@ class TestSensitivity(StaticCase):
eval_program,
place, ["conv4_weights"],
eval_func_for_args,
eval_args=[eval_program],
eval_args=[['image', 'label']],
sensitivities_file="./sensitivites_file_params",
pruned_ratios=[0.1, 0.2, 0.3, 0.4])
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册