神经网络量化与反量化( int8与float32之间的转换)+C语言实现

神经网络量化与反量化(int8与float32之间的转换)

    • 一、背景知识
    • 二、量化与反量化结果做对比

一、背景知识

量化并不是什么新知识,我们在对图像做预处理时就用到了量化。回想一下,我们通常会将一张 uint8 类型、数值范围在 0~255 的图片归一成 float32 类型、数值范围在 0.0~1.0 的张量,这个过程就是反量化。类似地,我们经常将网络输出的范围在 0.0~1.0 之间的张量调整成数值为 0~255、uint8 类型的图片数据,这个过程就是量化。所以量化本质上只是对数值范围的重新调整,可以「粗略」理解为是一种线性映射。(之所以加「粗略」二字,是因为有些论文会用非线性量化,但目前在工业界落地的还都是线性量化,所以本文只讨论线性量化的方案)。

不过,可以明显看出,反量化一般没有信息损失,而量化一般都会有精度损失。这也非常好理解,float32 能保存的数值范围本身就比 uint8 多,因此必定有大量数值无法用 uint8 表示,只能四舍五入成 uint8 型的数值。量化模型和全精度模型的误差也来自四舍五入的 clip 操作。

这篇文章中会用到一些公式,这里我们用 r 表示浮点实数,q 表示量化后的定点整数。浮点和整型之间的换算公式为:

神经网络量化与反量化( int8与float32之间的转换)+C语言实现_第1张图片

其中,S是 scale,表示实数和整数之间的比例关系,Z 是 zero point,表示实数中的 0 经过量化后对应的整数,它们的计算方法为:

神经网络量化与反量化( int8与float32之间的转换)+C语言实现_第2张图片

rmax 、 rmin 分别是r的最大值和最小值, qmax、 qmin 同理。这个公式的推导比较简单,很多资料也有详细的介绍,这里不过多介绍。需要强调的一点是,定点整数的 zero point 就代表浮点实数的 0,二者之间的换算不存在精度损失,这一点可以从公式 (2) 中看出来,把 r=0代入后就可以得到 q=Z。这么做的目的是为了在 padding 时保证浮点数值的 0 和定点整数的 zero point 完全等价,保证定点和浮点之间的表征能够一致。

二、量化与反量化结果做对比

#include
#include
#include
typedef char int8;


float find_float32_max(float *r)
{
    int len = sizeof(r) / sizeof(float); 
    float tmp=0.0f;
    for(int i=0;i<len;i++)
    {
        if(r[i]>tmp)
        {
            tmp = r[i];
        }  
    }
    return tmp;  
}

float find_float32_min(float *r)
{
    int len = sizeof(r) / sizeof(float); 
    float tmp=r[0];
    for(int i=0;i<len;i++)
    {
        if(r[i]<tmp)
        {
            tmp= r[i];
        }  
    }
    return tmp;  
}

//s 是scale 量化系数
float s_value(float *r,int8 *q)
{
    float r_max = find_float32_max(r);
    float r_min = find_float32_min(r);
    int8 q_max = 127;
    int8 q_min = -128;

    float s = (r_max-r_min)/(q_max-q_min);
    return s;
}

//z是zero point 表示实数中的0经过量化后的整数
float z_value(float *r,int8 *q)
{
    float r_max = find_float32_max(r);
    int8 q_max = 127;
    float s = s_value(r,q);
    float z = round(q_max-r_max/s); //越界了
    return z;
}

int float32_to_int8(float *r,int8 *q,float *s,float *z)
{

    *s = s_value(r,q);
    *z = z_value(r,q);

    printf("s=%f\n",*s);
    printf("z=%f\n",*z);

    int len = sizeof(r) / sizeof(float); 
    for(int i=0;i<len;i++)
        q[i] = (int8)round(r[i]/(*s)+(*z));

    return 0;
}

void float32_to_int8_test()
{
    float r[2]={2.1,8.6};
    int8 q[2];
    float s;  
    float z;
    int i;
    int len = sizeof(r) / sizeof(float); 
    float32_to_int8(r,q,&s,&z);

    printf("quantification result:\n");
    for( i=0;i<len;i++)
        printf("q[%d]=%d ",i,q[i]);

    printf("\n");

    printf("inverse quantization result:\n");
    for(i=0;i<len;i++)
    {
        r[i] =  s*(q[i]-z);
        printf("r[%d]=%f ",i,r[i]);
    }
}

int main()
{
    float32_to_int8_test();
    return 0;
}


输入的在这里插入图片描述

量化后的结果为对应为-128,127 ,反量化后的结果与没量化的初始结果发现精度有略微的损失,但是效率 可以提高四倍。
神经网络量化与反量化( int8与float32之间的转换)+C语言实现_第3张图片

你可能感兴趣的:(深度学习C语言实现,C语言,人工智能与机器学习,神经网络,c语言,机器学习)