构建QConfig
定义及原理
QConfig 的定义
qconfig(Quantization Configuration)指的是量化配置,是深度学习模型量化过程中的关键参数集合,模型的量化方式由 qconfig 决定,在准备 qat / calibration 模型之前,需要先给模型设置 qconfig。
因历史原因,Plugin 中有不同 qconfig 的定义和用法,早期版本的 qconfig 将在不久的将来被废弃,我们只推荐您使用此文档中介绍的 qconfig 用法。
一个 qconfig 对象可以设置 input / weight / output 三个关键字,分别表示算子输入/权重/输出的量化配置,prepare 模型时会根据这些配置决定是否要在对应位置插入 FakeQuantize / FakeCast 节点,None 表示不插入任何节点。
FakeQuantize 的定义
FakeQuantize 是伪量化节点,会对输入进行量化反量化操作,插入伪量化可以在浮点模型的前向中模拟量化产生的误差。horizon_plugin_pytorch 支持 FakeQuantize / PACTFakeQuantize / _LearnableFakeQuantize 三种伪量化,我们只推荐您使用基于统计的 FakeQuantize,可以满足绝大部分需求。标准流程不对 PACTFakeQuantize 和_LearnableFakeQuantize 两种方法做详细说明,如果一定有需求,请在阅读相关论文后再使用。
可以调用 FakeQuantize 的 with_args 方法得到构造器,并按上一节的代码示例用它构造 qconfig。with_args 的参数包括 FakeQuantize 和 observer 支持配置的参数,理论上可以配置所有 FakeQuantize 和 observer 类 __init__ 方法声明中的参数,但为了屏蔽无关紧要的细节,我们只推荐您配置 observer 相关参数。
不同 observer 的参数不同,下面列出常用 observer 构造 FakeQuantize 的例子,其他 observer 的具体用法见校准章节。
FakeCast 的定义
FakeCast 是伪转换节点,会将输入转换为 float32 类型,如果数据类型是 float16,那么还会在中间模拟转 float16 产生的截断误差,此节点主要用于标志需要浮点计算的算子。
使用 FakeCast 构造 qconfig 的方法与 FakeQuantize 类似,但只有 dtype 一个参数。
构造 QConfig
在构造 Qconfig 时,有两种方法供您选择:
-
(推荐) 使用
get_qconfig接口构造 Qconfig。此接口较直接构造 QConfig 对象的方法更简单易用,可以满足大部分场景的需求。 -
按照上文介绍的定义方法,直接构造 QConfig 对象。这种方法比较灵活,可以配置任何可配置的参数,但同时要求也比较高,需要您对 QConfig 有一定的理解。
通过 QconfigSetter 进行 qconfig 配置
QconfigSetter 根据模型的计算图,按设定的规则自动设置 qconfig,是我们最推荐的设置 qconfig 方法。使用 QconfigSetter 依赖 prepare 过程的图模式,用法如下:
模板说明
您可配置的模板如下:
-
ModuleNameTemplate(必要,需要覆盖到全部量化算子):通过 module name 指定 dtype 配置或量化阈值。 -
ConvDtypeTemplate(必要):指定 Conv 类算子的 input 和 weight dtype。 -
MatmulDtypeTemplate(必要):指定 Matmul 算子的 input dtype。 -
SensitivityTemplate(可选): 根据敏感度将 top-n 的算子配置为高精度。 -
LoadFromFileTemplate:加载 qconfig.pt 文件,用于复现之前的量化配置。此时 enable_optimize 必须为 False,否则配置结果的正确性无法保证,部署时可能存在 cpu 算子。
这些模板按配置顺序生效,前面模板的配置可被后面的模板覆盖。
ModuleNameTemplate 详解
-
module name 可以为算子名或 prefix。当一个
ModuleNameTemplate中不同的 module name 存在覆盖关系时,越长的名字优先级越高,例如: -
可以指定算子的 threshold(前提是算子中存在对应的伪量化节点),此时量化 scale 的计算方式为
scale = threshold / -qdtype.min,例如: -
dtype 和 threshold 默认配置到算子的输出上,可通过指定 key 的方式配置 input 或 weight,在算子有多个输入时,可使用 None 做占位符,例如:
场景示例
-
全 int8:
-
feature int16, weight int8:
-
gemm 算子双 int8,其他算子 fp16:
-
gemm 算子双 int8,其他算子 int16, 并将高敏感度 gemm 配置为 int16:
默认优化 pass 说明
除您可配置的模板外,QconfigSetter 还集成了一系列优化和合法化模板,在本章节进行说明。
-
CanonicalizeTemplate: 按算子类型对 dtype 配置进行合法化,当前默认规则有:-
Gemm 类算子不支持 float 输入(含 weight)。
-
插值类算子:默认使用 int8 (当前 nash-e/m/p/h 平台上的插值类算子虽支持了 fp16 和 int16,但性能较差,所以模板默认仍使用了 int8。若插值算子需要使用其他 dtype,请使用
ModuleNameTemplate(..., freeze=True)将它的上一个算子的输出配置为需要的 dtype)。 -
DPP、RPP 等特殊算子仅支持 int8。
-
其他算子的通用规则:算子的 input dtype 和 output dtype 不能同时存在 qint 和 float。
-
-
EqualizeInOutScaleTemplate:对于 relu,relu6,concat,stack 算子,应该在算子之后统计 scale,否则精度或性能存在损失。为此:-
将前面算子的 output dtype 配置为 float32。
-
Relu,concat,stack 算子在 export hbir 时,在 input 处插入伪量化,scale 复用 output scale。
-
-
FuseConvAddTemplate:硬件支持 conv + add 的 fuse,为此:-
将 conv 的 output dtype 配置为 float32。
-
将 add 对应的 input dtype 配置为 float32。
-
-
GridHighPrecisionTemplate:根据经验,grid sample 的 grid 计算过程用 qint8 精度不够,因此自动将相关算子配置为高精度。 -
InternalQuantsTemplate:模型分段部署场景下,会在分段点处插入 QuantStub,用于记录此处的 dtype 和 scale,此类 QuantStub 的 dtype 配置必须和输入保持一致。 -
OutputHighPrecisionTemplate:当 Gemm 类算子作为模型输出时,将其配置为高精度输出。 -
PropagateTemplate:对于拆分为子图实现的算子,存在经验性配置,如LayerNorm和Softmax内部小算子应该使用高精度。 -
SimpleIntPassTemplate:性能优化,对于 op0->op1->op2 此类计算图,若以下条件同时成立,则将 op1 输出类型修改为 int:-
op2 需要 int 输入。
-
op0 可以输出 int。
-
op1 当前输出为 float16,且属于以下类型:
-
cat,stack。
-
mul_scalar。
-
无精度风险的查表算子(即在 fp16 上默认使用查表实现的算子)。
-
-
-
SimplifyTemplate:删除多余的量化节点配置(将对应的 dtype 修改为 None)。
