模型量化(1):模型量化简介
转自AI Studio,原文链接:模型量化(1):模型量化简介 - 飞桨AI Studio引入在 AI 模型训练时,通常使用浮点数(Float32 等)进行计算,这样能够确保更好的精度表现当然浮点数运算也是一把双刃剑,在提升了计算精度的同时带来了更多的计算量和储存空间占用具体的表现就是模型的计算速度较慢、模型的文件体积较大在模型推理的时候,大多数时候并不需要如此高的计算精度,或者说低精度的运算不会
转自AI Studio,原文链接:模型量化(1):模型量化简介 - 飞桨AI Studio
引入
-
在 AI 模型训练时,通常使用浮点数(Float32 等)进行计算,这样能够确保更好的精度表现
-
当然浮点数运算也是一把双刃剑,在提升了计算精度的同时带来了更多的计算量和储存空间占用
-
具体的表现就是模型的计算速度较慢、模型的文件体积较大
-
在模型推理的时候,大多数时候并不需要如此高的计算精度,或者说低精度的运算不会对模型精度产生太大的影响
-
这个时候就可以将模型映射到较低精度的运算上,降低计算量,提升运行速度,减少模型文件的体积,方便传输
-
这样的将模型从高精度运算(一般是浮点运算)转换到低精度运算(一般是整数运算)的过程叫做模型量化
2. 量化原理
-
模型量化桥接了定点与浮点,建立了一种有效的数据映射关系,使得以较小的精度损失代价获得了较好的收益
-
要弄懂模型量化的原理就是要弄懂这种数据映射关系,浮点与定点数据的转换公式如下:
Q=RS+ZQ=\frac{R}{S}+ZQ=SR+Z
R=(Q−Z)∗SR=(Q-Z) * SR=(Q−Z)∗S
- R 表示输入的浮点数据
- Q 表示量化之后的定点数据
- Z 表示零点(Zero Point)的数值
- S 表示缩放因子(Scale)的数值
-
我们可以根据 S 和 Z 这两个参数来确定这个映射关系
-
求解 S 和 Z 有很多种方法,这里列举中其中的一种求解方式(MinMax)如下:
S=Rmax−RminQmax−QminS=\frac{R_{\max }-R_{\min }}{Q_{\max }-Q_{\min }}S=Qmax−QminRmax−Rmin
Z=Qmax−Rmax÷SZ=Q_{\max }-R_{\max } \div SZ=Qmax−Rmax÷S
- RmaxR_{\max }Rmax 表示输入浮点数据中的最大值
- RminR_{\min }Rmin 表示输入浮点数据中的最小值
- QmaxQ_{\max }Qmax 表示最大的定点值(127 / 255)
- QminQ_{\min }Qmin 表示最小的定点值(-128 / 0)
-
当然这不是模型量化的全部,这里只是简单的介绍一下线性量化的基本原理和思想
3. 量化类型
- 线性量化可分为对称量化和非对称量化
3.1 对称量化
-
对称量化即使用一个映射公式将输入数据映射到 [-128,127] 的范围内
-
映射公式需要保证原始的输入数据中的零点通过映射公式后仍然对应 [-128,127] 区间的零点
3.2 非对称量化
-
即使用一个映射公式将输入数据映射到[0,255]的范围内
4. 量化实例
-
上面介绍了量化的基本原理和思想
-
下面通过代码来实现一个简单的量化过程
4.1 数据量化
- 使用上面介绍的那种方法,对随机生成的浮点数据进行量化,转换为定点数据
In [1]
import sys
import time
import numpy as np
# 随机生成一些浮点数据(Float32)
data_float32 = np.random.randn(10).astype('float32')
# 量化上下限(UInt8)
Qmin = 0
Qmax = 255
# 计算缩放因子(Scale)
S = (data_float32.max() - data_float32.min()) / (Qmax - Qmin)
# 计算零点(Zero Point)
Z = Qmax - data_float32.max() / S
# 将浮点数据(Float32)量化为定点数据(UInt8)
data_uint8 = np.round(data_float32 / S + Z).astype('uint8')
# 将定点数据(UInt8)反量化为浮点数据(Float32)
data_float32_ = ((data_uint8 - Z) * S).astype('float32')
4.2 数据对比
- 将原始输出,量化后的数据,反量化后的数据进行对比
- 可以看出,通过量化、反量化之后数据会略有变化,但是差异较小,在可接受范围内
In [2]
# 使用均方误差计算差异
mse = ((data_float32-data_float32_)**2).mean()
print("原始数据:", data_float32)
print("反量化后数据:", data_float32_)
print("量化后数据:", data_uint8)
print("原始数据和反量化后数据的均方误差:", mse)
原始数据: [-0.47069794 0.8608386 -0.947035 -1.0697421 0.10035864 0.0013437 0.6782814 0.7141242 -0.09668107 0.4391506 ] 反量化后数据: [-0.4716406 0.8608386 -0.94860756 -1.069742 0.10374816 -0.00224451 0.6791369 0.7169914 -0.09309536 0.43686795] 量化后数据: [ 79 255 16 0 155 141 231 236 129 199] 原始数据和反量化后数据的均方误差: 5.4746083e-06
4.3 内存对比
-
对比量化前后的数据内存占用情况
-
可以看到从数据从 Float32 转换为 UInt8 格式,内存占用可以缩小至原来的 1/4
In [3]
# 空数组的内存占用
empty_size = sys.getsizeof(np.array([]))
# 计算实际数据的内存占用
float32_size = (sys.getsizeof(data_float32) - empty_size)
uint8_size = (sys.getsizeof(data_uint8) - empty_size)
print("原始数据内存占用:%d Bytes" % float32_size)
print("量化后数据内存占用:%d Bytes" % uint8_size)
print("量化后数据与原始数据内存占用之比:", uint8_size / float32_size)
原始数据内存占用:40 Bytes 量化后数据内存占用:10 Bytes 量化后数据与原始数据内存占用之比: 0.25
4.4 速度对比
-
通过数据自身求和 n 次来测试运算速度
-
可以看到在这样的运算中,UInt8 相比 Float32 可以有两倍左右的加速比
In [5]
# 预热次数
warmup = 100
# 重复次数
repeat = 10000
# 预热
sum = data_float32
for i in range(warmup):
sum += data_float32
sum = data_uint8
for i in range(warmup):
sum += data_uint8
# 速度测试
start = time.time()
sum = data_float32
for i in range(repeat):
sum += data_float32
float32_time = time.time() - start
start = time.time()
sum = data_uint8
for i in range(repeat):
sum += data_uint8
uint8_time = time.time() - start
print("原始数据求和 %d 次耗时 :%f s" % (repeat, float32_time))
print("量化后数据求和 %d 次耗时:%f s" % (repeat, uint8_time))
print("量化后数据与原始数据计算耗时之比:", uint8_time / float32_time)
原始数据求和 10000 次耗时 :0.017538 s 量化后数据求和 10000 次耗时:0.008749 s 量化后数据与原始数据计算耗时之比: 0.49884448069603043
5. 量化特点
-
从上面的实例中可以看出量化操作的几个优点:
- 计算快,效率高,计算时的耗能降低
- 内存占用小,方便数据文件的储存和传输
-
当然缺点也是有的,也很显而易见,就是计算精度会有一定程度的下降
6. 量化分类
-
根据执行模型量化的时机,可将常用的模型量化方法分为如下两类:
-
训练时量化:
- 感知量化训练:量化训练让模型感知量化运算对模型精度带来的影响,使用大量有标签的数据,通过 finetune 训练降低量化误差。
-
训练后量化:
- 静态量化:使用少量无标签校准数据,采用KL散度等方法计算量化比例因子。
- 动态量化:无需额外数据,仅将模型中特定算子的权重从浮点类型映射成整数类型。
-
-
各种量化方法之间的关系图如下:
-
各种量化方法之间的对比图如下:
7. 量化实践
-
模型量化已经是相对成熟的技术了,有很多好用的工具可以快速地对 AI 模型进行量化操作
-
比如 Paddle 官方开发的 PaddleSlim 就包含模型量化的功能
-
对于 ONNX 模型而言,也可以使用 ONNXRuntime 内置的量化工具对模型进行量化
-
光说不练假把式,本文为系列文章,接下来的几篇将介绍模型量化的实践操作
-
感兴趣的可点击下方链接,跳转至对应文章:
更多推荐
所有评论(0)