未验证 提交 baac90af 编写于 作者: B Bai Yifan 提交者: GitHub

【Cherry-Pick】Add quantization doc (#654)

上级 72edb126
# 图像分类INT8量化模型在CPU上的部署和预测
# PaddleSlim INT8量化模型在CPU上的部署和预测
## 概述
......
# PaddleSlim量化模型的TensorRT预测
# PaddleSlim INT8量化模型使用TensorRT的部署和预测
本教程将介绍使用TensortRT部署PaddleSlim量化得到的模型的详细步骤。
## 概述
NVIDIA TensorRT 是一个高性能的深度学习预测库,适用于Nvidia GPU,可为深度学习推理应用程序提供低延迟和高吞吐量。PaddlePaddle 采用子图的形式对TensorRT进行了集成,即我们可以使用该模块来提升Paddle模型的预测性能。本教程将介绍如何使用TensortRT部署PaddleSlim量化得到的模型,无论是量化训练(QAT)还是离线量化(PTQ)模型均可支持。对于常见图像分类模型,INT8模型的推理速度通常是FP32模型的3.2-6.7倍。
流程步骤如下:
- 产出量化模型:使用PaddleSlim量化训练或离线量化得到量化模型。注意模型中被量化的算子的参数值应该在INT8范围内,但是类型仍为float型。
- 在Nvidia GPU上部署预测:在GPU上以INT8类型进行预测部署。
## 1. 准备环境
......@@ -157,6 +163,16 @@ val/ILSVRC2012_val_00000002.jpg 0
### 2.4 部署预测
相比FP32模型的TensorRT预测,量化模型的预测需要在开启TensorRT时另外设定精度为`kInt8`, 核心代码如下:
```python
config.EnableTensorRtEngine(workspace_size, \
batch_size, \
min_subgraph_size, \
paddle::AnalysisConfig::Precision::kInt8, \
false, \
false);
```
### 运行demo
* 执行以下命令,完成一个分类模型的TensorRT预测。
......
......@@ -11,3 +11,4 @@
pruners/index.rst
ofa/index.rst
quanter/index.rst
QAT
==================
.. py:class:: paddleslim.QAT(config=None, weight_preprocess=None, act_preprocess=None, weight_quantize=None, act_quantize=None)
`源代码 <https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/dygraph/quant/quanter.py>`_
使用量化训练方法(Quant Aware Training, QAT)得到模拟量化模型,在需要量化的算子前插入模拟量化节点,为其激活和权重输入提前执行`量化-反量化`逻辑。
**参数:**
- **config(dict, Optional)** - 量化配置表。 默认为None,表示使用默认配置, 默认配置参考下方文档。
- **weight_preprocess(class, Optional)** - 自定义在对权重做量化之前,对权重进行处理的逻辑。这一接口只用于实验不同量化方法,验证量化训练效果。默认为None, 表示不对权重做任何预处理。
- **act_preprocess(class, Optional)** - 自定义在对激活值做量化之前,对激活值进行处理的逻辑。这一接口只用于实验不同量化方法,验证量化训练效果。默认为None, 表示不对激活值做任何预处理。
- **weight_quantize(class, Optional)** - 自定义对权重量化的方法。这一接口只用于实验不同量化方法,验证量化训练效果。默认为None, 表示使用默认量化方法。
- **act_quantize(class, Optional)** - 自定义对激活值量化的方法。这一接口只用于实验不同量化方法,验证量化训练效果。默认为None, 表示使用默认量化方法。
**返回:** 量化训练器实例。
**示例代码:**
.. code-block:: python
from paddleslim import QAT
quanter = QAT()
..
量化训练方法的参数配置
.. code-block:: python
{
# weight预处理方法,默认为None,代表不进行预处理;当需要使用`PACT`方法时设置为`"PACT"`
'weight_preprocess_type': None,
# activation预处理方法,默认为None,代表不进行预处理`
'activation_preprocess_type': None,
# weight量化方法, 默认为'channel_wise_abs_max', 此外还支持'channel_wise_abs_max'
'weight_quantize_type': 'channel_wise_abs_max',
# activation量化方法, 默认为'moving_average_abs_max', 此外还支持'abs_max'
'activation_quantize_type': 'moving_average_abs_max',
# weight量化比特数, 默认为 8
'weight_bits': 8,
# activation量化比特数, 默认为 8
'activation_bits': 8,
# 'moving_average_abs_max'的滑动平均超参, 默认为0.9
'moving_rate': 0.9,
# 需要量化的算子类型
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
..
.. py:method:: quantize(model)
inplace地对模型进行量化训练前的处理,插入量化-反量化节点。
**参数:**
- **model(paddle.nn.Layer)** - 一个paddle Layer的实例,需要包含支持量化的算子,如:`Conv, Linear`
**示例:**
.. code-block:: python
from paddle.vision.models import mobilenet_v1
from paddleslim import QAT
net = mobilenet_v1(pretrained=False)
quant_config = {
'activation_preprocess_type': 'PACT',
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
quanter = QAT(config=quant_config)
quanter.quantize(lenet)
paddle.summary(net, (1, 3, 224, 224))
..
.. py:method:: save_quantized_model(model, path, input_spec=None)
将指定的动态图量化模型导出为静态图预测模型,用于预测部署。
**参数:**
- **model(paddle.nn.Layer)** - 量化训练结束,需要导出的量化模型,该模型由`quantize`接口产出。
- **path(str)** - 导出的量化预测模型保存的路径,导出后在该路径下可以找到`model`和`params`文件。
- **input_spec(list[InputSpec|Tensor], Optional)** - 描述存储模型forward方法的输入,可以通过InputSpec或者示例Tensor进行描述。如果为 None ,所有原 Layer forward方法的输入变量将都会被配置为存储模型的输入变量。默认为 None。
**示例:**
.. code-block:: python
from paddle.vision.models import mobilenet_v1
from paddleslim import QAT
net = mobilenet_v1(pretrained=False)
quant_config = {
'activation_preprocess_type': 'PACT',
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
quanter = QAT(config=quant_config)
quanter.quantize(lenet)
paddle.summary(net, (1, 3, 224, 224))
quanter.save_quantized_model(
net,
'./quant_model',
input_spec=[paddle.static.InputSpec(shape=[None, 3, 224, 224], dtype='float32')])
..
# 图像分类模型量化训练-快速开始
量化训练要解决的问题是将FP32浮点数量化成INT8整数进行存储和计算,通过在训练中建模量化对模型的影响,降低量化误差。
PaddleSlim使用的是模拟量化训练方案,一般模拟量化需要先对网络计算图进行一定的处理,先在需要量化的算子前插入量化-反量化节点,再经过finetune训练减少量化运算带来的误差,降低量化模型的精度损失。
下面该教程将以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的模型量化接口]()。
该示例包含以下步骤:
1. 导入依赖
2. 构建模型和数据集
3. 进行预训练
4. 量化训练
5. 导出预测模型
以下章节依次次介绍每个步骤的内容。
## 1. 导入依赖
请参考PaddleSlim安装文档,安装正确的Paddle和PaddleSlim版本,然后按以下方式导入Paddle和PaddleSlim:
```python
import paddle
import paddle.vision.models as models
from paddle.static import InputSpec as Input
from paddle.vision.datasets import Cifar10
import paddle.vision.transforms as T
from paddleslim.dygraph.quant import QAT
```
## 2. 构建网络和数据集
该章节构造一个用于对CIFAR10数据进行分类的分类模型,选用`MobileNetV1`,并将输入大小设置为`[3, 32, 32]`,输出类别数为10。
为了方便展示示例,我们使用Paddle高层API提供的预定义[mobilenetv1分类模型](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/vision/models/mobilenetv1/MobileNetV1_cn.html#mobilenetv1)
调用`model.prepare`配置模型所需的部件,比如优化器、损失函数和评价指标,API细节请参考[文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/hapi/model/Model_cn.html#prepare-optimizer-none-loss-function-none-metrics-none)
```python
net = models.mobilenet_v1(pretrained=False, scale=1.0, num_classes=10)
inputs = [Input([None, 3, 32, 32], 'float32', name='image')]
labels = [Input([None, 1], 'int64', name='label')]
optimizer = paddle.optimizer.Momentum(
learning_rate=0.1,
parameters=net.parameters())
model = paddle.Model(net, inputs, labels)
model.prepare(
optimizer,
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy(topk=(1, 5)))
transform = T.Compose([T.Transpose(), T.Normalize([127.5], [127.5])])
train_dataset = Cifar10(mode='train', backend='cv2', transform=transform)
val_dataset = Cifar10(mode='test', backend='cv2', transform=transform)
```
## 3. 进行预训练
对模型进行预训练,为之后的量化做准备。
执行以下代码对模型进行预训练
```python
model.fit(train_dataset, epochs=5, batch_size=256, verbose=1)
model.evaluate(val_dataset, batch_size=256, verbose=1)
```
## 4. 量化训练
### 4.1 将模型转换为模拟量化模型
当使用普通在线量化时`weight_preprocess_type` 用默认设置None即可,当需要使用PACT在线量化时,则设置为'PACT'。
```python
quant_config = {
# weight preprocess type, default is None and no preprocessing is performed.
'weight_preprocess_type': None,
# for dygraph quantization, layers of type in quantizable_layer_type will be quantized
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
quanter = QAT(config=quant_config)
quanter.quantize(net)
```
### 4.2 训练量化模型
在这里我们对量化模型进行finetune训练,我们可以看到量化训练得到的模型与原模型准确率十分接近,代码如下所示:
```python
model.fit(train_dataset, epochs=2, batch_size=256, verbose=1)
model.evaluate(val_dataset, batch_size=256, verbose=1)
```
在量化训练得到理想的量化模型之后,我们可以将其导出用于预测部署。
## 5. 导出预测模型
通过以下接口,可以直接导出量化预测模型:
```python
path="./quant_inference_model"
quanter.save_quantized_model(
net,
path,
input_spec=inputs)
```
导出之后,可以在`path`路径下找到导出的量化预测模型
# 图像分类模型离线量化-快速开始
离线量化又称为训练后量化,仅需要使用少量校准数据,确定最佳的量化参数降低量化误差。这种方法需要的数据量较少,但量化模型精度相比在线量化稍逊。
下面该教程将以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的模型量化接口]()。
该示例包含以下步骤:
1. 导入依赖
2. 构建模型和数据集
3. 进行预训练
4. 量化训练
5. 导出预测模型
以下章节依次次介绍每个步骤的内容。
## 1. 导入依赖
请参考PaddleSlim安装文档,安装正确的Paddle和PaddleSlim版本,然后按以下方式导入Paddle和PaddleSlim:
```python
import paddle
import paddle.vision.models as models
from paddle.static import InputSpec as Input
from paddle.vision.datasets import Cifar10
import paddle.vision.transforms as T
from paddleslim.dygraph.quant import QAT
```
## 2. 构建网络和数据集
该章节构造一个用于对CIFAR10数据进行分类的分类模型,选用`MobileNetV1`,并将输入大小设置为`[3, 32, 32]`,输出类别数为10。
为了方便展示示例,我们使用Paddle高层API提供的预定义[mobilenetv1分类模型](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/vision/models/mobilenetv1/MobileNetV1_cn.html#mobilenetv1)
调用`model.prepare`配置模型所需的部件,比如优化器、损失函数和评价指标,API细节请参考[文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/hapi/model/Model_cn.html#prepare-optimizer-none-loss-function-none-metrics-none)
```python
net = models.mobilenet_v1(pretrained=False, scale=1.0, num_classes=10)
inputs = [Input([None, 3, 32, 32], 'float32', name='image')]
labels = [Input([None, 1], 'int64', name='label')]
optimizer = paddle.optimizer.Momentum(
learning_rate=0.1,
parameters=net.parameters())
model = paddle.Model(net, inputs, labels)
model.prepare(
optimizer,
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy(topk=(1, 5)))
transform = T.Compose([T.Transpose(), T.Normalize([127.5], [127.5])])
train_dataset = Cifar10(mode='train', backend='cv2', transform=transform)
val_dataset = Cifar10(mode='test', backend='cv2', transform=transform)
```
## 3. 进行预训练
对模型进行预训练,为之后的量化做准备。
执行以下代码对模型进行预训练
```python
model.fit(train_dataset, epochs=5, batch_size=256, verbose=1)
model.evaluate(val_dataset, batch_size=256, verbose=1)
```
训练完成后导出预测模型:
```python
paddle.jit.save(net, "./fp32_inference_model", input_spec=[inputs])
```
## 4.离线量化
调用slim接口将原模型转换为离线量化模型, 导出的模型可以直接用于预测部署:
```python
paddle.enable_static()
place = paddle.CPUPlace()
exe = paddle.static.Executor(place)
paddleslim.quant.quant_post_static(
executor=exe,
model_dir='./',
model_filename='fp32_inference_model.pdmodel',
params_filename='fp32_inference_model.pdiparams',
quantize_model_path='./quant_post_static_model',
sample_generator=train_dataset,
batch_nums=10)
```
......@@ -5,5 +5,7 @@
.. toctree::
:maxdepth: 1
static/index.rst
pruning_tutorial.md
dygraph_pruning_tutorial.md
dygraph_quant_aware_training_tutorial.md
dygraph_quant_post_tutorial.md
../../../../demo/quant/deploy/TensorRT/README.md
\ No newline at end of file
../../../../demo/mkldnn_quant/README.md
\ No newline at end of file
# PaddleSlim模型量化方法总览
# 图像分类INT8量化模型在CPU上的部署和预测
PaddleSlim主要包含三种量化方法:量化训练(Quant Aware Training, QAT)、动态离线量化(Post Training Quantization Dynamic, PTQ Dynamic)、静态离线量化(Post Training Quantization Static, PTQ Static)。
- [量化训练](quant_aware_training_tutorial.md) 量化训练让模型感知量化运算对模型精度带来的影响,通过finetune训练降低量化误差。
- [动态离线量化](quant_post_dynamic_tutorial.md) 动态离线量化仅将模型中特定算子的权重从FP32类型映射成INT8/16类型。
- [静态离线量化](quant_post_static_tutorial.md) 静态离线量化使用少量无标签校准数据,采用KL散度等方法计算量化比例因子。
除此之外,PaddleSlim还有一种对embedding层量化的方法,将网络中embedding层参数从float32类型量化到int8类型。
- [Embedding量化](../static/embedding_quant_tutorial.md) Embedding量化仅将embedding参数从float32类型映射到int8类型,可以降低embedding参数体积。
下图展示了如何根据需要选择模型量化方法
![模型量化算法选择](https://user-images.githubusercontent.com/52520497/95644539-e7f23500-0ae9-11eb-80a8-596cfb285e17.png)
下表综合对比了模型量化方法的使用条件、易用性、精度损失和预期收益。
![模型量化算法对比](https://user-images.githubusercontent.com/52520497/95644609-59ca7e80-0aea-11eb-8897-208d7ccd5af1.png)
| 量化方法 | API接口 | 功能 | 经典适用场景 |
| :------------------------------: | :----------------------------------------------------------: | :------------------------------------: | :---------------------------------------------: |
| 在线量化 (QAT) | 动态图:`paddleslim.QAT`; 静态图:`paddleslim.quant.quant_aware` | 通过finetune训练将模型量化误差降到最小 | 对量化敏感的场景、模型,例如目标检测、分割, OCR |
| 静态离线量化 (PTQ Static) | `paddleslim.quant.quant_post_static` | 通过少量校准数据得到量化模型 | 对量化不敏感的场景,例如图像分类任务 |
| 动态离线量化 (PTQ Dynamic) | `paddleslim.quant.quant_post_dynamic` | 仅量化模型的可学习权重 | 模型体积大、访存开销大的模型,例如BERT模型 |
| Embedding量化(Quant Embedding) | `paddleslim.quant.quant_embedding` | 仅量化模型的Embedding参数 | 任何包含Embedding层的模型 |
# 量化训练
在线量化是在模型训练的过程中建模定点量化对模型的影响,通过在模型计算图中插入量化节点,在训练建模量化对模型精度的影响降低量化损失。
PaddleSlim包含`QAT量化训练``PACT改进的量化训练`两种量化方法
- [QAT]()
- [PACT]()
## 使用方法
在线量化的基本流程可以分为以下四步:
1. 选择量化配置
2. 转换量化模型
3. 启动量化训练
4. 保存量化模型
下面分别介绍以下几点:
### 1. 选择量化配置
首先我们需要对本次量化的一些基本量化配置做一些选择,例如weight量化类型,activation量化类型等。如果没有特殊需求,可以直接拷贝我们默认的量化配置。全部可选的配置可以参考PaddleSlim量化文档,例如我们用的量化配置如下:
```python
quant_config = {
'weight_preprocess_type': None,
'activation_preprocess_type': None,
'weight_quantize_type': 'channel_wise_abs_max',
'activation_quantize_type': 'moving_average_abs_max',
'weight_bits': 8,
'activation_bits': 8,
'dtype': 'int8',
'window_size': 10000,
'moving_rate': 0.9,
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
```
### 2. 转换量化模型
在确认好我们的量化配置以后,我们可以根据这个配置把我们定义好的一个普通模型转换为一个模拟量化模型。我们根据量化原理中介绍的PACT方法,定义好PACT函数pact和其对应的优化器pact_opt。在这之后就可以进行转换,转换的方式也很简单:
```python
import paddleslim
quanter = paddleslim.QAT(config=quant_config)
quanter.quantize(net)
```
### 3. 启动量化训练
得到了量化模型后就可以启动量化训练了,量化训练与普通的浮点数模型训练并无区别,无需增加新的代码或逻辑,直接按照浮点数模型训练的流程进行即可。
### 4. 保存量化模型
量化训练结束后,我们需要对量化模型做一个转化。PaddleSlim会对底层的一些量化OP顺序做调整,以便预测使用。转换及保存的基本流程如下所示:
```python
import paddleslim
quanter.save_quantized_model(
model,
path,
input_spec=[paddle.static.InputSpec()])
```
## PACT在线量化
PACT方法是对普通在线量化方法的改进,对于一些量化敏感的模型,例如MobileNetV3,PACT方法一般都能降低量化模型的精度损失。
使用方法上与普通在线量化方法相近:
```python
# 在quant_config中额外指定'weight_preprocess_type'为'PACT'
quant_config = {
'weight_preprocess_type': 'PACT',
'weight_quantize_type': 'channel_wise_abs_max',
'activation_quantize_type': 'moving_average_abs_max',
'weight_bits': 8,
'activation_bits': 8,
'dtype': 'int8',
'window_size': 10000,
'moving_rate': 0.9,
'quantizable_layer_type': ['Conv2D', 'Linear'],
}
```
详细代码与例程请参考:
- [动态图量化训练](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/demo/dygraph/quant)
## 实验结果
| 模型 | 压缩方法 | 原模型Top-1/Top-5 Acc | 量化模型Top-1/Top-5 Acc |
| :---------------: | :--------------: | :-------------------: | :---------------------: |
| MobileNetV1 | quant_aware | 70.99%/89.65% | 70.63%/89.65% |
| MobileNetV2 | quant_aware | 72.15%/90.65% | 72.05%/90.63% |
| ResNet50 | quant_aware | 76.50%/93.00% | 76.48%/93.11% |
| MobileNetV3_large | pact_quant_aware | 78.96%/94.48% | 77.52%/93.77% |
## 参考文献
1. [Quantizing deep convolutional networks for efficient inference: A whitepaper](https://arxiv.org/pdf/1806.08342.pdf)
2. [PACT: Parameterized Clipping Activation for Quantized Neural Networks](https://arxiv.org/abs/1805.06085)
# 动态离线量化
动态离线量化,将模型中特定OP的权重从FP32类型量化成INT8/16类型。
量化前需要有训练好的预测模型,可以根据需要将模型转化为INT8或INT16类型,目前只支持反量化预测方式,主要可以减小模型大小,对特定加载权重费时的模型可以起到一定加速效果。
- 权重量化成INT16类型,模型精度不受影响,模型大小为原始的1/2。
- 权重量化成INT8类型,模型精度会受到影响,模型大小为原始的1/4。
## 使用方法
- 准备预测模型:先保存好FP32的预测模型,用于量化压缩。
- 产出量化模型:使用PaddlePaddle调用动态离线量化离线量化接口,产出量化模型。
主要代码实现如下:
```python
import paddleslim
model_dir = path/to/fp32_model_params
save_model_dir = path/to/save_model_path
paddleslim.quant.quant_post_dynamic(model_dir=model_dir,
save_model_dir=save_model_dir,
weight_bits=8,
quantizable_op_type=['conv2d', 'mul'],
weight_quantize_type="channel_wise_abs_max",
generate_test_model=False)
```
# 静态离线量化
静态离线量化是基于采样数据,采用KL散度等方法计算量化比例因子的方法。相比量化训练,静态离线量化不需要重新训练,可以快速得到量化模型。
静态离线量化的目标是求取量化比例因子,主要有两种方法:非饱和量化方法 ( No Saturation) 和饱和量化方法 (Saturation)。非饱和量化方法计算FP32类型Tensor中绝对值的最大值`abs_max`,将其映射为127,则量化比例因子等于`abs_max/127`。饱和量化方法使用KL散度计算一个合适的阈值`T` (`0<T<mab_max`),将其映射为127,则量化比例因子等于`T/127`。一般而言,对于待量化op的权重Tensor,采用非饱和量化方法,对于待量化op的激活Tensor(包括输入和输出),采用饱和量化方法 。
## 使用方法
静态离线量化的实现步骤如下:
- 加载预训练的FP32模型,配置reader;
- 读取样本数据,执行模型的前向推理,保存待量化op激活Tensor的数值;
- 基于激活Tensor的采样数据,使用饱和量化方法计算它的量化比例因子;
- 模型权重Tensor数据一直保持不变,使用非饱和方法计算它每个通道的绝对值最大值,作为每个通道的量化比例因子;
- 将FP32模型转成INT8模型,进行保存。
主要代码实现如下:
```python
import paddleslim
exe = paddle.static.Executor(place)
paddleslim.quant.quant_post(
executor=exe,
model_dir=model_path,
quantize_model_path=save_path,
sample_generator=reader,
model_filename=model_filename,
params_filename=params_filename,
batch_nums=batch_num)
```
详细代码与例程请参考:[静态离线量化](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/demo/quant/quant_post)
## 实验结果
| 模型 | 压缩方法 | 原模型Top-1/Top-5 Acc | 量化模型Top-1/Top-5 Acc |
| :---------------: | :--------------: | :-------------------: | :---------------------: |
| MobileNetV1 | quant_post_static | 70.99%/89.65% | 70.18%/89.25% |
| MobileNetV2 | quant_post_static | 72.15%/90.65% | 71.15%/90.11% |
| ResNet50 | quant_post_static | 76.50%/93.00% | 76.33%/93.02% |
# Embedding量化
Embedding量化将网络中的Embedding参数从`float32`类型量化到 `8-bit`整数类型,在几乎不损失模型精度的情况下减少模型的存储空间和显存占用。
Embedding量化仅能减少模型参数的体积,并不能显著提升模型预测速度。
## 使用方法
在预测时调用paddleslim `quant_embedding`接口,主要实现代码如下:
```python
import paddleslim
place = paddle.CUDAPlace(0) if use_cuda else paddle.CPUPlace()
exe = paddle.static.Executor(place)
main_program = paddleslim.quant.quant_embedding(main_program, place, config)
```
详细代码与例程请参考:[Embedding量化](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/demo/quant/quant_embedding)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册