ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

简单理解nvidia tensorRT模型量化原理

2021-06-29 11:32:28  阅读:483  来源: 互联网

标签:fp weight nvidia tensorRT 直方图 input 量化 int8


参考资料:
某人的量化原理笔记
https://blog.csdn.net/sinat_31425585/article/details/101607785
某人对int8比较详细的介绍
https://zhuanlan.zhihu.com/p/58182172
某人对ncnn的量化原理和源码理解(ncnn量化是基于tensorRT改进的)
https://zhuanlan.zhihu.com/p/72375164
 

一、两张图粗糙理解量化思路

一句话把原始值等比例映射到-127~127, 以便用int8表示.
原始思想是, 把原始值的绝对值的最大值作为阈值, 但是如果原始值分布不均匀, 部分值域被空余浪费, 导致精度损失很大.(比如原始值范围-1270.0~+1270.0, 但是大多数都在0~+10, 那么量化后全部都变成了int8的+1).
所以很多情况下是截取其中一部分(也就是选用小的阈值)来建立映射关系. 为什么这么做(为什么右边的饱和截取就ok呢?见https://zhuanlan.zhihu.com/p/58182172)

整个量化的问题就变成怎样找到最优的阈值使得精度损失最小?
 

二、int8量化后的模型推理

首先以简单的1个网络层中的乘法为例, 看看使用int8量化后的forward推理公式:

缩放系数: s = 127 / T, 这个T就是在原始值上要找的阈值.
输入向量:
    input_int8 = s_input * input_fp,
input_int8是input量化后的值, s_input是input的缩放系数, input_fp是input的原始值, fp表示float类型
模型权重:
    weight_int8 = s_weght * weight_fp,
weight_int8是weght量化后的值, s_weight是weight的缩放系数, weight_fp是weight的原始值
forward时计算二者乘积: 
    input_fp * weight_fp = (input_int8 * weight_int8) / (s_input * s_weight)
令ss_fp = 1 / (s_input * s_weight), 那么这个计算就可以分两步:
第一是int8计算:
    inner_int8 = (input_int8 * weight_int8)
第二是乘上float类型的系数变回原来的float类型
    output_fp = inner_int8 * ss_fp

总结一下: 在每层计算时是需要先将feature map量化到INT8,然后将weights量化到INT8,最后卷积计算得到INT32的输出值,输出值乘以scale(float)值反量化到float,然后加上浮点格式的bias作为下一层的输入。(这段话是https://zhuanlan.zhihu.com/p/58182172里面的原话, 讲的是ncnn的底层量化).
 

三、如何寻找最优阈值T?

有了阈值T就有了缩放系数, 问题就差不多解决了.
怎么样寻找最优阈值T? 就是通过计算量化前后两个数据分布的差异性, 分布差异最小的阈值T就是最优的阈值.
原理中使用KL散度(相对熵)来计算两个分布的差异性:

p(i)表示原始分布P中某个元素的值, q(i)表示量化后分布Q中某个元素的值.
但是模型推理过程中数值都是连续的, 如何得到P和Q分布? 就是用直方图统计来表示.

原始值的直方图统计(也就是分布P)计算过程:
 

1. 计算原始fp类型数据的最大绝对值(即最大值和最小值绝对值的最大值): 
    mav = max(abs(min_v), abs(max_v))
2. 将原始数据划分成2048个bins, 每个bin的宽度:
    interval = mav / 2048
3. 统计原始值的直方图, p(i)就是第i个bin中原始值数据点的个数了.


接下来是寻找最优阈值T的过程, 也是整个量化算法的中心(记住, T是<=mav的, 使用时是通过-T和+T来截断数据的):

for num_bins from 127 to 2048:
    a. T_cur = interval * num_bins, (ncnn是使用(interval+0.5)*num_bins, 取的是某个bin的中心点),
       使用T_cur阶段数据, 得到截断后的分布P'(截断区外面的值被加到截断区最后一个值上面, 比如num_bins是200, 也就是最后一个bin的序号是200,  那么201~2048bin上的频数(频数还是频率?)都被加到了第200个bin上)
    b. 把P'分布映射成Q, Q的bins是128, Q就是量化后的数据分布
       (注意, 这里是直接对直方图P'映射成128个bins的直方图Q, 而不是对原始数据做映射到int8再统计直方图)
       (看https://zhuanlan.zhihu.com/p/7237516知道 ncnn里大概的思路就是把原始的num_bins的直方图重新均匀分成128个bins)
    c. 将Q扩展到和P'一样的长度, 得到Q_expand(要计算KL散度前提是两个分布长度一样)
    d. 计算P'和Q_expand的KL散度, 并判断KL散度是否为最小

具体过程可以看https://zhuanlan.zhihu.com/p/72375164中对NCNN量化源码的理解, 挺详细的, 值得一看, 可以理解得更深入.

上面步骤c是将Q扩展到和P'一样长度, 举例过程如下(从https://zhuanlan.zhihu.com/p/72375164复制来的):

P=[1 2 2 3 5 3 1 7]     // fp32的统计直方图,T=8
// 假设只量化到两个bins,即量化后的值只有-1/0/+1三种
Q=[1+2+2+3, 5+3+1+7] = [8, 16]
// P 和 Q现在没法做KL散度,所以要将Q扩展到和P一样的长度
Q_expand = [8/4, 8/4, 8/4, 8/4, 16/4, 16/4, 16/4, 16/4] = [2 2 2 2 4 4 4 4]
D = KL(P||Q_expand)  // 这样就可以做KL散度计算了

这个扩展的操作,就像图像的上采样一样,将低精度的统计直方图(Q),上采样的高精度的统计直方图上去(Q_expand)。由于Q中一个bin对应P中的4个bin,因此在Q上采样的Q_expand的过程中,所有的数据要除以4.

done.


 

标签:fp,weight,nvidia,tensorRT,直方图,input,量化,int8
来源: https://blog.csdn.net/ying86615791/article/details/118329732

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有