从TensorRT看INT8量化原理 - nanmi - 博客园从TensorRT看INT8量化原理 - nanmi - 博客园
- 简单的将一个tensor 中的 -|max| 和 |max| FP32 value 映射为 -127 和 127 ,中间值按照线性关系进行映射。
- 称这种映射关系为不饱和的(No saturation ),对称的。
- 这种做法不是将 ±|max| 映射为 ±127,而是存在一个 阈值 |T| ,将 ±|T| 映射为±127,显然这里 |T|<|max|。
- 超出 阈值 ±|T| 外的直接映射为阈值 ±127。比如上图中的三个红色点,直接映射为-127。
- 称这种映射关系为饱和的(Saturate ),不对称的。
- 只要 阈值 选取得当,就能将分布散乱的较大的激活值舍弃掉,也就有可能使精度损失不至于降低太多;
网络的前向计算涉及到两部分数值:权值和激活值(weights 和activation,二者要做乘法运算),Szymon Migacz 也提到他们曾经做过实验,说对weights 做saturation (饱和量化)没有什么变化,因此 对于weights的int8量化就使用的是不饱和的方式;而对activation做saturation就有比较显著的性能提升,因此对activation使用的是饱和的量化方式。
那现在的问题是 如何确定|T|?我们来思考一下,现在有一个FP32的tensor,FP32肯定是能够表达这个tensor的最佳分布。现在我们要用一个不同的分布(INT8)来表达这个tensor,这个 INT8 分布不是一个最佳的分布。饱和的INT8分布由于阈值 |T|的取值会有很多种情况(128−|max|
既然如此,我们就需要一个衡量指标来衡量不同的 INT8 分布与原来的FP3F2分布之间的差异程度。这个衡量指标就是 相对熵(relative entropy),又称为KL散度(Kullback–Leibler divergence,简称KLD);
KL散度 = 交叉熵 - 信息熵
其中,交叉熵公式如下:
信息熵公式如下:
那么,具体如何得到阈值T呢?
这里的做法是 从验证集 选取一个子集作为校准集(Calibration Dataset ),校准集应该具有代表性,多样性,最好是验证集的一个子集,不应该只是分类类别的一小部分。激活值分布就是从校准集中得到的。
- 首先在 校准集上 进行 FP32 inference 推理;
- 对于网络的每一层(遍历):
- 收集这一层的激活值,并做 直方图(histograms ),分成几个组别(bins)(官方给的一个说明使用的是2048组),分组是为了下面遍历 |T| 时,减少遍历次数;
- 对于不同的 阈值 |T| 进行遍历,因为这里 |T|的取值肯定在 第128-2047 组之间,所以就选取每组的中间值作为 T 进行遍历;
- 选取使得 KL_divergence(ref_distr, quant_distr) 取得最小值的 |T|。
- 返回一系列 |T|值,每一层都有一个 |T|。创建 CalibrationTable 。
上面解释一下:假设 最后 使得 KL散度最小的|T|值是第200组的中间值,那么就把原来 第 0-200组的 数值线性映射到 0-128之间,超出范围的直接映射到128