模型量化1-概述2:

1. 引入

  • 在 AI 模型训练时,通常使用浮点数(Float32 等)进行计算,这样能够确保更好的精度表现

  • 当然浮点数运算也是一把双刃剑,在提升了计算精度的同时带来了更多的计算量和储存空间占用

  • 具体的表现就是模型的计算速度较慢、模型的文件体积较大

  • 在模型推理的时候,大多数时候并不需要如此高的计算精度,或者说低精度的运算不会对模型精度产生太大的影响

  • 这个时候就可以将模型映射到较低精度的运算上,降低计算量,提升运行速度,减少模型文件的体积,方便传输

  • 这样的将模型从高精度运算(一般是浮点运算)转换到低精度运算(一般是整数运算)的过程叫做模型量化

2. 量化原理

  • 模型量化桥接了定点与浮点,建立了一种有效的数据映射关系,使得以较小的精度损失代价获得了较好的收益

  • 要弄懂模型量化的原理就是要弄懂这种数据映射关系,浮点与定点数据的转换公式如下:

    • R 表示输入的浮点数据
    • Q 表示量化之后的定点数据
    • Z 表示零点(Zero Point)的数值
    • S 表示缩放因子(Scale)的数值
  • 我们可以根据 S 和 Z 这两个参数来确定这个映射关系

  • 求解 S 和 Z 有很多种方法,这里列举中其中的一种求解方式(MinMax)如下:

    • Rmax​ 表示输入浮点数据中的最大值
    • Rmin​ 表示输入浮点数据中的最小值
    • Qmax​ 表示最大的定点值(127 / 255)
    • 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. 量化实践




模型量化(1):模型量化简介 - 飞桨AI Studio星河社区 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值