在本系列文章中,我一直在开发一个系统,该系统可以监听咖啡包机的声音,并使用人工智能确定机器的状态。

该项目在第一篇文章中介绍,并具有远程录制咖啡包机音频的能力。在第二篇文章中,远程音频被输入到预训练音频神经网络 (PANN) 中,以对从远程麦克风流式传输的声音进行分类。PANN 可以识别 500 多种声音类别。

此时,您可以使用该系统识别您自己项目的 527 个声音类别中的任何一个。

在本文中,我探讨了微调 PANN 模型以识别来自咖啡包机(或您可能想要监控的任何其他设备或来源)的特定声音意味着什么。为了限制文章的长度,我将把实现留到后续文章中。

将显示缩放图像

作为快速回顾,我们将在 Raspberry Pi 单板计算机上使用 Python 应用程序来捕获 3 秒的声音片段并将它们发送到 Mac 进行识别。目的是根据咖啡包机发出的声音来确定咖啡包机的状态。

我使用“片段”这个词来指代从麦克风捕获的 3 秒声音窗口。这避免了与“样本”混淆,“样本”是指从麦克风输出的每个值。以每秒 48,000 个采样率计算,这意味着一个代码片段包含 144,000 个样本。

微调

除非您是一个大量喝咖啡的人,或者可以召集成千上万的朋友来提供帮助,否则您的咖啡包机将获得有限的声音片段来从头开始训练 AI。我所说的有限是指不到 1,000 甚至 10,000。从我的机器中,我有大约 40 个每个声音类别的片段。

我们没有从头开始训练人工智能,而是使用一个已经在数百种不同声音上进行预训练的模型,并提供了许多示例。这就是 PANN。它已经接受了超过 5,000 小时的 YouTube 录音的训练,其中包含 500 多种不同的声音,并由手工分类。这是一个著名的数据集,称为 AudioSet

通过对如此庞大的数据集进行训练,PANN 已经接受了识别声音特征的训练,并能够将它们组合在一起以识别一组 527 类声音。我们现在希望利用这种训练来教它使用它可以识别的功能,识别原始集合中没有的不同声音类别。

这称为迁移学习。这是使用为一项任务训练的 AI 作为基础,为不同的任务训练不同的 AI。

CNN 图层

PANN 模型使用卷积神经网络 (CNN) 层的方法,能够推断输入到模型的声音的分类。

我不建议在这里深入探讨这个问题,因为它是一个复杂的领域,但在查看代码之前,有一些基本理论来帮助理解正在发生的事情很重要。

如果您对微调感到满意,可以跳过本文并转到我构建训练解决方案的下一篇文章。

让我们打个比方。你会如何画房子?你可能会为墙壁画一个正方形,为屋顶添加一个三角形,放一些窗户,然后放一扇门。然后您可以开始添加细节,例如窗帘、鲜花等。

将显示缩放图像

这有点像特征提取。从最后一张图片中,我们可以识别正方形、矩形、三角形、圆形和椭圆形。从那里我们可以识别屋顶和墙壁。然后是门窗。我们甚至可以识别出花朵,尽管它们都略有不同,但通常都是相同的。

想象一下,您创建了一个人工智能来识别房屋图纸。现在想象一下它有可以识别事物的层。第一层可以识别形状,例如圆形、正方形和三角形以及它们的位置。使用这些信息,下一层识别墙壁和屋顶以识别房屋的轮廓。然后,下一层可能会识别门窗。下一层是窗帘,下一层是鲜花。

现在我们已经确定了房子的所有特征,然后人工智能可以推断(或分类)它所看到的是(或不是)房子。

将显示缩放图像

高度简化的概念图像,说明神经网络如何分层

输入层将原始数据作为线性数组。该线性数组可以是扁平图像(即:2D -> 1D)或文本行或来自频谱图的一组值(我在之前的文章中讨论过这一点)。通常,输入层采用这组线性数据,并经历一个过程,将其缩小到模型可以接受的大小。然后,输入层将以固定维度输出一组数字到下一层。

在每一层,输入集到输出的转换都是通过计算完成的,计算形成了人工智能处理的基本单位,称为神经元。输出值集称为嵌入

然后,第一层的输出连接到另一层,该层旨在开始识别特征,例如边缘。在全连接(或密集)层中,该层的神经元与上一层所有神经元的输出具有 1:1 的映射。

如果第二层识别边缘,则下一层可以识别形状等,通过模型的层向上。

请注意,实际特征是由模型学习的,而不是它运行的代码的一部分。

当我们到达更高级别的特征(例如墙壁和屋顶)时,我们不需要与前一层相同数量的神经元。这被称为池化层,它减少了信息的维度。

我们的目标是获得一个单一的值,我们可以用它来回答我们的问题,例如:这是一栋房子吗?这是分类器层的工作。

最终输出是一组值,这些值反映了输入属于给定类的置信度。这些称为 logits,可以是任何值(它们的总和不等于 1 / 100%,因此不是概率)。

如果我们现在想使用这个模型来识别卡车的图纸,我们需要做什么?

将显示缩放图像

我们可以重新训练模型中的所有层。通常,这需要大量的卡车图纸、大量的计算能力和很长时间。

将显示缩放图像

问题是,从抽象上讲,这与我们的房子非常相似。它有正方形、圆形和三角形。它只需要将这些识别为车轮、驾驶室和拖车。然后,我们可以更改最终层并训练我们的分类器来识别卡车。

将显示缩放图像

如果我们试图识别不同类型的建筑物,而不是卡车,那么可能只需要训练我们的分类器。

将显示缩放图像

而且,我们的咖啡包机监控系统也是如此。它可以利用 PANN 训练,我们可以重新训练分类器,或者可能重新训练上层来识别(分类)来自机器的声音。

通过冻结较低级别并仅训练几个较高级别,可以使用更少的计算机能力和更少的时间进行更少的数据进行训练。

训练数据

训练 AI 模型需要高质量的数据。质量好并不意味着没有噪音的清晰,而是数据经过准确分类和标记。

有不同的方法可以获取这些数据,也有不同的使用方法。

在我们的例子中,由于我们捕获声音片段的机会可能有限,因此我们最终得到的训练数据相对较少。此外,我们实际上需要三组数据:

  1. 训练模型的集合
  2. 用于评估模型训练情况的集合
  3. 一组用于测试我们的模型在训练后如何推断它从未听到过的声音的类别

这些必须是不同的套装,否则我们将成为过度拟合诅咒的牺牲品。这就是我们的模型在训练中没有足够多样性的地方,它会偏向于新声音的决定,使其成为它听到最多的声音,而不是计算它实际上可能是什么。

这些集合可以有不同的尺寸,因此您可以保留 70% 用于培训,20% 用于评估,10% 用于测试。

标签(即:类)

在我们进一步讨论之前,重要的是要谈谈标签。在我之前对微调的高级描述中,我们正在寻找是/否的答案。这是房子吗?这是卡车吗?

在聆听我们的咖啡包机时,我们可能会想问,您刚刚听到了什么?这仍然是一个分类问题(即:识别声音的来源或类别),但现在我们有很多东西想要识别,例如:

  • 吊舱门打开
  • 插入 Pod
  • 吊舱门关闭
  • Pod 已丢弃

其中每一个都被归类为一个标签。

模型不会说,“那个声音是吊舱门打开”,它会说,“这些是概率”:

  • 吊舱门打开 — 63%
  • 插入的豆荚 — 10%
  • 吊舱门关闭 — 22%
  • 吊舱掉落 — 5%

该模型说很可能是吊舱门开口。

您应该能够看到模型将输出其每个标签生成声音的概率。

训练的想法是尝试让模型在大多数时候被问到正确答案。

使用什么标签/类?

这是一个难题。您拥有的标签越多,就越难区分它们,因此准确性会下降。这意味着会有更多不正确的预测(即:更多的误报)。

您确实需要为要解决的问题提供最少的标签集,以便在您尝试识别的片段之间提供最大的区别。

例如,我首先查看了我可以监控的可能状态。这些是:

  • 吊舱门打开(无吊舱)—pod_bay_open_no_pod
  • 吊舱门打开(吊舱插入) —pod_bay_open_with_pod
  • 豆荚放入 —pod_in
  • 吊舱门关闭 —pod_closed
  • 牛奶放在架上——milk_on
  • 牛奶加热 —milk_heating
  • 咖啡冲泡 —brewing

虽然这是咖啡包机可能处于的所有状态,但某些声音非常接近,因此很难区分。例如,打开带和不带吊舱的吊舱门很难区分。但是,如果我们想知道机器中是否有 Pod,那么知道机器中是否有 Pod 是无关紧要的,因为这两个作都会清除机器。这意味着我们可以通过放弃了解机器中是否存在豆荚的要求来合理化标签集。

在聆听不同的声音时,也很明显,将牛奶放在支架上和从支架上取下,与豆荚舱门的作非常接近。我们需要知道牛奶是否在支架上吗?可能不会。可以进一步合理化。

另一件需要考虑的事情是,由于我们有一个声音的滑动窗口,给定窗口的开头或开头的部分声音听起来可能很像另一个声音,从而导致错误的预测。稍后会详细介绍。

标签合理化的结果是:

  • pod_bay_open
  • pod_in
  • pod_bay_closed
  • milk_heating
  • brewing

现在,您可以在全集上进行训练,然后,当您得到结果时,合并结果,即: = 或 。这更复杂,并且几乎不会为您购买额外的知识,因为额外的知识是不可靠的。我建议只针对您需要的标签进行培训。pod_bay_openpod_bay_open_no_podpod_bay_open_with_pod

我不得不承认,我只是在尝试训练模型后才进行了这种合理化。当我发现声音的接近导致了模型问题时,我进行了合理化,结果准确率从大约 65% 显着提高到 95%。

你仍然可以根据较大的标签集捕获声音,因为这可能会在以后扩展解决方案时对你有所帮助。您不想再次捕获和分类声音!

训练集

我说的是拥有三组数据。每组数据应包含该集中每个标签的大致相等的代码段集。

这意味着,假设您有 4 个标签和每个标签的 30 个片段,则总共有 120 个片段。您需要将其分为三组,即训练、评估和测试。

我将在培训、评估和测试中使用 70:20:10% 的分配。对于小集合来说,这是一个很好的起点。

例如,我们将有 120 * 70% = 84 个训练、120 * 20% = 24 个评估和 120 * 10% = 12 个测试片段。

重要的是,在每个集合中,每个标签的片段数量大致相同,以避免任何偏差。例如,在测试集中,4 个标签中的每一个都有 3 个代码段 = 12 个代码段。

您现在可能明白为什么我说您不太可能拥有大量数据。仅仅生成一组至少 10 个声音片段,例如,每组每个标签 - 特别是如果您最终得到 5 个或更多标签!

获取数据

好的,那么我们如何收集这些数据呢?我们可以连续录制声音,然后播放它们并将它们拆分成 Audacity 之类的东西,然后通过将它们分配给唱片公司来手动分类它们,但这是一项大量工作,而且相当困难。

我发现,通过修改第一篇文章中的 Raspberry Pi 项目,我可以让它询问要制作什么类别的声音,然后将类名嵌入到文件名中。然后它将捕获 3 个样本,即大约 5 秒的音频。rpi_audio_stream

然后,它就变成了一个简单的情况,即选择类、发出声音并使用 ,也从第一篇文章中捕获结果。basic_sound_receiver

由于这只是一个收集数据的工具,因此我将简单地将您指向我的 GitHub 存储库中的代码,而不是引导您完成此处的步骤。rpi_audio_snippet_capture

确保您正在运行,并且已设置为连接到它(请参阅文件)。basic_sound_receiverrpi_audio_snippet_captureREADME.md

在 Raspberry PI 上运行该工具,它将提供声音分类菜单,您可以从咖啡包机录制这些声音分类。选择与您的选择关联的数字,然后发出声音。然后,一组声音文件将保存在 Mac 上的文件夹中,其名称反映了录制声音的类别。rpi_audio_snippet_capturebasic_sound_receiver/uploads

对数据进行排序

一旦数据与文件名中的实际类一起保存,现在需要对其进行排序。

首先,我们创建将在培训计划中使用的文件夹结构:

  • ./data
  • ./data/<label name>

然后,使用文件名,我们可以将每个文件移动到适当的分类/标签子文件夹中。

还有一个额外的步骤。重要的是,每个片段都包含完整且单一的声音。这是因为包含部分分类和多分类的片段会在训练中引入噪声,从而由于分类混淆而降低其准确性。

这并不是说应该消除背景噪音和环境声音,因为这增强了人工智能在正常环境中对声音进行分类的能力。

在某些情况下,例如机器中已有和没有吊舱的吊舱托架开口,片段可以放置在同一个子文件夹 () 中。无需重命名文件。./data/pod_bay_open

我还录制了我说的“重置”,目的是如果咖啡包机显示器不同步,它可以轻松重置为已知状态。我制作了一个重置文件夹 () 并在捕获应用程序中给它一个标签。./data/reset

训练

之前,我谈到了在 PANN 中冻结层并在原始模型之上训练我们自己的分类器。经过初步试验,我发现这产生的准确率非常低(约 64%)。

然后我解锁了 1、2 和 3 层,这样下层特征层也可以训练。这提高了准确性。第 1 层和第 2 层之间存在显着差异,然后解锁第 3 层后又进行了改进。

样品预处理

为了在声音样本上进行训练,它们必须与模型兼容。这需要对代码段进行预处理。这包括:

  • 将采样率 (48,000) 调整为模型预期的速率 (16,000)
  • 更改样本类型(例如:int 16 -> float 32)
  • 将信号归一化为 -1 和 +1 之间

分类层

要训练新的分类层,我们可以添加新层来替换原始层。第一层就像池化层一样,将前一层的输出(例如:2048 个值)减少到更小的层(例如:256 个值),从而细化模型将使用的特征集。

然后使用辍学层。这会在训练期间将给定百分比的神经元的神经元输出随机设置为零。这可以教模型不要过度拟合,并提高其对以前没有听到的声音的识别。

在此之后,我们有一个进一步的简化层,从 256 个减少到我们拥有的标签数量,从而产生一组 logits,这些值表示新片段属于给定标签的可能性。

然后,这些输出可以通过一个函数传递,该函数将 logits 转换为加起来为 100% 的百分比概率,例如函数。sofmax

非线性激活函数

将输出从一层传输到下一层时,需要激活函数。

现在,如果激活函数是线性的,那么整个模型可以编译为单个矩阵计算。这意味着输入和输出之间存在简单的关系。这可以防止从以前从未听过的声音中推断出来,并产生所谓的过度拟合(如果我所知道的只是香蕉,那么一切都是香蕉)。

因此,在层之间,我们可以引入非线性函数来提供输入到输出的直接计算以外的其他东西,从而允许模型学习。有几种不同类型的非线性激活函数,但有些存在梯度消失的问题,其中乘法因子(称为权重)变得太小而不重要。

我们将使用整流函数 (ReLU),对于正值,将输出设置为输入,对于负值,将输出设置为 0。

时代

关于培训,最后要了解的是,它是一个迭代过程。您不会运行一次训练计算,而是多次运行。每次通过都称为一个纪元

简而言之,在每个纪元中,您:

  1. 使用训练数据和损失函数更新模型中的计算参数,告诉您需要调整多少
  2. 使用评估数据评估模型的性能,以查看训练的进展情况

随着每个纪元,模型的准确性可能会上升和下降

这很重要。最好的纪元可能不是最后一个。

可以这样想,你正在用铅笔描出一条线。你是保持在线上还是在它上方和下方漂移?

让我们更进一步。

如果您非常快速地进行跟踪,您可能会犯更多错误并更频繁地在线之外(高损耗、低精度),但线路会很平滑(低噪声)。

另一方面,如果您非常缓慢而仔细地追踪,您会犯更少的错误,并且更少的次数超出线路(低损耗,高精度),但线路可能会不那么平滑,更锯齿状(更多的噪音)。

学习计算中的噪声很重要,因为如果正在进行的非常小的调整的大小与代码片段中的噪声相等,那么模型实际上是在尝试使用随机噪声进行训练,从而导致准确性问题。

培训也是如此。您可以设置更高和更低的学习率(您遵循这条线的速度)。

值越小,每个纪元中调整的步长就越小。这通常会产生更好的结果,但需要更多的纪元,这意味着更多的功率和更多的时间。

值越大,在每个纪元中调整的步长就越大。这通常会产生更差的结果,但需要更少的纪元,这意味着更少的功率和更少的时间。

有时,这种权衡会导致为模型中的不同层设置不同的学习率。

设置

当您调整训练时,我们已经看到了您可以调整的几件事:

  1. 您尝试检测的类
  2. 您冻结与解锁的图层
  3. 纪元数
  4. 学习率
  5. 附加分类层
  6. 任何培训辍学率
  7. 任何非线性(激活)函数

总结

在本文中,我介绍了我们将微调 PANN 模型以执行我们需要它完成的任务的方法,即:根据咖啡包机发出的噪音识别咖啡包机的状态。

我们粗略地了解了 CNN 模型如何使用图层来提取特征并最终回答“模型听到了什么”的问题。

一旦我们了解了层的工作原理,我们就研究了如何通过根据我们的要求微调这些层来重新分配这些层。

数据对训练非常重要,我们需要分类的声音片段才能完成训练。我们研究了如何使用应用程序的修改版本来捕获和分类数据。然后,我们对捕获的数据进行排序和清理,以便将其用于训练。audio_stream

最后,讨论了训练过程,以及解冻层、预处理片段和运行训练循环以构建最佳模型。

下一篇文章中,我们将查看执行所有这些作的代码。如果您对微调概念的高级概述感兴趣,可以在我的相关文章中阅读。

我希望您喜欢这篇文章,并且您通过学习新东西来扩展您的技能,即使是一些小东西。

如果您发现这篇文章感兴趣,请给我鼓掌,因为这有助于我确定人们认为有用的内容以及我将来应该写哪些文章。如果您有任何建议,请将其添加为注释或回复。

Logo

更多推荐