本文来自社区投稿与征集,作者王玉成,ML&IoT Google Developers Expert,温州大学智能锁具研究院总工程师。
了解更多:https://blog.csdn.net/wfing
经过前面几章的讨论,我们了解了 TinyML 的概念,完成了一个最简单的 TinyML 的模型并且运行在微控制器上,得出最基本的结果。但是在实际的工程应用中,模型建立之后,也需要再进一步结合许多优化方面的技术,这就是我们接下来想要探讨的。
5. 优化
我们先不要急于去看这本书到底介绍了哪些优化方法,我们仔细回顾一下,在之前的项目中,我们接触的工程中包括了优化的哪些方面?
由于低功耗微控制器本身的存储容量特别小,不断减小模型的尺寸,也是优化的一个方向。加入了这些限制条件之后,就不得不从系统限制,内存,存储,运行效率等各个方面去评价这个模型是不是最优化的。类似场景的模型优化的评价方法是一直需要不断探讨的。没有最好,只有更好。
通过上面的要素分析,我们会发现工程的优化并不仅仅只是提供一个运行效率最快的模型,还涉及到许多许多其它方面的考虑。那么我们这一次的阅读会重点关注是低功耗的微控制器上如何去做优化。
由于整本书优化的方向比较多,我们就分两个大的部分来讨论优化的具体内容,模型优化主要讲述如何确定产品需求,到最后实现整个模型优化所采取的一些主要方法;工程优化主要讨论当模型导入到低功耗微控制器之后如何去运行所考虑的一些问题。
产品设计方面的优化从严格意义上来说,并不是技术优化的范围,但是他却直接影响着整个工程运行效率的高低。这一部分内容没办法用特别好的技术方法去做模型上的评估,但是在不同产品的工程需求捕获阶段,确确实实存在很大的差异性。
数据:这儿所理解的数据是一些传感器的数据上报,由于传感器的功耗已经做得非常非常低了,所以优化方向仍然与采样频率相关,如何在合适的时间使模型推断运行的最好?
这样看来,如果是我们需要主导一款低功耗嵌入式人工智能产品的设计研发,需要考虑到微控制器的性能、数据采集终端、采样时间及采样频率的限制。并且从以上的限制条件中,再去定制人工智能模型。
以往的人工智能模型设计我们会用最多的资源,最好的环境去研发出最优的模型。但是基于低功耗的微控制器模型的运行环境,并不再强调模型一定是最好的,而在于运行的速度和效率的一个折中。只要最后的推理结果在我们认可的范围内就可以了。
当我们把模型调整优化好了之后,需要对 ML 模型的延迟进行评估。由于低功耗微控制器上面没有浮点运算单元的硬件支撑,那么,评估 ML 模型中浮点操作的次数,用量化的方法去减少浮点运算的执行时间,最终在微控制器上运行量化过的 ML 模型进行推理,成为一个比较好的方法。由于现在神经网络中的大部分计算是矩阵的乘法运算,那么可以用一次推理运行所需的浮点操作 (或 FLOP) 的数量来近似估算。例如,完全连接的层需要的 FLOP 数量等于输入向量的大小,再乘以输出向量的大小。我们也可以从模型架构讨论的论文中间去估计 FLOP 的值。通过这样的方法,我们就可以完全估计出浮点运算,或者量化之后运算所产生的时间损耗。然后再去优化最耗时的代码。获得更低的延迟。
最近几年,对于 TensorFlow 在嵌入式处理器上的应用,已经有很多的优化工作的成果。TensorFlow Lite 针对 TensorFlow 的优化大概包括以下几个方面的内容:量化、FlatBuffer、移动优化解释器和硬件加速。对于 TinyML 来说,主要以软件方式的优化为主。
我们以量化为例,量化的核心在于利用精度的损失来换取运行速度,那么如何平衡精度与效率之间的关系,就成为量化优化中最核心的问题。由于量化的过程是在整个模型已经建立完成之后去换算的,对于权重来说,可以用正确的缩放系数来进行转换。但是对于激活来说比较棘手,因为通过模型参数和连接层检查,并不清楚每层输出的实际范围是什么。如果选择的范围太小,输出将被裁剪为最小值或最大值,但是如果选择的范围太大,则输出的精度将小于其可能的精度,并且冒着使总体结果失去准确性的风险。现在转换器中提供了一个 representative_dataset() 函数,这是一个 Python 函数,可产生激活范围估计过程所需的输入,能够得到更好的优化。
针对于不同的硬件,甚至有时候我还会专门去看硬件指令所能带来的一些优化方法。这种方法虽没有通用性,但是对于单个产品的模型加速仍然比较重要。但麻烦的问题在于:既需要了解 CPU 是否有特殊功能的支持,还需要了解编译器是否对这些指令做了专门的优化。甚至有可能出现最难以预料的方式,加入内联汇编去支持,这已经超出常规的优化范围了。
从这一类方法可以看出,基于微控制器的优化更强调硬件个体有没有优化的空间。
硬件选择性问题,尽量的去选择 TinyML 支持的硬件,这样硬件驱动会有一个完善的支持,而且对开发版的优化已经做的比较充足了。
低功耗的微控制器通常使用的是电池作为电源。休眠是一个重要的因素,合理的设计会极大的延长电池使用时间。我们可以用硬件占空比来设计休眠、唤醒时间。传感器采样频率、时间、功耗等因素,在产品设计优化部分已经阐述,同时也需要做许多折中的选择,但是他们同时也是硬件优化的范围。
级联设计是另一个比较好的考虑角度。与传统的过程编程相比,机器学习的一大优势在于,它可以轻松扩展或缩减所需的计算和存储资源数量,并且准确性通常会适度降低。这意味着可以创建模型级联。比如说我们有一个多级的模型,当第 1 级的硬件计算能力达不到我们的要求时,我们会主动启动第 2 级开销,这样的话我们可以运用更短的时间得到精确的结果。
无论选择哪种平台,闪存 (Flash) 和内存 (RAM) 都可能非常有限。大多数基于微控制器的系统在闪存中只有不到 1 MB 的只读存储,最小的几十 KB。RAM 也是如此:很少有超过 512 KB 的静态 RAM (SRAM)。针对微控制器的 TinyML 可以低于 20 KB 的闪存和 4 KB 的 SRAM 进行工作,但是您将需要仔细设计应用程序并进行工程设计,以保持较低的占用空间。
大多数嵌入式系统都具有一种体系结构,其中程序和其他只读数据存储在 Flash 中,仅在加载新的可执行文件时才将其写入,作为可以读写的内存使用。此技术与大型 CPU 上的缓存使用的技术相同,它可以快速访问以降低功耗,但尺寸有限。更高级的微控制器可以使用耗电较大但可扩展的技术,例如动态 RAM(DRAM) 来提供第二层可读写的内存。那么,外设读写操作次数的多少,直接影响到运行效率。
Flash 的使用率也是一个考虑的因素。分析 ELF 文件中各段的使用效率,以及无用代码的优化,预编译优化,将会节省大量的 Flash 空间。
RAM 优化,主要包括程序在编译时以及运行时的一些考虑。ML 框架的核心运行时应用并不需要大的内存,并且其数据结构在 SRAM 中的空间不应超过几千字节。这些是作为解释器使用的类的一部分进行分配的,因此,无论您的应用程序代码将它们创建为全局对象还是本地对象,都将决定它们是在堆栈中还是在通用内存中。我们通常建议将它们创建为全局或静态对象,因为空间不足通常会在链接时引起错误,而堆栈分配的本地变量可能会导致运行时崩溃。所以程序编译之前,堆栈的设计,以及程序的内存驻留空间,需要有一个比较好的设计方案。
二进制大小的优化。TensorFlow Lite 占用了多少空间是空间优化的一个重要内容。测最简单方法是注释掉对框架的所有调用(包括创建 OpResolvers 和解释器之类的对象),然后查看二进制文件变小了多少。如果没有看到类似的内容,则应再次检查是否已捕获所有引用,有没有不必要的引用。由于链接器会删除永远不会调用的所有代码,并将其从封装中删除。降低代码的耦合度,规范的编程,会影响到最终生成的二进制文件的大小。
这一部分的讨论涉及到了硬件的选型问题,硬件上的各种参数的限制问题。从编译方向出发,特别是从编译阶段,链接阶段以及代码运行阶段可以优化的一些方向。这一部分优化的过程就是量身打造利器的过程。
到此为止,这一系列的文章,都已经完全分享完了。我们从这一系列文章中,了解了 TinyML 的作用,并参照项目示例中的源码,实现了一个基于 TinyML 的项目工程,最后,通过剖析项目开发的流程以及关注点,学习了工程优化方法。但是,TinyML 这本书介绍的内容远不止于此,书中还包含一些更复杂的示例以及剖析,包括一些优化方法的原理性解释,如果对于这个方向感兴趣,那就赶快买这本书来阅读吧。
🌟将我们设为星标
第一时间收到更新提醒
不再错过精彩内容!
分享 💬 点赞 👍 在看 ❤️
以“三连”行动支持优质内容!