离线量化又称为训练后量化,仅需要使用少量校准数据,确定最佳的量化参数降低量化误差。这种方法需要的数据量较少,但量化模型精度相比在线量化稍逊。
离线量化的基本流程可以分为以下三步:
- 选择量化配置
- 采样收集量化信息
- 转换量化模型
Observer
:用于统计OP输入或输出,并计算出量化相关的统计量,比如scale、zero_point等。每个离线量化算法对应一个Observer,现已有的Observer包含:
AVGObserver
:收集目标Tensor的平均值作为量化scaleMSEObserver
:收集最大绝对值并通过最小化MSE误差,收集量化scaleEMDObserver
:收集最大绝对值并通过最小化EMD误差,收集量化scaleHistObserver
:将张量值收集到直方图中,并根据百分比计算量化scaleKLObserver
:以最小化浮点值分布与量化浮点值分布之间的 Kullback-Leibler散度计算量化scaleAbsMaxChannelWiseWeightObserver
:根据目标权重的通道维度,收集最大绝对值作为量化scaleMSEChannelWiseWeightObserver
:根据目标权重的通道维度,收集最大绝对值并通过最小化MSE误差,收集量化scale
Quanter
:对OP的输入或输出执行量化或模拟操作操作,同时还可以对输入Tensor的数值进行统计分析。每个量化训练算法对应一个Quanter,现已有的Quanter包含:
PACTQuanter
WeightLSQplusQuanter
:ActLSQplusQuanter
QuantConfig
:在执行量化操作之前,首先要配置量化相关的信息,主要是指定每层的各个输入使用什么Observer或Quanter。可通过以下调用方法,根据需求添加每层的量化配置信息:
QuantConfig接口 | 传入参数及其含义 | 注意事项 |
---|---|---|
add_layer_config | layer : 指定模型的某一层或某些层的listactivation : 用于量化激活以上指定layer的Observer 或Quanter weight : 用于量化权重以上指定layer的Observer 或Quanter |
此方法是最高优的要求,这些层的量化方式将按照这里的要求,而不是按照其他配置进行量化 |
add_name_config | layer_name : 指定模型的某一层的名字或某些层的名字的list activation : 用于量化激活以上指定layer的Observer 或Quanter weight : 用于量化权重以上指定layer的Observer 或Quanter |
此方法的优先级仅此于add_layer_config |
add_type_config | layer_type :指定需要量化的layer类型,可以为单个layer类型,或一个layer类型的list,layer类型必须为paddle.nn.Layer的子类 activation : 用于量化激活以上指定layer的Observer 或Quanter weight : 用于量化权重以上指定layer的Observer 或Quanter |
此方法的优先级此于add_name_config,指定需要量化的layer类型,如nn.Linear, 量化时将对所有nn.Linear进行量化,并指定weight和activation的quanter类型 |
add_qat_layer_mapping | source :被量化的layer target :量化的layer |
source和target必须为paddle.nn.Layer的子类;当指定需要量化的layer类型,如果在框架中没有实现该层量化时,需要指定该layer的量化层,比如ColumnParallelLinear对应PaddleSlim中实现的QuantizedColumnParallelLinear |
PTQ接口 | 传入参数及其含义 | 介绍 |
---|---|---|
quantize | model :需要被量化的模型 inplace :inplace=True时,该模型会被inplace的量化;inplace=False时,不改变原模型,并且会return一个量化的模型 |
对模型需要量化的层插入observers以采样到需要的量化信息 |
convert | model :需要被转化的量化模型 inplace :inplace=True时,该模型会被inplace的量化;inplace=False时,不改变原模型,并且会return一个量化的模型 |
将模型转化成onnx形式,进行此步骤之后才能对量化模型进行验证、导出成静态图等 |
import paddle
import paddleslim
from paddle.vision.models import mobilenet_v1
from paddle.quantization import QuantConfig
from paddle.quantization import PTQ
from paddleslim.quant.observers import HistObserver, KLObserver, EMDObserver, MSEObserver, AVGObserver, MSEChannelWiseWeightObserver, AbsMaxChannelWiseWeightObserver
# create the model
model = mobilenet_v1()
# define QuantConfig
q_config = QuantConfig(activation=None, weight=None)
# define act_quanter and weight_quanter
act_quanter = MSEObserver()
weight_quanter = MSEObserver()
# map ColumnParallelLinear to QuantizedColumnParallelLinear
q_config.add_qat_layer_mapping(ColumnParallelLinear,
QuantizedColumnParallelLinear)
# map RowParallelLinear to QuantizedRowParallelLinear
q_config.add_qat_layer_mapping(RowParallelLinear,
QuantizedRowParallelLinear)
# for each layer if type in [paddle.nn.Linear, ColumnParallelLinear, RowParallelLinear]
# make them quantizable
q_config.add_type_config(
[paddle.nn.Linear, ColumnParallelLinear, RowParallelLinear],
activation=activation,
weight=weight,
)
ptq = PTQ(q_config)
model = ptq.quantize(model, inplace=True)
# ptq sample
ptq_step = 100
for step, data in enumerate(dataloader):
pred = model(data)
if step == ptq_step:
break
# convert to quant model that can evaluate and export
model = ptq.convert(model, inplace=True)