1 Numeric Data Types
1.1 Integer
1.2 Fixed-Point Number
定点数
1.3 Floating-Point Number
- Larger Range
浮点数Family
深度学习引发的数值表示的革新
权衡要表示的range和小数位的精度
- 单精度FP32、半精度FP16…
- BF16 (Google) 为了追求表示更大的范围 在训练时小数没那么重要,所以增加了Exponent
- TF32(Nvidia)FP32的Exponent bits + FP16的Fraction bits
计算一个FP16的例子
计算一个BF16的例子
2 K-Means-based Weight Quantization
it does not hurt the accuracy!(for inference)
[[Reference_Deep Compression]]
- 对原始权重进行聚类,得到4个聚类中心,index分别设置为0、1、2、3
- 用2bit就可以表示index,所以原本需要32bit存储的每个weight数据变成2bit存储
- 聚类结果仍用32bit存储
- 最终节约3.2倍的storage
- 假设参数量M远大于量化比特数量N,原本存储需要32Mbit,K-means量化后需要NMbit,减少了32/N倍的storage
note: specialized data path is required to support such technique
2.1 Fine-tuning Quantized Weights
- 按照原始weights求梯度,然后根据聚类后的group将梯度求和
- -0.03+0.12+0.02-0.07=0.04
- 用求得的梯度和(一个group)来更新centroids聚类中心
2.2 Accuracy vs. Compression rate
Question:我们应该先剪枝还是先量化?
Answer:先剪枝,减少参数量,然后再进行量化
剪枝后、fine-tuning后的参数分布如下图
量化后
量化后+重训练后
slightly shifted, left or right
直接这样看几乎没看出啥区别,但是在ppt里两张来回翻翻还是能看出权重的略微移动
2.3 How Many Bits do We Need?
2.4 Summary
计算图如下
- 在执行推理时,利用查找表将权重压缩
- K-Means量化只节约了模型的存储开销
- 所有的计算和内存访问依然是浮点数
Linear Quantization不仅用Integer来存储,也用Interger来计算
3 Huffman Coding
在对权重进行量化后,根据权重分布的直方图可以很直观的发现它不是一个均匀分布,有些权重出现的概率大,有些权重出现的概率小,所以不需要把所有权重都按上面说4bit或2bit来表示,而是把出现概率大的用更少的bit表示,md韩松博士是学过信息论的哈哈哈
To further reduce the storage required
4 Deep Compression Pipeline
实验结果
Can we make compact models to begin with?
SqueezeNet
压缩后的SqueezeNet的模型尺寸,比AlexNet小510倍
5 Linear Quantization
不需要定制化的硬件,可以在CPU/GPU上跑起来
[[Reference_Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference]]
- zero point:2bit有符号整数
- scale:32bit浮点数
- 跟K-means最大的区别是量化后的值是线性的
- (K-means量化后的四个值-1, 0 ,1.5, 2)
量化与反量化,课件里直接从反量化切入,实际上先把实数激活值量化成整数,与权重矩阵相乘得到整数结果 Y i n t / q Y Y_{int} / q_{Y} Yint/qY后,再进行反量化
从q到r的映射 = 从量化的权重到真实权重的映射
- Scale是一个量化参数
- Zero point是另一个量化参数,使得r中的0能够被准确的表示(因为剪枝后的权重中存在很多的零,我们希望q经过映射能把零给准确的表达出来)
下图表示了量化后的权重到真实权重的映射,根据
r
m
i
n
,
r
m
a
x
r_{min}, r_{max}
rmin,rmax 可以知道量化后的 q 需要多少bit来表达和存储
根据量化后的bit,可以表达的数据范围为
−
2
N
−
1
,
2
N
−
1
−
1
-2^{N-1},2^{N-1}-1
−2N−1,2N−1−1
5.1 如何计算量化参数scale(非对称量化):
计算scale的例子:
首先找到真实权重的最大值和最小值 ->得到分子
然后确定用多少bit来表示量化后的权重,这里采用2bt,得到分母为3
计算得到S为1.07
5.2 如何计算量化参数Z(非对称量化):
得到S后,用 r m i n = S ( q m i n − Z ) r_{min}=S(q_{min}-Z) rmin=S(qmin−Z)或 r m a x = S ( q m a x − Z ) r_{max}=S(q_{max}-Z) rmax=S(qmax−Z)就可以算出Z,将计算结果求整
计算Z的例子
5.3 非对称量化Non-symmetric
假设没有bias的情况,浮点数(Y, W, X)通过量化后的整数 -> 反量化得到
- W:权重,可以提前计算qw, Zw ,Sw,常量
- X:输入激活值input activation,dynamic
如何计算qY
哪些是常量?
integer的乘法,32bit加法来防止溢出
- q W , Z W q_{W},Z_{W} qW,ZW都是可以提前计算的,因为W权重在推理时候是已知的看作常数
- 经验上,
S
W
S
X
S
Y
\frac{S_{W}S_{X}}{S_{Y}}
SYSWSX 永远保持在 (0,1) 的范围内,所以可以用定点数计算代替
2的-n次方可以用移位实现,硬件中替代除法的友好方法
权重的分布通常是正态分布,所以可以认为权重的Zero Point为0(意味着量化后的0映射到浮点数中也是0,也就是说认为权重大概是对称分布的)
从而在计算时消除这项(Zero Point = 0 也就是对称量化,因为权重的分布特性,所以在量化权重时常用对称量化!)
5.4 对称量化Symmetric
在这种情况下,
r
m
i
n
=
−
∣
r
∣
m
a
x
,
Z
=
0
r_{min}=-|r|_{max}, Z=0
rmin=−∣r∣max,Z=0 可以算出S
这种方法在Pytorch和ONNX中使用
但是,只采用一边的范围来计算( q m i n q_{min} qmin)此时量化后的正数和负数不平均,如图中3+ 4-
相反,采用
q
m
a
x
q_{max}
qmax来计算S是TensorFlow等框架中采用的方法
5.5 对称量化vs非对称量化
两者的主要区别是量化值区间是否约束了量化前后的零点对应
-
非对称量化使用整个范围,可以处理好原始数据分布不均匀的情况
-
部署更加困难,且计算zero point需要额外的逻辑资源
-
对称量化会忽略掉一半的范围
-
由于Activation Tensor在经过ReLU后没有负数,所以对称量化会浪费1bit有效位(3bit表示8个量化值,但实际上只有0和整数,即4个量化值去映射不存在的负数,只有4个量化值有效,那其实2bit就能表示,所以浪费了1bit有效位)
-
部署比较简单
对称量化的zero point = 0 所以消除了2项,减少了计算量
小白看到这里有点懵,在网上找找相关资料
6 参考博客
6.1 参考一
- 对称量化的实数0也对应整数的0,而非对称量化的实数0不一定对应着0,而是Zero Point Z
- 对称量化实数的范围是对称的 [ − α , α ] [-\alpha, \alpha] [−α,α] ,而非对称量化的范围不对称 [ − β , α ] [-\beta,\alpha] [−β,α]
- 对称量化整数的范围是对称的 [ − 127 , 127 ] [-127,127] [−127,127],而非对称量化的不对称 [ − 128 , 127 ] [-128,127] [−128,127]
简单概括非对称量化: f ( X ) = S ⋅ X + Z f(X)=S\cdot X+Z f(X)=S⋅X+Z ,其中Z为Zero Point,这个数字就表示实数0映射到整数是多少,而对称量化就是 f ( X ) = S ⋅ X f(X)=S\cdot X f(X)=S⋅X
需要注意的一点是,不论是非对称还是对称量化,是基于线性量化(也可以称作均匀量化)的一种。线性量化将FP32映射到INT8数据类型,每个间隔是相等的,而不相等的就称为非线性量化。非线性量化因为对部署并不是很友好,虽然能够更好地捕捉到权重分布的密集点
以下根据oldpan大佬的博客,试图对应上课件,来理解对称量化和非对称量化
课件中: r m a x = S ( q m a x − Z ) r_{max}=S(q_{max}-Z) rmax=S(qmax−Z),这里是反量化的公式 (整数值减去zero point再乘上scale得到实数值),所以在计算S的时候分子分母是反的!即博客中的 s = 1 / S s=1/S s=1/S
-
对称量化
int8对称量化的表示范围 [ − 2 b − 1 + 1 , 2 b − 1 − 1 ] [-2^{b-1}+1,2^{b-1}-1] [−2b−1+1,2b−1−1](对应课件的 q m i n , q m a x q_{min},q_{max} qmin,qmax),其中 b=8 bit,因此量化后的输入x可以表示为
x q = c l i p [ r o u n d ( x ⋅ s , − 127 , 127 ) ] x_{q}=clip[round(x\cdot s,-127,127)] xq=clip[round(x⋅s,−127,127)]
s = 2 b − 1 − 1 α = q m a x r m a x s=\frac{2^{b-1}-1}{\alpha}=\frac{q_{max}}{r_{max}} s=α2b−1−1=rmaxqmax
课件中计算对称量化的S用的是 r m i n / q m i n r_{min}/q_{min} rmin/qmin 我的理解,对称的时候用最大值相除或者用最小值相除都可以(课件中也提到过不同的框架会分别采用 r m i n , r m a x r_{min} , r_{max} rmin,rmax)
其中,截断函数clip:
c l i p ( x , l , u ) = l , x < l x , l < = x < = u u , x > u clip(x,l,u)={ \begin{aligned} &l, &x<l \\ &x, &l<=x<=u \\ &u, &x>u \end{aligned} } clip(x,l,u)=l,x,u,x<ll<=x<=ux>u
整理为通用公式表达有:
x q = q u a n t i z e ( x , b , s ) = c l i p [ r o u n d ( x ⋅ s , − − 2 b − 1 + 1 , 2 b − 1 − 1 ) ] x_{q}=quantize(x,b,s)=clip[round(x\cdot s,--2^{b-1}+1,2^{b-1}-1)] xq=quantize(x,b,s)=clip[round(x⋅s,−−2b−1+1,2b−1−1)] -
非对称量化
s = 2 b − 1 α − β = q m a x − q m i n r m a x − r m i n s=\frac{2^{b}-1}{\alpha-\beta}=\frac{q_{max}-q_{min}}{r_{max}-r_{min}} s=α−β2b−1=rmax−rminqmax−qmin
注意: 2 b − 1 − 1 − ( − 2 b − 1 ) = 2 b − 1 2^{b-1}-1-(-2^{b-1})=2^{b}-1 2b−1−1−(−2b−1)=2b−1
其中, z = − r o u n d ( β ⋅ s − 2 b − 1 ) = − r o u n d ( r m i n × s − q m i n ) z=-round(\beta\cdot s-2^{b-1}) =-round(r_{min}\times s -q_{min}) z=−round(β⋅s−2b−1)=−round(rmin×s−qmin)
注意:量化与反量化的 Z 相差一个负号,且 s = 1/S(again!课件中给出的是反量化公式)整理为通用公式表达有:
x q = q u a n t i z e ( x , b , s , z ) = c l i p [ r o u n d ( x ⋅ s + z , − − 2 b − 1 , 2 b − 1 − 1 ) ] x_{q}=quantize(x,b,s,z)=clip[round(x\cdot s + z,--2^{b-1},2^{b-1}-1)] xq=quantize(x,b,s,z)=clip[round(x⋅s+z,−−2b−1,2b−1−1)] -
量化的方式
-
pre-tensor
- 同一块输入,比如某个卷积前的输入tensor,采用一个scale,该层的所有输入共享一个scale值
-
pre-channel
- 一般用于权重,比如一个卷积权重的维度是 [ 64 , 3 , 3 , 3 ] [64,3,3,3] [64,3,3,3](输入通道数为3,kernel size = 3 x 3,输出通道数64),就会产生64个scale值
-
分析一下为什么是这样
- 卷积操作量化,卷积操作可以拆分成im2col+矩阵乘法
- 偏置bias一般可以去掉,对精度影响也不大,暂时不考虑
- 上图输入X的维度为
[
m
,
p
]
[m,p]
[m,p]而W的维度为
[
p
,
n
]
[p,n]
[p,n],因此i的范围为
[
0
,
m
)
[0,m)
[0,m),k的范围为
[
0
,
p
)
[0,p)
[0,p)。W和Y同理。这里的输入和权重都是FP32精度,也就是实数。而对应的INT8精度的输入和权重,q下标就代表quantize也就是量化:
X q = ( x q , i k ) ∈ Z m × p W q = ( w q , k j ) ∈ Z p × n \begin{aligned} X_ {q} =(x_{q,ik}) \in Z^{m\times p} \\ W_{q} = (w_{q,kj}) \in Z^{p\times n} \end{aligned} Xq=(xq,ik)∈Zm×pWq=(wq,kj)∈Zp×n
把矩阵拆分成细粒度计算,即行和列每个元素相乘然后求和
y i j = ∑ k = 1 p x i k ⋅ w k j ≈ ∑ k = 1 p d e q u a n t i z e ( x q , i k , s q , i k ) ⋅ d e q u a n t i z e ( w q , k j , s w , k j ) = ∑ k = 1 p 1 s x , i k x q , i k ⋅ 1 s w , k j w q , k j y_ {ij}=\sum_{k=1}^{p} x_{ik} \cdot w_ {kj} \approx \sum _ {k=1}^ {p} dequantize( x_ {q,ik} , s_ {q,ik} ) \cdot dequantize( w_ {q,kj} , s_ {w,kj} ) = \sum_{k=1}^ {p} \frac {1}{s_ {x,ik}} x_ {q,ik} \cdot \frac {1}{s_{w,kj}} w_{q,kj} yij=k=1∑pxik⋅wkj≈k=1∑pdequantize(xq,ik,sq,ik)⋅dequantize(wq,kj,sw,kj)=k=1∑psx,ik1xq,ik⋅sw,kj1wq,kj
进一步,两个浮点型的运算可以被近似为INT8反量化后的运算,进一步等于量化后的运算:
= ∑ k = 1 p 1 s x , i k x q , i k ⋅ 1 s w , k j w q , k j = \sum_{k=1}^ {p} \frac {1}{s_ {x,ik}} x_ {q,ik} \cdot \frac {1}{s_{w,kj}} w_{q,kj} =k=1∑psx,ik1xq,ik⋅sw,kj1wq,kj
可以看到上式子每个输入元素都有自己的scale值,而我们必须把x和w的scale值提到最前面才能让x和w实现INT8类型的矩阵运算
= 1 s x , i ⋅ s w , j ∑ k = 1 p x q , i k ⋅ w q , k j = \frac {1}{s_{x,i}\cdot s_{w,j}} \sum_{k=1}^ {p} x_ {q,ik} \cdot w_{q,kj} =sx,i⋅sw,j1k=1∑pxq,ik⋅wq,kj
为了把S提出来,k必须被干掉
卷积操作可以拆分成im2col+矩阵乘法,下图左边的kernel矩阵,每一行代表一个输出通道的kernel集合(这里因为输入图像是三通道的,因此kernel有三个,不同颜色代表一个kernel):
这就是pre-channel
或者详细点就是per-output-channel
也就是卷积输出通道,我们对每一个卷积权重的输出通道那一维进行量化,然后共享一个scale,这也就呼应了上述的公式!
6.2 参考二
对称量化vs非对称量化
这里的Wx应该一样高?like WX
7 考虑bias如何进行线性量化
对权重使用对称量化,Zero point = 0;(权重的分布特点)
为了把权重、激活、bias三项的Scale合并,令
S
b
=
S
W
S
X
S_{b}=S_{W}S_{X}
Sb=SWSX
且对bias使用对称量化,Zero point = 0;(bias的分布特点)
令
q
b
i
a
s
=
q
b
−
Z
X
q
w
q_{bias}=q_{b}- Z_{X}q_{w}
qbias=qb−ZXqw
7.1 对于FC Layer
只有整数的乘法、32bit的加法
7.2 对于Conv Layer
- 量化后的激活值和权重进行矩阵乘法(Nbit乘法
- 与32bit的 q b i a s q_{bias} qbias相加(这里加法都用int32是为了防止溢出
- 转Nbit int后,与scale相乘
- 加上Nbit的 Zero point Y
如何计算Y的量化参数Z和S?用数据集的图 计算统计值 called: collaboration)
7.3 线性量化性能
8 Summary
- K-means量化,存储整数权重+浮点数码本、进行浮点数计算
- 线性量化,存储整数权重、进行整数计算
- 课程是按照量化的"aggressive"程度来梳理的,一开始的K-means计算还是浮点数计算,然后线性量化直接变成Integer计算,下一节课介绍更aggressive的量化方法