导语
在深度学习中,推理指的是神经网络的一次前向传播过程,也就是将输入数据送入神经网络,然后从中得到输出结果的过程。比如,我们可以通过将3D点云送入一个点云分类网络以确定该帧点云所属的类别。
提前了解推理时间可以帮助我们更好地设计深度学习模型,并且针对推理进行性能优化。比如,我们可以将标准的卷积换成可分离卷积来减少计算量,也可以通过剪枝、量化和模型冻结等方式来减少计算量,这些优化技术可以减少很多推理时间。本文将对这些技术进行简要的介绍。
如何计算模型的推理时间
为了理解如何优化神经网络,我们必须有一个指标,通常该指标为推理时间,推理时间指的是神经网络执行一次前向传播所需要的时间。通常我们用一秒钟内模型能够执行的推理次数来表示模型的推理速度,单位用fps
表示。假如模型推理一次需要的时间为0.1s
,那么其推理速度可以表示为1/0.1=10fps
。
为了衡量模型的推理时间,我们首先需要弄明白3个概念:FLOPs
, FLOPS
, 和 MACs
。
- FLOPs
为了衡量模型的推理时间,我们可以计算模型必须执行的计算操作的总数。在这里提出术语FLOP(Floating Point Operation)
,即浮点运算操作,这些运算操作包括加、减、乘、除和其他任何跟浮点数相关的操作。一个模型的FLOPs
为该模型全部的FLOP
之和,这个数值可以告诉我们模型的复杂程度。
- FLOPS
FLOPS
是Floating Point Operations per Second
的缩写,表示每秒钟内可以执行的浮点运算数。该指标可以用来衡量我们使用的计算平台的性能,其值越大,表明在该计算平台上每秒钟内可执行的浮点操作数越多,那么模型的推理速度就越快。
- MACs
MACs
是Multiply-Accumulate Computations
的缩写。一次MAC
操作表示执行了一次加法和一次乘法操作,相当于做了两次浮点操作,所以有1 MAC = 2 FLOPS
。
理解了以上几个概念后,我们自然而然能够想到,如果想让深度学习模型运行地更快,那么我们有两种选择:
-
降低模型中的
FLOPs
值; -
提升计算平台上的
FLOPS
值。
对于一个特定的计算平台,我们不能改变其计算能力,所以如果想要达到目的,那么就需要对我们的模型进行优化,采用一定的技术来降低模型的FLOPs
值。
如何计算模型的FLOPs值
我们以下面的模型为例介绍如何计算模型的FLOPs
值,该模型是一个对MNIST
手写数字数据集进行分类的模型:
-
输入数据为
28x28x1
的灰度图; -
2个卷积层,每个卷积层包含5个大小为
3x3
的卷积核; -
一个全连接层,包含128个神经元;
-
一个全连接层,包含10个神经元,分别代表从0到9范围内的10个类别;
计算模型FLOPs
值的公式如下:
- 卷积层:
FLOPs = 2 * 卷积核数量 * 卷积核大小 * 输出尺寸
其中,输出尺寸=(输入尺寸 - 卷积核大小) + 1
- 全连接层:
FLOPs = 2 * 输入尺寸 * 输出尺寸
根据上述计算方法,可以计算出该分类模型的FLOPs
值,
第1个卷积层的FLOPs值 = 2x5x(3x3)x26x26 = 60,840 FLOPs
第2个卷积层的FLOPs值 = 2x5x(3x3x5)x24x24 = 259,200 FLOPs
第1个全连接层的FLOPs值 = 2x(24x24x5)x128 = 737,280 FLOPs
第2个全连接层的FLOPs值 = 2x128x10 = 2,560 FLOPs
那么整个模型全部的FLOPs
值 = 60,840 + 259,200 + 737,280 + 2,560 = 1,060,400 FLOPs。
假设我们的计算平台的计算能力为1GFLOPS
,那么可以计算在该计算平台上模型推理所需要的时间为:
推理时间 = 1,060,400 / 1,000,000,000 = 0.001s = 1ms
可以看到,如果我们知道计算平台的FLOPS
值和模型的FLOPs
值是很容易计算出模型的推理时间的。
如何优化模型
前面介绍了如何计算模型的推理时间,接下来进入正题,介绍如何通过优化模型来减少模型的推理时间。优化模型的方法主要分为两种类型:
-
减少模型尺寸;
-
减少计算操作;
减少模型尺寸
减少模型尺寸的好处是可以减少占用的存储空间、提升模型加载速度、提升推理速度等。通常我们可以通过以下3种方法减少模型尺寸:
-
量化
-
知识蒸馏
-
权重共享
量化
量化是将数值从较大的集合映射到较小的集合的过程。换句话说,我们通过减少大的连续数,并用较小的连续数甚至整数替换它们来达到量化的目的,比如我们可以将权重值2.87950387409
量化为2.9
。我们可以对权重和激活函数进行量化,量化的结果是改变了数值的精度,最常用的是将32位浮点数(FP32)或16位浮点数(FP16)量化为8位整型数(INT8)甚至4位整型数(INT4)。量化的好处是减少了模型占用的内存量并且降低了计算的复杂度,但是由于降低了模型权重和激活函数的精度,通常会使得量化后模型的推理精度会有所下降。
权重共享
权重共享是指不同神经元之间共享权重,通过共享的方式,我们可以减少一部分权重,从而减少模型的大小。
知识蒸馏
知识蒸馏是一种将规模较大、精度较高的模型(teacher)获得的知识迁移到结构简单、计算成本较低的模型(student)上的一种方法。知识蒸馏可以比较形象地理解为老师教学生的过程:老师将自己学习到的知识进行提炼,然后将这些知识教给学生,学生学到的知识虽然不如老师那么广博,但是也深得其精髓。
减少计算操作
减少计算操作的方法主要是用过一些更高效、计算量更少的操作来代替模型中原有操作。本文介绍3种减少计算操作的方法:
-
池化(Pooling)
-
可分离卷积(Separable Convolutions)
-
模型剪枝(Model Pruning)
池化
如果你对深度学习有所了解,那么一定听过最大池化操作和平均池化操作,这些池化操作都是为了减少计算操作。池化层相当于是一个下采样层,可以减少从一层传递到另一层的参数量。池化层通常用在卷积层之后以保留空间信息,同时减少参数数量。下图是最大池化层的示例,最大池化操作是保留窗口中的最大值而丢掉其他的值,从而达到减少参数的目的。
可分离卷积
可分离卷积是将标准卷积层分为两个卷积层:depthwise
卷积和pointwise
卷积。
-
depthwise
卷积也是普通的卷积,但是与标准卷积的操作方式不同。举个例子,假设卷积层的输入通道为4,输出通道为8,标准卷积是在4个输入通道上同时做卷积操作然后把得到的结果作为一个输出通道,如果要输出8个通道则要进行8次这样的卷积过程;depthwise
卷积则是分别在4个通道上做卷积操作,然后每个通道得到的结果作为一个输出通道,也就是输入是几个通道,输出就是几个通道。在这个例子中,标准卷积生成一个输出通道的计算量是depthwise
卷积生成一个输出通道计算量的4倍。 -
pointwise
卷积是1x1的卷积,通常用在depthwise
卷积之后,用于对depthwise
卷积生成的特征图在深度方向进行信息整合。
用depthwise
卷积+pointwise
卷积的可分离卷积代替标准卷积,可以非常显著地减少计算量,我们来看下图的例子:
在上图中,depthwise
卷积+pointwise
卷积的FLOPs
值大约为734KFLOPs
。让我们再来看使用同样参数的标准卷积层的计算量:
可以看出,使用标准卷积的FLOPs
值大约为20MFLOPs
,是可分离卷积的20多倍。换句话说,在这个例子中,用可分离卷积代替标准卷积可以将推理速度提升20倍之多!
模型剪枝
修剪是一种模型压缩技术,该方法在尽可能地保留网络原始精度的条件下删除冗余的网络参数以达到降低模型计算复杂度的目的。为了实现该技术,我们首先根据每个神经元对整个模型的重要程度进行分级,然后删除级别较低的神经元,为此,我们可以通过将神经元的连接设置为0或将权重设置为0来删除某个神经元。通过模型剪枝方法,我们可以得到规模更小、推理速度更快、推理精度损失最小的模型。
结语
模型优化是一个复杂的问题,正所谓鱼和熊掌不可兼得。虽然我们能够通过一些方法对模型进行优化来提升推理速度,但是这样做不可避免的会带来精度的损失,所以我们必须在速度和精度上做到均衡,选择最适合、最合理的方法对模型进行优化。