使用 YAMNet 进行音频数据的迁移学习
2021 年 3 月 2 日

作者:Luiz GUStavo Martins,开发者倡导者

迁移学习 是一种流行的机器学习技术,您可以通过重复使用先前模型学习的信息来训练新模型。迁移学习最常见的应用是视觉领域,使用少量数据来训练准确的图像分类器或对象检测器,或者用于文本,其中使用预训练的文本嵌入或 BERT 等语言模型来改进自然语言理解任务,例如情感分析或问答。在本文中,您将学习如何将迁移学习用于一种新的重要数据类型:音频,以构建声音分类器。

音频分类有很多重要的用例,包括 保护野生动物检测鲸鱼,甚至 打击非法砍伐森林

使用 YAMNet,您可以在几个简单的步骤中创建一个定制的音频分类器

  • 准备和使用公共音频数据集
  • 使用 YAMNet 从音频文件中提取嵌入
  • 创建一个简单的两层分类器并对其进行训练。
  • 保存并测试最终模型

您可以按照此教程中的代码进行操作 此处

YAMNet 模型

YAMNet(“Yet another Audio Mobilenet Network”)是一个预训练模型,它根据 AudioSet 语料库 预测 521 个音频事件。

此模型在 TensorFlow Hub 上可用,包括 TFLite 和 TF.js 版本,用于在移动设备和网络上运行该模型。代码可以在他们的 存储库 中找到。

该模型有 3 个输出

  • 您将用于推理的类别分数
  • 嵌入,这是迁移学习的重要部分
  • 对数梅尔谱图,以提供输入信号的可视化

该模型采用以 16 kHz 采样表示的波形,范围为 [-1.0, 1.0],将其框定在 0.96 秒的窗口中,并在 0.48 秒的跳跃中,然后运行模型的核心以提取一批这些帧的嵌入。

The 0.96 seconds windows hopping over a waveform
在波形上跳跃 0.96 秒的窗口

例如,尝试使用此音频文件 [链接] 与模型一起使用将为您提供这些结果

The first graph is the waveform. The second graph is the log-mel spectrogram. The third graph shows the class probability predictions per frame of audio, where darker is more likely.
第一个图是波形。第二个图是对数梅尔谱图。第三个图显示了每帧音频的类别概率预测,其中深色表示可能性更高。

ESC-50 数据集

要使用该模型进行迁移学习,您将使用 环境声音分类数据集,简称 ESC-50。这是一个包含 50 个类别中的 2000 个环境音频录音的集合。每个录音长 5 秒,最初来自 Freesound 项目

ESC-50 包含您需要的狗和猫类别。

该数据集有两个重要的组成部分:音频文件和一个包含每个音频文件元数据的元数据 csv 文件。

元数据 csv 文件中的列包含将用于训练模型的信息

  • 文件名提供 .wav 音频文件的名称
  • 类别是数字目标 ID 的人类可读类别名称
  • 目标是类别的唯一数字 ID
  • 折叠确保来自相同初始源的剪辑始终包含在同一组中。这对于在将数据拆分为训练集、验证集和测试集以及进行交叉验证时避免交叉污染非常重要。

有关更详细的信息,您可以阅读 原始 ESC 论文

使用数据集

要加载数据集,您将从元数据文件开始,并使用 Pandas 方法 read_csv 加载它。

使用加载的数据框,下一步是按将要使用的类别进行过滤,在本例中为:狗和猫。

下一步将是加载音频文件以开始该过程,但如果音频文件过多,仅将所有音频文件加载到内存可能会造成很大的负担并导致内存不足问题。最佳解决方案是在需要时延迟加载音频文件。TensorFlow 可以使用 tf.data.Dataset 和 map 方法轻松实现这一点。

让我们从之前创建的 pandas 数据框创建数据集,并将 load_wav 方法应用于所有文件

filenames = filtered_pd['filename']
targets = filtered_pd['target']
folds = filtered_pd['fold']
 
main_ds = tf.data.Dataset.from_tensor_slices((filenames, targets, folds))
main_ds = main_ds.map(load_wav_for_map)

在这里,还没有将任何音频文件加载到内存中,因为映射尚未进行评估。例如,如果您请求数据集的大小,例如 (len(list(train_ds.as_numpy_iterator()))

,这将使 map 函数进行评估并加载所有文件。

相同的技术将用于从每个音频文件中提取所有特征(嵌入)。

提取音频嵌入

在这里,您将从 TensorFlow Hub 加载 YAMNet 模型。您只需要模型的句柄,并调用 tensorflow_hub 库中的 load 方法。

yamnet_model_handle = 'https://tfhub.dev/google/yamnet/1'
yamnet_model = hub.load(yamnet_model_handle)

这将加载模型到内存中,准备使用。

对于每个音频文件,您将使用 YAMNet 模型提取嵌入。对于每个音频文件,将执行 YAMNet。嵌入输出与音频文件中的相同标签和文件夹配对。

def extract_embedding(wav_data, label, fold):
  ''' run YAMNet to extract embedding from the wav data '''
  scores, embeddings, spectrogram = yamnet_model(wav_data)
  num_embeddings = tf.shape(embeddings)[0]
  return (embeddings,
            tf.repeat(label, num_embeddings), 
            tf.repeat(fold, num_embeddings))

main_ds = main_ds.map(extract_embedding).unbatch()

这些嵌入将是分类模型的输入。从模型的 文档 中,您可以看到,对于给定的音频文件,它会将波形框定在长度为 0.96 秒、跳跃为 0.48 秒的滑动窗口中,然后运行模型的核心。因此,总而言之,对于每 0.48 秒,模型将输出一个包含 1024 个浮点值的嵌入数组。此部分也是使用 map() 完成的,因此也是延迟评估,这就是它执行如此快的原因。

最终数据集包含三个使用的列:嵌入、标签和折叠。

最后一个数据集操作是拆分为训练集、验证集和测试集。为此,请使用 filter() 方法并将折叠字段(1 到 5 之间的整数)用作条件。

cached_ds = main_ds.cache()
train_ds = cached_ds.filter(lambda embedding, label, fold: fold < 4)
val_ds = cached_ds.filter(lambda embedding, label, fold: fold == 4)
test_ds = cached_ds.filter(lambda embedding, label, fold: fold == 5)

训练分类器

有了 YAMNet 嵌入向量和标签,下一步是训练一个分类器来学习什么是狗的声音,什么是猫的声音。

分类器模型非常简单,只有两层密集层,但正如您将看到的,这对于使用的数据量来说已经足够了。

my_model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=(1024), dtype=tf.float32, name='input_embedding'),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(len(my_classes))            
])

保存最终模型

训练的模型可以正常工作并且具有良好的准确率,但它所期望的输入不是音频波形,而是一个嵌入数组。为了解决这个问题,最终模型将 YAMNet 作为输入层,并将刚刚训练的模型组合在一起。这样,最终模型将接受波形并输出类别

input_segment = tf.keras.layers.Input(shape=(), dtype=tf.float32,
                                       name='audio')
embedding_extraction_layer = hub.KerasLayer('https://tfhub.dev/google/yamnet/1', trainable=False)
scores, embeddings, spectrogram = embedding_extraction_layer(input_segment)
serving_outputs = my_model(embeddings_output)
serving_outputs = ReduceMeanLayer(axis=0, name='classifier')(serving_outputs)
serving_model = tf.keras.Model(input_segment, serving_outputs)
serving_model.save(saved_model_path, include_optimizer=False)

要尝试重新加载的模型,您可以使用之前在 colab 中使用它的方式

reloaded_model = tf.saved_model.load(saved_model_path)
reloaded_results = reloaded_model(testing_wav_data)
cat_or_dog = my_classes[tf.argmax(reloaded_results)]

此模型也可以与 TensorFlow Serving 一起使用,使用 'serving_default'

serving_results =  reloaded_model.signatures['serving_default'](testing_wav_data)
cat_or_dog = my_classes[tf.argmax(serving_results['classifier'])]

在这篇文章中,您学习了如何使用 YAMNet 模型进行迁移学习,以识别来自 ESC-50 数据集的狗和猫的音频。

请查看 tfhub.dev 上的 YAMNet 模型和 tensorflow.org 上的教程。您可以将此技术应用于您自己的数据集,或应用于 ESC-50 数据集中的其他类别。

我们很想知道您可以用它构建什么!使用标签 #TFHub 在社交媒体上与我们分享您的项目。

致谢

我们要感谢许多同事对这项工作的贡献:Dan Ellis、Manoj Plakal 和 Eduardo Fonseca 为我们提供了出色的 YAMNet 模型,并支持 colab 和多次审阅。

Mark DaoustElizabeth Kemp 极大地改善了本文和相关教程中材料的演示。

下一篇文章
Transfer Learning for Audio Data with YAMNet

- 作者:Luiz GUStavo Martins,开发者倡导者迁移学习 是一种流行的机器学习技术,您可以通过重复使用先前模型学习的信息来训练新模型。迁移学习最常见的应用是视觉领域,使用少量数据来训练准确的图像分类器或对象检测器,或者用于文本,其中使用预训练的文本嵌入或 BERT 等语言模型来改进自然语言理解任务,例如情感分析或问答。…