欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

模型压缩-量化算法概述

最编程 2024-03-30 19:42:01
...

前言

浮点数和定点数

浮点数(floating point number)和定点数(fixed point number)都是计算机中表示实数(即带有小数部分的数)的方式。在数值计算中,小数在内存中是以浮点数格式表示和参与运算。浮点数和定点数中的“点”指的是小数点。

1,浮点数表示法是用科学计数法表示实数,即:

fraction×baseexponent\text{fraction} \times \text{base}^\text{exponent}

其中,fractionfraction 表示尾数,basebase 表示基数(通常为 2),exponent\text{exponent} 表示指数。浮点数的表示法将尾数和指数分别存储在计算机内存中,由于指数可为负数或小数,可以表示非常大或非常小的实数,同时也具有一定的精度。浮点数一般使用 IEEE754 标准表示,如单精度浮点数使用 32 位-FP32(1 位符号位,8 位指数位,23 位尾数位)来表示实数。

2,定点数表示法是用一个固定的小数位数来表示实数,即用一定数量的二进制数表示小数部分。定点数没有指数部分,小数部分的位数是固定的,所以它的范围和精度比较有限。定点数一般使用有符号或无符号整数表示,比如常见的 INT8

INT8 是 8 位定点数,其取值范围为 [128,127][-128,127],共 256256 个不同的取值。

IEEE754 标准

我们知道,浮点数是一种对于数学中实数的近似值数值表现法,其由符号、尾数、基和指数组成,如 0.3141592610×102−0.3141592610 \times 10^20.101001_2×230.101001\_{2} \times 2^{3}

在数学中,实数(real number)是有理数和无理数的总称,直观理解就是小数

IEEE 754 标准中, 浮点数是用二进制表示的, 形式如下:

V=(1)s×f×2eV = (-1)^s \times f \times 2^{e}

其由三部分组成:

  • 符号,sign,其值用 ss 表示。
  • 指数,exponent,其值用 ee 表示,对于单精度浮点数,指数占 8
  • 尾数,fraction,其值用 ff 表示。

IEEE 754 标准中的单精度和双精度格式如下图所示:

ieee754

单精度浮点数值的分类:

1,规格化的(一般情况):

指数位既不全为 0(数值 0),也不全位 1(单精度数值 255,双精度数值 2047)。这种情况下,指数为被定义以偏置形式表示的有符号整数,即指数位数值是 E=eBiasE = e - Bias。计算过程省略,但是由此产生的指数的取值范围,对于单精度是 [126,127][-126, 127],而对于双精度是 [1022,1023][-1022, 1023]

对于单精度的规格化数,其数值范围[1×2126,(2ε)×2127],ε=0[1\times 2^{-126}, (2-\varepsilon) \times 2^{127}], \varepsilon = 0,经过换算十进制数范围为 [1.17×1038,3.4×1038][1.17 \times 10^{-38}, 3.4 \times 10^{38}]

2^{-126} = 1.17*10^{-38}
2 * 2^{127} = 3.4*10^{38}

2,非规格化的

CSAPP 中文书上翻译为阶码域为全 0 时,所表示的数时非规格化形式,即指数为全为 0(数值 0)。

3,特殊值

和非规格化相反,阶码域为全 1 。

一,模型量化概述

所谓量化,其实可以等同于低精度(Low precision)概念,常规模型精度一般使用 FP32(32 位浮点数,单精度)存储模型权重参数,低精度则表示使用 INT8FP16 等权重数值格式。

模型量化(Model Quantization,也叫网络量化)过程分为两部分:将模型的单精度参数(一般 FP32-32浮点参数)转化为低精度参数(一般 INT8-8定点参数),以及模型推理过程中的浮点运算转化为定点运算,这个需要推理框架支持。

模型量化技术可以降低模型的存储空间、内存占用和计算资源需求,从而提高模型的推理速度,也是为了更好的适配移动端/端侧 NPU 加速器。简单总结就是,模型变小了,速度变快了,支持的场景更多了。

最后,现在工业界主流的思路就是模型训练使用高精度-FP32 参数模型,模型推理使用低精度-INT8 参数模型。

1.1,模型计算量和参数量统计

之所以需要做模型量化,是因为训练好的原始 FP32 模型实在太大了,在端侧/移动端都很好直接部署,即使运行起来速度和功耗也很大。经典 backbone 的参数量和计算量统计结果如下:

参数量和计算量统计

来源 thop,也叫 PyTorch-OpCounter 工具统计结果。

Pytorch 框架中常用的参数量和计算量统计分析工具有,torchsummarythop。以 thop 为例,其基础用法如下所示:

import torch
from torchvision.models import resnet50
from thop import profile
model = resnet50()
input = torch.randn(1, 3, 224, 224)
macs, params = profile(model, inputs=(input, ))
print("flops: %.2f\nparameters: %.2f" % (macs, params))

运行结果如下所示:

[INFO] Register count_convNd() for <class 'torch.nn.modules.conv.Conv2d'>.
[INFO] Register count_normalization() for <class 'torch.nn.modules.batchnorm.BatchNorm2d'>.
[INFO] Register zero_ops() for <class 'torch.nn.modules.activation.ReLU'>.
[INFO] Register zero_ops() for <class 'torch.nn.modules.pooling.MaxPool2d'>.
[INFO] Register zero_ops() for <class 'torch.nn.modules.container.Sequential'>.
[INFO] Register count_adap_avgpool() for <class 'torch.nn.modules.pooling.AdaptiveAvgPool2d'>.
[INFO] Register count_linear() for <class 'torch.nn.modules.linear.Linear'>.
flops: 4133742592.00
parameters: 25557032.00

二,模型量化的方案

在实践中将浮点模型转为量化模型的方法有以下三种方法:

  1. data free:不使用校准集,传统的方法直接将浮点参数转化成定点数,使用上非常简单,但是一般会带来很大的精度损失,但是高通最新的论文 DFQ 不使用校准集也得到了很高的精度。
  2. calibration:基于校准集方案,通过输入少量真实数据进行统计分析,从而确定浮点值域(min/max)。很多芯片厂商都提供这样的功能,如 tensorRT、高通、海思、地平线、寒武纪。TensorRT 校准时,一般是用训练集的子集。这里使用校准集,是因为第一个 conv 需要统计输入 的 min、max 外,其他层都只需要统计中间输出 feature 的 min、max 即可。
  3. finetune:基于训练 finetune 的方案,将量化误差在训练时仿真建模,调整权重使其更适合量化。好处是能带来更大的精度提升,缺点是要修改模型训练代码,开发周期较长。

TensorFlow 框架按照量化阶段的不同,其模型量化功能分为以下两种:

  • Post-training quantization PTQ(训练后量化、离线量化);
  • Quantization-aware training QAT(训练时量化,伪量化,在线量化)。

PTQ Post Training Quantization 是训练后量化,也叫做离线量化。

  1. 根据量化零点 xzero_pointx_{zero\_point} 是否为 0,训练后量化分为对称量化非对称量化。根

  2. 据数据通道顺序 NHWC(TensorFlow) 这一维度区分,训练后量化又分为逐层量化逐通道量化。目前 nvidiaTensorRT 框架中使用了逐层量化的方法,每个网络层采用同一个阈值来进行量化。逐通道量化就是对每一层的每个通道都有各自的阈值,对精度可以有一个很好的提升。

2.1,量化算术的分类

目前已知的加快推理速度概率较大的量化方法主要有:

  1. 二值化,其可以用简单的位运算来同时计算大量的数。对比从 nvdia gpu 到 x86 平台,1bit 计算分别有 5 到128倍的理论性能提升。且其只会引入一个额外的量化操作,该操作可以享受到 SIMD(单指令多数据流)的加速收益。
  2. 线性量化(最常见),又可细分为非对称,对称和 ristretto 几种。在 nvdia gpux86arm 和 部分 AI 芯片平台上,均支持 8bit 的计算,效率提升从 1 倍到 16 倍不等,其中 tensor core 甚至支持 4bit计算,这也是非常有潜力的方向。线性量化引入的额外量化/反量化计算都是标准的向量操作,因此也可以使用 SIMD 进行加速,带来的额外计算耗时不大。
  3. 对数量化,一种比较特殊的量化方法。两个同底的幂指数进行相乘,那么等价于其指数相加,降低了计算强度。同时加法也被转变为索引计算。目前 nvdia gpux86arm 三大平台上没有实现对数量化的加速库,但是目前已知海思 351X 系列芯片上使用了对数量化。

与非线性量化不同,线性量化采用均匀分布的聚类中心,原始浮点数据和量化后的定点数据存在一个简单的线性变换关系,因为卷积、全连接等网络层本身只是简单的线性计算,因此线性量化中可以直接用量化后的数据进行直接计算。

三,量化算法

3.1,浮点转定点算术(量化)

32-bit 浮点数和 8-bit 定点数的表示范围如下表所示:

数据类型 最小值 最大值
FP32 -1.17e38 3.4e38
int8 -128 127
uint8 0 255

原始神经网络模型的推理由浮点运算构成。FP32INT8 的值域是 [1.17×1038,3.4×1038][1.17 \times 10^{-38}, 3.4 \times 10^{38}][128,127][−128,127],而取值数量大约分别为 2322^{32}282^8FP32 取值范围非常广,因此,将网络从 FP32 转换为 INT8 并不像数据类型转换截断那样简单。

根据偏移量 ZZ 是否为 0,可以将浮点数的线性量化分为两类-对称量化和非对称量化

当浮点值域落在 (1,1)(-1,1) 之间,权重浮点数据的量化运算可使用下式的方法将 FP32 映射到 INT8,这是对称量化。其中 xfloatx_{float} 表示 FP32 权重, xquantizedx_{quantized} 表示量化的 INT8 权重,xscalex_{scale} 是缩放因子(映射因子、量化尺度(范围)/ float32 的缩放因子)。

xfloat=xscale×xquantizedxquantized=xfloatxquantizedx_{float} = x_{scale} \times x_{quantized} \\ x_{quantized} = \frac{x_{float}}{x_{quantized}}

对称量化的浮点值和 8 位定点值的映射关系如下图,从图中可以看出,对称量化就是将一个 tensor 中的 [max(x),max(x)][-max(|\mathrm{x}|),max(|\mathrm{x}|)] 内的 FP32 值分别映射到 8 bit 数据的 [-128, 127] 的范围内,中间值按照线性关系进行映射,称这种映射关系是对称量化。可以看出,对称量化的浮点值和量化值范围都是相对于零对称的。

对称量化

因为对称量化的缩放方法可能会将 FP32 零映射到 INT8 零,但我们不希望这种情况出现,于是出现了数字信号处理中的均一量化,即非对称量化。数学表达式如下所示,其中 xzero_pointx_{zero\_point} 表示量化零点(量化偏移)。

xfloat=xscale×(xquantizedxzero_point)x_{float} = x_{scale} \times (x_{quantized} - x_{zero\_point})

大多数情况下量化是选用无符号整数,即 INT8 的值域就为 [0,255][0,255] ,这种情况,显然要用非对称量化。非对称量化的浮点值和 8 位定点值的映射关系如下图:

非对称量化

总的来说,权重量化浮点值可以分为两个步骤

  1. 通过在权重张量(Tensor)中找到 minminmaxmax 值从而确定 xscalex_{scale}xzero_pointx_{zero\_point}
  2. 将权重张量的每个值从 FP32 转换为 INT8 。