近年来,基于深度学习的方法在图像分类、目标检测、实例分割等任务上取得了巨大的成就,这样的成就得益于神经网络模型的深层次结构和庞大的模型参数量。但是,庞大的参数量意味着很难将模型部署到资源受限的边缘设备中,比如智能手机、智能穿戴设备、无人机、机器人、自动驾驶汽车等,这些设备通常对神经网络的执行过程有着严格的时间限制或者在长期执行时对功耗有着严苛的要求。因此,迫切需要优化模型的技术,以减少模型尺寸、实现更低的功耗和更快的推理速度。
量化将信号的连续取值(float32)近似为有限多个离散值(int8等),是减少神经网络模型计算时间和功耗最常用的手段。但是,神经网络量化后引入的量化/反量化节点,在进行逐点elementwise运算过程中需要消耗大量时间进行数据遍历,严重影响量化模型推理的性能。为了减少计算时间,在模型部署实践中通常将反量化并入模型的后处理环节进行计算,以减少数据重复遍历的时间消耗,称为反量化节点融合。
如下表为efficientdetd0模型反量化节点融合前后在Horzion XJ3开发板上的性能,显然,当把反量化节点融入到模型的后处理后,模型的性能具有以下变化:

反量化节点融合进后处理的耗时远远小于融合前的Dequantize节点+后处理的耗时,减少一次数据遍历耗时的效果明显;
模型的workflow满帧率运行的FPS相对于反量化节点融合之前提升了惊人的34.18%。
所以,本文将以目标检测模型efficientdetd0为例,描述反量化节点融合的相关背景和基本过程。
1. 量化与反量化计算
模型量化(Quantize)就是建立一种浮点数据和定点数据间的映射关系,把featuremap或者常见的操作(卷积、激活、池化,均一化等)转化为等价的int8类型的操作,使得以较小的精度损失代价获得了较大的收益。反量化( Dequantize)则是指量化的逆运算,把整型数据转化为浮点型数据。
1.1 Quantize
Quantize节点用于将模型 float 类型的输入数据量化至 int8或 int16 类型,下面将对PTQ和QAT量化工具的公式和代码进行介绍。
1.1.1 PTQ量化
1.量化公式

int8量化python代码:
1.量化公式

int8量化python代码:
int16量化python代码:
int8量化C++实现代码:
int16量化C++实现代码:
1.2 Dequantize
Dequantize节点则用于将模型中整型的输出数据反量化回 float 类型,其计算公式如下:
如下为Dequantize节点的C++的实现代码:
这里的zero_point和 scale与Quantize节点中的意义相同。
2. 反量化节点融合基本过程
通常情况下,对于 BPU/CPU 混合异构模型,单帧串行下的耗时包括以下几个方面:
输入量化CPU节点+模型BPU算子+输出反量化CPU节点+CPU后处理
输入量化CPU节点:float32->int8,只有 featuremap 输入模型包含,图像输入模型不包含此类节点;
输出反量化CPU节点:int8->float32;
另外,根据神经网络量化计算的背景知识可知:
模型部署阶段神经网络量化/反量化操作的本质是对输入张量的逐点elementwise乘加运算,即神经网络中的权值与量化参数scale和zero point进行的运算;
乘加操作和数据传输消耗了神经网络推理期间消耗的大量能量;
乘加运算需要遍历张量的每个元素,相对于乘加运算,遍历元素消耗的时间更多。
对于存在前后处理的网络模型,其必须要进行遍历数据的操作,那么就可以在模型量化过程中手动移除模型首尾部的Quantize和Dequantize节点,然后将Quantize和Dequantize操作融入模型前后处理的代码中实现,以减少数据重复遍历的损耗。
2.1 反量化节点删除
地平线AI工具链提供的移除模型尾部的Dequantize节点的方法主要有以下两种:
1. 配置yaml文件
如此便可以删除模型输入输出端的Quantize和Dequantize节点。这里需要提及的是,yaml参数设置后只会删除模型开头或者末尾的节点。
2. hb_model_modifier工具
2.2 scale值查看
量化比例因子scale是进行反量化计算的必要因素,删除Dequantize节点后,仍然需要获取删除节点的scale值以进行后续的反量化融合操作。可以使用以下方式来查看scale值:
1.方式1

2. 方式2
开发机端运行hb_model_info 工具:

3.方式3
板端运行hrt_model_exec 工具:

4. 方式4
如下为这两个API的定义和成员介绍:
成员名称 | 描述 |
|---|---|
sysMem | 存放张量的内存 |
properties | 张量的信息 |
成员名称 | 描述 |
|---|---|
validShape | 张量有效内容的形状 |
alignedShape | 张量对齐内容的形状 |
tensorLayout | 张量的排布形式 |
tensorType | 定点转浮点的偏移量 |
shift | 定点转浮点的偏移量 |
scale | 定点转浮点的缩放量 |
quantiType | 定点转浮点的量化类型 |
alignedByteSize | 张量对齐内容的内存大小 |
2.3 反量化节点融合示例
移除Dequantize节点。可以通过2.1节的两种方法移除模型尾部的Dequantize节点。
- 获取量化比例因子scale的值。模型的Dequantize节点删除后,删除节点的信息会存放在BIN模型中,可以使用tensor.properties.scale.scaleData来获取scale值 。
将获取的scale值融入到模型的后处理环节进行反量化计算,从而避免一次数据遍历的冗余操作。
runtime中反量化节点的C++实现代码如下:
最后,按照上述公式进行反量化计算,然后再进行其他后处理操作。如下为efficientdetd0示例进行反量化计算的部分C++代码:
return 0;
}
3.参考文献
Nagel M, Fournarakis M, Amjad R A, et al. A white paper on neural network quantization[J]. arXiv preprint arXiv:2106.08295, 2021.
Gholami A, Kim S, Dong Z, et al. A survey of quantization methods for efficient neural network inference[J]. arXiv preprint arXiv:2103.13630, 2021.
https://pytorch.org/blog/introduction-to-quantization-on-pytorch/
```




