NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

应对未来物联网大潮:如何在内存有限的情况下部署深度学习?

  • 2018-11-22
  • 本文字数:4659 字

    阅读完需:约 15 分钟

应对未来物联网大潮:如何在内存有限的情况下部署深度学习?

对于任何想要构建大型服务的人来说,部署需要大量内存的深度学习算法都是一项挑战。从长期使用的角度来说,云服务过于昂贵。相比之下,在边缘设备上离线部署模型更为便宜,并且还有许多其他好处。这种做法唯一的缺点就是,边缘设备缺乏内存和计算能力。


本文探讨了一些可用于在内存受限的配置下部署神经网络的技术。由于训练和推理阶段所使用的技术不同,这两部分将分开来讲。

训练阶段

某些应用必须要在线学习。也就是说,模型性能的提高是依赖于反馈或附加数据的。在边缘设备上部署此类应用程序会对模型造成切实的资源限制。以下 4 种方法可以减少此类模型的内存消耗。

1. 设置梯度检查点

TensorFlow 这类框架的训练需要消耗大量内存。在前向传递期间,计算图中每个节点的值都需要被计算并保存在内存中。这是在反向传播期间计算梯度所必需的。



图:节点中的每个值都要被保存下来,用以在一次反向传播中计算梯度。(来源:https://github.com/openai/gradient-checkpointing)


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



图:重新计算节点的值用以计算梯度。注意,我们需要好几次分步的前向传递,才能计算一次反向传播。(来源:https://github.com/openai/gradient-checkpointing)


然而,从上图可以看出,这样做会使计算成本显著增加。一个比较好的权衡是在内存中只保存一些节点,同时在需要时重新计算其他节点。这些保存的节点称为检查点。这大大减少了深度神经网络内存消耗。如下图所示:



图:左数第二个节点为检查点。这种做法可以减少内存消耗,为此增加的计算时间也在接受范围之内。(来源:https://github.com/openai/gradient-checkpointing)

2.牺牲速度换取内存(重计算)

将上述想法进一步扩展,我们可以重新计算某些操作以节省内存的消耗。内存高效的 DenseNet 的实现就是一个很好的例子。



图:DenseNet 中的一个稠密块。(来源:https://arxiv.org/abs/1608.06993)


DenseNet 中的参数效率很高,但其内存效率却很低。产生这种矛盾的原因是 DenseNet 的拼接结构及 batchnorm 本身的性质。


想要在 GPU 上实现高效卷积,数值必须连续放置。因此,在拼接操作之后,cuDNN 会将值在 GPU 上连续排列。这会产生大量的冗余内存分配。同样,batchnorm 也会产生过多的内存分配,如论文中所述。这两种操作都会导致内存呈平方级增长。DenseNet 结构中含有大量的拼接操作和 batchnorm,因此其内存效率十分低下。



图:直接的拼接和 batchnorm 操作及其高效内存实现的对比。(来源:https://arxiv.org/pdf/1707.06990.pdf)


针对上述问题,有一个简洁的解决方案。该方案基于两个关键的现象。


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


这个共享空间可以被覆盖,用来存储其他拼接操作的输出。我们可以在需要时对拼接操作进行重新计算,用以计算梯度。同样,我们可以将这种做法扩展到 batchnorm 操作。这个简单的技巧可以节省大量的 GPU 内存,而计算时间不会增加太多。


3.牺牲浮点数精度

在一篇优秀的博客(链接:https://petewarden.com/2015/05/23/why-are-eight-bits-enough-for-deep-neural-networks/)中,Pete Warden 解释了如何使用 8 比特浮点数训练神经网络。但由于浮点数精度的降低,会产生许多问题,其中一些问题如下:


  • 如这篇论文中所述(链接:https://arxiv.org/pdf/1412.7024.pdf) ,“激活函数、梯度和参数”的取值范围大不相同。用一个固定点来表示的方式不会很理想。论文表示,可以设置一个“动态固定点”表示,这对于低浮点数精度的神经网络会很有效。

  • Pete Warden 在其另一个博客(链接:https://petewarden.com/2017/06/22/what-ive-learned-about-neural-network-quantization/) 中讲到,浮点数精度越低意味着预测值距正确值的偏差越大。一般来讲,如果错误完全随机,那么他们相互之间很可能会抵消。然而,在 padding,dropout 和 ReLU 操作中,0 这个值被广泛采用。而如果采用低浮点数精度格式,0 这个值很难被精确地表示,因此会带偏整个网络的性能。

4.调整网络架构

调整网络架构也就是设计一个新的神经网络结构,该结构需要做到优化精度、内存和速度。下面讲述几种可以在空间或时间上优化卷积操作的方法。




图:谷歌自动化机器学习 Google AutoML 示意图。(来源:https://www.youtube.com/watch?v=Y2VF8tmLFHw)


关于这一问题,还有一个非常有趣的解决方案,即针对具体问题让机器自己决定哪个架构最好。网络结构搜索技术(链接:https://arxiv.org/pdf/1707.07012.pdf)利用机器学习为给定的分类问题寻找最好的神经网络结构。在ImageNet上,使用该技术生成的网络(NASNet)是目前性能最好的。谷歌的自动化机器学习(链接:https://ai.googleblog.com/2017/11/automl-for-large-scale-image.html)就是基于这一理论构建的。

推理阶段

在推理阶段将模型部署到边缘设备相对来说比较简单。本节介绍了几种技术,可以为这种边缘设备优化你的神经网络。

1.去掉“冗余部件”

TensorFlow 这类机器学习框架往往需要消耗巨大的存储空间用于创建计算图。这些额外的空间消耗可以加速训练,但在推理阶段却没什么用。因此,专门用于训练的这部分图可以直接被剪掉。我们可以将这部分图称为“冗余部件”。


对于 TensorFlow,建议将模型的检查点转换为冻结的推理图。这一过程会自动删除占内存的冗余部件。直接使用模型检查点的图时报出的资源耗尽错误有时可以通过这种转换为冻结推理图的方式解决。

2.特征剪枝

Scikit-Learn 上的一些机器学习模型(如随机森林和 XGBoost)会输出名为 feature_importances_的属性。这一属性代表了在当前分类或回归问题中每个特征的显著性。我们可以直接将不显著的特征剪枝掉。如果你的模型中的特征非常非常多,又难以通过其他方法降低特征量,那么这种方法会非常有效。



图:特征重要性图示例。(来源:https://machinelearningmastery.com/feature-importance-and-feature-selection-with-xgboost-in-python/)


相似地,在神经网络中,很多权重的值很接近于 0。我们可以直接剪去那些权重趋于 0 的连接。然而,移除层间独立的连接可能会产生稀疏矩阵。不过,已经出现了高效推理器(硬件)的相关工作(链接:https://arxiv.org/pdf/1602.01528.pdf),可以无缝处理稀疏矩阵问题。然而,大部分的机器学习框架还是将稀疏矩阵转换为稠密格式,然后传输到GPU上。



图:删除不显著的卷积核。(来源:http://machinethink.net/blog/compressing-deep-neural-nets/)


另一种做法是,我们可以移除不显著的神经元,并对模型稍加重新训练。对于 CNN 来说,我们也可以移除整个卷积核。研究(链接:https://arxiv.org/pdf/1510.00149.pdf)和实验(链接:http://machinethink.net/blog/compressing-deep-neural-nets/)表明,使用这种方法,我们可以做到在最大程度上保持精度,同时减少模型的大部分空间占用。

3.共享权重

为更好地解释权重共享,我们可以考虑一篇关于深度压缩(链接:https://arxiv.org/pdf/1510.00149.pdf)的论文中的例子。考虑一个4x4的权重矩阵。它含有16个32比特浮点值。


我们可以将权重值量化为 4 个层,但保留其 32 比特的属性。现在,这个 4x4 的权重矩阵只有 4 个值了。这 4 个不同值被保存到一个独立的(共享的)内存空间中。我们可以分别给这 4 个不同值赋予一个 2 比特的地址(比如 0,1,2,3)。



图:权重共享示意。(来源:https://arxiv.org/pdf/1510.00149.pdf)


我们可以使用 2 比特地址来索引权重值。因此,我们获得了一个由 2 比特地址表示的新的 4x4 矩阵,矩阵中的每个位置代表其在共享存储空间中的位置。使用该方法表示整个矩阵仅需要 160 比特(16 * 2 + 4 * 32)。这样,我们获得了 3.2 的尺寸缩减系数。


当然,这种尺寸的缩减所伴随的将是时间复杂度的增加。但是,访问共享内存的时间不会成为非常严重的时间损失。

4.量子化和降低浮点数精度(推理阶段)

回想一下我们在前文中训练部分讲到的降低浮点数精度的方法。对于推理阶段,降低浮点数精度不会像训练阶段那样麻烦。权重可以直接被转换为低精度格式(详见https://heartbeat.fritz.ai/8-bit-quantization-and-tensorflow-lite-speeding-up-mobile-inference-with-low-precision-a882dfcafbbd),并直接用于推理。不过,如果想要将精度降低很多,那么权重需要稍作调整。

5.编码

通过使用编码,可以进一步对修剪和量化过的权重的空间占用进行优化。霍夫曼编码可以用较低的位数表示使用最频繁的权重值。因此,在比特级别,经过霍夫曼编码的字符串比正常的字符串的空间占用更少。


深度压缩使用的是无损压缩技术(如霍夫曼算法)进行压缩。而也有研究使用了有损压缩技术(详见https://arxiv.org/abs/1711.04686)。这两种方法都有一个缺点,即翻译过程的开销过大。

6.推理优化器

到目前为止,我们已经讨论了一些很棒的想法,但是从头开始实施它们需要相当长的时间。在这里,推理优化器就有了用武之地。例如,Nvidia 的 TensorRT 结合了所有这些伟大的想法(甚至还包括更多),并在训练好的神经网络的基础上提供了优化的推理引擎。



图:TensorRT。(来源:https://developer.nvidia.com/tensorrt)


此外,TensorRT 还可以优化模型,使其更好地利用 Nvidia 的硬件。下面是一个示例,其中使用 TensorRT 优化过的模型能更有效地兼容 Nvidia V100。



图:在 Nvidia V100 上使用 TensorRT 优化过的模型。(链接:https://devblogs.nvidia.com/tensorrt-3-faster-tensorflow-inference/)

7.知识蒸馏

我们可以教小型模型来模仿强大的大型模型的性能,而不是执行花哨的优化技术。这种技术叫做为知识蒸馏,它被集成在 Google Learn2Compress 中。



图:教师-学生模型。(来源:https://ai.googleblog.com/2018/05/custom-on-device-ml-models.html)


通过使用此方法,我们可以强行使边缘设备上的较小模型达到较大模型的性能水平。研究表明,使用此方法所造成的精度下降很小。如果想要详细了解此技术,请参考 Hinton 的论文(链接:https://arxiv.org/pdf/1503.02531.pdf)。


英文原文链接:https://heartbeat.fritz.ai/how-to-fit-large-neural-networks-on-the-edge-eb621cdbb33?mkt_tok=eyJpIjoiTmpVMFpURTBOakV6TXpOaiIsInQiOiJjRE5YUmJ3dmlFcEpRMUZ0bFlNUmJjRmtsWUxZRGowVkJlSnZhNUJvUEhmeGs0bWhnV1JkNnh6XC9zRU9ja3BKeWowUjlNWjFaSDhQWlNwTHgxYm9IY1BqWEh0V1lrMGxKYjlQMDZId25aRWZyZ0Z3R1ZpYjhISFhlVVcwOWh3ODUifQ%3D%3D


公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2018-11-22 12:022299

评论 1 条评论

发布
暂无评论
发现更多内容

Linux安装Vue环境

代码的路

Linux Vue

Spring+Vue工程部署在Linux

代码的路

Java spring Linux Vue

通过TiDB Operator升级TiDB集群

TiDB 社区干货传送门

集群管理 管理与运维 故障排查/诊断 安装 & 部署 扩/缩容

【堡垒机小知识】堡垒机属于安全设备吗?为什么?有什么作用?

行云管家

堡垒机 自动化运维 安全设备

研讨会回顾 | UI自动化测试现场演示

龙智—DevSecOps解决方案

软件测试 自动化测试 测试软件

实测 | Apipost和Apifox哪个更好用?

不想敲代码

接口调试 接口管理工具 API接口工具

【Unity渲染】一文看懂!Unity通用渲染管线URP介绍

3DCAT实时渲染

Unity 渲染 实时云渲染 渲染服务 Unity3D

TiDB 生产集群与加密通讯TLS的辛酸苦辣 - 工具篇

TiDB 社区干货传送门

集群管理 管理与运维 备份 & 恢复

聊聊 SpringMVC 是如何工作的?

风铃架构日知录

程序员 springmvc IT springboot

TiDB Operator升级

TiDB 社区干货传送门

实践案例 集群管理 管理与运维 安装 & 部署

4个MySQL 数据同步 Elasticsearch 的方案!

风铃架构日知录

Java MySQL elasticsearch IT 数据同步

面试官:什么是自旋锁?自旋的好处和后果是什么呢?

风铃架构日知录

Java 程序员 后端 自旋锁 循环

岁末年初再添佳誉丨Kyligence 荣获多个奖项及榜单认可

Kyligence

数据分析 多维数据库

华为云云筑·开发者年度盛典精彩回顾

华为云开发者联盟

云计算 开发者 华为云 数字人 企业号 1 月 PK 榜

卷扩容业务失败了,在线等…

华为云开发者联盟

开发 华为云 企业号 1 月 PK 榜 卷扩容 磁盘扩容

现代数据平台要实现自助用数,要解决的三个问题

华为云开发者联盟

大数据 后端 华为云 企业号 1 月 PK 榜

GoLang的安装和使用

代码的路

golang

选择LED显示屏电源的7个指南

Dylan

电源电路 LED显示屏 led显示屏厂家

Hackathon特别策划 | 72小时灵感冲刺,创意就该这么玩

LigaAI

敏捷开发 研发管理 hackathon 黑客马拉松 企业号 1 月 PK 榜

【社区智慧合集】TiDB 相关 SQL 脚本大全

TiDB 社区干货传送门

Inspur KOS 龙蜥衍生版面向智慧新媒体转型的探索与实践 | 龙蜥案例

OpenAnolis小助手

龙蜥社区 CentOS迁移 浪潮信息 KOS 服务器操作系统

阿里云化身“智能云管”,助力中国联通首次实现大规模平台自主运维

云布道师

阿里云

软件测试/测试开发 | 单元测试体系集成

测试人

软件测试 单元测试 自动化测试 JUnit 测试开发

2023年主流知名堡垒机重点推荐

行云管家

网络安全 堡垒机

为什么建议SQL初学者尽量选择Web版SQL工具?

雨果

sql 数据库管理工具 SQL开发

代码质量与安全 | 展望:2023年商业软件开发的五大关键目标

龙智—DevSecOps解决方案

静态代码分析

TiCDC 集群工作过程解析

TiDB 社区干货传送门

OpenMLDB v0.7.0 发布

第四范式开发者社区

人工智能 机器学习 开源 特征 数据库·

openEuler资源利用率提升之道 05:虚机混部介绍与功耗管理技术

openEuler

数据库 Linux 操作系统 openEuler OpenStack

TiDB Operator高可用配置

TiDB 社区干货传送门

集群管理 管理与运维 安装 & 部署

【从零开始学爬虫】采集丁香医生新冠问答数据

前嗅大数据

数据采集 爬虫教程 爬虫案例 爬虫工具 爬虫技术

应对未来物联网大潮:如何在内存有限的情况下部署深度学习?_AI&大模型_Bharath Raj_InfoQ精选文章