转自AI Studio,原文链接:模型量化(2):Paddle 模型的静态量化和动态量化 - 飞桨AI Studio

1. 引入

  • 前文简单介绍了一下模型量化的基本原理和简单实例

  • 本次就继续前文内容,结合深度学习模型压缩的工具库 PaddleSlim

  • 介绍一下如何使用 PaddleSlim 对 Paddle 模型进行动态量化和静态量化

2. 参考资料

3. PaddleSlim

3.1 介绍

  • PaddleSlim 是 Paddle 官方开发的一个专注于深度学习模型压缩的工具库

  • 提供剪裁、量化、蒸馏、和模型结构搜索等模型压缩策略,帮助用户快速实现模型的小型化

3.2 功能

  • PaddleSlim 支持以下功能,也支持自定义量化、裁剪等功能

    QuantizationPruningNASDistilling
    QATSensitivityPruner*Simulate Anneal based NAS*FSP
    PACTFPGMFilterPruner*Reinforcement Learning based NAS*DML
    PTQ-StaticL1NormFilterPruner**DARTS*DK for YOLOv3
    PTQ-DynamicL2NormFilterPruner**PC-DARTS
    Embedding Quant*SlimFilterPruner**Once-for-All
    *OptSlimFilterPruner*Hardware-aware Search
    • * 表示仅支持静态图
    • ** 表示仅支持动态图

3.3 效果

  • PaddleSlim 在典型视觉和自然语言处理任务上做了模型压缩

  • 并且测试了 Nvidia GPU、ARM 等设备上的加速情况,这里展示部分模型的压缩效果

  • 详细信息可以参考官方文档中 CV 和 NLP 模型压缩方案

    • YOLOv3: 在移动端 SD855 上加速 3.55 倍

    • PP-OCR: 体积由 8.9M 减少到 2.9M, 在 SD855 上加速 1.27 倍

    • BERT: 模型参数由 110M 减少到 80M,精度提升的情况下,Tesla T4 GPU FP16 计算加速 1.47 倍

3.3 安装

  • 使用 pip 命令可以快速安装 PaddleSlim

In [ ]

!pip install paddleslim

4. 模型量化

4.1 量化方法

  • PaddleSlim主要包含三种量化方法:

    • 量化训练 (Quant Aware Training, QAT):量化训练让模型感知量化运算对模型精度带来的影响,通过 finetune 训练降低量化误差。

    • 动态离线量化 (Post Training Quantization Dynamic, PTQ Dynamic):动态离线量化仅将模型中特定算子的权重从FP32类型映射成 INT8/16 类型。

    • 静态离线量化 (Post Training Quantization Static, PTQ Static):静态离线量化使用少量无标签校准数据,采用 KL 散度等方法计算量化比例因子。

4.2 方法选择

  • 下图展示了如何根据需要选择模型量化方法

4.3 方法对比

  • 下表综合对比了模型量化方法的使用条件、易用性、精度损失和预期收益

  • 下表综合对比了模型量化方法的API接口、功能和经典适用场景

    量化方法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 层的模型

4.4 更多详情

5. 量化实践

5.1 案例介绍

  • 模型选择:PaddleClas 提供的基于 ImageNet 预训练的 MobileNet v1 图像分类模型

  • 量化方法:本次介绍两种训练后量化方法,即动态量化和静态量化

5.2 下载模型

  • 使用 PaddleClas 提供的模型下载地址下载 MobileNet v1 预训练模型并解压

In [ ]

# 下载模型文件
!wget -P models https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar

# 解压缩模型文件
!cd models && tar -xf MobileNetV1_infer.tar

5.3 动态量化

  • 介绍:

    • 动态离线量化,将模型中特定 OP 的权重从 FP32 类型量化成 INT8/16 类型

    • 量化前需要有训练好的预测模型,可以根据需要将模型转化为 INT8 或 INT16 类型

    • 目前只支持反量化预测方式,主要可以减小模型大小,对特定加载权重费时的模型可以起到一定加速效果

    • 权重量化成 INT16 类型,模型精度不受影响,模型大小为原始的 1/2。

    • 权重量化成 INT8 类型,模型精度会受到影响,模型大小为原始的 1/4。

  • 使用:

    • 准备预测模型:先保存好FP32的预测模型,用于量化压缩

    • 产出量化模型:使用 PaddleSlim 调用动态离线量化离线量化接口,产出量化模型

  • 实践:

    • 动态量化是模型量化中相对简单的一种,无需额外数据,一般只需要调用量化工具相对应的 API 接口即可

    • 在 PaddleSlim 中,只需要调用 paddleslim.quant.quant_post_dynamic 这个接口即可实现模型的动态量化

    • 有一点需要注意的是,使用上述接口是需要处于 Paddle 的静态图模式之下,否则无法正常运行

    • 具体的动态量化实例代码如下:

In [ ]

import paddle
import paddleslim

# 开启静态图模式
paddle.enable_static()

# 模型的路径和文件名称
model_dir = "models/MobileNetV1_infer"
model_filename = 'inference.pdmodel'
params_filename = 'inference.pdiparams'
model_dir_quant_dynamic = "models/MobileNetV1_infer_quant_dynamic"

# 动态量化
paddleslim.quant.quant_post_dynamic(
    model_dir=model_dir, # 输入模型路径
    model_filename=model_filename, # 输入模型计算图文件名称
    params_filename=params_filename, # 输入模型参数文件名称
    save_model_dir=model_dir_quant_dynamic, # 输出模型路径
    save_model_filename=model_filename, # 输出模型计算图名称
    save_params_filename=params_filename, # 输出模型参数文件名称
    weight_bits=8, # 量化比特数 8/16 对应 INT8/16 类型
)

5.4 静态量化

  • 介绍:

    • 静态离线量化是基于采样数据,采用 KL 散度等方法计算量化比例因子的方法

    • 相比量化训练,静态离线量化不需要重新训练,可以快速得到量化模型

    • 静态离线量化的目标是求取量化比例因子,主要有两种方法:

      • 非饱和量化方法(No Saturation):非饱和量化方法计算 FP32 类型 Tensor 中绝对值的最大值 abs_max,将其映射为 127,则量化比例因子等于 abs_max/127

      • 饱和量化方法(Saturation):饱和量化方法使用 KL 散度计算一个合适的阈值 T(0 < T < mab_max),将其映射为 127,则量化比例因子等于 T/127

    • 一般而言,对于待量化 OP 的权重 Tensor,采用非饱和量化方法,对于待量化 OP 的激活 Tensor(包括输入和输出),采用饱和量化方法

  • 使用:

    • 加载预训练的 FP32 模型,配置 Reader

    • 读取样本数据,执行模型的前向推理,保存待量化 OP 激活 Tensor 的数值

    • 基于激活 Tensor 的采样数据,使用饱和量化方法计算它的量化比例因子

    • 模型权重 Tensor 数据一直保持不变,使用非饱和方法计算它每个通道的绝对值最大值,作为每个通道的量化比例因子

    • 将 FP32 模型转成 INT8 模型,进行保存

  • 实践:

    • 相比动态量化,静态量化会复杂一些,需要一些额外的无标签校准数据

    • 这里采用 ImageNet 的验证集中的少量图像作为校准数据

    • 静态量化时,需要首先构造一个校准数据读取器(Reader)

    • 然后调用 PaddleSlim 的 paddleslim.quant.quant_post_static 接口进行静态量化

    • 当然静态量化也需要处于 Paddle 的静态图模式之下才可以正常运行

    • 还有一点需要注意的是,PaddleSlim 静态量化后的模型并不是可以最终在 CPU 上部署的模型

    • 需要通过 Paddle 官方提供的转换脚本 save_quant_model.py 对产出的量化模型进行转换和优化操作

    • 具体的静态量化实例代码如下:

5.4.1 解压数据集

  • 解压 ImageNet 验证集数据

In [ ]

# 解压数据集
!mkdir ~/data/ILSVRC2012
!tar -xf ~/data/data68594/ILSVRC2012_img_val.tar -C ~/data/ILSVRC2012

5.4.2 模型静态量化

  • 读取校准数据 -> 构建读取器 -> 静态量化

In [ ]

import os
import paddle
import paddleslim
import numpy as np
import paddle.vision.transforms as T

from PIL import Image


# 开启静态图模式
paddle.enable_static()

# 模型的路径和文件名称
model_dir = "models/MobileNetV1_infer"
model_filename = 'inference.pdmodel'
params_filename = 'inference.pdiparams'
model_dir_quant_static = "models/MobileNetV1_infer_quant_static"

# 数据预处理
'''
    缩放 -> 中心裁切 -> 类型转换 -> 转置 -> 归一化 -> 添加维度
'''
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
val_transforms = T.Compose(
    [
        T.Resize(256, interpolation="bilinear"),
        T.CenterCrop(224),
        lambda x: np.asarray(x, dtype='float32').transpose(2, 0, 1) / 255.0,
        T.Normalize(mean, std),
        lambda x: x[None, ...]
    ]
)

# 校准数据读取
'''
    读取图像 -> 预处理 -> 组成数据字典
'''
img_dir = 'data/ILSVRC2012'
img_num = 32
datas = iter([
    val_transforms(
        Image.open(os.path.join(img_dir, img)).convert('RGB')
    ) for img in os.listdir(img_dir)[:img_num]
])

# 静态量化
paddleslim.quant.quant_post_static(
    executor=paddle.static.Executor(), # Paddle 静态图执行器
    model_dir=model_dir, # 输入模型路径
    model_filename=model_filename, # 输入模型计算图文件名称
    params_filename=params_filename, # 输入模型参数文件名称
    quantize_model_path=model_dir_quant_static, # 输出模型路径
    save_model_filename=model_filename, # 输出模型计算图文件名称
    save_params_filename=params_filename, # 输出模型参数文件名称
    batch_generator=None, # 数据批次生成器,需传入一个可调用对象,返回一个 Generator
    sample_generator=lambda:datas, # 数据采样生成器,需传入一个可调用对象,返回一个 Generator
    data_loader=None, # Paddle DataLoader
    batch_size=32, # 数据批次大小
    batch_nums=1, # 数据批次数量,默认为使用全部数据
    weight_bits=8, # 参数量化比特数 8/16 对应 INT8/16 类型
    activation_bits=8, # 激活值量化比特数 8/16 对应 INT8/16 类型
    weight_quantize_type='channel_wise_abs_max', # 参数量化方法,目前支持 'range_abs_max', 'moving_average_abs_max' 和 'abs_max'
    activation_quantize_type='range_abs_max', # 激活值量化方法,目前支持 'range_abs_max', 'moving_average_abs_max' 和 'abs_max'
    algo='KL', # 校准方法,目前支持 'KL', 'hist', 'mse', 'avg', 'abs_max'
)

5.4.3 模型转换

  • 执行转换脚本,转换导出最终的部署模型

In [ ]

# 执行转换脚本
!python save_quant_model.py \
    --quant_model_path "models/MobileNetV1_infer_quant_static" \
    --quant_model_filename "inference.pdmodel" \
    --quant_params_filename "inference.pdiparams" \
    --int8_model_save_path "models/MobileNetV1_infer_quant_static/quantized_model" \
    --save_model_filename "inference.pdmodel" \
    --save_params_filename "inference.pdiparams"

6. 模型部署

6.1 文件大小

  • 两种量化方式产生的模型文件大小对比如下表:

    模型计算图文件参数文件
    原始模型414.7KB16.2MB
    动态量化454.0KB7.2MB
    静态量化221.4KB16.1MB
  • 因为 Paddle 框架和模型储存格式的缘故,静态量化的模型参数仍以 Float32 格式储存,所以模型参数文件大小并没有缩小

  • 而动态量化产生的模型参数使用了 Int8 格式储存,故文件大小缩减至原模型的一半左右

6.2 部署框架

  • 动态量化:目前只有 PaddleLite 仅支持反量化预测方式,server 端预测(PaddleInference)不支持加载该量化模型

  • 静态量化:使用 PaddleLite 或者 PaddleInference 加载量化模型进行预测推理

6.3 部署参考资料

6.4 部署实践

  • 用 PaddleSlim 生成的量化模型,在 AIStuido 并不是很好部署,所以这里就不演示了

  • 一堆奇奇怪怪的坑,要不就是结果不对,要不就是加载不了,也不知道哪里错了,可能是技术不行吧,算了摆烂不搞了

7. 尾巴

  • 这当然部署 PaddleSlim 的全部,PaddleSlim 还有很多功能,本次只是介绍了其中比较简单的两种模型量化的方法使用

  • 有关 PaddleSlim 量化训练和其他功能的使用介绍,之后应该会慢慢分享的

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐