2021 年 10 月 6 日 - Sandeep Mistry 的客座文章,来自 Arm 简介 机器学习使开发人员和工程师能够在其应用程序中解锁新的功能。与明确定义计算机执行的指令和规则不同,您可以收集大量用于应用程序所需分类任务的数据,并训练一个 ML 模型来从数据中的模式中学习。训练通常在云端进行,在配备一个或多个 GPU 的计算机上进行。训练好模型后,根据模型的大小,可以将其部署到各种设备上进行推断。这些设备范围从云端的大型计算机(内存为 GB)到微型微控制器 (MCU)(通常只有 KB 的内存)。
Sandeep Mistry 的客座文章,来自 Arm
您需要的一些工具(了解更多信息请见下文!) |
机器学习使开发人员和工程师能够在其应用程序中解锁新的功能。与明确定义计算机执行的指令和规则不同,您可以收集大量用于应用程序所需分类任务的数据,并训练一个 ML 模型来从数据中的模式中学习。
训练通常在云端进行,在配备一个或多个 GPU 的计算机上进行。训练好模型后,根据模型的大小,可以将其部署到各种设备上进行推断。这些设备范围从云端的大型计算机(内存为 GB)到微型微控制器 (MCU)(通常只有 KB 的内存)。
微控制器是低功耗、自包含、经济高效的计算机系统,嵌入在您日常使用的设备中,例如微波炉、电动牙刷或智能门锁。基于微控制器的系统通常通过一个或多个传感器与其周围环境交互(想想按钮、麦克风、运动传感器),并使用一个或多个执行器执行操作(想想 LED、电机、扬声器)。
微控制器还提供隐私优势,并且可以在设备本地执行推断,无需将任何数据发送到云端。对于使用电池供电的设备,这也会带来功耗优势。
在本文中,我们将演示如何使用基于 Arm Cortex-M 的微控制器进行本地设备上 ML 以检测其周围环境中的音频事件。这是一篇教程式文章,我们将指导您通过训练一个基于 TensorFlow 的音频分类模型来检测火警声。
我们将向您展示如何使用 用于微控制器的 TensorFlow Lite 与 Arm CMSIS-NN 加速内核将 ML 模型部署到基于 Arm Cortex-M0+ 的微控制器板上以进行本地设备上 ML 推断。Arm 的 CMSIS-DSP 库(它为 Arm Cortex-M 处理器提供了优化的数字信号处理 (DSP) 函数实现)也将用于从实时音频数据中提取特征,然后进行推断。
虽然本指南侧重于检测火警声,但它可以适用于其他声音分类任务。您可能还需要调整特征提取阶段和/或调整 ML 模型架构以适应您的用例。
本教程的交互式版本可在 Google Colab 上找到,本指南的所有技术资产都可以在 GitHub 上找到。
您将需要以下基于 树莓派 RP2040 MCU 芯片的开发板之一,该芯片于 2021 年初发布。
SparkFun RP2040 MicroMod 和 MicroMod ML 载板
该板非常适合电子和微控制器初学者。它不需要烙铁、焊接知识或面包板接线知识。
树莓派 Pico 和 PDM 麦克风板
如果您知道如何焊接(或想学习),这个选项非常棒。它需要烙铁和面包板连接电子元件的知识。您将需要
以上两种选项都允许您从数字麦克风收集实时 16kHz 音频,并在开发板的 Arm Cortex-M0+ 处理器上处理音频信号,该处理器以 125MHz 的速度运行。在 Arm Cortex-M0+ 上运行的应用程序将具有一个数字信号处理 (DSP) 阶段,用于从音频信号中提取特征。然后将提取的特征馈送到神经网络以执行分类任务,以确定板上环境中是否存在火警声。
我们将首先使用 TensorFlow 使用 ESC-50:环境声音分类数据集 训练一个声音分类器(用于许多事件)。在对这个广泛的数据集进行训练后,我们将使用 迁移学习 为我们的特定音频分类任务对其进行微调。
该模型将在 ESC-50 数据集上进行训练,该数据集包含 50 种类型的声音。每个声音类别有 40 个音频文件,每个文件时长 5 秒。每个音频文件将被分成 1 秒的声音片段,任何包含纯静音的声音片段将被丢弃。
狗叫数据集中的样本波形。 |
我们不会将时间序列数据直接传递到 TensorFlow 模型中,而是将其转换为音频声谱图表示形式。这将创建一个音频信号频率内容随时间变化的二维表示形式。
我们将使用的输入音频信号将具有 16kHz 的采样率,这意味着一秒钟的音频将包含 16,000 个样本。使用 TensorFlow 的 tf.signal.stft(...) 函数,我们可以将 1 秒的音频信号转换为二维张量表示形式。我们将选择 256 的帧长度和 128 的帧步长,因此此特征提取阶段的输出将是一个形状为 (124, 129) 的张量。
狗叫的音频声谱图表示形式。 |
现在我们已经提取了音频信号的特征,我们可以使用 TensorFlow 的 Keras API 创建一个模型。您可以在上面链接的完整代码中找到它。该模型将包含 8 层
模型摘要可以在下面找到
请注意,该模型只有大约 15K 个参数(这非常小!)。
现在,我们将使用迁移学习并更改模型的分类头部(最后一个密集层)来训练一个用于火警声的二元分类模型。我们从 freesound.org 和 BigSoundBank.com 收集了 10 个火警剪辑。来自 SpeechCommands 数据集的背景噪声剪辑将用于非火警声音。该数据集很小,足以让我们入门。数据增强技术将用于补充我们收集的训练数据。
对于现实世界中的应用程序,收集更大的数据集非常重要(您可以在 TensorFlow 的 负责任的人工智能网站 上了解更多有关最佳实践的信息)。
数据增强是一组用于增加数据集大小的技术。这是通过稍微修改数据集中的样本或创建合成数据来完成的。在这种情况下,我们使用音频,我们将创建一些函数来增强不同的样本。我们将使用三种技术
除了增加数据集的大小之外,数据增强还有助于通过在不同的(非完美)数据样本上训练模型来减少过拟合。例如,在微控制器上,您不太可能拥有完美的优质音频,因此添加白噪声之类的技术可以帮助模型在麦克风偶尔出现噪声的情况下正常工作。
一个显示如何通过添加噪声来稍微改变声谱图的数据增强 gif(仔细观察,它可能有点难看)。 |
用于微控制器的 TensorFlow Lite (TFLu) 提供了 TensorFlow 操作的子集,因此我们无法在 MCU 上使用我们用于基线模型特征提取的 tf.signal.sft(...) API。但是,我们可以利用 Arm 的 CMSIS-DSP 库在 MCU 上生成声谱图。CMSIS-DSP 包含对浮点和定点 DSP 操作的支持,这些操作针对 Arm Cortex-M 处理器进行了优化,包括我们将部署 ML 模型的 Arm Cortex-M0+。Arm Cortex-M0+ 不包含浮点单元 (FPU),因此最好利用板载的 16 位定点 DSP 基特征提取管道。
我们可以在笔记本中利用 CMSIS-DSP 的 Python 包装器,使用 16 位定点数学在我们的训练管道中执行相同的操作。从高层次上看,我们可以使用以下基于 CMSIS-DSP 的操作来复制 TensorFlow SFT API
完整的 Python 代码有点长,但在 Google Colab 笔记本的 “迁移学习 -> 加载数据集”部分 中可以找到。
烟雾报警器声音的波形和音频声谱图。 |
有关如何使用 CMSIS-DSP 使用定点运算创建音频声谱图的详细说明,请参阅 Towards Data Science 的“面向数据科学家的定点 DSP”指南。
我们之前在 ESC-50 数据集上训练的模型预测了 50 种声音类型的存在,并导致模型的最终密集层具有 50 个输出。我们想要创建的新模型是二元分类器,需要具有单个输出值。
我们将加载基线模型,并将最终密集层替换为符合我们需求的层。
# We need a new head with one neuron.
model_body = tf.keras.Model(inputs=model.input, outputs=model.layers[-2].output)
classifier_head = tf.keras.layers.Dense(1, activation="sigmoid")(model_body.output)
fine_tune_model = tf.keras.Model(model_body.input, classifier_head)
这将导致以下模型.summary()
迁移学习是将为一项任务开发的模型重新训练以完成一项新的类似任务的过程。这个想法是,该模型已经学习了可迁移的“技能”,并且权重和偏差可以作为其他模型的起点使用。
作为人类,我们也使用迁移学习。你学习走路时培养的技能也可以用来学习跑步。
在神经网络中,模型的前几层开始执行“特征提取”,例如查找形状、边缘和颜色。后面的层用作分类器;它们获取提取的特征并将它们分类。
因此,我们可以假设前几层已经学习了相当普遍的特征提取技术,这些技术可以应用于类似的任务,因此我们可以冻结所有这些层,并在未来将其用于新任务。分类器层需要根据新任务进行训练。
为此,我们将该过程分为两个步骤
要在 TensorFlow 中冻结一层,我们可以设置 layer.trainable=False。让我们遍历所有层并执行此操作
for layer in fine_tune_model.layers:
layer.trainable = False
现在解冻最后一层(头部)。
fine_tune_model.layers[-1].trainable = True
我们现在可以使用二元交叉熵损失函数来训练模型。还将使用用于早期停止(以避免过度拟合)和动态学习率调度程序的 Keras 回调。
在我们使用冻结层训练完后,我们可以解冻它们
for layer in fine_tune_model.layers:
layer.trainable = True
并再次训练最多 10 个 epochs。你可以在 Colab 笔记本的 “迁移学习 -> 训练模型”部分 中找到此代码。
我们现在有了可以分类火灾警报器声音存在的 ML 模型。但是,该模型是在公开可用的声音记录上训练的,这些声音记录可能与我们将在推理中使用的硬件麦克风的声音特征不匹配。
Raspberry Pi RP2040 MCU 具有原生 USB 功能,允许它充当自定义 USB 设备。我们可以将应用程序刷入板载以使其充当我们 PC 的 USB 麦克风。然后,我们可以利用 Google Colab 的 Web Audio API 在现代 Web 浏览器(如 Google Chrome)中扩展 Google Colab 的功能来收集实时数据样本(全部来自 Google Colab 中!)
SparkFun MicroMod RP2040
要组装,请卸下载流板上的螺钉,以一定角度,将 MicroMod RP2040 处理器板滑入插座,并用螺钉将其固定到位。有关更多详细信息,请参阅 MicroMod 机器学习载流板连接指南。
树莓派 Pico
请遵循 “使用 Raspberry Pi Pico 创建 USB 麦克风”指南的硬件设置部分 中的说明进行组装。
顶部:Fritzing 接线图 底部:已组装的接线板 |
我们无需在个人计算机上设置 Raspberry Pi Pico 的 SDK。我们可以利用 Colab 内置的 Linux shell 命令功能,使用 CMake 和 GNU Arm 嵌入式工具链 设置 Pico SDK 开发环境。
还需要使用 git 将 pico-sdk 下载到 Colab 实例中。
%%shell
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule init
git submodule update
现在,我们可以使用 用于 Pico 的麦克风库 中的 USB 麦克风示例。可以使用 cmake 和 make 编译示例应用程序。然后,我们可以通过将板载置于“引导 ROM 模式”来将示例应用程序刷入板载,这将允许我们将应用程序上传到板载。
SparkFun
树莓派 Pico
如果你使用的是支持 WebUSB API 的浏览器(如 Google Chrome),你可以直接从 Google Collab 中将映像刷入板载!
从 Google Colab 和 WebUSB 中将 USB 麦克风应用程序下载到板载。 |
否则,你可以手动将 .uf2 文件下载到你的计算机,然后将其拖放到 RP2040 板载的 USB 磁盘上。
现在你已将 USB 麦克风应用程序刷入板载,它将显示为你的 PC 上的 USB 音频输入。
我们现在可以使用 Google Colab 来录制火灾警报器声音,在下拉菜单中选择“MicNode '' 作为音频输入源。然后,在按下烟雾警报器上的测试按钮时,单击 Google Colab 上的录制按钮来录制 1 秒的音频片段。重复此过程几次。
同样,我们也可以执行相同的操作来收集 Google Colab 中下一个代码单元中的背景音频样本。针对非火灾警报器声音(如静音、自己说话或环境中的任何其他正常声音)重复此操作几次。
现在我们已经收集了在推理过程中将使用到的额外的样本。我们可以使用新数据再次调整模型。
我们需要将我们已使用的 Keras 模型转换为 TensorFlow Lite 格式,以便我们可以在设备上使用它进行推理。
为了优化模型在 Arm Cortex-M0+ 处理器上运行,我们将使用称为模型量化的过程。模型量化将模型的权重和偏差从 32 位浮点值转换为 8 位值。 pico-tflmicro 库是针对 RP2040 的 Pico SDK 的 TFLu 端口,包含 Arm 的 CMSIS-NN 库,该库支持针对 Arm Cortex-M 处理器上的量化 8 位权重进行优化的内核操作。
我们可以使用 TensorFlow 的量化感知训练 (QAT) 功能轻松将浮点模型转换为量化模型。
我们现在将使用 tf.lite.TFLiteConverter.from_keras_model(...) API 将量化的 Keras 模型转换为 TF Lite 格式,然后将其保存到磁盘上作为 .tflite 文件。
converter = tf.lite.TFLiteConverter.from_keras_model(quant_aware_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
train_ds = train_ds.unbatch()
def representative_data_gen():
for input_value, output_value in train_ds.batch(1).take(100):
# Model has only one input so each data point has one element.
yield [input_value]
converter.representative_dataset = representative_data_gen
# Ensure that if any ops can't be quantized, the converter throws an error
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
# Set the input and output tensors to uint8 (APIs added in r2.3)
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
tflite_model_quant = converter.convert()
with open("tflite_model.tflite", "wb") as f:
f.write(tflite_model_quant)
由于 TensorFlow 也支持使用 tf.lite 加载 TF Lite 模型,因此我们也可以在 Google Colab 中验证量化模型的功能,并将它的精度与常规非量化模型进行比较。
我们正在部署的板载上的 RP2040 MCU 没有内置文件系统,这意味着我们无法直接在板载上使用 .tflite 文件。但是,我们可以使用 Linux `xxd` 命令将 .tflite 文件转换为 .h 文件,然后在下一步的推理应用程序中对其进行编译。
%%shell
echo "alignas(8) const unsigned char tflite_model[] = {" > tflite_model.h
cat tflite_model.tflite | xxd -i >> tflite_model.h
echo "};"
我们现在有一个可以部署到设备的模型。我们已经创建了一个 用于推理的应用程序模板,可以使用我们为模型生成的 .h 文件对其进行编译。
C++ 应用程序使用 pico-sdk 作为基础,以及 CMSIS-DSP、pico-tflmicro 和用于 Pico 的麦克风库。它的总体结构如下
为了实时运行,推理循环的每个周期必须在(512 / 16000)= 0.032秒或32毫秒内完成。我们训练和转换的模型推理时间为24毫秒,这为我们提供了约8毫秒的时间来完成循环中的其他操作。
上面使用128是为了匹配频谱图训练管道中使用的128步长。我们在频谱图中使用了4个偏移量以满足我们面临的实时约束。
现在我们可以使用CMake生成编译所需的构建文件,然后使用make进行编译。
“cmake ..”行需要根据你使用的板子进行修改
你需要将板子再次置于“引导ROM模式”以将新应用程序加载到板子上。
SparkFun
树莓派 Pico
如果你使用的是支持WebUSB API的浏览器(例如Google Chrome),你可以直接从Google Colab中将镜像刷入板子。否则,你可以手动将.uf2文件下载到你的电脑上,然后将其拖放到RP2040板子的USB磁盘上。
现在推理应用程序已经运行在板子上,你可以通过两种方式观察它的运行情况
视觉上观察板子上的LED亮度。在没有火警声的情况下,LED应该保持关闭或微弱 - 在有火警声的情况下,LED应该亮起
连接到板子的USB串口以查看推理应用程序的输出。如果你使用的是支持Web Serial API的浏览器(例如Google Chrome),可以在Google Colab中直接完成此操作
你现在已经将模型的第一个版本部署到板子上,它正在对16,000 kHz的实时音频数据进行推理!
测试各种声音以查看模型是否具有预期的输出。也许火警声被错误地检测到(假阳性)或在应该被检测到时没有被检测到(假阴性)。
如果发生这种情况,你可以通过将USB麦克风应用程序固件刷入板子,为场景录制更多新的音频数据,重新训练模型并将其转换为TF Lite格式,以及重新编译+刷入推理应用程序到板子上,来录制更多新的音频数据。
监督式机器学习模型通常只能与其训练数据一样好,因此这些场景的额外训练数据可能会有所帮助。你也可以尝试改变模型架构或特征提取过程 - 但请记住,你的模型必须足够小且足够快才能在RP2040 MCU上运行。
本文介绍了如何训练自定义音频分类模型以在使用Arm Cortex-M0+处理器的开发板上本地运行的端到端流程。使用TensorFlow训练模型,使用迁移学习技术以及更小的数据集和数据增强技术。我们还从麦克风收集了自己的数据,这些数据在推理时通过将USB麦克风应用程序加载到板子上并使用Web Audio API和JavaScript扩展Colab的功能来使用。
项目的训练部分结合了Google的Colab服务和Chrome浏览器以及开源的TensorFlow库。推理应用程序从数字麦克风捕获音频数据,使用Arm的CMSIS-DSP库进行特征提取阶段,然后使用TensorFlow Lite for Microcontrollers with Arm CMSIS-NN加速内核,使用8位量化模型对Arm Cortex-M0+处理器上的实时16kHz音频输入进行分类。
Google Chrome的Web Audio API、Web USB API和Web Serial API功能被用来扩展Google Colab的功能以与开发板交互。这使我们能够完全使用网页浏览器来实验和开发我们的应用程序,并将其部署到受限的开发板上进行设备上的推理。
由于ML处理是在开发板的RP2040 MCU上进行的,因此在推理时没有音频数据离开设备。
你可以在即将到来的Arm DevSummit上了解更多并获得使用TinyML的实践经验,这是一个为期三天的虚拟活动,时间为10月19日至21日。该活动包括关于针对现实世界嵌入式设备的tinyML计算机视觉和使用基于Arm Cortex-M的MCU构建大型词汇语音控制的研讨会。我们希望在那里见到你!
2021年10月6日 — Sandeep Mistry(来自Arm)的客座文章 介绍 机器学习使开发人员和工程师能够在他们的应用程序中解锁新的功能。与其为计算机执行明确定义指令和规则,你可以为应用程序所需的分类任务收集大量数据,并训练一个ML模型来学习数据中的模式。训练t…