TensorRT的生态系统包含两部分:转换(Convesion)与部署(Deployment)
1、转换(Convesion)
2、部署(Deployment)
Inputs:可以输入多种类型的模型框架。
Network definition :TensorRT中模型的表示。 网络定义是张量和运算符的图。
Optimization Parameters:对Batch Size 与Precision进行设定。
推理时,当优先考虑延迟时选择小的batch,而优先考虑吞吐量时选择较大的batch。 较大的batch需要更长的处理时间,但可以减少每个sample的平均时间。
推理通常需要比训练少的数字精度。 较低的精度可以在不牺牲的精度的情况下提供更快的计算速度和更低的内存消耗。 TensorRT支持TF32,FP32,FP16和INT8精度。
Builder :TensorRT的模型优化器。 构建器将网络定义作为输入,执行与设备无关和针对特定设备的优化,并创建引擎。
Engine :由TensorRT构建器优化的模型的表示,可以对模型进行序列化。
Plan(Bytes) :序列化格式的优化后的推理引擎。 典型的应用程序将构建一次引擎,然后将其序列化为计划文件以供以后使用。要初始化推理引擎,应用程序将首先从plan文件中反序列化模型。
Runtime :TensorRT的组件,可在TensorRT引擎上执行推理。
两种类型的runtime:C++和Python绑定的独立的runtime,以及与TensorFlow的原生集成的runtime。
为了优化推理模型,TensorRT会采用你的网络定义,执行包括平台特定的优化,并生成推理引擎。 此过程称为build阶段。
build阶段可能会花费大量时间,尤其是在嵌入式平台上运行时。 因此,典型的应用程序将只构建一次引擎,然后将其序列化为plan文件以供以后使用。
构建阶段对图执行以下优化:
上述的这些算子融合、动态显存分配、精度校准、多steam流、自动调优等操作,TensorRT都帮你做了。这样通过TensorRT帮你调优模型后,自然模型的速度就上来了。
TensorRT的这些优化策略代码虽然是闭源的,但是大部分的优化策略我们或许也可以猜到一些,也包括TensorRT官方公布出来的一些优化策略:
(1)左上角是原始网络(googlenet),
(2)右上角相对原始层进行了垂直优化,将conv+bias(BN)+relu进行了融合优化;
(3)而右下角进行了水平优化,将所有1x1的CBR融合成一个大的CBR;
(4)左下角则将concat层直接去掉,将contact层的输入直接送入下面的操作中,不用单独进行concat后在输入计算,相当于减少了一次传输吞吐;
BN层往往用在深度神经网络的卷积层之后、激活层之前。其作用可以加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。
借一下Pytorch官方文档中的BN公式简单回顾一下:
上述的式子很简单,无非就是减均值除方差(其实是标准差),然后乘以一个权重加上一个系数,其中权重和系数是可以学习的,在模型forward和backward的时候会进行更新。
BN层在推理的时候也只需要之前训练好固定的参数:均值 、方差 、权重 以及偏置,假设上一层卷积的输出为 w ∗ x + b w*x + b w∗x+b,将其带入上述的x中(上一层的输出就是这一层的输入)有:
对于最后的w、x和偏置b,发现只需要将卷积权重缩放一定倍数,并对偏置进行一定变化,就可以将BN层的参数融合进Conv中了。这就相当于两次线性变化,两个线性变化是可以叠加融合的。
融合后的Conv+BN就相当于一个Conv了,因为大部分网络结构中Conv+BN这样的组合很多,所以一般来说仅仅是这个融合操作就可以使模型加速10%左右。
而一般的ReLU函数可以通过if-else与取max方式实现,因此整合起来不算很难。
在利用TensorRT进行模型解析时,比如从ONNX中解析成TensorRT的网络结构,会提前对BN层的一些操作进行合并和融合。
训练好的模型权重一般都是FP32也就是单精度浮点型,在深度学习训练和推理的过程中,最常用的精度就是FP32。当然也会有FP64、FP16、BF16、TF32等更多的精度:
对于浮点数来说,指数位表示该精度可达的动态范围,而尾数位表示精度。
1、FP32->FP16
从FP32->FP16由于部分精度丢失,模型的精度也会下降一些。其实从FP32->FP16几乎是无损的(CUDA中使用__float2half
直接进行转换),不需要calibrator
去校正、更不需要retrain
。
而且FP16的精度下降对于大部分任务影响不是很大,甚至有些任务会提升。NVIDIA对于FP16有专门的Tensor Cores可以进行矩阵运算,相比FP32来说吞吐量直接提升一倍,提速效果明显!
2、FP32->INT8
实际点来说,量化就是将我们训练好的模型,不论是权重、还是计算op,都转换为低精度去计算。因为FP16的量化很简单,所以实际中谈论的量化更多的是INT8的量化。
经过INT8量化后的模型:
所以说,随着我们模型越来越大,需求越来越高,模型的量化自然是少不了的一项技术,但是FP32与INT8之间范围相差太大,不能直接进行类型转换,否则会造成很大的类型损失。
左边是非对称量化,右边是对称量化(也称为Affine quantization和Scale quantization)。可以观察到:
所以上述的非对称量化过程可以简述为 f ( x ) = s ∗ x + z f(x)=s*x+z f(x)=s∗x+z ,其中z是zero-point
,这个数字就代表实数0映射到整数是多少,而对称量化则是 f ( x ) = s ∗ x f(x)=s*x f(x)=s∗x。
即x = 0,直接带入算出来也是0,如果采用的是对称量化,这样算就没问题!
不论是非对称还是对称量化,是基于线性量化(也可以称作均匀量化)的一种。线性量化将FP32映射到INT8数据类型,每个间隔是相等的,而不相等的就称为非线性量化。非线性量化因为对部署并不是很友好,虽然能够更好地捕捉到权重分布的密集点,但感觉用的并不多,这里也就先不多说了。
如何定量评估两种精度的差异? —— 考虑两种编码的相对熵(KL散度)
在校准数据数据集上运行FP32推理,校准数据集可以由训练集中抽取一部分图片构成
对每一层:
要做INT8量化,需要:
步骤
如何寻找最优的阈值T使得精度的损失最小?
NVIDIA选择的是KL-divergence,其实就是相对熵。相对熵表述的就是两个分布的差异程度,这里就是量化前后两个分布的差异程度。差异最小就是最好的了,因此问题转换为求相对熵的最小值。
KL散度来精确测量这种最优和次优之间的差异。
FP32就是原来的最优编码,INT8就是次优的编码,用KL散度来描述这两种编码之间的差异。
相对熵表示的是采用次优编码时会多需要多少个bits来编码,也就是与最优编码之间的bit差;
而交叉熵表示的是用次优编码方式时确切需要多少个bits来表示;
因此,最优编码所需要的bits=交叉熵-相对熵。
校准(Calibration)算法:基于实验的迭代搜索阈值。
基本步骤:
• 提供一个样本数据集(最好是验证集的子集),称为“校准数据集”,用来做校准。
• 在校准数据集上运行FP32推理。收集激活的直方图,并生成一组具有不同阈值的8位表示法,并选择具有最少KL散度的表示。
KL散度是在参考分布(即FP32激活)和量化分布之间(即INT8量化激活)之间。
TensorRT提供了IInt8EntropyCalibrator,该接口需要由客户端实现,以提供校准数据集和一些用于缓存校准结果的样板代码。
上面就是一个循环,不断地构造P和Q,并计算相对熵,然后找到最小(截断长度为m)的相对熵,此时表示Q能比较好地拟合P分布了。而阈值就等于(m + 0.5)*一个bin的长度。
校准过程我们是不用参与的,全部都由TensorRT内部完成,但是,需要告诉校准器如何获取一个batch的数据,也就是说,需要重写校准器类中的一些方法。
TensorRT优缺点?
优点:
量化:量化即IN8量化或者FP16以及TF32等不同于常规FP32精度的使用,这些精度可以显著提升模型执行速度并且不会保持原先模型的精度
算子融合(层与张量融合):简单来说就是通过融合一些计算op或者去掉一些多余op来减少数据流通次数以及显存的频繁使用来提速。对于网络结构进行了重构和优化,主要体现在一下几个方面。
(1) TensorRT通过解析网络模型将网络中无用的输出层消除以减小计算。
(2) 对于网络结构的垂直整合,即将目前主流神经网络的Conv、BN、Relu三个层融合为了一个层,例如将图1所示的常见的Inception结构重构为图2所示的网络结构。
(3) 对于网络结构的水平组合,水平组合是指将输入为相同张量和执行相同操作的层融合一起,例如图2向图3的转化。
内核自动调整:根据不同的显卡构架、SM数量、内核频率等(例如1080TI和2080TI),选择不同的优化策略以及计算方式,寻找最合适当前构架的计算方式
动态张量显存:我们都知道,显存的开辟和释放是比较耗时的,通过调整一些策略可以减少模型中这些操作的次数,从而可以减少模型运行的时间
多流执行:使用CUDA中的stream技术,最大化实现并行操作
“缺点”:
内卷成啥了还不知道TensorRT?超详细入门指北,来看看吧!
不看必进坑~不论是训练还是部署都会让你踩坑的Batch Normalization
必看部署系列~懂你的神经网络量化教程:第一讲!
YOLOv5(PyTorch)目标检测实战:TensorRT加速部署