您是否通过深度学习模型获得了良好的准确性,却发现推理时间不足以部署到生产环境中?您是否对如何优化模型的推理速度迷失了方向?那么这篇文章是给你的。
众所周知,数据科学项目有一个奇特的特性,即项目者需要不断转换关注重点,根据业务或项目的不同需求。下面罗列了一些具体的关注点:
· 数据集是如何获取的?是否是自己创建的数据集?(如果是自己的数据集,那么准确的标签是什么?任务中需要使用的样本需要多少?)
· 如何将模型充分利用到实际项目中来?即使一个模型很好,但是如何将这个模型投入到实际项目中?
· 你将使用什么模型?目前,学术界已经发表了很多具有价值的算法模型,而我们在研究中时,很多技术已经发生了改变。
· 最重要的问题:这上述的一切是否可行,也就是按照目前的预算,是否可以用数据集训练出一个模型?并且这样的模型,是否能够满足我们应用场景的需要?
通常,在最后一个问题中,我们主要关注获得数据集的最佳预测准确性。这是最有意义的,因为它允许我们验证项目是否可行。反正,如果我们需要为模型投资更多数据以实现其目标,那么我们就需要权衡效率的问题。对于某些项目,缓慢的推理速度并不是一个破坏因素。但是,如果真的是这样的情况,那么具体会发生什么事情?
这种情况,可能发生在任何深度学习项目中,但在将涉及对象检测的项目部署到生产时通常会出现这种情况。当操作来自相机的图像时,每秒处理的每个帧都很重要。在硬件上投入更多资金绝对可以解决或缓解我们的问题。
但是,如果我们打算将我们的产品作为解决方案(比如带有集成边缘 GPU 的摄像机)销售,这会线性增加产品的成本,直到我们无法从投资中获得回报。
针对许多实际情况,我们经历了上述的问题。这里,我们给出不同的方法清单,用来提高算法模型的推理速度。
![](https://img-blog.csdnimg.cn/img_convert/e41d84bf3acbcf8e96ffd31660ca4641.png)
1.改变模型的权重
从我们的经验来看,优化运行时模型的第一步是充分利用模型的架构。
1.1.1 训练后量化
将算法模型权重的精度,由浮点类型(32-bits)转换到整型(8-bits),将会降低模型的准确度。但是,从内存存储角度来看,这大大降低了存储消耗,反正提高了CPU和硬件加速器的延迟。
具体如何实现这个方法,主要取决于此算法模型所实现的框架。如果算法模型是用TensorFlow实现,那么很幸运,因为TensorFlow给出了模型量化的封装函数。如果算法模型使用PyTorch实现的,那么稍微有点困难。就在写本文的时候,PyTorch对于模型量化的支持,还局限于CPU端的支持。未来PyTorch对于模型量化的支持,将扩充到GPU端,就目前来看对于GPU端的模型量化支持还不够成熟。
当然,针对PyTorch实现的算法进行量化优化也不是没有任何方法,可以利用NVIDIA的TensorRT进行实现,但是为了能够在TensorRT运行时上运行PyTorch模型,模型权重需要先转化为中间模型权重格式。目前,可以实施的方法就是将PyTorch模型权重转换为ONNX模型格式,最终转化到TensorRT上进行运行。
![](https://img-blog.csdnimg.cn/img_convert/6272b8723cff6fa4df2038ce2b83c91c.png)
1.1.2 实验举例
如果我们尝试量化一个用PyTorch框架实现的Faster R-CNN算法模型,但是不幸的是,这回产生很多问题。在论文上,所有这些过程都是有道理的,应该很容易做到。然而,在实践中,所有这些转换都可能出现问题。这主要是因为 PyTorch、ONNX 和 TensorRT 的发展会朝着多个方向发展,当一项功能被添加到一个功能中时,旧的集成不一定支持它。可能的问题如下:
· 我们的模型可能能够在ONNX上运行,但是问题可能发生在将ONNX格式的模型转为TensorRT格式的过程中。尤其是,针对某些网络层,例如是PyTorch中的Resize层。
· 在我们尝试进行这种转换时,我们碰巧使用PyTorch v1.3 或更高版本构建的模型可以转换并在ONNX Runtime 中运行,但无法通过 ONNX 优化器运行(这在转换网络是尤其重要的一步)。
请记住,这些问题可能会出现也可能不会出现,这取决于我们模型的架构,我们在转换简单的CNN网络时没有问题,但是对于我们正在使用的Faster R-CNN实现,这是另一回事。一些用户可以通过降级PyTorch设法解决了转换过程中的问题。但是,这限制了我们可以访问的ONNX的opset,这反过来也限制了我们可以使用哪个TensorRT 版本来运行您的引擎。希望所有这些问题都能在不久的将来得到解决……但考虑到所有这些框架的开发速度都很快,很可能总会有短暂的不兼容期。
训练后量化绝对是一个强大的工具,虽然有些PyTorch模型不能用这种方法量化,但你仍然应该试一试,考虑到将你的模型导出到ONNX后,可以很方便的使用命令行 trtexec 来转换模型。顺便说一下,它与Nvidia TensorRT docker容器中的 TensorRT 一起很容易获得。如果PyTorch量化失败,那么如果量化仍然是你想要的方法,我们建议你寻找TensorFlow实现。
![](https://img-blog.csdnimg.cn/img_convert/fd34d6d00ad24d36c44e51add50dd045.png)
1.2.1 将模型转换为半精度
与之前的方法类似,这种替代方法旨在权衡速度和内存效率的准确性。它提供了FP32和UInt8之间的中间点,其中:
· 模型大小最多减少一半(而不是最多 75%)
· 精度的下降小于 UInt8,这使得精度权衡更接近 FP32。
· 大多数神经网络权重已经落入这个范围内,尽管进行这种转换有梯度下溢(小梯度值变为零)的风险,这会阻止网络正确学习任何东西。
考虑到如今 GPU 的架构已转向针对 FP16 操作进行优化,尤其是使用张量核心,这种方法为提高速度提供了很好的权衡。此外,事实证明,并非网络的所有层在推理过程中都花费大量时间。这意味着我们可以通过仅在需要速度提升的层(例如卷积)中使用半精度并将其余部分留在 FP32 中来找到更好的权衡。更好的是,在 FP32 中有一些层有助于防止梯度下溢。这种方法称为自动混合精度,它在量化方面的不同之处在于,不是对训练模型的权重进行后处理,而是应该从一开始就使用混合精度来训练模型。
1.2.2 实验举例
TensorFlow 通过为我们提供原生支持来实现这一目标,再次在很大程度上让我们的实际开发更轻松。而如果您使用PyTorch,NVIDIA Apex是你应该使用的工具,幸运的是,根据我们的经验,它比使用PyTorch 模型进行量化所带来的痛苦要少得多。集成 Apex 被宣传为仅在你的代码中添加三行。而实际上,还不止这些。你必须对其进行初始化,更改反向传递调用以使用 Apex 的缩放损失,并修改你保存和加载检查点的方式,实例如下:
from apex.fp16_utils import *from apex import amp, optimizers...# Initializationopt_level = 'O1'model, optimizer = amp.initialize(model, optimizer, opt_level=opt_level)# Train your model...with amp.scale_loss(loss, optimizer) as scaled_loss:scaled_loss.backward()...
结果也很不错。你获得多少加速将在很大程度上取决于你正在训练的模型。对于我们上面提出的Faster R-CNN模型实例来说,我们获得了超过30%的速度提升,而对我们的Faster R-CNN 模型的准确性没有任何影响。
2.寻找最优模型
我们试图从我们的模型中提取最后一个dropout以缩短推理时间。但也许这还不够。也许我们模型的架构对于我们试图解决的问题来说太大了。减小模型的大小也会降低我们的准确性吗?不必要!这不仅取决于我们希望模型解决的问题的具体性质。而且通过研究,不断提出和试验新的模型架构,通常会产生更纤薄的架构,通过设计实现更高的准确性!更好的是,如果我们实现了前面描述的任何方法,我们就可以重用该工作,并决定对我们的模型进行任何修改。
2.1 改变模型的backbone
在进行迁移学习时,我们可以查看模型的主干和对其进行预训练的数据集,仅作为我们在网格搜索中使用的超参数。我们不需要经过完全训练的模型来评估推理时间。这使我们可以对多个主干进行实验,并查看哪些可以更好地改善推理时间。我们应该期待我们的推理时间有相当大的改进,记住我们的模型仍然需要通过一个主干,虽然相当数量的推理时间发生在主干上,我们模型的外层仍然可以有对推理时间有很大影响。在研究了哪些主干提供了更好的时序之后,使用它的模型需要完全重新训练,以便我们分析主干对模型准确性的影响。
2.2 改变整个模型
数据科学家的日常工作不仅是从事数据科学项目,还要密切关注研究以及它如何影响当前的技术状态。尽管我们模型的主干是我们模型的一个重要组成部分,但我们只能通过尝试优化一些保持其他事物静态的东西来做这么多。如果毕竟方法推理时间仍然不符合你的喜好,那么是时候查看新开发的模型并验证这些模型所承诺的内容是否适用于你的实际用例。
2.3 实验举例
以物体检测问题为例,一些模型专门针对速度进行了优化,例如 YOLO,而同时其他模型提供了多种配置,这些配置随神经网络的深度和它们接收的输入的大小而变化,例如 EfficentDet,允许你训练和比较准确度与速度之间的权衡如何变化。更重要的是,令人惊叹的机器学习社区通常会提供这些模型的开源实现,供我们协作并帮助我们不要重新发明轮子!例如zylo117对EfficientDet 的 PyTorch 实现。
![](https://img-blog.csdnimg.cn/img_convert/88a33162c0c7fbdca0e1555a6c4865ac.png)
3.知识蒸馏
我们最后提出的改善模型推理时间的选项是通过知识蒸馏。假设我们有一个大模型(或模型的集合),它的预测精度很高,但其推理速度并不理想。Knowledge Distillation 建议通过使用我们的大模型作为训练器来训练一个具有较少参数的较小模型。这实质上是训练我们的小模型输出与我们的大模型或集成相同的预测。这样做的一个很大优势是我们不仅限于使用标记数据。
请注意,虽然我们的准确性可能会受到影响,但我们应该能够从中获得不错的速度提升。不幸的是,我们没有愉快地自己实施这种方法。但是知识蒸馏最近非常流行,并已用于对象分类、对象检测、声学模型和 NLP等。如果你想了解更多关于 知识蒸馏的信息,请查看Geoffrey Hinton等人的这篇论文。
4.总结
在本文中,我们描述了五种方法来改善深度学习模型的推理时间。特别是,我们建议你按照我们列出的顺序实现它们,因为我们为实现模型量化和自动混合精度所做的任何编码对于我们对模型进行的任何进一步更改都具有重要价值。
我们希望这篇文章对你有价值,无论是通过为你当前面临的问题提供指导,还是在需要时用我们的知识武装你的项目!如果你有本博文中未涵盖的加速模型推理的方法,请告诉我们……对模型的推理速度优化有任何疑问吗?如果可以,我们很乐意在评论中回答这些问题。
5.参考链接
https://www.tensorflow.org/api docs/python/tf/quantization/quantize
https://pytorch.org/docs/stable/quantization.html
https://developer.nvidia.com/zh-cn/tensorrt
https://arxiv.org/abs/1506.01497
https://github.com/onnx/onnx-tensorrt/issues/302
https://github.com/NVIDIA/TensorRT/issues/284
https://github.com/onnx/onnx/issues/2417
https://ngc.nvidia.com/catalog/containers/nvidiatensorrt
https://developer.nvidia.com/blog/mixed-precision-training-deep-neural-networks/