浮点量化逼近策略

目录

知识直通车

定点法(Fixed Point Approximation)

动态定点法(Dynamic Fixed Point Approximation)

动态定点法代码

迷你浮点法(Minifloat Approximation)

迷你浮点数量化代码

乘法变移位法(Multiplier-free arithmetic)

 乘法变移位法量化代码


知识直通车

参考github链接:https://github.com/Ewenwan/MVision/tree/master/CNN/Deep_Compression/quantization/Ristretto

参考csdn链接:https://blog.csdn.net/yiran103/article/details/80336425

定点法(Fixed Point Approximation)

所谓定点数和浮点数,
是指在计算机中一个数的小数点的位置是固定的还是浮动的:
如果一个数中小数点的位置是固定的,则为定点数;
如果一个数中小数点的位置是浮动的,则为浮点数。
一般来说,定点格式可表示的数值的范围有限,但要求的处理硬件比较简单。
而浮点格式可表示的数值的范围很大,但要求的处理硬件比较复杂。

IL.FL
固定整数位二进制长度和小数位二进制长度。
最大表示数: 
   Xmax = 2^(IL-1) - 2^(-FL)

32浮点数----->8位定点(Q4.4)
       ----->16位定点 ( Q8.8    Q9.7)
       
 例如 0 1 1 0 1 1 0 1 , FL = 2
 符号位: 0
 尾数部分全部作为整数位: 1 1 0 1 1 0 1 B  = 109D 
 小数部分最后两位,所以整体需要除以2^(2)
 则表示的数为: R = (-1)^0 * 109 * 2^(-2) = 27.25
 
 浮点数 ff  得到量化的数:
  放大/缩小: sff = ff  * 2^(fl) 
  取整:      round(sff) 

动态定点法(Dynamic Fixed Point Approximation)

CNN的不同部分具有显着的动态范围,
在大的层中,输出是数以千计的积累的结果,因此网络参数比层输出小得多。
定点只具有有限的能力来覆盖宽动态范围。
使用B位来表示,其中一位符号位s, fl是分数长度(小数长度),其余为整数位,
除去符号位s,后面的都是尾数,每个尾数位数字为xi

n = (-1)^s * 2^(-fl)*sum(2^i * xi)  , 0 =< i <= B-2
第一项确定符号,
第二项确定分数比例,
第三项确定总值大小(所有尾数对应的十进制的值)。

由于网络中的中间值具有不同的范围,所以希望将定点数值分组为具有常数f1(不同小数长度)的组中。
所以分配给小数部分的比特数在 同一组内是恒定的,但与其他组相比是不同的。

这里每个网络层分为三组:
    一个用于层输入,
    一个用于权重,
    一个用于层输出。
这可以更好地覆盖层激活和权重的动态范围,因为权重通常特别小,层的输入输出通常比较大(上一层各个神经元累计)。

上图为4个数 属于 两个不同组 的 动态定点数的例子。
注意第二组的 分数长度 是负数。 
所以第二组的分数长度是-1,整数长度是9,位宽是8。
第一组的分数长度为2,整数长度为6,位宽为8.

动态定点法代码

template <typename Dtype>
void BaseRistrettoLayer<Dtype>::Trim2FixedPoint_cpu_featuremap(Dtype* data, const int cnt,
      const int bit_width, const int rounding, int fl) {
   for (int index = 0; index < cnt; ++index) {
      // Saturate data
      Dtype max_data = (pow(2, bit_width - 1) - 1);
      Dtype min_data = -pow(2, bit_width - 1);

      data[index] /= pow(2, -fl);
      // clip (min,max)
      data[index] = std::max(std::min(data[index], max_data), min_data);
  
      data[index] = round(data[index]);
      data[index] *= pow(2, -fl);
   }
}

 

迷你浮点法(Minifloat Approximation)

不清楚浮点数二进制表示的可参考:https://blog.csdn.net/qq_20880415/article/details/99688542

IEEE-754 32位浮点数:
            struct MYFLOAT
            {
            bool bSign : 1;                // S 符号,表示正负,1位
            char cExponent : 8;            // E 指数,8位,存储的时候,比实际的多 127
            unsigned long ulMantissa : 23; // M 尾数,23位
            };
            // B = (-1)^S * 2^E * M
            // 不过指数部分在存储的时候回 加上127(使用移位存储, 正负表达的范围较平均)
            // 所以 指数部分按二进制数转到10进制数后需要减去 127, E = E' - 127
    bSign --- cExponent --- ulMantissa
    符号位 --- 指数位    --- 尾数位
    
迷你浮点数 16bit  5位指数部分 10位位数部分(精度, 1/(2^(10)) = 0.0009765, 精确到小数点后3位 )  

由于神经网络的训练是以浮点的方式完成的,
所以将这些模型压缩成比特宽度减少的浮点数是一种直观的方法。

为了压缩网络并减少计算和存储需求,Ristretto可以用比IEEE-754标准少得多的位表示浮点数。

我们在16bit、8bit甚至更小的数字上都遵循这个标准,但是我们的格式在一些细节上有所不同。

也就是说,根据分配给指数的比特数降低指数偏差:
   bias = 2^(exp_bits−1) − 1, 这里exp_bits提供分配给指数的位数。
例如8位指数,为了表示正负,使用移位存储,
   存储的数据为 原数据+127 ,  基数 bais =  2^(8-1) - 1 =127
与IEEE标准的另一个区别是我们不支持非规范化的数字,I

NF和NaN。INF由饱和数字代替,
非规格化数字NaN 由0代替。

最后,分配给指数和尾数部分的位数不遵循特定的规则。
更确切地说,Ristretto选择指数位,以避免发生饱和。  

迷你浮点数量化代码

typedef union {
    float d;
    struct {
        unsigned int mantisa : 23;
        unsigned int exponent : 8;
        unsigned int sign : 1;
    } parts;
} float_cast;

template <typename Dtype>
void BaseRistrettoLayer<Dtype>::Trim2MiniFloat_cpu(Dtype* data, const int cnt,
const int bw_mant, const int bw_exp, const int rounding) {
    for (int index = 0; index < cnt; ++index) {
        int bias_out = pow(2, bw_exp - 1) - 1;
        float_cast d2;
        // This casts the input to single precision
        d2.d = (float)data[index];
        int exponent=d2.parts.exponent - 127 + bias_out;
        double mantisa = d2.parts.mantisa;
        // Special case: input is zero or denormalized number
        if (d2.parts.exponent == 0) {
            data[index] = 0;
            return;
        }
        // Special case: denormalized number as output
        if (exponent < 0) {
            data[index] = 0;
            return;
        }
        // Saturation: input float is larger than maximum output float
        int max_exp = pow(2, bw_exp) - 1;
        int max_mant = pow(2, bw_mant) - 1;
        if (exponent > max_exp) {
            exponent = max_exp;
            mantisa = max_mant;
        } else {
            // Convert mantissa from long format to short one. Cut off LSBs.
            //相当于动态定点中的乘以标bw_mant后round
            double tmp = mantisa / pow(2, 23 - bw_mant);
            switch (rounding) {
                case QuantizationParameter_Rounding_NEAREST:
                    mantisa = round(tmp);
                break;
                case QuantizationParameter_Rounding_STOCHASTIC:
                    mantisa = floor(tmp + RandUniform_cpu());
                break;
                default:
                break;
            }
        }
        // Assemble result
        data[index] = pow(-1, d2.parts.sign) * ((mantisa + pow(2, bw_mant)) /
        pow(2, bw_mant)) * pow(2, exponent - bias_out);
    }
}

 

乘法变移位法(Multiplier-free arithmetic)

利用这种近似策略,乘法由位移代替。卷积运算由加法和乘法组成,其中乘法器需要更大的芯片面积。
这促使以前的研究通过使用整数幂权重来消除所有的乘法器。这些权重可以被认为是具有零尾数位的minifloat数值。
权重和层激活之间的乘法变成了位移。幂的两个参数可以写成如下:
n=(−1)^s * 2^exp
这里exp是一个整数,对于网络参数通常是负的。由于接近于零的小参数对网络输出没有太大影响,
所以可以忽略很小的指数。因此,可能的指数值可以显着减少。对于AlexNet,我们用{−8,−1}范围的值取得了很好的效果。
在这种近似模式下,激活是动态定点格式

 乘法变移位法量化代码

template <typename Dtype>
void BaseRistrettoLayer<Dtype>::Trim2IntegerPowerOf2_cpu(Dtype* data,
      const int cnt, const int min_exp, const int max_exp, const int rounding) {
	for (int index = 0; index < cnt; ++index) {
    float exponent = log2f((float)fabs(data[index]));
    int sign = data[index] >= 0 ? 1 : -1;
    switch (rounding) {
    case QuantizationParameter_Rounding_NEAREST:
      exponent = round(exponent);
      break;
    case QuantizationParameter_Rounding_STOCHASTIC:
      exponent = floorf(exponent + RandUniform_cpu());
      break;
    default:
      break;
    }
    exponent = std::max(std::min(exponent, (float)max_exp), (float)min_exp);
    data[index] = sign * pow(2, exponent);
	}
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值