TensorRT8 使用手记(1)模型测试 Conv+BN+Relu 结构融合

TensorRT8 使用手记(1)模型测试 Conv+BN+Relu 结构融合,第1张

在主流卷积神经网络模型中 Conv+BN+Relu 是一种常见的模型结构。在模型推理和训练中,BN层往往与其他层合并,以减少计算量。

node_of_325

node_of_326

node_of_327

在TensorRT中会对网络结构进行垂直整合,即将 Conv、BN、Relu 三个层融合为了一个层,即CBR融合

在BN层中,首先对输入 进行归一化( 输入张量的均值, 输入张量的方差),然后对归一化的结果进行比例缩放和位移。 [1][2]

展开可得:

带入替换后可得:

此时可以将BN层视为一个1x1卷积层。

BN层的输入特征(Conv层的输出特征) 的形状为 ,对于Conv层:

,因此BN与Conv融合之后

融合之后:

线性整流函数(Rectified Linear Unit, ReLU)即: ,又称修正线性单元,是一种人工神经网络中常用的激活函数(activation function)。

在神经网络中,线性整流作为神经元的激活函数,定义了该神经元在线性变换之后的非线性输出结果。换言之,对于来自上一层卷积层的输入向量 ,使用线性整流激活函数可以得到输出:

在Int8量化模型中,Conv+ReLU 一般也可以合并成一个Conv进行运算 [3] 。

对于Int8ReLU,其计算公式可以写为 :

由于ReLU的输入(数值范围为 )和输出(数值范围为 )的数值范围不同,因此需要保证 和 、 和 是一致的。由于ReLU的截断 *** 作,因此需要使用 和 ,即对于ReLU的输入,使用输出对应的和保证其对小于0截断的输入进行截断,对大于等于0的输入映射至[0,255]范围内。

在Int8Conv的计算过程中,首先使用量化计算公式 对输入和权重值进行量化计算,将其转换为数值范围为(0,255)的整数,在完成卷积计算后再将计算结果进行反量化计算。而 ReLU 本身没有做任何的数学运算,只是一个截断函数。假设Int8Conv的卷积输出为 122( ),则对应反量化输出 -0.3,经过Int8ReLU( ),对该值进行Int8量化,对应的输出为0。因此在ReLU层对输入进行截断之前,即可得到需要截断的数值。

因此,通过在完成卷积计算后直接使用 ReLU 后的 scale 和 zeropoint进行反量化,实现了将 ConvReLU融合。

现有深度学习框架在训练深度神经网络时,往往都会使用 FP32 的数据精度来表示权值、偏置、激活值等。但是当网络很深时,网络参数量大,计算量大。如此大的计算量,如果中间值都使用 FP32 的精度来计算的话,势必会很费时间。这对于嵌入式设备或者移动设备来说,简直就是噩梦。

因此解决此问题的方法之一就是在部署推理时(inference)使用低精度数据,比如INT8,注意此处只是针对推理阶段,训练时仍然使用 FP32 的精度。除此之外,当然还有模型压缩之类的方法。为什么进行低精度推理不会造成太大的精度损失可以参考 Why are Eight Bits Enough for Deep Neural Networks? 这篇博文。

最简单的映射方式就是线性映射(或称线性量化,linear quantization), 就是说映射前后的关系满足下式:

是每一层上每一个 tensor 的换算系数或称比例因子(scaling factor),因此现在的问题就变成了如何确定比例因子。简单的做法如下图所示:

简单的将一个 tensor 中的 -|max| 和 |max| FP32 映射为 -127 和 127,中间值按照线性关系进行映射,这种映射关系为不饱和的(No saturation ),对称的。但是这样做会导致比较大的精度损失。

TensorRT的做法如下图所示:

我们需要一个衡量指标来衡量不同的 INT8 分布与原来的 FP32 分布之间的差异程度。这个衡量指标就是 相对熵(relative entropy),又称为KL散度(Kullback–Leibler divergence,简称KLD)。每一层的 tensor 的 |T| 值都是不一样的,确定每一层的 |T| 值的过程称为 校准(Calibration )。

从验证集 选取一个子集作为校准集(Calibration Dataset),校准集应该具有代表性,多样性,最好是验证集的一个子集,不应该只是分类类别的一小部分。

先说结论:部署的方式取决于需求

需求一:简单的demo演示,只要看看效果的,像是学校里面的demo展示这种

caffe、tf、pytorch等框架随便选一个,切到test模式,拿python跑一跑就好,顺手写个简单的GUI展示结果

高级一点,可以用CPython包一层接口,然后用C++工程去调用

需求二:要放到服务器上去跑,但一不要求吞吐二不要求时延的那种,说白了还是有点玩玩的意思

caffe、tf、pytorch等框架随便选一个,按照官方的部署教程,老老实实用C++部署,例如pytorch模型用工具导到libtorch下跑(官方有教程,很简单)

这种还是没有脱离框架,有很多为训练方便保留的特性没有去除,性能并不是最优的;

另外,这些框架要么CPU,要么NVIDIA GPU,对硬件平台有要求,不灵活;还有,框架是真心大,占内存(tf还占显存),占磁盘

需求三:放到服务器上跑,要求吞吐和时延(重点是吞吐)

这种应用在互联网企业居多,一般是互联网产品的后端AI计算,例如人脸验证、语音服务、应用了深度学习的智能推荐等。由于一般是大规模部署,这时不仅仅要考虑吞吐和时延,还要考虑功耗和成本。所以除了软件外,硬件也会下功夫,比如使用推理专用的NVIDIA P4、寒武纪MLU100等。这些推理卡比桌面级显卡功耗低,单位能耗下计算效率更高,且硬件结构更适合高吞吐量的情况软件上,一般都不会直接上深度学习框架。对于NVIDIA的产品,一般都会使用TensorRT来加速(我记得NVIDIA好像还有TensorRT inference server什么的,名字记不清了,反正是不仅可以加速前传,还顺手帮忙调度了)。TensorRT用了CUDA、CUDNN,而且还有图优化、fp16、int8量化等。反正用NVIDIA的一套硬软件就对了

需求四:放在NVIDIA嵌入式平台上跑,注重时延

比如PX2、TX2、Xavier等,参考上面(用全家桶就对了),也就是贵一点嘛

需求五:放在其他嵌入式平台上跑,注重时延

硬件方面,要根据模型计算量和时延要求,结合成本和功耗要求,选合适的嵌入式平台。比如模型计算量大的,可能就要选择带GPU的SoC,用opencl/opengl/vulkan编程;也可以试试NPU,不过现在NPU支持的算子不多,一些自定义Op多的网络可能部署不上去对于小模型,或者帧率要求不高的,可能用CPU就够了,不过一般需要做点优化(剪枝、量化、SIMD、汇编、Winograd等)顺带一提,在手机上部署深度学习模型也可以归在此列,只不过硬件没得选,用户用什么手机你就得部署在什么手机上23333。为老旧手机部署才是最为头疼的上述部署和优化的软件工作,在一些移动端开源框架都有人做掉了,一般拿来改改就可以用了,性能都不错。

需求六:上述部署方案不满足我的需求

比如开源移动端框架速度不够——自己写一套。比如像商汤、旷世、Momenta都有自己的前传框架,性能应该都比开源框架好。只不过自己写一套比较费时费力,且如果没有经验的话,很有可能费半天劲写不好


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/bake/11633138.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-17
下一篇2023-05-17

发表评论

登录后才能评论

评论列表(0条)

    保存