文章目录

前言

模型大小评估指标

  • 计算量
  • 参数量
  • 访存量
  • 内存占用
  • 小结
  • 模型推理影响因素
  • 计算密度与 RoofLine 模型
  • 计算密集型算子与访存密集型选自
  • 推理时间
  • 硬件限制对性能上界的影响
  • 系统环境对性能的影响
  • 小结
  • 面向推理速度的模型设计
  • 方法论
  • 网络设计建议

前言

在运算资源有限制的机器上部署深度学习模型,需要了解模型大小和模型推理速度的关系,设计能发挥硬件计算能力的模型结构。而衡量模型大小和模型推理速度关系往往跟计算量、参数量、访存量、内存占用等指标密切相关。因此本博客对这些指标进行分析,介绍这些指标对模型部署推理的影响。本篇博客主要参考:深度学习模型大小与模型推理速度的探讨

模型大小评估指标

计算量

计算量是模型所需的计算次数,反映了模型对硬件计算单元的需求。计算量一般用FLOPs(Floating Point Operations)即浮点次数表示。模型的整体计算量等于模型中每个算子的计算量之和。对于卷积而言,计算量公式为:大模型应用方式架构图_人工智能

参数量

参数量是模型中参数的总和,与模型占用磁盘空间大小直接相关。对于卷积神经网络而言,参数量主要与卷积层和全连接层的权重组成。参数量往往是被算作访存量的一部分,因此参数量不直接影响模型推理性能。但是参数量一方面会影响内存占用,另一方面也会影响程序初始化时间。另一方面参数量对软件包大小也会有影响。减少软件包大小可以通过设计一些高效简单模型,另一方面可以通过模型压缩方式,如Protobuf对模型进行高效编码进行压缩。 参数量在Tensorflow里面通过model.summary()即可打印出参数量信息。

访存量

访存量是指模型计算时所需访问存储单元的字节大小,反映了模型对存储单元带宽的需求。访存量一般用 Bytes(或者 KB/MB/GB)来表示,即模型计算到底需要存/取多少 Bytes 的数据。和计算量一样,模型整体访存量等于模型各个算子的访存量之和。

内存占用

内存占用是指模型运行时,所占用的内存/显存大小。一般有工程意义的是最大内存占用,当然有的场景下会使用平均内存占用。和参数量一样,内存占用不会直接影响推理速度,往往算作访存量的一部分。但在同一平台上有多个任务并发的环境下,如推理服务器、车载平台、手机 APP,往往要求内存占用可控。可控一方面是指内存/显存占用量,如果占用太多,其他任务就无法在平台上运行;另一方面是指内存/显存的占用量不会大幅波动,影响其他任务的可用性。

小结

计算量、参数量、访存量、内存占用从不同维度定义了模型的大小,应根据不同的场合选用合适的指标进行评价。模型推理速度不单单受模型计算量的影响,也与访存量和一些其他因素息息相关。

模型在特定硬件上的推理速度,除了受计算量影响外,还会受访存量、硬件特性、软件实现、系统环境等诸多因素影响,呈现出复杂的特性。因此,在手头有硬件且测试方便的情况下,实测是最准确的性能评估方式。

模型推理影响因素

计算密度与 RoofLine 模型

计算密度是指一个程序在单位访存量下所需的计算量,单位是 FLOPs/Byte。其计算公式很简单,很多教材、资料里也称之为计算访存比,用于反映一个程序相对于访存来说计算的密集程度:大模型应用方式架构图_深度学习_02当程序的计算密度I较小时,程序访存多而计算少,性能受内存带宽限制,称为访存密集型程序。反之如果计算密度I较大,程序性能受硬件最大计算峰值(下文简称为算力)限制,称为计算密集型程序。

计算密集型算子与访存密集型选自

网络中的算子可以根据计算密度进行分类。一般来讲,Conv、FC、Deconv 算子属于计算密集型算子;ReLU、EltWise Add、Concat 等属于访存密集型算子。同一个算子也会因参数的不同而导致计算密度变化,甚至改变性质,比如在其他参数不变的前提下,增大 Conv 的 group,或者减小 Conv 的 input channel 都会减小计算密度。

大模型应用方式架构图_大模型应用方式架构图_03

在不同参数下卷积算子的计算密度有很大的差异。第 4 个算子 Depthwise Conv 计算密度仅有 2.346,在当下的很多设备上都属于访存密集型算子。算子的计算密度越大,约有可能提升硬件的计算效率,充分发挥硬件性能。

推理时间

按照 RoofLine 模型,算子的实际执行时间:

大模型应用方式架构图_cnn_04

则有:

大模型应用方式架构图_系统环境_05

对于访存密集型算子,推理时间跟访存量呈线性关系,而对于计算密集型算子,推理时间跟计算量呈线性关系。按照 RoofLine 模型,在计算密集区,计算量越小,确实推理时间越小。但是在访存密集区,计算量与推理时间没关系,真正起作用的是访存量,访存量越小,推理的时间才越快。在全局上,计算量和推理时间并非具有线性关系。

硬件限制对性能上界的影响

前面 RoofLine 模型使用的峰值算力及内存带宽,是根据纸面数据计算得到的,是理论上的最大值。但在实际情况下,硬件会因为种种原因,无法达到这个理论值。因此建议大家对硬件进行micro-benchmark,以获取硬件的真实性能上限。

系统环境对性能的影响

小结

计算量并不能单独用来评估模型的推理时间,还必须结合硬件特性(算力&带宽),以及访存量来进行综合评估。并非是计算量越低模型推理越快。在评价模型大小时,也建议加上访存量作为重要的评价指标。 除了峰值算力和内存带宽之外,还有硬件限制、系统环境、软件实现等诸多因素会影响程序的实际性能,使得其非线性特性更加严重。因此 RoofLine 模型仅仅只能提供一个性能上界的评估方式,并不代表能够达到的实际性能。实际性能最准确的测量方式只有真机实测。RoofLine 模型仅能用于估计模型所能达到的性能上界,而实际部署时,还会受硬件限制、系统环境、软件实现等因素的影响,导致无法达到 RoofLine 模型所定义的性能上界。

面向推理速度的模型设计

以下建议摘自:深度学习模型大小与模型推理速度的探讨。

方法论

  • 了解目标硬件的峰值算力和内存带宽,最好是实测值,用于指导网络设计和算子参数选择。
  • 明确测试环境和实际部署环境的差异,最好能够在实际部署环境下测试性能,或者在测试环境下模拟实际部署环境。
  • 针对不同的硬件平台,可以设计不同计算密度的网络,以在各个平台上充分发挥硬件计算能力(虽然工作量可能会翻好几倍【捂脸)。
  • 除了使用计算量来表示/对比模型大小外,建议引入访存量、特定平台执行时间,来综合反映模型大小。
  • 实测是最准确的性能评估方式,如果有条件快速实测的话,建议以实测与理论分析相结合的方式设计并迭代网络。
  • 遇到性能问题时,可以逐层 profiling,并与部署/优化同学保持紧密沟通,具体问题具体分析(适当了解一下计算相关理论的话,可以更高效的沟通)。

网络设计建议

  • 对于低算力平台(CPU、低端 GPU 等),模型很容易受限于硬件计算能力,因此可以采用计算量低的网络来降低推理时间。
  • 对于高算力平台(GPU、DSP 等),一味降低计算量来降低推理时间就并不可取了,往往更需要关注访存量。单纯降低计算量,很容易导致网络落到硬件的访存密集区,导致推理时间与计算量不成线性关系,反而跟访存量呈强相关(而这类硬件往往内存弱于计算)。相对于低计算密度网络而言,高计算密度网络有可能因为硬件效率更高,耗时不变乃至于更短。
  • 面向推理性能设计网络结构时,尽量采用经典结构,大部分框架会对这类结构进行图优化,能够有效减少计算量与访存量。例如 Conv->BN->ReLU 就会融合成一个算子,但 Conv->ReLU->BN 就无法直接融合 BN 层
  • 算子的参数尽量使用常用配置,如 Conv 尽量使用 3x3_s1/s2、1x1_s1/s2 等,软件会对这些特殊参数做特殊优化。
  • CNN 网络 channel 数尽量选择 4/8/16/32 的幂次,很多框架的很多算子实现在这样的 channel 数下效果更好(具体用多少不同平台不同框架不太一样)。
  • 框架除了计算耗时外,也处理网络拓扑、内存池、线程池等开销,这些开销跟网络层数成正比。因此相比于“大而浅”的网络,“小而深”的网络这部分开销更大。一般情况下这部分开销占比不大。但在网络算子非常碎、层数非常多的时候,这部分开销有可能会影响多线程的扩展性,乃至于成为不可忽视的耗时因素。
  • 除了优化网络结构、推理框架性能外,还可以考虑通过一些其他工程技巧来提升系统整体的性能。例如:对推理服务流水化,并行数据读取与计算的过程,掩盖 IO 延时。

关于实际工程部署,有一些技巧/注意的点可以保证不浪费访存量:

  • channel 数尽量保持在 4/8/16/32 的倍数,不要设计 channel = 23 这种结构。目前大部分推理框架为了加速计算,都会用特殊的数据排布,channel 会向上 pad。比如框架会把 channel pad 到 4 的倍数,那么 channel = 23 和 24 在访存量上其实是一致的。
  • 一些非常细碎乃至毫无意义的后处理算子,例如 Gather、Squeeze、Unsqueeze 等,最好给融合掉。这种现象往往见于 PyTorch 导出 onnx 的时候,可以尝试使用 onnxsim 等工具来进行融合,或者手动添加大算子。
  • 尝试一些部署无感的技巧,例如蒸馏、RepVGG等。