模型量化-数据映射方式

模型量化-数据映射方式

对称量化

bqem1g.png

例子:

👉👉输入数据如下:

x_f = [0.32, -1.76, 0.025, -1.22]

目标是将输入数据量化为INT8

👉👉对称量化先取输入数据绝对值的最大值

absmax = max(
    abs(x_f)
)
absmax = 1.76

👉👉计算缩放尺度scale

计算缩放尺度scale前,先明确我们数据映射的源域目标域

源域:[s_min, s_max]=[-absmax, absmax]=[-1.76, 1.76] → \quad \rightarrow \quad 目标域:[q_min, q_max]=[-128, 127]

注:有时为了计算方便,舍弃目标域的-128,此时的目标域为:[-127, 127]

s c a l e = s m a x − s m i n q m a x − q m i n = 1.76 − ( − 1.76 ) 127 − ( − 128 ) = 3.52 255 = 0.013803921568627451 \mathbf{scale}=\frac{s_{max}-s_{min}}{q_{max}-q_{min}}=\frac{1.76-(-1.76)}{127-(-128)}=\frac{3.52}{255}=0.013803921568627451 scale=qmaxqminsmaxsmin=127(128)1.76(1.76)=2553.52=0.013803921568627451

scale = 0.013803921568627451

👉👉量化:

q_rets = np.round(x_f / scale)
q_rets = [23, -128, 2, -88]

量化结果为:q_rets=[23, -128, 2, -88]

👉👉反量化:

x_f_recs = q_rets * scale
x_f_recs = [0.3174902, -1.76690196, 0.02760784, -1.2147451]

反量化结果为:[0.3174902, -1.76690196, 0.02760784, -1.2147451]

👉👉矩阵乘法中的应用:

输入 x f x_f xf和权重 w f w_f wf分别被量化为 x q x_q xq w q w_q wq,如下。

ptd1xg.png

x f w f = ( x q ∗ s x ) @ ( w q ∗ s w ) = ( s x ∗ s w ) ( x q w q ) x_fw_f=(x_q*s_x)@(w_q*s_w)=(s_x*s_w)(x_qw_q) xfwf=(xqsx)@(wqsw)=(sxsw)(xqwq),下图是直接计算量化反量化计算结果对比:

原始结果 = [ − 1.3485 − 1.5792 − 1.8245 − 2.4014 2.4463 4.4340 0.0904 − 1.2283 − 0.2202 ] 量化计算结果 = [ − 1.3610 − 1.5720 − 1.8344 − 2.3754 2.4279 4.4627 0.0821 − 1.2400 − 0.2306 ] \begin{aligned} \text{原始结果} &= \begin{bmatrix} -1.3485 & -1.5792 & -1.8245 \\ -2.4014 & 2.4463 & 4.4340 \\ 0.0904 & -1.2283 & -0.2202 \end{bmatrix} \\ \text{量化计算结果} &= \begin{bmatrix} -1.3610 & -1.5720 & -1.8344 \\ -2.3754 & 2.4279 & 4.4627 \\ 0.0821 & -1.2400 & -0.2306 \end{bmatrix} \end{aligned} 原始结果量化计算结果= 1.34852.40140.09041.57922.44631.22831.82454.43400.2202 = 1.36102.37540.08211.57202.42791.24001.83444.46270.2306

👉👉对称量化的问题:

通过absmax来确定缩放比例,一定存在目标域的某一部分被浪费,如下图所示。

q1gdsm.png

非对称量化

qz790t.png

例子:

👉👉输入数据如下:

x_f = [0.32, -1.76, 0.025, -1.22]

目标是将输入数据量化为UINT8

👉👉计算缩放尺度scale

计算缩放尺度scale前,先明确我们数据映射的源域目标域

源域:[s_min, s_max]=[min(x_f), max(x_f)]=[-1.76, 0.32] → \quad \rightarrow \quad 目标域:[q_min, q_max]=[0, 255]

s c a l e = s m a x − s m i n q m a x − q m i n = 0.32 − ( − 1.76 ) 255 − 0 = 2.08 255 = 0.00815686274509804 \mathbf{scale}=\frac{s_{max}-s_{min}}{q_{max}-q_{min}}=\frac{0.32-(-1.76)}{255-0}=\frac{2.08}{255}=0.00815686274509804 scale=qmaxqminsmaxsmin=25500.32(1.76)=2552.08=0.00815686274509804

scale = 0.00815686274509804

👉👉计算零点zero_point

将上式变形为:

q m a x − q m i n = s m a x − s m i n s c a l e → 一般地 q − q m i n = s − s m i n s c a l e q_{max}-q_{min}=\frac{s_{max}-s_{min}}{\mathbf{scale}} \quad\xrightarrow{\text{一般地}}\quad q-q_{min}=\frac{s-s_{min}}{\mathbf{scale}} qmaxqmin=scalesmaxsmin一般地 qqmin=scalessmin

特别地,取上式 s = 0 s=0 s=0,则 q 0 − q m i n = 0 − s m i n s c a l e = − s m i n s c a l e q_0-q_{min}=\frac{0-s_{min}}{scale}=\frac{-s_{min}}{scale} q0qmin=scale0smin=scalesmin,最终得到:

q 0 = − s m i n s c a l e + q m i n = 1.76 0.00815686274509804 + 0 = 215.76923076923075 q_0=\frac{-s_{min}}{\mathbf{scale}}+q_{min}=\frac{1.76}{0.00815686274509804}+0=215.76923076923075 q0=scalesmin+qmin=0.008156862745098041.76+0=215.76923076923075

四舍五入取整后: q 0 = r o u n d ( 215.76923076923075 ) = 216 q_0=\mathbf{round}(215.76923076923075)=216 q0=round(215.76923076923075)=216

q_0 = np.round((-s_min) / scale) + q_min

👉👉量化:

推导一般量化公式:

q − q m i n = s − s m i n s c a l e → q = s s c a l e + ( − s m i n s c a l e + q m i n ) = s s c a l e + q 0 q-q_{min}=\frac{s-s_{min}}{\mathbf{scale}}\quad\rightarrow\quad q=\frac{s}{\mathbf{scale}}+\big(-\frac{s_{min}}{\mathbf{scale}}+q_{min}\big)=\frac{s}{\mathbf{scale}}+q_0 qqmin=scalessminq=scales+(scalesmin+qmin)=scales+q0

可得: q = r o u n d ( s s c a l e ) + q 0 q=\mathbf{round}(\frac{s}{\mathbf{scale}})+q_0 q=round(scales)+q0,为保证结果在[0, 255]范围内,最后q表示为: q = c l a m p ( r o u n d ( s s c a l e ) + q 0 ,    0 ,    255 ) q=\mathbf{clamp}(\mathbf{round}(\frac{s}{\mathbf{scale}})+q_0,\;0,\;255) q=clamp(round(scales)+q0,0,255)

q_rets = np.clip(
    np.round(x_f / scale) + q_0,
    0,
    255
)
q_rets = [255, 0, 219, 66]

👉👉反量化:

s = ( q − q 0 ) × s c a l e s = (q - q_0)\times\mathbf{scale} s=(qq0)×scale

x_f_recs = (q_rets - q_0) * scale
x_f_recs = [0.31811765, -1.76188235, 0.02447059, -1.22352941]

👉👉矩阵乘法中的应用:

输入 x f x_f xf和权重 w f w_f wf分别被量化为 x q x_q xq w q w_q wq,如下。

8cbbwh.png

则量化反量化计算:

x f w f = ( x q − z x ) ∗ s x @ ( w q − z w ) ∗ s w = s x ∗ s w ( x q w q − x q z w − w q z x + z x z w ) \begin{aligned} x_fw_f& =(x_{q}-z_{x})*s_{x} @ (w_{q}-z_{w})*s_{w} \\ &=s_{x}*s_{w} (x_{q}w_{q}-x_{q}z_{w}-w_{q}z_{x}+z_{x}z_{w}) \end{aligned} xfwf=(xqzx)sx@(wqzw)sw=sxsw(xqwqxqzwwqzx+zxzw)

下图是直接计算量化反量化计算结果对比:

原始结果 = [ − 1.3485 − 1.5792 − 1.8245 − 2.4014 2.4463 4.4340 − 0.2966 − 1.0333 − 0.3492 ] 量化计算结果 = [ − 1.3599 − 1.5718 − 1.8264 − 2.4041 2.4476 4.4357 0.0993 − 1.2225 − 0.2247 ] \begin{aligned} \text{原始结果} &= \begin{bmatrix} -1.3485 & -1.5792 & -1.8245 \\ -2.4014 & 2.4463 & 4.4340 \\ -0.2966 & -1.0333 & -0.3492 \end{bmatrix} \\ \text{量化计算结果} &= \begin{bmatrix} -1.3599 & -1.5718 & -1.8264 \\ -2.4041 & 2.4476 & 4.4357 \\ 0.0993 & -1.2225 & -0.2247 \end{bmatrix} \end{aligned} 原始结果量化计算结果= 1.34852.40140.29661.57922.44631.03331.82454.43400.3492 = 1.35992.40410.09931.57182.44761.22251.82644.43570.2247

👉👉非对称量化的问题:

1️⃣仍然存在目标域的某一部分被浪费;

2️⃣多个值被量化为同一个整数值。

6aep9q.png

分位数量化

在标准正态分布中,对于分布X给定的概率值 α \alpha α,如果存在 u α u_{\alpha} uα使得它的累积分布函数(CDF) P ( X < u α ) = α P(X<u_{\alpha})=\alpha P(X<uα)=α则称 u α u_{\alpha} uα是标准正态分布的 α \alpha α分位数,如下图。

m1kaok.png

相关研究认为,神经网络的激活值和权重的数值分布符合0均值某方差 σ 2 \sigma^2 σ2的正态分布;且当将输入数据进行量化时,每个可能的k-bit的整数值出现的频率是相等的(意思是将连续的输入值离散化为 2 k 2^k 2k个可能整数值时每一个可能的整数值出现的频率相同)。

因此,我们可以借助标准正态分布来产生量化标准对输入数据进行量化

例子:

👉👉输入数据如下:

x_f = [0.32, -1.76, 0.025, -1.22]

目标是将输入数据量化为UINT4

👉👉确定量化标准(如何计算我们后面说明):

Q m a p p i n g = [ − 1.0000 , − 0.8102 , − 0.6541 , − 0.5172 , − 0.3925 , − 0.2755 , − 0.1635 , − 0.0542 , 0.0542 , 0.1635 , 0.2755 , 0.3925 , 0.5172 , 0.6541 , 0.8102 , 1.0000 ] \mathbf{Q_{mapping}}=\begin{aligned} &[-1.0000, -0.8102, -0.6541, -0.5172, -0.3925, -0.2755, -0.1635, \\ &-0.0542, 0.0542, 0.1635, 0.2755, 0.3925, 0.5172, 0.6541, 0.8102, 1.0000] \end{aligned} Qmapping=[1.0000,0.8102,0.6541,0.5172,0.3925,0.2755,0.1635,0.0542,0.0542,0.1635,0.2755,0.3925,0.5172,0.6541,0.8102,1.0000]

👉👉输入数据规范化到[-1, 1]

constant = np.abs(x_f).max()
constant = 1.76  

x_f_normalized = x_f / constant

'''
x_f_normalized:
    [0.181818,
    -1.000000,
    0.014205,
    -0.693182]
'''

👉👉从量化标准中找出距离规范化的输入数据最近元素的索引,得到的结果即为将输入数据量化为UNIT4的结果q_rets=[9, 0, 8, 2]

def find_nearest(array, value):
    return (np.abs(array - value)).argmin()  

q_rets = []
for ele in x_f_normalized:
    min_index = find_nearest(Q_mapping, ele)
    q_rets.append(min_index)
print(q_rets)  
# [9, 0, 8, 2]

👉👉量化标准的确定方式:

量化标准依据标准正态分布的一组等差分位数

选定概率区间:[1-offset, offset], offset=0.9,将概率区间划分为 2 n ∣ n = 4 = 16 2^n|_{n=4}=16 2nn=4=16份(因为目标是将输入数据量化为UINT4)。

注:为什么要添加偏置offset?因为如果考虑完整的概率区间:[0, 1],此时概率范围的首尾为0和1,其在标准正态分布中对应的位置为 − i n f -inf inf + i n f +inf +inf,这是不可计算的。

n_bits = 4
n_bins = 2 ** n_bits
offset = 0.9
print('考虑的概率范围', '[{},{}]'.format(1 - offset, offset), sep=' | ')
probs = np.linspace(1 - offset, offset, n_bins + 1)
print('概率位置', '{}'.format(probs.tolist()), sep=' | ')
positions = norm.ppf(probs)  # inverse of CDF
positions

注意:上面代码中等差分位数对应positions(在标准正态分布中的位置)的获得是借助标准正态分布的逆累积概率函数(inverse of CDF)得到的。

结果:

考虑的概率范围 | [0.09999999999999998,0.9]
概率位置 | [0.09999999999999998, 0.14999999999999997, 0.19999999999999998, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7000000000000001, 0.75, 0.8, 0.85, 0.9]
positions:
array([-1.2816, -1.0364, -0.8416, -0.6745, -0.5244, -0.3853, -0.2533,
       -0.1257,  0.    ,  0.1257,  0.2533,  0.3853,  0.5244,  0.6745,
        0.8416,  1.0364,  1.2816])

如下图所示,生成的17个概率点位probs将概率区间[0.1, 0.9]划分为16份,也即产生16个bins。

dhp4u9.png

计算每个bin的中点,一共16个值。

# 计算每个区间的中点位置
midpoints = (positions[:-1] + positions[1:]) / 2
midpoints

结果:

array([-1.159 , -0.939 , -0.7581, -0.5994, -0.4549, -0.3193, -0.1895,
       -0.0628,  0.0628,  0.1895,  0.3193,  0.4549,  0.5994,  0.7581,
        0.939 ,  1.159 ])
5cdjye.png

将中点位置的值规范化到[-1, 1](与输入数据规范化时选定的范围对应,可任意选定)。

r_max, r_min = m_max, m_min
r_max, r_min  
# (1.158992477519195, -1.1589924775191949)
# 规范化到[-1, 1]的范围
scale = (r_max - r_min) / (1 - (-1))
scale  
# 1.1589924775191949

zero_point = (0 - r_min) / scale + (-1)
zero_point  
# 0.0

quantile_mapping = midpoints / scale + zero_point
quantile_mapping  
'''
array([-1.    , -0.8102, -0.6541, -0.5172, -0.3925, -0.2755, -0.1635,
       -0.0542,  0.0542,  0.1635,  0.2755,  0.3925,  0.5172,  0.6541,
        0.8102,  1.    ])
'''

如下图:

6dqxev.png

得到的[-1, 1]区间的映射结果即为最终的量化标准Q_mapping

[ − 1.0000 , − 0.8102 , − 0.6541 , − 0.5172 , − 0.3925 , − 0.2755 , − 0.1635 , − 0.0542 , 0.0542 , 0.1635 , 0.2755 , 0.3925 , 0.5172 , 0.6541 , 0.8102 , 1.0000 ] \begin{aligned} &[-1.0000, -0.8102, -0.6541, -0.5172, -0.3925, -0.2755, -0.1635, \\ &-0.0542, 0.0542, 0.1635, 0.2755, 0.3925, 0.5172, 0.6541, 0.8102, 1.0000] \end{aligned} [1.0000,0.8102,0.6541,0.5172,0.3925,0.2755,0.1635,0.0542,0.0542,0.1635,0.2755,0.3925,0.5172,0.6541,0.8102,1.0000]

NF4

以上分位数量化的一个问题是在确定Q_mapping时无法保证0的映射值是0,而0又是神经网络激活值和权重中一个重要的值(神经网络激活值和权重是稀疏的)。

NF4在确定映射标准时将正数,零和负数分别考虑

offset = 0.9677083
probs_pos = np.linspace(offset, 0.5, 9)[:-1]  # 8个值
probs_neg = np.linspace(offset, 0.5, 8)[:-1]  # 7个值

👉👉正数部分:

probs_pos:
array([0.9677, 0.9092, 0.8508, 0.7923, 0.7339, 0.6754, 0.6169, 0.5585])
positions_pos = norm.ppf(probs_pos)
positions_pos
# array([1.8481, 1.3361, 1.0398, 0.8145, 0.6245, 0.4548, 0.2974, 0.1471])

正数部分如下图:

mrongw.png

👉👉零单独考虑:

positions_zero = np.array([0.])
positions_zero 
# array([0.])
probs_zero = norm.cdf(positions_zero)
probs_zero 
# array([0.5])

👉👉负数部分:

probs_neg:
array([0.9677, 0.9009, 0.8341, 0.7673, 0.7004, 0.6336, 0.5668])
# 注意这里负数部分positions_neg的产生方式
positions_neg = -norm.ppf(probs_neg)
positions_neg
# array([-1.8481, -1.2867, -0.9704, -0.7299, -0.5257, -0.3415, -0.1683])
true_probs_neg = norm.cdf(positions_neg)
true_probs_neg
# array([0.0323, 0.0991, 0.1659, 0.2327, 0.2996, 0.3664, 0.4332])

负数部分如下图:

apn8et.png

👉👉16个位置:

positions = np.concatenate([positions_pos, positions_zero, positions_neg])
positions 
'''
array([ 1.8481,  1.3361,  1.0398,  0.8145,  0.6245,  0.4548,  0.2974,
        0.1471,  0.    , -1.8481, -1.2867, -0.9704, -0.7299, -0.5257,
       -0.3415, -0.1683])
'''
94hz52.png

与之前的分位数量化不同的是这里是直接使用得到的16个位置(不取中间位置)规范化到[-1, 1]产生NF4映射标准。

sorted_ = np.sort(positions)
sorted_ 
'''
array([-1.8481, -1.2867, -0.9704, -0.7299, -0.5257, -0.3415, -0.1683,
        0.    ,  0.1471,  0.2974,  0.4548,  0.6245,  0.8145,  1.0398,
        1.3361,  1.8481])
'''

NF4 = sorted_ / sorted_.max()
NF4 
'''
array([-1.    , -0.6962, -0.5251, -0.3949, -0.2844, -0.1848, -0.091 ,
        0.    ,  0.0796,  0.1609,  0.2461,  0.3379,  0.4407,  0.5626,
        0.723 ,  1.    ])
'''

NF4映射标准如下图所示:

v76zoy.png

例子:

👉👉输入数据如下:

x_f = [0.32, -1.76, 0.025, -1.22]

目标是将输入数据量化为UINT4

👉👉量化和反量化过程:

74c0jp.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值