前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 FastAI 和即时频率变换进行音频分类

使用 FastAI 和即时频率变换进行音频分类

作者头像
AI研习社
发布2019-05-15 09:41:07
1.8K0
发布2019-05-15 09:41:07
举报
文章被收录于专栏:AI研习社
简介

目前深度学习模型能处理许多不同类型的问题,对于一些教程或框架用图像分类举例是一种流行的做法,常常作为类似“hello, world” 那样的引例。FastAI 是一个构建在 PyTorch 之上的高级库,用这个库进行图像分类非常容易,其中有一个仅用四行代码就可训练精准模型的例子。随着v1版的发布,该版本中带有一个data_block的API,它允许用户灵活地简化数据加载过程。今年夏天我参加了Kaggle举办的Freesound General-Purpose Audio Tagging 竞赛,后来我决定调整其中一些代码,利用fastai的便利做音频分类。本文将简要介绍如何用Python处理音频文件,然后给出创建频谱图像(spectrogram images)的一些背景知识,示范一下如何在事先不生成图像的情况下使用预训练图像模型。

点击原文查看文中涉及的代码,以及相关的notebooks。

音频文件转图像

起初把音频文件作为图像分类听起来有些怪异。图像是二维数据(其中包含RGBA等4个通道), 而音频文件通常是一维的 (可能包含2个维度的通道,单声道和立体声)。本文只关注单声道的音频文件。我们知道,每个音频文件会有一个采样率,即音频的每秒采样数。如果文件是一个3秒长采样率为44100Hz的声音片段,这就意味着文件是由 3*44100 = 132300 表示气压变化的连续数字组成。 librosa是Python中处理音频效果最好的库。

代码语言:javascript
复制
clip, sample_rate = librosa.load(filename, sr=None)
clip = clip[:132300] # first three seconds of file

虽然从上图可以感受到各时点音频的响亮或安静程度,但图中基本看不出当前所在的频率。为获得频率,一种非常通用的方案是去获取一小块互相重叠的信号数据,然后运行Fast Fourier Transform (FFT) 将数据从时域转换为频域。经过FFT处理后,我们可以将结果转换为极坐标,就得到不同频率的幅度和相位。虽然相位信息在某些情况下适用,本文中主要适用幅度信息,我们将其转换为分贝单位,因为耳朵是以对数尺度感知声音的。

代码语言:javascript
复制

n_fft = 1024  # frame length 
start = 45000 # start at a part of the sound thats not silence
x = clip[start:start+n_fft]
X = fft(x, n_fft)
X_magnitude, X_phase = librosa.magphase(X)
X_magnitude_db = librosa.amplitude_to_db(X_magnitude)

以1024为长度计算FFT,我们得到一个以1024为频点的频谱。谱的第二部分是多余的,因而实际处理我们只用前(N/2)+1个频点,在本例中也就是513。

我们用一个采样窗口长度为1024的FFT计算获取整个文件的频谱信息,每次计算向前滑动512个样本(hop length),这样采样窗口就会互相重叠。第二个文件将产生步长为259的频谱,可以看作是一张二维图像。我们把这些操作称为短时傅里叶变化(STFT),它可以提供一段时间内频率变化的信息。

代码语言:javascript
复制
stft = librosa.stft(clip, n_fft=n_fft, hop_length=hop_length)
stft_magnitude, stft_phase = librosa.magphase(stft)
stft_magnitude_db = librosa.amplitude_to_db(stft_magnitude)

本例中我们可以看到那些有趣的频率,所有低于12500 Hz的数据。另外可以看到有相当多的无用的频点,这些信息并没有准确反映人类是如何感知频率的。事实上人类是以对数尺度的频率结合声音强弱来进行感知的。我们可以分辨对数尺度上相同‘距离’的频率,比如50Hz到100Hz,这感受如同400Hz到800Hz的变化。

这就是为什么许多人会用 melspectrogram 表示频谱的原因,该操作即将频点转换为梅尔刻度(mel scale)。用Librosa库,可以方便的把常规的谱数据转换为melspectrogram格式,我们需要定义有多少“点” ,并给出需要划分的最大最小频率范围。

代码语言:javascript
复制
mel_spec = librosa.feature.melspectrogram(clip, n_fft=n_fft, hop_length=hop_length, n_mels=n_mels, sr=sample_rate, power=1.0, fmin=fmin, fmax=fmax)
mel_spec_db = librosa.amplitude_to_db(mel_spec, ref=np.max)

上面的melspectrogram我采用的频点数为64(n_mels)。不同点在于,右侧图像里只关注20Hz到8000Hz的频率范围。 这样显著减少了从最初513点每时点进行转换的规模。

用fastai分类声音频谱

虽然我们可以分类原始声音波形数据,但目前更流行用melspectrogram分类音频,这种方法相当好用。那么我们需要将整个数据集用上述方法转换为频谱图。在GCP实例上用了所有CPU,我大约花了10分钟处理完这些数据。以下是我生成melspectrogram用到的参数:

代码语言:javascript
复制

n_fft = 1024
hop_length = 256
n_mels = 40
f_min = 20
f_max = 8000
sample_rate = 16000

本文从此处往下,我采用的都是NSynth数据集,该数据集由Google Magenta团队提供。该数据集非常有趣,是由305979个音符组成,每段长4秒。我裁剪了这个数据集,只保留用声学方法生成的音符,这样管理起来相对简单。分类目标是从10种乐器家族中分辨出音符是由哪个乐器家族产生的。

用fastai最新的data_block API,大大简化了构建DataBunch对象的过程,数据集包括所有频谱图像机器对应的标签—— 本例中用正则表达式通过解析文件名获得所有分类标签。

代码语言:javascript
复制
NSYNTH_IMAGES = 'data/nsynth_acoustic_images'
instrument_family_pattern = r'(\w+)_\w+_\d+-\d+-\d+.png$'
data = (ImageItemList.from_folder(NSYNTH_IMAGES)
          .split_by_folder()
          .label_from_re(instrument_family_pattern)
          .databunch())

数据一旦加载完成实例化预训练卷积神经网络 (CNN) ,这里用的是 resnet18,然后在频谱上做fine-tune。

代码语言:javascript
复制
learn = create_cnn(data, models.resnet18, metrics=accuracy)
learn.fit_one_cycle(3)

2分14秒后,模型在验证集(与训练集完全分离的数据集)上准确度达到了84% 。当然数据一定有一些过拟合,这里没有做数据增强或类似正则化的优化,不过这是一个很不错的开始!

利用fastai提供的ClassificationInterpretation类,可以快速查看错误是从哪来的,如下:

代码语言:javascript
复制
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(figsize=(10, 10), dpi=60)

看起来木槌跟吉他有点混了,另外簧片声音月铜管乐器分不清。有了这些信息,我们可以更进一步查看这些乐器的频谱数据,看看是否可以调整参数,从而更好的分辨它们。

为什么在训练过程中生成频谱?

如果用图像分类音频效果这么好,你也许会问在训练过程中生成频谱图有什么好处(相对于之前的方法)。可能有这么几个原因:

  1. 生成图像的时间 前例中,我们花了10分钟产生所有图像的频谱图。我经常会尝试不同的参数设置,或把melspectrogram换成简单的STFT,这样就需要重新生成所有图片,这样就很难快速测试不同的参数配置。
  2. 磁盘空间 同样的每次生成数据集后,数据集就会占用大量磁盘空间,大小依赖于数据集以及变换。本例中,生成的数据占了1G空间。
  3. 数据增强 提升图像分类器性能的一个最有效的策略是采用数据增强。常规图像变换诸如(rotating, flipping, cropping等) 在谱分类算法中可能不怎么用得上。但是我们可以处理基于时域的音频文件,然后再转换为频谱,最后进行分类。
  4. GPU 与 CPU 过去我一直用 librosa 进行转换,主要用CPU。但我们可以用 PyTorch提供的stft方法,该方法可直接使用GPU处理,这样就会快很多,并且可以进行批处理 (而不是一次处理一张图)。

如何在训练过程中生成频谱?

前几天我一直在试验创建一个新的基于fastai的声音处理模块。后来参考great new fastai documentation,写出一个简单类用于加载原始音频文件,然后用PyTorch提供的方法使用GPU以批处理方式生成频谱。我也创建了一个 create_cnn 函数,裁剪预训练模型用以预测单通道数据(频谱) ,此前模型是使用3通道。让我惊喜的是,代码和图像分类器运行的速度差不多,不需要额外创建实际的图像。现在建立数据集的代码如下:

代码语言:javascript
复制
tfms = get_frequency_batch_transforms(n_fft=n_fft, 
                                      n_hop=n_hop, 
                                      n_mels=n_mels,
                                      sample_rate=sample_rate)
data = (AudioItemList
            .from_folder(NSYNTH_AUDIO)
            .split_by_folder()
            .label_from_re(instrument_family_pattern)
            .databunch(bs=batch_size, tfms=tfms))

fastai库支持预览批次中的数据:

代码语言:javascript
复制
data.show_batch(3)

在预训练模型上进行fine tuning跟之前步骤一样,这里不同的是需要把卷积的第一层修改为只接收单通道数据 (感谢fastai论坛的David Gutman).

代码语言:javascript
复制

learn = create_cnn(data, models.resnet18, metrics=accuracy)
learn.fit_one_cycle(3)

这一次训练多花了30秒,执行了3个epoch后在验证集上的精度为80%! 之前在CPU上创建整个数据集大约需要10分钟。这样就可以进行快速试验,可以微调频谱的参数,同时也可以对谱计算进行各种增强。

未来的工作

现在的方法已经可以通过不落地的方法直接生成不同谱的表示,我对如何通过数据增强改进原始音频文件非常感兴趣。在librosa库中有很多方法,从pitch shifting到time stretching,随机选出音频的一段,可以做很多实验。

同时比较感兴趣的地方是,如果预训练模型是基于声音图像(而不是基于图像的),能否达到更好的精度。

最后感谢阅读本文! 如果有任何评论或改进请告诉我。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 音频文件转图像
  • 用fastai分类声音频谱
  • 为什么在训练过程中生成频谱?
  • 如何在训练过程中生成频谱?
  • 未来的工作
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档