操作说明 ======================================================================== Lyngor主要包括2个使用场景: - 首次构建引擎 - 重复加载引擎 .. note:: 在获得引擎后,执行推理的操作相同。 Lyngor的简化操作流程如图所示。 .. figure:: _images/Lyngor操作流程.png :alt: Lyngor操作流程 图 Lyngor操作流程 .. _1st_build: 首次构建引擎 ------------------------------------------------------------------------ 如首次构建计算图的推理引擎,在构建完成后,保存引擎,便于后续重复使用该引擎,进行计算图推理, 避免重复编译计算图。 1. 导入Lyngor库,以便后续使用Lyngor接口进行计算图相关操作。具体操作参见 :ref:`import_lyngor` 。 .. note:: 在使用Lyngor接口前,须确认已导入Lyngor库。 2. 判断是否已有模型: - 否:使用Lyngor算子构建计算图。具体操作参见 :ref:`build_graphic` 。 - 是:采用以下2种方式之1,导入模型转换计算图。 - 使用Lyngor接口,具体操作参见 :ref:`model_convert` 。 - 使用编译模型工具,具体操作参见 :ref:`mc_tool` 。 .. attention:: 在导入模型转换计算图时,如模型中存在Lyngor无法支持的算子,会产生报错。 在这种情况下,可通过开发Lyngor自定义算子,实现相应功能。 - Lyngor支持的算子参见:《Lyngor算子说明书》 - 开发Lyngor自定义算子参见:《Lyngor自定义算子开发指南》 3. (可选)如需加速模型推理,减少模型的权重体积,则需要增加量化校准过程,提供校准数据。具体操作参见 :ref:`quantification` 。 4. 构建推理引擎。支持以下2种方式: - 无参数:具体操作参见 :ref:`no_para_engine` 。 - 有参数:具体操作参见 :ref:`para_engine` 。 .. note:: 如需包含量化校准参数,在构建推理引擎时,传入量化配置,具体操作参见 :ref:`include_s_quantize` 。 5. 反序列化引擎。具体操作参见 :ref:`deserialize_engine` 。 6. 执行推理。具体操作参见 :ref:`perform_inference` 。 .. _2nd_build: 重复加载引擎 ------------------------------------------------------------------------ 如已构建计算图的推理引擎,可加载已保存的引擎,避免重复编译计算图。 前提条件:已序列化引擎。具体操作参见 :ref:`build_inference_engine` 。 1. 导入Lyngor库,以便后续使用Lyngor接口进行计算图相关操作。具体操作参见 :ref:`import_lyngor` 。 .. attention:: 在使用Lyngor接口前,须确认已导入Lyngor库。 2. 反序列化引擎,加载已保存的引擎,重复使用编译结果。具体操作参见 :ref:`deserialize_engine` 。 3. 执行推理。具体操作参见 :ref:`perform_inference` 。 INT8量化原理 ------------------------------------------------------------------------ 量化运算的定义 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 对于一个取值范围为(:math:`x_{\min},\ x_{\max}`)的浮点变量,将其量化到范围为(0, :math:`N_{levels} - 1`)需要求出两个参数:量化因子(\ :math:`\mathrm{\Delta}`\ )和零点(\ :math:`z`\ )。 此时,量化计算为: .. math:: x_{int} = round\left( \frac{x}{\mathrm{\Delta}} \right) + z .. math:: x_{Q} = clamp(0,\ N_{levels} - 1,\ x_{int}) 其中 .. math:: clamp(a,\ b,x) = a\ \ \ \ \ \ x \leq a = x\ \ \ \ \ \ a \leq x \leq b = b\ \ \ \ \ \ x \geq b 反量化操作为 .. math:: x_{float} = (x_{Q} - z)\mathrm{\Delta} :math:`x_{Q}`\ 为量化后数据,\ :math:`x_{float}`\ 为反量化后的数据,对于8比特精度 而言\ :math:`N_{levels} = 256`\ 。 使用零点,可以更准确的量化任意数据范围的浮点变量,但是零点的引入也会导致更大的计算 量,对于神经网络而言,激活数据很多情况下近似为0均值的正态分布,所以我们可以省略零点 (设置零点为0)来简化运算,这种量化方式也称为对称量化。 此时,计算公式变为 .. math:: x_{int} = round\left( \frac{x}{\mathrm{\Delta}} \right) .. math:: x_{Q} = clamp\left( 0,\ N_{levels} - 1,\ x_{int} \right)\ \ \ \ if\ unsigned .. math:: x_{Q} = clamp\left( \frac{{- N}_{levels}}{2},\frac{N_{levels}}{2} - 1,\ x_{int} \right)\ \ \ \ if\ signed .. math:: x_{float} = x_{Q}\mathrm{\Delta} 在Lyngor中我们只考虑有符号的情况和对称量化。 量化参数计算 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 对于INT8对称量化,\ :math:`\mathrm{\Delta}`\ 是唯一需要确定的参数。 对于卷积算子而言,有两种数据需要量化,输入数据和权重数据。这两种数据有着 不同的特性导致它们的量化方法也不一样。通过给模型大量不同的输入(校准数据集), 我们可以获取很多的卷积输入数据,这样我们可以使用统计的方法来求卷积输入的量化 参数;而模型参数数量却是固定的,往往不具备统计特性,只能使用较简单直接的方法 来计算卷积权重的量化参数。 卷积权重量化参数计算使用一种较直接的方法:\ :math:`\mathrm{\Delta} = \frac{x_{\max} - x_{\min}}{N_{level} - 1}`\ 。 由于只考虑INT8的对称量化,可以设\ :math:`x_{\max} > 0,\ x_{\max} = - x_{\min}`\ 。 所以,只需要考虑如何确定\ :math:`x_{\max}`\ 。 在Lyngor中我们实现了POW2和MAX两种方法来计算\ :math:`x_{\max}`\ 。 在MAX方法中\ :math:`x_{\max} = max(abs(w))`\ ; 而在POW2的方法中\ :math:`x_{\max} = 2^{ceil(\log_{2}(max(abs(w))))}`\ 。 而对于卷积输入,则使用基于KL散度的统计方法来计算量化参数。 计算权重量化参数时使用的那种简单方法,有一个很大的弊端就是没有考虑数据的分布。 举个简单的例子,比如大量的数据分布在(0,10)的区间范围内,仅有个别野点在区间(90,100), 这时使用上述简单方法就会造成较大的量化误差。基于KL散度的方法由于考虑了数据的统计分布, 就能有效地解决上面的问题。 离散KL散度的计算公式为:\ :math:`{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两种模式,它们的不同之处在于计算\ :math:`max(abs(w))`\ 时\ :math:`w`\ 的取值范围。Per-Layer模式下,取值范围为整个输入权重tensor,计算出的量化参数为一个标量;而在Per-Channel模式下,取值范围为每个输出Channel对应的权重数据,计算出的量化参数为一个tensor。 比如,一个权重tensor的Layout为HWIO,形状为(3, 3, 49, 204),在Per-Layer模式 下就是在\ :math:`3*3*49*204`\ 个数中取最大值;而在Per-Channel模式下就是 在\ :math:`3*3*49`\ 个数中取最大值,得到一个最大值tensor,形状为(204), 而最后计算出的量化参数的形状也为(204)。 Per-Channel模式能够在一定程度上应对离群点的问题,所以效果更好,是Lyngor权重量化的默认方式。 输入数据量化参数计算 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 在量化输入数据时,需要考虑该输入数据的分布是否适合量化?如果该数据分布天然就 对量化不友好,即使使用KL散度来求量化参数,也会使模型性能有较大地损失。所以我 们需要有一种方法来识别量化不友好的分布,当输入出现这种分布时,该Conv2D算子就 不进行量化。 目前我们基于三个准则来判断一个输入是否是量化友好的。一是该输入分布计算出的KL 散度相比起其它KL散度值是否显著的大,二是输入数据的动态范围是否比INT8可表示范 围大很多(远远大于128),三是数据分布是否存在相距较远的波峰。如果这三个条件都 满足,我们就认为其是量化不友好的,该Conv2D不进行量化。