CNN中的量化

reference:

https://blog.csdn.net/niaolianjiulin/article/details/82764511
https://blog.csdn.net/baidu_24281959/article/details/52015811

而定点与浮点在《计算机组成原理》有很详细的说明,但是电子专业的学生只学《单片机》,而《单片机》并没有介绍定点与浮点的内容,所以电子专业的学生缺了这点基础,导致在FPGA处理运算时遇到拦路虎。

定点

定点数是非常简单的,它最早在小学的时候就已经学了,只不过那个时候并没有使用定点这个术语。


图片.png

如上图所示,31.5米=315分米,显然根据单位的不同,小数点是可以移动的。只要单位固定了,那么小数点也固定,所以叫定点数。那么二进制的定点数,也是同样的原理。

浮点

由于定点数所表示数的范围非常有限,所以才弄了浮点数,以扩充数的范围,以便给精度要求高的场合下使用。于是,得到IEEE754标准的格式,如下图所示。


图片.png

虽然在CPU内部运算时使用,但是在显示给人看的时候,根据实际需求,可以转成10的e次方等形式。

浮点数在运算过程中,需要对阶(也就是让指数位相同),而对阶过程中就常常使小数点移动,所以叫浮点数。

使用浮点数格式处理加、减、乘、除等运算,有两种方法,一种是使用定点模拟浮点,另一种是使用FPU(浮点处理单元)。

定点模拟浮点,运算速度较慢,在一些没有FPU的CPU会用得上(如51单片机)。

FPU是在CPU内部集成的外设,运算速度较快,但是CPU的成本会增加(如STM32F4系列)。

然而大多数FPGA内部没有集成FPU,要么使用IP核(OpenCores有),要么自己用HDL写一个。

浮点:小数点非固定的数,可表示数据范围较广,整数,小数都可表示。包含float,double;
定点:小数点固定,可表示整数,小数。int本质是小数点位于末尾的32位定点数而已;

IEEE 754标准

规定浮点数格式为:
  • s表示符号位,当s=0,V为正数;当s=1,V为负数
  • m表示尾数
  • n表示阶码
图片.png

例如,前面绘出的浮点数的表示形式中,s=0,n=132,m=(1/2+0/4+0/8+1/16+0/32+……),则计算结果为50.0

定点数的表示法

对于计算机来说,浮点定点的概念是看不见的,因为它只能看到:0…00001110,至于它表示多少,是逻辑层面的设置。你如果让它是int那就按照int表示法对每个位赋予意义,如果你让它是float就按照float表示法赋予意义。

对于00011100表示的定点数:

  • 如果我们设定小数点是位于最后一位的,即00011100. 则其表示28
  • 若设定小数点位于后三位的,即00011.100 则其表示3.50
  • 若设定小数点位于后四位的,即0001.1100 则其表示1.75

可以看到:

  • 小数位数越多,表示的精度越高。若小数点后有n位,则其表示的最大精度为1/(2n);
  • 整数位数越多,可表示的最大值越大。

以8位为例,最高位为符号位:

  • 若整数位占4位,小数位占3位,则其最大精度为0.125,最大值为15.875
  • 若整数位占5位,小数位占2位,则其最大精度为0.250,最大值为31.750
  • 若整数位占6位,小数位占1位,则其最大精度为0.500,最大值为63.500
  • 若整数位占7位,小数位占0位,则其最大精度为1.000,最大值为127

为何要把浮点数转换为定点数呢?

1.这来源于项目中神经网络的需求,网络中大量的参数,如果全部用F32表示,一是占用空间大,二是读取效率不高。
如果我们可以将某些浮点数转换为定点数表示,在接受精度损失的前提下,每次就可以读取多个进行运行,可显著提高运算效率;
2.浮点数在运算过程中,需要对阶(也就是让指数位相同),而对阶过程中就常常使小数点移动;
3.FPU是在CPU内部集成的外设

举例来说,我们用8位定点数,1个符号位,4个整数位,3个小数位,则其可表示范围是-16.00~15.875,最大精度0.125。
有几个浮点数:0.145,1.231,2.364,7.512,每个需要32bit表示。
如果我们将每个量化成一个8位定点数,比如通过某种方法得到:1,10,19,60
此时每个数需要8bit表示。那么读一个浮点数,可以同时读4个定点数,且计算效率可以提高。当然这样做是有风险的:

  • 损失精度,比如再将上述定点数转化为浮点数:0.125,1.250, 2.375,7.500;
  • 定点数表示范围有限,加法有可能会溢出,需要拿int16或int32来暂存中间结果;

如何将浮点数转换为定点数?

我们用8位定点数,1个符号位,4个整数位,3个小数位。这个3称为量化系数。该过程称为量化。
(我们总是将非离散值量化到离散值空间,处理更为简单)


如何将定点数转换为浮点数

该过程称为反量化。


还有个note:

  • 定点数加减时需要量化系数相同,其值有可能溢出,需要更大定点数来暂存中间值;
  • 两个定点数乘法后如果需要转化为f32,则反量化系数变为2∗n

16位量化

参与数值运算的数为16位的整型数,通过设定小数点在16位数中的不同位置,就可以表示不同大小和不同精度的小数。

Q表示 S表示 十进制数表示范围
Q15 S0.15 -1≤x≤0.9999695
Q14 S1.14 -2≤x≤1.9999390
Q13 S2.13 -4≤x≤3.9998779
Q12 S3.12 -8≤x≤7.9997559
Q11 S4.11 -16≤x≤15.9995117
Q10 S5.10 -32≤x≤31.9990234
Q9 S6.9 -64≤x≤63.9980469
Q8 S7.8 -128≤x≤127.9960938
Q7 S8.7 -256≤x≤255.9921875
Q6 S9.6 -512≤x≤511.9804375
Q5 S10.5 -1024≤x≤1023.96875
Q4 S11.4 -2048≤x≤2047.9375
Q3 S12.3 -4096≤x≤4095.875
Q2 S13.2 -8192≤x≤8191.75
Q1 S14.1 -16384≤x≤16383.5
Q0 S15.0 -32768≤x≤32767

不同的Q所表示的数不仅范围不同,而且精度也不相同。
Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。

转换关系

浮点数与定点数的转换关系可表示为:

浮点数(Fx)转换为定点数(Ix):Ix = (int)x* 2^Q
定点数(Ix)转换为浮点数(Fx):Fx= (float)Ix*2^(-Q)

转换示例:
浮点数 Fx = 0.5,定标 Q = 15,则定点数:
Ix = floor(0.5*32768) = 16384
反之,一个用 Q = 15 表示的定点数Ix = 16384,其浮点数为:
Fx = 16384 * 2^(-15) = 16384 / 32768 = 0.5
浮点数转换为定点数时,为了降低截尾误差,可以在取整前可以先加上0.5,视情况而定。

定点加减

将浮点加法/减法转化为定点加法/减法时最重要的一点就是必须保证两个操作数的定标:
若两者不一样,则在做加法/减法运算前先进行小数点的调整。
为保证运算精度,需使Q值小的数调整为与另一个数的Q值一样大。
此外,在做加法/减法运算时,必须注意结果可能会超过16位表示。
如果加法/减法的结果超出16位的表示范围,则必须保留32位结果,以保证运算的精度,否则可能会出现严重的精度丢失。

    # 设x的Q值为Qx,y的Q值为Qy,且Qx > Qy,加法/减法结果z的定标值为Qz
    # 所以定点加法可以描述为:
    int16 x,y;
    #结果用更大长度的存
    int z;
    int temp; // 临时变量
    temp = y << (Qx - Qy);
    # Q大-Q小,Q大的变量分辨率更高,Q小的左移增加其Q
    temp = x + temp;
    z = (temp >> (Qx - Qz)); // if Qx >= Qz 
    z = (temp << (Qz - Qx)); // if Qx <= Qz

    // 设x = 0.5,y = 3.1,则浮点运算结果为z = x+y = 0.5+3.1 = 3.6;
    // Qx = 15,Qy = 13,Qz = 13,则定点加法为:
    x = 16384;y = 25395;
    temp = 25395 << 2 = 101580;
    temp = x+temp = 16384+101580 = 117964;
    z = (int)(117964L >> 2) = 29491;

   #因为z的Q值为13,所以定点值z = 29491即为浮点值z = 29491/8192 = 3.5999755859375

自己理解的版本:因为计算过程中并不知道实际的Qz,因此上个版本实际上不可行,因此有:

    # 设x的Q值为Qx,y的Q值为Qy,且Qx > Qy,加法/减法结果z的定标值为Qz
    # 所以定点加法可以描述为:
    int16 x,y;
    #结果用更大长度的存
    int z;
    int temp; // 临时变量
    temp = y << (Qx - Qy);
    # Q大-Q小,Q大的变量分辨率更高,Q小的左移增加其Q
    z= x + temp;
   
    # 设x = 0.5,y = 3.1,则浮点运算结果为z = x+y = 0.5+3.1 = 3.6
    # Qx = 15,Qy = 13,Qz = 13,则定点加法为:
    x = 16384;y = 25395;
    temp = 25395 << 2 = 101580;
    temp = x+temp = 16384+101580 = 117964;
    z = (int)(temp) = 117964;
    #因为x的Q值为15,所以定点值z = 117964即为浮点值z = 117964/32768= 3.5999755859375,与上一版相同

定点乘法

int x,y,z;
long temp;
temp = (long)x;
z = (temp×y) >>(Qx+Qy-Qz);

# 设x = 18.4,y = 36.8,则浮点运算值为z = 18.4×36.8 = 677.12;
# 根据上节,得Qx = 10,Qy = 9,Qz = 5,所以
x = 18841;y = 18841;
temp = 18841L; // Long int
z = (18841L * 18841) >> (10+9-5) = 354983281L >> 14 = 21666;
# 因为z的定标值为5,故定点 z = 21666即为浮点的 z = 21666/32 = 677.0768756866455078125 产生了精度损失

自己理解的版本:因为计算过程中并不知道实际的Qz,因此上个版本实际上不可行,因此有:

int16 x,y,
#结果用更大长度的存
int z;
int temp;
temp = (int)x;
z = x*y

# 设x = 18.4,y = 36.8,则浮点运算值为z = 18.4×36.8 = 677.12;
# 根据上节,得Qx = 10,Qy = 9,所以
x = 18841;y = 18841;
z=18841*18841=354983281
#浮点z = 354983281/(2^10+2^9) =354983281/(524288)= 677.0768756866455078125 产生了精度损失

你可能感兴趣的:(CNN中的量化)