前提:
边缘端需要的模型尺寸更小、推断更快、耗电更低
两种方法:
一种方法是从头构建一个高效的模型。另一种技术是通过量化、裁剪和压缩来降低模型尺寸。
主要讨论:
训练后量化 线性量化
模型量化
是一种模型压缩技术,即原来表示一个权重需要使用float32表示,量化后只需要使用int8来表示,可以获得接近4倍的网络加速。
训练后量化 Post Training Quantization (PTQ)
- 从预训练模型开始,并使用量化数据集对其进行量化
- 量化数据用来对模型进行量化,其可以是训练数据集的子集
- 量化过程:计算权重和激活值的动态范围(Gather layer statistics),用于确定量化参数(q-parms)
- 使用量化参数量化模型
量化感知训练 Quantization Aware Training (QAT)
- 从预训练模型开始,在不同网络层中添加量化操作,利用若干epoch模型进行调优
- 模拟在推理过程中发生的量化过程
- 通过训练学习量化参数,减少量化模型和与预训练模型之间的精度损失
模型量化分类
根据映射函数是否是线性可以分为两类-即线性量化和非线性量化,本文主要研究的是线性量化技术。
线性量化
常见的线性量化过程可以用以下数学表达式来表示:r = Round ( S ( q − Z ) )
其中,q 表示的是原始的float32数值;
Z表示的是float32数值的偏移量,在很多地方又叫Zero Point;
S表示的是float32的缩放因子,在很多地方又叫Scale;
Round(⋅) 表示的是四舍五入近似取整的数学函数,除了四舍五入,使用向上或者向下取整也是可以的;
r表示的是量化后的一个整数值。
-
对称量化
对称量化,即使用一个映射公式将输入数据映射到[-128,127]的范围内,图中-max(|Xf|)表示的是输入数据的最小值,max(|Xf|)表示输入数据的最大值。对称量化的一个核心即零点的处理,映射公式需要保证原始的输入数据中的零点通过映射公式后仍然对应[-128,127]区间的零点。总而言之,对称量化通过映射关系将输入数据映射在[-128,127]的范围内,对于映射关系而言,我们需要求解的参数即Z和S。
在对称量化中,r 是用有符号的整型数值(int8)来表示的,此时 Z=0,且 q=0时恰好有r=0。在对称量化中,我们可以取Z=0,S的取值可以使用如下的公式,也可以采用其它的公式。
其中,n 是用来表示该数值的位宽,x 是数据集的总体样本。 -
非对称量化
非对称量化,即使用一个映射公式将输入数据映射到[0,255]的范围内,图中min(Xf)表示的是输入数据的最小值,max(Xf)表示输入数据的最大值。总而言之,非对称量化通过映射关系将输入数据映射在[0,255]的范围内,对于映射关系而言,我们需要求解的参数即Z和S。
在非对称量化中,r 是用有符号的整型数值(uint8)来表示的。在非对称量化中,我们可以取Z=min(x),S的取值可以使用如下的公式,也可以采用其它的公式。
例:
非对称量化
比如进行int8的量化,数据范围是[-128,127],最大值最小值分别是Xmax, Xmin,X_q表示量化后的数据,X_f表示浮点数据。
X_q=X_f/scale +zero
scale=(Xmax-Xmin)/(127-(-128))=(Xmax-Xmin)/255
zero=0-round(Xmin/scale)或者zero=255-round(Xmax/scale)
非对称量化
训练后的模型权重或激活值往往在一个有限的范围内分布,如激活值范围为[-2.0, 6.0],然后我们使用int8进行模型量化,则定点量化值范围为[-128, 127],那么S和Z的求值过程如下所示:
如果此时我们有一个真实的激活值为0.28即R=0.28,那么对应Q的求解过程如下所示:
整个网络中的其它参数也按照这种方法就可以获得量化之后的数值。
对称量化
训练后的模型权重或激活值往往在一个有限的范围内分布,如激活值范围为[-2.0, 6.0],然后我们使用int8进行模型量化,则定点量化值范围为[-127, 127],那么S和Z的求值过程如下所示:
偏移量 Z = 0
整个网络中的其它参数也按照这种方法就可以获得量化之后的数值。
用int8进行定点量化的可表示范围为[-127,127],且zero-point就是量化值0;
用int8进行定点量化的可表示范围为[-128,127],其zero-point在[-128,127]内依据公式求得;
逐层量化、逐组量化和逐通道量化
根据量化的粒度(共享量化参数的范围)可以分为逐层量化、逐组量化和逐通道量化。
逐层量化以一个层为单位,整个layer的权重共用一组缩放因子S和偏移量Z;
逐组量化以组为单位,每个group使用一组S和Z;
逐通道量化则以通道为单位,每个channel单独使用一组S和Z;
当 group=1 时,逐组量化与逐层量化等价;当 group=num_filters (即dw卷积)时,逐组量化逐通道量化等价。
权重量化和权重激活量化
根据需要量化的参数可以分类两类-权重量化和权重激活量化。
权重量化,即仅仅需要对网络中的权重执行量化操作。由于网络的权重一般都保存下来了,因而我们可以提前根据权重获得相应的量化参数S和Z。由于仅仅对权重执行了量化,这种量化方法的压缩力度不是很大。
权重激活量化,即不仅对网络中的权重进行量化,还对激活值进行量化。由于激活层的范围通常不容易提前获得,因而需要在网络推理的过程中进行计算或者根据模型进行大致的预测。
在许多情况下,我们希望在不重新训练模型的前提下,只是通过压缩权重或量化权重和激活输出来缩减模型大小,从而加快预测速度。“训练后量化”就是这种使用简单,而且在有限的数据条件下就可以完成量化的技术。
实际运用
训练后静态量化
量化模型架构:
- 模型解析 analysis
- 网络结构切割,将诸如conv + relu或conv + batchnorm + relu之类的组合操作融合在一起以提高模型的准确性和性能 segmentation
- 模型数据映射 mapping
- 量化 quantization
- 保存量化位宽和偏移量并生成网络结构中间件
- 解析中间件 生成可编译的cpp文件
- 编译SDK
一般的训练后量化(PTQ) 的做法是用一部分校准数据(一般来自训练集) 来获取模型权重和激活函数的数据范围。
模型量化实现步骤
对于模型量化任务而言,具体的执行步骤如下所示:
- 在输入数据(通常是权重或者激活值)中统计出相应的min_value和max_value;
- 选择合适的量化类型,对称量化(int8)还是非对称量化(uint8);
- 根据量化类型、min_value和max_value来计算获得量化的参数Z/Zero point和S/Scale;
- 根据标定数据对模型执行量化操作,即将其由FP32转换为INT8;
- 验证量化后的模型性能,如果效果不好,尝试着使用不同的方式计算S和Z,重新
求出量化参数Scale后,需要转换成2的x次方 x表示量化的位宽。
量化方法
逐通道量化方法可以提供很好的精度,可以作为权重和激活输出的先训练后量化的很好的基线,而且非对称量化方式对于所有模型都能提高和浮点型很相近的精度。
激活输出做8bit量化后,精度几乎没有损失。由于以下原因,激活输出的动态范围一般很小:
(a) 无缩放的批归一化:它可以确保所有特征的激活输出服从均值为0,方差为1的分布。
(b) ReLU6: 它可以将所有的特征输出都固定到(0, 6)范围内,从而消除大动态范围的可能。
像Resnets 和 Inception-v3 这种有更多参数的模型与Mobilenets 这种参数较少的模型相比,对于量化的鲁棒性会更高。
当采用逐层量化的方式对权重进行量化时,会有非常大的精度损失,尤其对于Mobilenet架构。
几乎所有的量化精度损失都是源于权重量化而导致的。
采用逐层量化的方式对权重进行量化时,会产生很大的精度损失,这是因为bn操作在特征层的各个通道间的动态范围差异非常大。激活输出仍然需要采用逐层对称量化方式来量化。
可以对模型采取量化的方式进行优化,使用pytorch quantization工具,比如finetune、calibrate
https://github.com/NVIDIA/TensorRT/tree/master/tools/pytorch-quantization
量化感知训练(QAT)
https://zhuanlan.zhihu.com/p/397854365
https://blog.csdn.net/u010420283/article/details/114483073
https://blog.csdn.net/WZZ18191171661/article/details/103332338
https://www.jianshu.com/p/b360b4252ab8
https://blog.csdn.net/guvcolie/article/details/81286349