操作说明

Lyngor主要包括2个使用场景:

  • 首次构建引擎

  • 重复加载引擎

备注

在获得引擎后,执行推理的操作相同。

Lyngor的简化操作流程如图所示。

Lyngor操作流程

图 Lyngor操作流程

首次构建引擎

如首次构建计算图的推理引擎,在构建完成后,保存引擎,便于后续重复使用该引擎,进行计算图推理, 避免重复编译计算图。

  1. 导入Lyngor库,以便后续使用Lyngor接口进行计算图相关操作。具体操作参见 导入Lyngor库

    备注

    在使用Lyngor接口前,须确认已导入Lyngor库。

  2. 判断是否已有模型:

    注意

    在导入模型转换计算图时,如模型中存在Lyngor无法支持的算子,会产生报错。 在这种情况下,可通过开发Lyngor自定义算子,实现相应功能。

    • Lyngor支持的算子参见:《Lyngor算子说明书》

    • 开发Lyngor自定义算子参见:《Lyngor自定义算子开发指南》

  3. (可选)如需加速模型推理,减少模型的权重体积,则需要增加量化校准过程,提供校准数据。具体操作参见 量化校准

  4. 构建推理引擎。支持以下2种方式:

    备注

    如需包含量化校准参数,在构建推理引擎时,传入量化配置,具体操作参见 包含量化校准参数

  5. 反序列化引擎。具体操作参见 反序列化引擎

  6. 执行推理。具体操作参见 执行推理

重复加载引擎

如已构建计算图的推理引擎,可加载已保存的引擎,避免重复编译计算图。

前提条件:已序列化引擎。具体操作参见 构建推理引擎

  1. 导入Lyngor库,以便后续使用Lyngor接口进行计算图相关操作。具体操作参见 导入Lyngor库

    注意

    在使用Lyngor接口前,须确认已导入Lyngor库。

  2. 反序列化引擎,加载已保存的引擎,重复使用编译结果。具体操作参见 反序列化引擎

  3. 执行推理。具体操作参见 执行推理

INT8量化原理

量化运算的定义

对于一个取值范围为(\(x_{\min},\ x_{\max}\))的浮点变量,将其量化到范围为(0, \(N_{levels} - 1\))需要求出两个参数:量化因子(\(\mathrm{\Delta}\))和零点(\(z\))。

此时,量化计算为:

\[x_{int} = round\left( \frac{x}{\mathrm{\Delta}} \right) + z\]
\[x_{Q} = clamp(0,\ N_{levels} - 1,\ x_{int})\]

其中

\[ \begin{align}\begin{aligned}clamp(a,\ b,x) = a\ \ \ \ \ \ x \leq a\\ = x\ \ \ \ \ \ a \leq x \leq b\\ = b\ \ \ \ \ \ x \geq b\end{aligned}\end{align} \]

反量化操作为

\[x_{float} = (x_{Q} - z)\mathrm{\Delta}\]

\(x_{Q}\)为量化后数据,\(x_{float}\)为反量化后的数据,对于8比特精度 而言\(N_{levels} = 256\)

使用零点,可以更准确的量化任意数据范围的浮点变量,但是零点的引入也会导致更大的计算 量,对于神经网络而言,激活数据很多情况下近似为0均值的正态分布,所以我们可以省略零点 (设置零点为0)来简化运算,这种量化方式也称为对称量化。

此时,计算公式变为

\[x_{int} = round\left( \frac{x}{\mathrm{\Delta}} \right)\]
\[x_{Q} = clamp\left( 0,\ N_{levels} - 1,\ x_{int} \right)\ \ \ \ if\ unsigned\]
\[x_{Q} = clamp\left( \frac{{- N}_{levels}}{2},\frac{N_{levels}}{2} - 1,\ x_{int} \right)\ \ \ \ if\ signed\]
\[x_{float} = x_{Q}\mathrm{\Delta}\]

在Lyngor中我们只考虑有符号的情况和对称量化。

量化参数计算

对于INT8对称量化,\(\mathrm{\Delta}\)是唯一需要确定的参数。

对于卷积算子而言,有两种数据需要量化,输入数据和权重数据。这两种数据有着 不同的特性导致它们的量化方法也不一样。通过给模型大量不同的输入(校准数据集), 我们可以获取很多的卷积输入数据,这样我们可以使用统计的方法来求卷积输入的量化 参数;而模型参数数量却是固定的,往往不具备统计特性,只能使用较简单直接的方法 来计算卷积权重的量化参数。

卷积权重量化参数计算使用一种较直接的方法:\(\mathrm{\Delta} = \frac{x_{\max} - x_{\min}}{N_{level} - 1}\)。 由于只考虑INT8的对称量化,可以设\(x_{\max} > 0,\ x_{\max} = - x_{\min}\)。 所以,只需要考虑如何确定\(x_{\max}\)。 在Lyngor中我们实现了POW2和MAX两种方法来计算\(x_{\max}\)。 在MAX方法中\(x_{\max} = max(abs(w))\); 而在POW2的方法中\(x_{\max} = 2^{ceil(\log_{2}(max(abs(w))))}\)

而对于卷积输入,则使用基于KL散度的统计方法来计算量化参数。

计算权重量化参数时使用的那种简单方法,有一个很大的弊端就是没有考虑数据的分布。 举个简单的例子,比如大量的数据分布在(0,10)的区间范围内,仅有个别野点在区间(90,100), 这时使用上述简单方法就会造成较大的量化误差。基于KL散度的方法由于考虑了数据的统计分布, 就能有效地解决上面的问题。

离散KL散度的计算公式为:\({KL}_{divergence(P,\ Q)} = \sum_{i}^{}{P\lbrack i\rbrack*\log_{2}\frac{P\lbrack i\rbrack}{Q\lbrack i\rbrack}}\), 其中P、Q为离散概率分布。在量化参数计算中,分别对应原始数据的离散概率分布、量化后再 反量化的离散概率分布。离散概率分布可以通过直方图统计。量化+反量化的效果类似于取平 均值,比如假设1个量化间隔由3个统计区间(直方图统计)组成,每个统计区间的数据分别为 3、4、5,那么量化+反量化后的直方图就变为了4、4、4。

量化精度调优

权重数据量化参数计算

权重量化可以采用Per-Layer和Per-Channel两种模式,它们的不同之处在于计算\(max(abs(w))\)\(w\)的取值范围。Per-Layer模式下,取值范围为整个输入权重tensor,计算出的量化参数为一个标量;而在Per-Channel模式下,取值范围为每个输出Channel对应的权重数据,计算出的量化参数为一个tensor。

比如,一个权重tensor的Layout为HWIO,形状为(3, 3, 49, 204),在Per-Layer模式 下就是在\(3*3*49*204\)个数中取最大值;而在Per-Channel模式下就是 在\(3*3*49\)个数中取最大值,得到一个最大值tensor,形状为(204), 而最后计算出的量化参数的形状也为(204)。

Per-Channel模式能够在一定程度上应对离群点的问题,所以效果更好,是Lyngor权重量化的默认方式。

输入数据量化参数计算

在量化输入数据时,需要考虑该输入数据的分布是否适合量化?如果该数据分布天然就 对量化不友好,即使使用KL散度来求量化参数,也会使模型性能有较大地损失。所以我 们需要有一种方法来识别量化不友好的分布,当输入出现这种分布时,该Conv2D算子就 不进行量化。

目前我们基于三个准则来判断一个输入是否是量化友好的。一是该输入分布计算出的KL 散度相比起其它KL散度值是否显著的大,二是输入数据的动态范围是否比INT8可表示范 围大很多(远远大于128),三是数据分布是否存在相距较远的波峰。如果这三个条件都 满足,我们就认为其是量化不友好的,该Conv2D不进行量化。