解惑丨如何在边缘设备上适配大型神经网络?

对于任何想要创建可扩展服务的人来说,部署有内存限制的深度学习算法都是一种挑战。从长远来看,相比昂贵的云服务,在边缘设备上离线部署模型更为便宜,而且还有其他好处。唯一的缺点是它们缺乏内存和计算能力。

 

本文探索了一些可以用来在内存受限的设备中适配神经网络的技术。因为“训练”和“推理”阶段用到了不同的技术,所以将把它们分开来讨论。

 

训练

 

某些应用程序需要在线学习。也就是说,模型要根据反馈或额外的数据进行改进。在边缘设备部署这样的应用程序会给您的模型带来一个切实的资源约束。这里有4种方法可以减少此类模型的内存消耗。

 

梯度检查点

 

像TensorFlow 这样的框架需要消耗大量的内存用于训练。在前向传播过程中,计算图中的每个节点的值都会被计算并保存在内存中,因为在反向传播时计算梯度时需要用到。

 

 

每个节点的值在前向传播后保存,以便在反向传播中计算梯度。源码:

 

https://github.com/openai/gradient-checkpointing

 

通常情况下,这是可以做到的,但当模型变得越来越复杂时,内存消耗会急剧增加。一个巧妙的回避解决方案是在需要时重新计算节点的值,而不是将它们保存到内存中。

 

 

重新计算节点值用以计算梯度。注意,我们需要分别做若干次前向传播来完成一个反向传播。源码:

 

https://github.com/openai/gradient-checkpointing

 

然而,如上所示,计算成本将显着增加。解决办法是在内存中只保存一些节点,而在需要时重新计算其他节点。这些保存的节点称为“检查点”。这极大地减少了因深层神经网络而积累的内存消耗。如图所示:

 

 

左边的第二个节点是检查点节点。它在减少了内存消耗的同时提供了合理的时间成本。源码:

 

https://github.com/openai/gradient-checkpointing

 

以速度换内存(重计算)

 

根据上面的想法,可以重新计算某些操作用以节省时间。Memory EfficientDenseNet实现就是一个很好的例子。

 

 

DenseNet的一个 dense 块

 

原文:https://arxiv.org/abs/1608.06993

 

DenseNets 具有很高的参数效率,但内存效率也很低。之所以出现这种矛盾,是因为拼接和批标准化操作造成的。

 

为了使卷积在 GPU 上更高效,必须连续地设置这些值。因此,在连接之后,cudNN 在 GPU 上连续地排列这些值。这涉及到大量的冗余内存分配。类似地,正如本文所解释的那样,批标准化涉及到过多的内存分配。这两种操作都会造成内存以二次增长。DenseNets 有大量的拼接和批标准化,因此它们的内存效率很低。

 

 

将原始的拼接和批标准化操作

 

与它们的高效内存实现相比较原文:

 

https://arxiv.org/pdf/1707.06990.pdf

 

上述问题的一个简洁的解决方案涉及两个关键的观察。

 

首先,拼接和批标准化操作不是时间密集型的。因此,我们可以在需要的时候重新计算这些值,而不是保存所有冗余内存。其次,我们可以使用“共享内存空间”来转储输出,而不是为输出分配“新”内存空间。

 

我们可以重写这个共享空间来存储其他拼接操作的输出。也可以在需要的时候重新计算拼接操作来进行梯度计算。同理还可以将它扩展到批标准化操作。这个简单的技巧节省了大量的 GPU 内存,通过增加少量的计算时间来换取。

 

 

减少精度

 

在一篇优秀的博客中,Pete Warden 解释了如何用8位浮点值训练神经网络。由于精度的降低,出现了一些问题,其中一部分如下:

 

如本文所述,“激活值、梯度和参数”有非常不同的范围。一个定点表征并不理想。这篇论文声称,一个“动态的固定点”表征将非常适合于低精度的神经网络。

 

正如 Pete Warden 的另一篇博客所述,较低的精度意味着更大的偏差。通常,如果错误是完全随机的,那幺它们很有可能相互抵消。然而,0被广泛用于 padding、dropout 和ReLU,在低精度浮点格式中,0的精确表示也许是不可能的,因此可能会在性能中引入总体偏差。

 

神经网络的架构工程

 

架构工程涉及到设计准确率、内存和速度最优的神经网络结构。有几种方法可以在空间和时间上优化卷积。

 

将 NxN 的卷积分解成 Nx1 和 1 xN 卷积的组合。这可以节省大量的空间,同时也提高了计算速度。在 Inception 网络的更新版本中使用了这种方案以及其他一些优化技巧。

更详细的讨论,请查看这篇博客:

https://towardsdatascience.com/a-simple-guide-to-the-versions-of-the-inception-network-7fc52b863202

 

在 MobileNet 和 Xception Net 中使用深度可分离的卷积。

关于卷积的类型的详细讨论,请查看这篇博客:

https://towardsdatascience.com/types-of-convolutions-in-deep-learning-717013397f4d

 

使用1×1卷积作为一个瓶颈来减少传入通道的数量。这种技术已经在几种流行的神经网络中使用了。

 

 

谷歌的 AutoML 的描述,视频:

 

https://www.youtube.com/watch?reload=9&v=Y2VF8tmLFHw

 

一个有趣的解决方案是让机器为特定的问题确定最佳的架构。神经架构搜索使用 ML 来为给定的分类问题找到最好的神经网络体系结构。当在ImageNet上使用时,它生成的网络(NASNet)是迄今为止创建的最佳性能模型之一。谷歌的 AutoML 也遵循同样的原则。

 

推理

 

为边缘设备推理拟合模型相对比较容易。本节将介绍可用于优化边缘设备神经网络的技术。

 

去除臃肿

 

像TensorFlow 这样的机器学习框架,消耗了大量的内存空间来创建计算图。这个额外的空间对于加速训练过程是很有用的,但是它不用于推理。因此,专门用于训练的计算图部分可以被删除,我们把这部分称为计算图的“臃肿”。

 

建议将模型检查点转换为冻结的推理图。这个过程会自动删除消耗内存的“臃肿”部分。当转换为一个冻结的推理图时,从模型检查点抛出“资源耗尽的错误”的计算图有时可以被适配进内存中。

 

修剪特征

 

在 Scikit-Learn 上的一些机器学习模型(如随机森林和XGBoost)会输出一个名为特征重要度(feature_importance)的属性。这个属性代表了分类或回归任务的每个特性的重要性。我们可以简单地删除那些最不重要的特征。如果您的模型有大量的特征,而且不能通过任何其他方法来减少,这将非常有用。

 

 

一个特征重要度可视图的例子:

 

Feature Importance and Feature Selection With XGBoost in Python

 

类似地,在神经网络中,许多权重值接近于零。我们可以简单地删除这些连接。然而,删除层间的单个连接会形成稀疏的矩阵。创建高效推理引擎(硬件)方面的工作正在进行,它可以无缝地处理稀疏操作。然而,大多数 ML 框架都是将稀疏矩阵转换成密集的形式,然后再将它们发送到 GPU 上。

 

 

去掉一个无关紧要的过滤器,原文:

 

http://machinethink.net/blog/compressing-deep-neural-nets/

 

相反,可以移除无关紧要的神经元,并稍微对模型进行重新训练。对于 CNNs,也可以删除整个过滤器。研究和实验表明,通过使用这种方法,可以保留大部分的精确度,同时获得大规模的缩小。

 

权重共享

 

为了更好地说明权重的共享,参考一下这篇深度压缩论文:https://arxiv.org/pdf/1510.00149.pdf中给出的例子。一个4×4的权重矩阵。它有16个32位浮点值。我们需要512位(16*32)来表示这个矩阵。

 

我们将权重值量化到4个级别,但是保留它们的32位性质。现在,4×4的权重矩阵只有4个唯一的值。4个唯一的值存储在一个单独的(共享)内存空间中。我们可以分别给这4个唯一值一个2位地址(可能的地址值为0、1、2和3)。

 

 

可以通过使用2位地址来引用权重。因此,这里获得了一个新的4×4矩阵,其中包含2位地址,矩阵中的每个位置都指向共享内存空间中的一个位置。这个方法需要160位(162+432)来表示整个矩阵,得到了3.2的大小减少因子。

 

不用说,这种规模的缩小伴随着时间复杂度的增加。但是,访问共享内存并不会消耗太多的时间。

 

量子化和更低的精度(推理)

 

回想一下,在本篇文章的“训练”部分中提到了精度的降低。对于推理来说,精确度的降低并不像训练过程那幺麻烦。权重可以被转换成更低的精度格式,并发送到推理过程中。但是,可能需要轻微的权重调整来防止精确度的急剧下降。

 

编码

 

修剪和量子化的权重可以通过编码进一步优化。Huffman 编码可以用更少的位表示最频繁的权重值。因此,在比特级别上,一个 Huffman 编码的字符串比普通字符串占用更小的空间。

 

深度压缩探讨了使用无损压缩技术的编码,如 Huffman 。然而,研究也探讨了有损压缩技术的使用。这两种方法的缺点是翻译的开销。

 

推理优化器

 

到目前为止,我们已经讨论了一些很有建设性的想法,但是若从头开始实现它们需要相当长的时间。这就是推理优化器发挥作用的地方。例如,Nvidia 的TensorRT 包含了所有以上这些想法。并提供了一个经过训练的神经网络的“优化推理引擎”。

 

 

TensorRT:

 

https://developer.nvidia.com/tensorrt

 

此外,TensorRT 可以优化模型,使其能够更好地利用 Nvidia 的硬件。下面是一个使用TensorRT 优化的模型更有效地利用了 Nvidia 的 V100 的例子。

 

 

在 Nvidia 的 V100 上使用TensorRT 优化的模型:

 

TensorRT 3: Faster TensorFlow Inference and Volta Support

 

知识蒸馏

 

我们可以“教”较小的模型来模拟更健壮、更大的模型,而无需花哨的优化技术。这项技术被称为“知识蒸馏”,它是谷歌“ Learn2Compress ”的重要组成部分。

 

 

Teacher-Student 模型:

 

https://ai.googleblog.com/2018/05/custom-on-device-ml-models.html

 

通过使用这种方法,我们可以强制使用更小的模型,这些模型可以适配在边缘设备上,以达到大型模型的性能水平。据统计,精确度的下降是最小的。

 

更多信息可以参考Hinton的论文:

 

https://arxiv.org/pdf/1503.02531.pdf

原文作者:Bharath Raj,译者:TalkingData 赵磊,原文地址:http://t.cn/Rkofy1e。

发表评论

电子邮件地址不会被公开。 必填项已用*标注