https://blog.tensorflow.org/2021/09/TinyML-Audio-for-everyone.html
Sandeep Mistry的嘉宾帖子,Arm 2021年10月6日
此项目需要的一些工具
介绍
机器学习使开发人员和工程师能够在其应用程序中开发新功能。您可以为应用程序所需的分类任务收集大量数据,并训练ML模型从数据中的模式中学习,而不是显式定义计算机要执行的指令和规则。
训练通常在配备一个或多个GPU的计算机上的云中进行。一旦一个模型经过训练,根据它的大小,它可以部署在各种设备上进行推理。这些设备从拥有千兆字节内存的云中大型计算机,到通常只有千字节内存的微型微控制器(或MCU)。
微控制器是一种低功耗、自给自足、经济高效的计算机系统,它嵌入到日常使用的设备中,如微波炉、电动牙刷或智能门锁。基于微控制器的系统通常通过一个或多个传感器(按钮、麦克风、运动传感器)与其周围环境交互,并使用一个或多个执行器(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上获得(https://colab.research.google.com/github/ArmDeveloperEcosystem/ml-audio-classifier-example-for-pico/blob/main/ml_audio_classifier_example_for_pico.ipynb),本指南的所有技术资产都可以在GitHub上找到(https://github.com/ArmDeveloperEcosystem/ml-audio-classifier-example-for-pico)。
你需要什么来开始
开发环境
Google Colab(https://colab.research.google.com/notebooks/)
硬件
您将需要以下基于树莓派的RP2040(https://www.raspberrypi.org/products/rp2040/) MCU芯片的开发板之一,该芯片于2021年初发布。
SparkFun RP2040 MicroMod和MicroMod ML载体
对于刚接触电子和微控制器的人来说,这个电路板非常棒。它不需要一个烙铁,知道如何焊接,或如何连接面包板。
- SparkFun MicroMod RP2040处理器(https://www.sparkfun.com/products/17720)。这是实验的核心。它包含树莓派的RP2040 MCU和16MB闪存。
- SparkFun MicroMod机器学习载体板(https://www.sparkfun.com/products/16400)。这可以实现USB连接,并提供内置麦克风、IMU和摄像头接口。
- USB-C连接线,用于将主板连接到计算机。
- 十字螺丝刀。
树莓派Pico和PDM 麦克风子板
如果您知道如何焊接(或想学习),此选项非常有用。它需要一个烙铁和如何用电子元件连接试验板的知识。您需要:
- 树莓派Pico。
- Adafruit 麦克风。
- 半尺寸或全尺寸的试验板。
- 跨接导线。
- USB-B micro 连接线,用于将主板连接到计算机。
- 烙铁。
上述两个选项都允许您从数字麦克风实时采集16 kHz音频,并在开发板的Arm Cortex-M0+处理器上处理音频信号,该处理器的工作频率为125 MHz。Arm Cortex-M0+上运行的应用程序将具有数字信号处理(DSP)阶段,以从音频信号中提取特征。然后,将提取的特征输入神经网络,以执行分类任务,以确定电路板环境中是否存在火灾报警声。
数据集
我们将使用ESC-50:环境声音分类数据集,使用TensorFlow训练声音分类器(用于许多事件)。在对这个广泛的数据集进行训练之后,我们将使用迁移学习对其进行微调,以完成特定的音频分类任务。
该模型将在ESC-50数据集上进行训练,该数据集包含50种声音。每个声音类别有40个音频文件,每个文件的长度为5秒。每个音频文件将被分割成1秒的声带,任何包含纯静音的声带都将被丢弃。
来自狗叫声数据集的采样波形
声谱图
我们将把音频数据转换成音频谱图表示,而不是将时间序列数据直接传递到我们的TensorFlow模型中。这将创建音频信号随时间变化的频率内容的2D表示。
我们将使用的输入音频信号的采样率为16kHz,这意味着一秒钟的音频将包含16000个样本。使用TensorFlow的tf.signal.stft(…)函数,我们可以将1秒音频信号转换为2D张量表示。我们将选择256帧长和128帧步长,因此该特征提取阶段的输出将是形状为(124129)的张量。
狗叫声的声谱图
ML模型
现在我们已经从音频信号中提取了特征,我们可以使用TensorFlow的KerasAPI创建一个模型。您可以找到上面链接的完整代码。模型将由8层组成:
- 输入层。
- 预处理层,将输入张量从124x129x1调整为32x32x1。
- 一个规范化层,它将在-1和1之间缩放输入值
- 一个二维卷积层,具有:8个过滤器、8x8内核大小、2x2步长和ReLU激活功能。
- 大小为2x2的2D max池层
- 将二维数据展平到1D的展平层
- 脱落层,有助于减少训练期间的过度贴合
- 具有50个输出和softmax激活功能的密集层,该功能输出声音类别的可能性(介于0和1之间)。
模型摘要如下所示:
请注意,这个模型只有大约15K个参数(这个参数非常小!)
微调
现在,我们将使用转移学习并改变模型的分类头(最后一个密集层),以训练火灾报警声音的二元分类模型。我们从freesound.org和BigSoundBank.com上收集了10个火灾报警片段。SpeechCommands数据集中的背景噪音片段将用于非火灾报警声音。这个数据集很小,足以让我们开始使用。数据扩充技术将用于补充我们收集的训练数据。
对于现实世界的应用程序,收集更大的数据集非常重要(您可以在TensorFlow负责的AI网站上了解更多关于最佳实践的信息)。https://www.tensorflow.org/responsible_ai
数据扩充
数据扩充是一组用于增加数据集大小的技术。这是通过稍微修改数据集中的样本或创建合成数据来实现的。在这种情况下,我们使用音频,我们将创建一些函数来增强不同的样本。我们将使用三种技术:
向音频样本添加白噪声。
为音频添加随机静音。
将两个音频样本混合在一起。
除了增加数据集的大小外,数据扩充还通过在不同(不完美)的数据样本上训练模型来帮助减少过度拟合。例如,在微控制器上,您不可能拥有完美的高质量音频,因此,添加白噪声等技术可以帮助模型在您的麦克风可能经常有噪声的情况下工作。
gif显示了数据增强如何通过添加噪声来轻微改变声谱图(仔细观察,可能有点难以看到)。
特征提取
TensorFlow Lite for Microcontroller(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包装器(https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/DSP/PythonWrapper#readme),使用16位定点数学在我们的培训管道上执行相同的操作。在高水平上,我们可以通过以下基于CMSIS-DSP的操作复制TensorFlow SFT API:
- 使用汉宁窗口公式和CMSIS-DSP的arm_cos_f32 API手动创建长度为256的汉宁窗口。
- 创建CMSIS-DSP arm_rfft_实例_q15实例,并使用CMSIS-DSP的arm_rfft_init_q15 API对其进行初始化。
- 在音频数据中循环一次256个样本,步幅为128(这与我们传递到TF sft API中的参数相匹配)
a.使用CMSIS-DSP的arm_mult_q15 API将256个样本乘以汉宁窗口
b.使用CMSIS-DSP的arm_rfft_q15 API计算上一步输出的FFT
c.使用CMSIS-DSP的arm_cmplx_mag_q15 API计算上一步的幅度
- 每个音频片段的FFT幅度代表声谱图的一列。
- 由于我们的基线模型需要浮点输入,而不是我们使用的16位量化值,CMSIS-DSP arm_q15_to_float API可用于将频谱图数据从16位定点值转换为浮点值进行训练。
完整的Python代码有点长,但是可以在GoogleColab笔记本的“转移学习->加载数据集”部分找到。(https://colab.research.google.com/github/ArmDeveloperEcosystem/ml-audio-classifier-example-for-pico/blob/main/ml_audio_classifier_example_for_pico.ipynb)
烟雾报警声音的波形和音频频谱图。
有关如何使用CMSIS-DSP定点操作创建音频频谱图的详细说明,请参阅面向数据科学的“数据科学家定点DSP”指南。(https://towardsdatascience.com/fixed-point-dsp-for-data-scientists-d773a4271f7f)
加载基线模型并更改分类头
我们之前在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)
这将产生以下model.summary()
迁移学习
迁移学习是重新训练一个为完成一项新的类似任务而开发的模型的过程。其想法是,该模型已经学会了可转移的“技能”,权重和偏差可以作为起点用于其他模型。
作为人类,我们也使用迁移学习。你学习走路的技能也可以用于以后学习跑步。
在神经网络中,模型的前几层开始执行“特征提取”,例如查找形状、边缘和颜色。后面的层用作分类器;他们将提取的特征进行分类。
因此,我们可以假设前几层已经学习了可以应用于类似任务的通用特征提取技术,因此我们可以冻结所有这些层,并在将来的新任务中使用它们。分类器层需要根据新任务进行训练。
为此,我们将流程分为两个步骤:
- 冻结模型的“骨干”,以相当高的学习率训练头部。我们慢慢地降低学习速度。
- 解冻“主干”,以低学习率微调模型。
要冻结TensorFlow中的层,我们可以设置layer.trainable=False。让我们遍历所有层并执行以下操作:
for layer in fine_tune_model.layers:
layer.trainable = False
现在解冻最后一层(头部):
fine_tune_model.layers[-1].trainable = True
我们现在可以使用二进制交叉熵损失函数来训练模型。Keras回调用于提前停止(以避免过度拟合)和动态学习速率调度器也将被使用。
在我们对frozen 层进行训练后,我们可以解冻它们:
for layer in fine_tune_model.layers:
layer.trainable = True
再训练10个epochs。您可以在Colab笔记本的“转移学习->训练模型”部分找到完整的代码。
([https://colab.research.google.com/github/ArmDeveloperEcosystem/ml-audio-classifier-example-for-pico/blob/main/ml_audio_classifier_example_for_pico.ipynb#scrollTo=Replace_Baseline_Model_Classification_Head_and_Train_Model](https://colab.research.google.com/github/ArmDeveloperEcosystem/ml-audio-classifier-example-for-pico/blob/main/ml_audio_classifier_example_for_pico.ipynb#scrollTo=Replace_Baseline_Model_Classification_Head_and_Train_Model))
记录您自己的训练数据
我们现在有了一个ML模型,可以对火灾报警声音进行分类。然而,该模型是根据公开的录音进行训练的,这些录音可能与我们将用于推断的硬件麦克风的声音特征不匹配。
树莓派 RP2040 MCU具有本机USB功能,使其可以像自定义USB设备一样工作。我们可以将一个应用程序闪存到电路板上,使其能够像USB麦克风一样作用于我们的PC。然后,我们可以在现代网络浏览器(如Google Chrome)上通过Web音频API(https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)扩展Google Colab的功能,以收集实时数据样本(全部来自Google Colab!)
硬件设置
SparkFun MicroMod RP2040
组装时,以一定角度拆下承载板上的螺钉,将MicroMod RP2040处理器板滑入插槽,并用螺钉将其固定到位。有关更多详细信息,请参阅MicroMod机器学习载体板连接指南。(https://learn.sparkfun.com/tutorials/micromod-machine-learning-carrier-board-hookup-guide?_ga=2.90268890.1509654996.1628608170-268367655.1627493370#hardware-hookup)
树莓派 PICO
按照《使用树莓派 Pico创建USB麦克风》指南中硬件设置部分的说明进行组装。
上部:接线图 底部:组装式试验板
设置固件应用程序工具链
而不是在你的个人电脑上设置树莓派Pico的SDK。我们可以利用Colab内置的LinuxShell命令特性,使用CMake和GNU Arm嵌入式工具链来设置PicoSDK开发环境。(https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm)
pico sdk(https://github.com/raspberrypi/pico-sdk)还必须使用git下载到Colab实例:
%%shell
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule init
git submodule update
编译并刷新USB麦克风应用程序
现在我们可以使用Pico话筒库中的USB话筒示例。示例应用程序可以使用cmake和make进行编译。然后,我们可以通过USB将示例应用程序闪存到电路板上,方法是将电路板置于“引导ROM模式”,这将允许我们将应用程序上载到电路板上。
SparkFun
- 将USB-C电缆插入主板和电脑,为主板供电。
- 按住电路板上的启动按钮的同时,轻触复位按钮。
**树莓派PICO
**
- 将USB Micro电缆插入电脑,但不要插入Pico端。
- 按住白色引导按钮,将micro USB电缆插入Pico。
如果您使用的是支持WebUSB API(https://wicg.github.io/webusb/)的浏览器,如Google Chrome,您可以从Google Collab内直接将固件烧写到电路板上!
从Google Colab和WebUSB中下载USB麦克风应用程序到主板。
否则,您可以手动将.uf2文件下载到计算机上,然后将其拖到RP2040板的USB磁盘上。
收集训练数据
现在,您已将USB麦克风应用程序闪存到电路板上,它将作为USB音频输入显示在您的电脑上。
我们现在可以使用Google Colab录制火灾报警声音,在下拉列表中选择“MicNode”作为音频输入源。然后在按下烟雾报警器上的测试按钮时,单击Google Colab上的录制按钮录制1秒的音频剪辑。重复此过程几次。
同样,我们也可以在GoogleColab的下一个代码单元中收集背景音频样本。对非火警警报声音重复几次,如静音、你自己说话或环境中的任何其他正常声音。
最终模型培训
现在,我们已经用麦克风收集了额外的样本,这些样本将在推断过程中使用。我们可以用新数据再次调整模型。
将模型转换为在MCU上运行
我们需要将我们使用的Keras模型转换为TensorFlow Lite格式,以便在设备上使用它。
量化
为了优化模型以在Arm Cortex-M0+处理器上运行,我们将使用一个称为模型量化的过程。模型量化将模型的权重和偏差从32位浮点值转换为8位值。pico tflmicro库(https://github.com/raspberrypi/pico-tflmicro)是RP2040的pico SDK的TFLu端口,包含Arm的CMSIS-NN库,该库支持Arm Cortex-M处理器上量化8位权重的优化内核操作。
我们可以使用TensorFlow的量化感知训练(QAT) (https://www.tensorflow.org/model_optimization/guide/quantization/training)功能轻松地将浮点模型转换为量化模型。
将模型转换为TF-Lite格式
现在,我们将使用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文件。但是,我们可以使用Linuxxxd
命令将.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作为基础,同时使用CMIS-DSP、pico-tflmicro和Pico麦克风库。其总体结构如下:
1.初始化
- 为输出配置板的内置LED。
- 应用程序将LED的亮度映射到模型的输出。
- (0.0 LED关闭,1.0 LED打开,全亮度)
- 设置TF-Lite库和TF-Lite模型进行推理
- 建立基于CMSIS-DSP的DSP流水线
- 设置并启动麦克风以获得实时音频
2.推理回路
- 等待来自麦克风的128*4=512个新音频样本
- 将频谱图阵列移动4列
- 将音频输入缓冲区移128*4=512个样本,并复制新样本
- 为更新的输入缓冲区计算4个新的谱图列
- 对频谱图数据进行推断
- 将推断输出值映射到板载LED的亮度,并将状态输出到USB端口
为了实时运行,推理循环的每个周期必须在(512/16000)=0.032秒或32毫秒以下。我们训练和转换的模型需要24毫秒的时间进行推理,这为循环中的其他操作提供了8毫秒的时间。
上面使用128来匹配频谱图训练管道中使用的128步幅。我们在光谱图中使用了4的移位,以适应我们的实时约束。
编译固件
现在我们可以使用CMake生成编译所需的构建文件,然后生成编译所需的生成文件。
“cmake..”行必须根据您使用的电路板进行更改
- SparkFun: cmake .. -DPICO_BOARD=sparkfun_micromod
- Raspberry Pi Pico: cmake .. -DPICO_BOARD=pico
将推理应用程序烧写到开发板
您需要再次将该板置于“启动ROM模式”以将新应用程序加载到其中。
SparkFun
- 将USB-C电缆插入主板和电脑,为主板供电。
- 按住电路板上的启动按钮的同时,轻触复位按钮。
树莓派PICO
- 将USB MICRO 电缆插入电脑,但不要插入Pico端。
- 按住白色引导按钮,将micro USB电缆插入Pico。
如果您使用的是支持WebUSB API的浏览器,如Google Chrome,您可以从Google Colab中直接将固件烧写到开发板上。否则,您可以手动将.uf2文件下载到计算机上,然后将其拖到RP2040板的USB磁盘上。
监视板上的推理
现在推断应用程序正在板上运行,您可以通过两种方式观察它的运行情况:
通过观察开发板上LED的亮度进行目视检查。当没有火灾报警声时,它应保持关闭或变暗-当有火灾报警声时,它应保持打开:
连接到电路板的USB串行端口以查看推理应用程序的输出。如果您使用的是支持Web串行API的浏览器,如Google Chrome,则可以直接从Google Colab执行此操作:
改进模型
现在,您已经将模型的第一个版本部署到了电路板上,它正在对16000 kHz的实时音频数据执行推断!
测试各种声音,看看模型是否有预期的输出。可能火灾报警声音被错误检测(假阳性)或未被检测到(假阴性)。
如果出现这种情况,您可以通过将USB麦克风应用程序固件闪存到电路板、记录用于培训的数据、重新培训模型并转换为TF-lite格式,以及重新编译并将推理应用程序闪存到电路板来为场景录制更多新的音频数据。
有监督的机器学习模型通常只能与它们所使用的训练数据一样好,因此针对这些场景的额外训练数据可能会有所帮助。您也可以尝试更改模型体系结构或特征提取过程,但请记住,您的模型必须足够小,足够快,才能在RP2040 MCU上运行。改进模型
现在,您已经将模型的第一个版本部署到了电路板上,它正在对16000 kHz的实时音频数据执行推断!
测试各种声音,看看模型是否有预期的输出。可能火灾报警声音被错误检测(假阳性)或未被检测到(假阴性)。
如果出现这种情况,您可以通过将USB麦克风应用程序固件闪存到电路板、记录用于培训的数据、重新培训模型并转换为TF-lite格式,以及重新编译并将推理应用程序闪存到电路板来为场景录制更多新的音频数据。
有监督的机器学习模型通常只能与它们所使用的训练数据一样好,因此针对这些场景的额外训练数据可能会有所帮助。您还可以尝试更改模型体系结构或特征提取过程,但请记住,您的模型必须足够小,足够快,才能在RP2040 MCU上运行。
结论
本文介绍了如何在使用Arm Cortex-M0+处理器的开发板上本地运行自定义音频分类器模型的端到端流程。TensorFlow使用迁移学习技术以及较小的数据集和数据扩充技术来训练模型。我们还通过将USB麦克风应用程序加载到电路板,并使用Web音频API和JavaScript扩展Colab的功能,从推断时使用的麦克风收集了我们自己的数据。
该项目的培训部分结合了谷歌的Colab服务和Chrome浏览器,以及开源的TensorFlow库。推理应用程序从数字麦克风捕获音频数据,使用Arm的CMSIS-DSP库进行特征提取阶段,然后,使用TensorFlow Lite(适用于带有Arm CMSIS-NN加速内核的微控制器)对8位量化模型进行推理,该模型对Arm Cortex-M0+处理器上的实时16 kHz音频输入进行分类。
Google Chrome的Web音频API、Web USB API和Web串行API功能用于扩展Google Colab的功能,以与开发板交互。这使我们能够完全使用web浏览器来试验和开发应用程序,并将其部署到受约束的开发板上进行设备推断。
由于ML处理是在开发板RP2040 MCU上执行的,因此推断时没有音频数据离开设备。
了解更多
在即将到来的Arm DevSummit https://www.google.com/url?q=https://devsummit.arm.com/en?&utm_source=armdevsummit&utm_medium=social&utm_campaign=2021_embdev_mk18_arm_na_na_awa&utm_term=tensorflow-blog&sa=D&source=editors&ust=1633460710178000&usg=AOvVaw2XFBCOK1Gn1XaEGeivml6g(10月19日至21日为期3天的虚拟活动)上,您可以了解更多信息并获得使用TinyML的实际体验。该活动包括tinyML计算机视觉真实世界嵌入式设备研讨会,以及使用基于Arm Cortex-M的MCU构建大词汇量语音控制。我们希望在那里见到你!