完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
工程流程一般都是基于以上的步骤进行部署的。在下一章中,我们将用学习任何语言的经典入门 “Hello World”,来阐述基于项目的 TinyML 工程开发流程。
Hello World — 梦开始的地方(上) Hello World 是每一个程序员进入程序世界,学说的第一句话。它的意义并不在于可以以一种特殊的方式去输入一串字符,而在于用一句简单的话,去了解最基本的流程,为自己的新世界打开一扇大门。 我们将 “Hello World — 梦开始的地方”分为上、中、下,三个部分来讲述如何去构建一个完整的、可以运行的、基于 TensorFlow Micro 的工程系统。其中上篇是手把手教开发者如何去建立并且训练模型,中篇主要讲解如何创建工程应用,下篇主要是讲解如何把工程部署到微控制器上。 事不宜迟,我们赶紧进入上篇吧。 1. 环境准备 项目在三个不同的开发板上都可以正常运行,我们以Arduino Nano 33 BLE Sense 为硬件,来实现基于 TensorFlow 的 ML 项目。 2. 项目流程 实现基于微控制器的 ML 项目开发流程如下所示: 1. 获得简单数据集 2. 训练深度学习模型 3. 评估模型性能 4. 转换成设备上运行的模型 5. 将代码转换成二进制文件 6. 部署二进制文件到微控制器 文章中所有的代码都基于 TensorFlow Micro 上的代码。当然,代码中也包括许多注释,我们会一一的分析代码中的最关键的部分以及为何要这么实现。 TensorFlow Micro 3. 准备前的工作 我们分享的 Hello World 示例,是用数学中的最基本的sine函数为原型,用 ML 的方式去预测数据。关于 sine 函数,我们在初中学三角函数时就接触过。这个函数在工业应用中非常广泛。一般的函数图形如图所示: 我们的目标是,如果有一个 x 值,我们能够预测出 x 的 sin 值 y。在真实的环境中,用数学计算的方法可以更快速的得到结果。这个例子是用 ML 的方法去实现预测,从而了解 ML 的整个流程。 整从数学角度来看,sine 函数能够在 -1 到 1 之间,周期性的平滑波动。我们可以用这种平滑的值,来控制 LED 灯光的亮度。 利用 Arduino 开发板运行这个项目的效果如图所示 工具准备: 配置开发环境,所用的编程语言当然是当之无愧的 Python,这是现在使用最广泛的,运用于科学、数学、以及 AI 领域的编程语言。版本为 3.x Python 可以在命令行下运行,但是还是推荐用 Jupyter Notebook 来开发,它的好处是可以把代码、文档、还有图片放在一起,既能当教程,又能分步运行。 如果有条件上 Google Colab,这个编译环境也是一个很好的选择。Colab 一直是由 Google 开发并维护的平台,并且在云平台上,已经安装了各种依赖软件。并且还可以免费去用 Google TPU 的服务。通过 Web 浏览器,可以运行自己编写的任何代码。甚至可以用 Colab 的配置文件,来选择加速硬件,从而加速模型训练。 ML 平台的选择,毫无疑问,选择 TensorFlow。因为 TensorFlow 是现在使用最广泛的AI加速平台,并且有成熟的 pip 安装包可以用。当然,在接下来的应用中,我们需要用 TensorFlow Lite,能得模型在嵌入式硬件上运行,我们也会用 TensorFlow 的高阶 API,Keras 来完成一些编程工作。 4. 创建模型 项目相关代码: 当然,如果在 Google Colab 上运行项目,可以直接运行。 我们就在本地以 Jupter notebook 的方式,运行我们的代码。我是以 git clone 的方式下载了 TensorFlow 的所有源码,从本地的 Linux 命令行进行操作。由于我运行环境的 Linux 系统,已经安装了所有的依赖软件,所以在 Jupter 的环境中,涉及到 Linux 中相关的 pip 安装和 apt 安装的代码都注释掉了。 首先运行 Jupyter: 启动之后弹出 chrome 浏览器,内容如图所示: 其中需要注意的是,在右上角的区域,如果提示不受信任,点击“不受信任”的文字,按提示操作,最终文字变成“信任”。如果说右上角区域 Python 版本为 2,那么需要在 Jupyter 的菜单栏中,Kernel Change Kernel 中选择 Python 3。 当然,代码的第一步是导入 TensorFlow、Numpy、Matplotlib 以及Math 库。其中 Numpy 用于数据处理。Matplotlib 用于数据的可视化。 其实在代码中,我注释掉了 #!pip install tensorflow==2.0.0-beta0 这一行,原因是因为我的 PC 机已经安装完了 TensorFlow 的最新版。 5. 产生数据 首先基于 sin 函数产生一系列的标准化数据。 可以看出,运行之后,数据相当规范。作者为了让大家细致入微的去理解代码,对这一段代码做了一些解释说明。当然,如果是要处理数据的话,Numpy 是一个最优的数学处理函数库。x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES), 用来产生特定范围内的一系列随机数。 当原始数据产生之后,我们要接着做的事情,便是数据清洗。我们要确保数据以真正的随机的方式反馈。很幸运,Numpy 的radom.shuffle()提供了这样的方法。 最后再把数据以二维坐标的方式画在图片上。 这么漂亮的数据,直接去训练可好?当然没有问题,但是,ML 做的是什么样的事情?从各种噪音中筛选数据,经过训练,最后预测的准确率越来越高。第一部操作,我们需要加入一些噪音进来。这也是机器学习中常用的方法。当样本的容易不够时,我们如何增加样本的数量,以便训练时准确率更高。 我们就完全创建的所有的数据。 接下来便是分成训练集和测试集: 就这一部分的关键代码详细解释一下: TRAIN_SPLIT = int(0.6 * SAMPLES) TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT) 整个数据集包括三个部分,训练集,测试集,验证集。示例中用了 60% 的数据当训练集,20% 的数据当测试集,20% 的数据当验证集。这个比例不是恒定的,可以按需求调整。 x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT]),虽然输入参数中只包括了训练的测试的部分,但是由于整个数据集分割了三部分,所以 np.split 的返回结果是三个,代码我们的三种场景。 6. 定义基本模型 我们用 Keras 来构建最开始的模型。 第一层采用标量输入,并且基于 “relu” 激活,用了 16 个神经元的密集层 (Dense Layer,也可叫做全连接层)。当我们进行预测时,它是推理过程中的神经元之一。每个神经元将然后被激活到一定程度。每个神经元的激活量是基于在训练过程中获得的 weight 和 bias 值来定义激活功能。神经元的激活将作为数字输出。激活是通过一个简单的公式来计算的,如 Python 中所示。我们将永远不需要自己编写此代码,因为它由 Keras 和 TensorFlow 处理, 在深入学习时但会有所帮助,计算公式的伪代码如下所示: activation = activation_function((input * weight) + bias) 要计算神经元的激活程度,需要将其输入乘以权重和偏差被添加到结果中。计算出的值被传递到激活函数中。结果就是神经元的激活。激活函数是一种数学函数,用于塑造神经元的输出。在我们的网络中,我们使用的是称为整流线性单元 (Rectified Linear Unit) 的激活函数,或简称为 ReLU。这在 Keras 中由参数 activation = relu 指定。ReLU 是一个简单的函数,如 Python 所示: def relu(input): return max(0.0, input) ReLU 返回较大的值:如果其输入值为负,ReLU 返回零。如果其输入值大于零,则输出保持不变。 用 ReLU 做为激活函数意义在哪儿? 没有激活函数,神经元的输出将始终是线性函数。这意味着网络只能建模线性关系,其中 x 和 y 之比在整个值范围内保持不变。但是正弦波又是非线性的,这将阻止网络对我们的正弦波进行建模。由于 ReLU 是非线性的,因此它允许多层神经元联合作用并建立模型复杂的非线性关系,每次x增量不会使y值以相同的方式增加。还有其他激活功能,但是ReLU是最常用的功能。作为ML算法,运用最优的激活函数是必要的。 我们再对输入、输出层做一些解读: 由于输出层是单个神经元,它将接收 16 个输入。由于这是我们的输出层,因此我们不指定确定激活功能-我们只需要原始结果。由于此神经元有多个输入,因此每个神经元都有一个对应的权重值。神经元的输出通过以下公式计算得出,如 Python 中所示:其中 “inputs” 和 “weights” 都是 NumPy 数组,每个数组有 16 个元素 output = sum((inputs * weights)) + bias 通过将每个输入与其对应的乘积获得输出值 weights,对结果求和,然后加上神经元的 bias。该网络的 weights 和 bias 是在培训期间学习的。 接下来,编译阶段的关键点,便是优化器的损失函数了。 model_1.compile(optimizer=‘rmsprop’, loss=‘mse’, metrics=[‘mae’]) 其中优化器,损失函数以及指标,都有许多种选择,我们不展开详述。您可以去以下链接去做更多的了解。 https://keras.io/optimizers/ https://keras.io/losses/ 最后我们再来看看模型概况: 其中输入层有 16 个神经元,共 2 层连接,所以全部的连接数为 16x2=32,每一个神经元都有一个 bias,网络总共有 17 个 bias。输入的 16,以及输出的 1。所以总的参数为 32+17=49。 7. 训练模型 利用 keras 的 fit() 方法能够很好的训练。下面就一些参数做一下最基本的解释: X_train, y_train 表示最基本的训练数据。 epochs 训练的周期,一般来说,周期越长,训练越精确,但是,一般来说,训练的时间到一定阶段,训练精度不会有很大差别。在这种清况下,一般要考虑去优化模型了。 batch_size 用于往网络中一次送入多少数据,如果值为 1,我们每一次会更新 weight 和 bias,并且会估计网络预测的损失,为下一次的运行做更精确的估计。越小的值,会带来很大的计算量,占用更多的计算资源。如果我们把值定要 600,一次性可以计算出更多的数据,但是会降低模型的精度。所以最好的方式是把值设置为 16 或者是 32。这个值的选择,实际上精度与时间花费权衡的结果。 接下来,我们最关心的,当然是训练的结果了。 该图显示了每个时期的损失(或模型的预测与实际数据之间的差异)。有几种计算损失的方法,我们使用的方法是均方误差。对于训练和验证数据有明显的损失值。 我们可以看到,损失的数量在前 25 个时期迅速减少,然后趋于平稳。这意味着该模型正在改进并产生更准确的预测! 我们的目标是在模型不再改善或训练损失小于验证损失 (Validation Loss) 时停止训练,这意味着该模型已经学会了很好地预测训练数据,也不需要新的数据来提高精度。 为了使图表的平坦部分更具可读性,我们用代码 SKIP = 50 跳过前 50 个 epochs,这仅仅是便于我们看图方便。 从图中可以分析出,大概 epochs 到了 600 左右,训练开始趋于稳定。意味着我们的 epochs 应该不需要超过 600。 但是,我们还可以看到最低的损失值仍在 0.155 左右。这意味着我们网络的预测平均降低了约 15%。另外,验证损失值 (Validation loss) 产生的很大的跳跃,并不稳定。我们需要改进方法。这次,我们将绘制平均绝对误差 (Mean Absolute Error)图,这是另一种衡量网络预测与实际数字的距离的方法: 从平均绝对误差图可以看到,训练数据显示出的错误始终比验证数据低,这意味着网络可能存在过拟合 (Overfit),或者过分地学习了训练数据,从而无法对新数据做出有效的预测。 此外,平均绝对误差值非常高,最多约为 0.305,这意味着该模型的某些预测至少可降低 30%。30% 的误差意味着我们离精确建模正弦波函数还很遥远。 该图清楚地表明,我们的网络已经学会了以非常有限的方式近似正弦函数。从 0 《= x 《= 1.1 开始,该行最适合,但是对于我们的其他 x 值,充其量只是一个大概的近似值。 结果表明,该模型没有足够的能力来学习正弦波函数的全部复杂度,因此只能以过于简单的方式对其进行近似。通过优化模型,我们应该能够改善其性能。 8. 优化模型 优化的关键是增加全连接层,这一层包含了 16 个神经元。 我们再对模型做评估: 通过图片分析,我们的网络达到了峰值精度的速度要快得多(在 200 个时期内,而不是 600 个时期内)。总损失和 MAE 比我们以前的网络要好得多。验证结果比训练结果更好,这意味着网络不会过度拟合。验证指标优于训练指标的原因是,验证指标是在每个时期结束时计算的,而训练指标是在整个时期计算的。 这一切都意味着我们的网络似乎运行良好!为了确认这一点,让我们对照我们先前放置的测试数据集检查其预测: 最终的测试结果如下所示: 我们来看这张图片,并不能说明这预测值完全跟 sine 的曲线一模一样。但是现在预测精度不是主要问题了,我们只想通过平滑的曲线来对开关进行控制。模型精度已经足够了。 9. 模型转换 模型转换的要点,就是 TensorFlow 到 TensorFlow Lite 的转换。其中有两个主要的组成部分: TensorFlow Lite 转换器 (TensorFlow Lite Converter) 这会将 TensorFlow 模型转换为一种节省空间的特殊格式,以用于内存受限的设备,并且可以应用进一步减少并优化模型尺寸,使其在小型设备上运行更快。 TensorFlow Lite 解释器 (Tensorflow Lite Interpreter) 这会使用最有效的方式运行经过适当转换的 TensorFlow Lite 模型到给定设备的有效操作。 同样,我们不需要操作,但是需要了解 convert 的 python API,以及创建 FlatBuffer,当然,还包括量化,即浮点精度的转换问题。这一类的话题,在我以前关于 TensorFlow Lite 的许多线下分享中都有过详细的原理上的分析及解释。 整个转换代码如下: 产生了两个模型,第一个模型是没有经过量化的。第二个模型是经过量化的。 对 TF Lite 模型进行预测,我们还需要完成如下的工作: 申明解释器对象实体 为模型分配内存 加载模型 从传感器中读取输出数据 由于这一段代码比较长,我们就看一下最终的分析结果: 我们再用代码来对比一下,量化和非量化模型的大小区别: 差距有20个字节。 10. 模型转换成二进制文件 准备模型以用于 TensorFlow Lite for Microcontrol 的最后一步。 到目前为止,在本章中,我们一直在使用 TensorFlow Lite 的 Python API。表示我们已经能够使用 Interpreter 构造函数加载模型磁盘中的文件。但是,大多数微控制器都没有文件系统,即使有,鉴于我们的限制,从磁盘加载模型所需的额外代码将很浪费空间。相反,作为一种优雅的解决方案,我们在 C 源文件中提供了该模型,包含在我们的二进制文件中并直接加载到内存中。 在文件中,模型定义为字节数组。幸运的是,有一个名为 xxd 的 Unix 工具,它能够将给定的文件转换为所需的格式。 以下输出为在我们的量化模型上运行 xxd,将输出写入名为sine_model_quantized.cc,并将其打印到屏幕上: 最终,生成二进制模型代码的工作搞定。 11. 结论 至此,我们完成了模型的构建。我们已经进行了训练,评估和配置建立了一个 TensorFlow 深度学习网络,该网络可以采用 0 到 2π 之间的数字并输出正弦的近似值。 这是我们使用 Keras 训练小模型的初次体验。在未来的项目中,我们将训练模型仍然很小,但是要复杂得多。 在下一篇中,我们将会分析如何去编写微控制器能够运行的 ML 应用。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1792 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1626 浏览 1 评论
1094 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
732 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1682 浏览 2 评论
1943浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
742浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
578浏览 3评论
601浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
563浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 16:51 , Processed in 0.930487 second(s), Total 48, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号