【量化学习2】矩阵运算的量化
d_matrix = None #量化前的矩阵q_matrix = None #量化后的整数矩阵de_matrix = None #反量化的浮点数矩阵scale = None #比例因子zero_point = None #便宜量d_matrix, #输入矩阵,需量化bits, #数据宽度unsigned = True #默认为无符号整数):#求输入浮点数矩阵的最大值和最小值# 量化表示的最大最小
【量化学习2】矩阵运算的量化
根据https://zhuanlan.zhihu.com/p/149659607,今天来学习矩阵的量化。
顺便用class把量化代码重新写一下。
矩阵量化
参考至https://zhuanlan.zhihu.com/p/149659607
由于卷积网络中的卷积层和全连接层本质上都是一堆矩阵乘法,因此我们先看如何将浮点运算上的矩阵转换为定点运算。
假设 r 1 r1 r1、$r2 是浮点实数上的两个 是浮点实数上的两个 是浮点实数上的两个N×N 的矩阵, 的矩阵, 的矩阵,r3$ 是 r 1 r1 r1、$r2 $相乘后的矩阵:
r 3 i , k = ∑ j = 1 N r 1 i , j r 2 j , k r_3^{i,k}=\sum_{j=1}^Nr_1^{i,j}r_2^{j,k} r3i,k=j=1∑Nr1i,jr2j,k
假设$ S_1 、 、 、Z_1$ 是 r 1 r_1 r1矩阵对应的 scale 和 zero point, S 2 S_2 S2 、 $Z_2 $、 S 3 S_3 S3 、 Z 3 Z_3 Z3 同理,那么可以推出:
S 3 ( q 3 i , k − Z 3 ) = ∑ j = 1 N S 1 ( q 1 i , j − Z 1 ) S 2 ( q 2 j , k − Z 2 ) S_3(q_3^{i,k}−Z_3)=\sum_{j=1}^{N}S_1(q_1^{i,j}−Z_1)S_2(q_2^{j,k}−Z_2) S3(q3i,k−Z3)=j=1∑NS1(q1i,j−Z1)S2(q2j,k−Z2)
整理一下可以得到:
q 3 i , k = S 1 S 2 S 3 ∑ j = 1 N ( q 1 i , j − Z 1 ) ( q 2 j , k − Z 2 ) + Z 3 q_3^{i,k}=\frac{S1S2}{S3}\sum_{j=1}^N(q_1^{i,j}−Z_1)(q_2^{j,k}−Z_2)+Z_3 q3i,k=S3S1S2j=1∑N(q1i,j−Z1)(q2j,k−Z2)+Z3
仔细观察可以发现,除了 S 1 S 2 S 3 \frac{S1S2}{S3} S3S1S2 ,其他都是定点整数运算。那如何把 S 1 S 2 S 3 \frac{S1S2}{S3} S3S1S2 也变成定点运算呢?这里要用到一个 trick。假设 M = S 1 S 2 S 3 M=\frac{S1S2}{S3} M=S3S1S2 ,由于 M M M通常都是 ( 0 , 1 ) (0, 1) (0,1) 之间的实数 (这是通过大量实验统计出来的),因此可以表示成 M = 2 − n M 0 M=2^{−n}M_0 M=2−nM0,其中 M 0 M_0 M0是一个定点实数。注意,定点数并不一定是整数,所谓定点,指的是小数点的位置是固定的,即小数位数是固定的。因此,如果存在 M = 2 − n M 0 M=2^{−n}M_0 M=2−nM0,那我们就可以通过 M 0 M_0 M0 的 bit 位移操作实现 2 − n M 0 2^{−n}M_0 2−nM0,这样整个过程就都在定点上计算了。
代码
Matrix类定义
class Matrix:
q_min = None
q_max = None
d_min = None
d_max = None
d_matrix = None #量化前的矩阵
q_matrix = None #量化后的整数矩阵
de_matrix = None #反量化的浮点数矩阵
scale = None #比例因子
zero_point = None #便宜量
def __init__(self,
d_matrix, #输入矩阵,需量化
bits, #数据宽度
unsigned = True #默认为无符号整数
):
#求输入浮点数矩阵的最大值和最小值
self.d_matrix = d_matrix
self.d_min = np.min(self.d_matrix)
self.d_max = np.max(self.d_matrix)
self.bits = bits
self.unsigned = unsigned
# 量化表示的最大最小值
# 有符号整数,有符号型的对称量化适合用于关于零大致对称的数据
# 无符号型的对称量化很适合单尾分布的数据,比如RELU的激活值
if self.unsigned:
self.q_min = 0
self.q_max = math.pow(2, self.bits) - 1
else:
self.q_min = -math.pow(2, self.bits - 1)
self.q_max = math.pow(2, self.bits - 1) - 1
# 求比例因子/规模因子
self.scale = (self.d_max - self.d_min) / (self.q_max - self.q_min)
# 求偏移量,保证零点的量化没有误差,
# 可确保像zero padding或ReLU这样的常规操作不会引起量化错误
self.zero_point = np.round(self.q_min - (self.d_min / self.scale))
代码解读见注释,在初始化阶段,计算好scale和zero_point
量化和反量化操作
#单一矩阵做量化
def quant(self):
B = self.d_matrix / self.scale + self.zero_point
self.q_matrix = np.round(np.clip(B, self.q_min, self.q_max))
return self.q_matrix
# 单一矩阵进行反量化
def dequant(self):
self.de_matrix = self.scale * (self.q_matrix - self.zero_point)
return self.de_matrix
矩阵乘法和主函数
def multiply(r1,r2,r3,bits):
m1 = Matrix(r1,bits)
m2 = Matrix(r2,bits)
m3 = Matrix(r3,bits)
M = (m1.scale * m2.scale)/m3.scale
t = time.time()
P = np.dot((m1.quant()-m1.zero_point),(m2.quant()-m2.zero_point))
# print("timeQ:", (time.time() - t))
calcM0(M,P)
return np.round(M*P) + m3.zero_point
if __name__ == '__main__':
# test_layer_op()
n = 8 #数据宽度
# r1 = np.array([[0.1, 0.2], [0.3, 0.4]])
# r2 = np.array([[0.2, 0.1], [0.4, 0.3]])
N = 4 #矩阵维度
r1 = np.random.rand(N, N)
r2 = np.random.rand(N, N)
t = time.time()
r3 = np.dot(r1, r2)
# print("time:",(time.time()-t))
# print(Matrix(r3,n).quant())
print(multiply(r1,r2,r3,n))
实验结果
原始r3和量化后的r3如下
[[1.07010784 1.03473495 0.88933572 0.93330366]
[1.45808753 1.75294973 1.10697695 1.29235102]
[0.92677285 0.64595958 0.47124794 0.72815896]
[1.79805959 1.56749148 0.94262982 1.52354675]]
[[115. 108. 80. 88.]
[189. 246. 122. 157.]
[ 87. 33. 0. 49.]
[255. 210. 90. 202.]]
疑问
- 在原公式里,计算q3需计算S3,而计算S3需要对r3求min和max值。本来量化就是为了计算q3代替计算r3,感觉绕不清楚了。
暂时解答:通过对Micronet代码的解析,大部分tensor里都是0-1的值,所以直接将0和1作为min和max,zero_point似乎也不必要。 - 具体矩阵运算到卷积、ReLU的深度学习网络中如何运算?
更多推荐
所有评论(0)