TensorRT使用INT8 原理总结

举报
lutianfei 发表于 2020/06/12 17:19:54 2020/06/12
【摘要】 目标: 在没有明显准确度丢失的情况下将FP32的CNNs网络转换为INT8 理由: INT8类型的存储方式有很高的通量和较低的内存需求 挑战: 相对于FP32, INT8有明显较低的精度和动态范围 解决方式: 在将权值以及计算时最小化有效信息损失. 结果: 上述转换可以通过TensorRT来进行实现,同时该方法不需要额外的大量调整和重新训练

目标: 在没有明显准确度丢失的情况下将FP32的CNNs网络转换为INT8

理由: INT8类型的存储方式有很高的通量较低的内存需求

挑战: 相对于FP32, INT8有明显较低的精度和动态范围

解决方式: 在将权值以及计算时最小化有效信息损失.

结果: 上述转换可以通过TensorRT来进行实现,同时该方法不需要额外的大量调整和重新训练

 

 

 

对于INT8 推断(Inference),需要生成一个校准表来量化模型。接下来主要关注INT8推断(Inference)的几个方面,即:如何生成校准表,如何使用校准表,和INT8推断(Inference)实例。

 

 

 

面临的挑战

  • 相对于FP32INT8的精度和动态范围要小很多:

  • FP32INT8需要不止一次的类型转换

 

 

 

 

1) 如何生成校准表?

 

校准表的生成需要输入有代表性的数据集, 对于分类任务TensorRT建议输入五百张到一千张有代表性的图片,最好每个类都要包括。生成校准表分为两步:第一步是将输入的数据集转换成batch文件;第二步是将转换好的batch文件喂到TensorRT中来生成基于数据集的校准表,可以去统计每一层的情况。

 

 

2) 如何使用校准表?

 

校准这个过程如果要跑一千次是很昂贵的,所以TensorRT支持将其存入文档,后期使用可以从文档加载,其中存储和加载的功能通过两个方法来支持,即writeCalibrationCachereadCalibrationCache。最简单的实现是从write()read()返回值,这样就必须每次执行都做一次校准。如果想要存储校准时间,需要实现用户自定义的write/read方法,具体的实现可以参考TensorRT中的simpleINT8实例。

 

 

 


 

INT8相较于FP32的计算量变小了,同样也需要适合小精度的计算单元来执行,否则同样在FP32计算单元上面执行,则只在模型大小上面有一定的优势,而并不能带来真正性能的提升,这时候就要谈到GPU为INT8计算的提供的硬件支持。

对于sm_61+如Tesla P4/P40 GPU,我们提供了新的INT8点乘运算的指令支持---DP4A,其将FP32单元“拆开“分成4个INT8单元,从而通过两个FP32单元实现4个INT8数的点乘操作,最后累加成INT32的结果,计算过程如下图所示:

从而对于Tesla P4来说,其拥有5.5T的FP32计算性能,通过DP4A指令为其赋予了INT8的计算能力,并达到了FP32的4倍也就是22T的计算性能。

 


 

线性量化

对于每一个FP32的Tensor(权值和激活值),我们无法直接用INT8来表示,因此最直接的表达方法为:

        Tensor Values = FP32 scale factor * int8 array + FP32 bias

这时候我们需要考虑一个问题,我们是否真的需要FP32的bias?

 

对于以下两个矩阵:

    A = scale_A * QA + bias_A

    B = scale_B * QB + bias_B

推理过程中绝大部分为矩阵乘法,因此这两个矩阵相乘的计算可以表示为:

    A * B = scale_A * scale_B * QA * QB + scale_A * QA * bias_B + scale_B * QB * bias_A + bias_A * bias_B

 

如果我们直接将去掉bias,则两者相乘为:

    A * B = scale_A * scale_B * QA * QB

 

通过去掉bias我们能极大的简化计算内容,降低对GPU中寄存器等资源的消耗,而我们的实验也发现去掉bias不会对性能产生很大的影响。故而TensorRT在这里采用的优化的对称线性量化方法:

    Tensor Values = FP32 scale factor * int8 array

 

现在问题就变成如何寻找一个最优的scale factor?

量化有以下两种方法:

  1. 图左-非saturation:对weights和activations使用线性量化,即找到其中绝对值最大的值,然后将这个范围映射回INT8

  2. 图右-saturation:选择一个阈值T,将范围T的FP32值映射至INT8,对于范围外的使用-127或128

 

根据实验证明,图左的方法转化后会带来很大的准确度损失。而对于图右的方法:

  1. weights:无法提升准确度

  2. activations:能有效提升准确度

 

因此对于weights和activations分别采用了不同的量化方法,前者使用了简单的非saturation的方法,而后者采用的是较为复杂的saturation方法。

量化整体流程

以卷积kernel为例:

输入为:INT8_INPUT,I8_weights

输出为:INT8_OUTPUT

所需参数:FP32 bias (来自于FP32模型中),FP32 scaling factors: input_scale, output_scale, weights_scale[K]

 

  1. 利用DP4A指令计算 INT8_INPUT与I8_weights的乘积获得I32_gemm_out

  2. 利用input_scale以及weights_scale将I32_gemm_out转化成为FP32的F32_gemm_out

  3. 利用input_scale, output_scale和weights_scale      将FP32的F32_gemm_out映射至输出的activation分布,获得rescaled_F32_gemm_out

  4. 给rescaled_F32_gemm_out加上FP32的bias获得rescaled_F32_gemm_out _with_bias

  5. 对rescaled_F32_gemm_out _with_bias执行relu从而获得F32_result

  6. 最后根据前文的阈值T将F32_result转成I8_output

 

根据所选的量化方法以及量化的整体流程,对于量化最关键的是如何实现saturation方法中的阈值T的选择,这个选择流程被称之为校准。

 

校准

 

上图分别是vgg19-conv3_4, resnet152-res4b8_branch2a, googlenet:incetion_3a/pool的activations的分布直方图,其中横轴为activation值纵轴是正则化后的值的数量级

对于将activations从FP32映射至INT8可以看成是信息的重编码过程,需要做的是在INT8中最大的保留FP32的信息,这里通过引入KL divergence来作为INT8的信息损失率评价指标。

 

 

校准的整体流程

  1. 选取validation数据集中一部分具有代表的数据作为校准数据集

  2. 对于校准数据进行FP32的推理,对于每一层

    1. 收集activation的分布直方图

    2. 使用不同的threshold来生成一定数量的量化好的分布

    3. 计算量化好的分布与FP32分布的KL divergence,并选取使KL最小的threshold作为saturation的阈值

  3. 整个流程将花费几分钟至几十分钟来完成

 

以下是几个常见网络的部分activation分布及校准结果:

    GOOGLENET:Inception_5a/5x5

 

ALEXNET:Pool2

 

 

    RESNET:Res4b30

 

如上三图分别是GOOGLENET/ALEXNET/RESNET中某一层的activation的分布,其中白线的左边的activation数据将映射至INT8中,而右边的将被截断

可以明显的看到前两个网络的FP32 activation分布在INT8中得以几乎完整的保留。

第三个图左的白线截断了其右侧activation的分布,第三个图右为截断后的分布图。图中的绿点即为截断部分映射至INT8的极大值所占比例,可以看到绿点在整体分布的所占比例并不大,因此损失的信息仍然是可以接受的,校准也极大的保留的FP32的activation的分布信息。

 

 

 

 

 


【版权声明】本文为华为云社区用户翻译文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容, 举报邮箱:cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。