音频数据处理与神经网络训练

释放双眼,带上耳机,听听看~!
本文介绍了音频数据的处理和神经网络训练过程,包括短时傅里叶变换在TensorFlow框架中的应用,以及频谱分析的重要性。

本文正在参加《终于,掘金有人讲傅里叶变换了》。看完需要半个小时。

音频数据处理与神经网络训练

我上面说的,谷歌公司早就知道了。因此,他们在TensorFlow框架中,早就内置了获取音频频谱的函数。它采用的是短时傅里叶变换stft

waveform = tf.squeeze(audio, axis=-1)
spectrogram = tf.signal.stft(waveform, frame_length=255, frame_step=128)

我们上面通过tf.audio.decode_wav解析了音频文件,它返回的数据格式是[[-0.00238037][-0.0038147 ]]这种形式。

你可能好奇,它为什么不是[-0.00238037, -0.0038147 ]这种形式,非要外面再套一层。回忆一下,我们的紫色像素的例子,一个像素点表示为[[198, 102, 145]],这表示RGB三个色值通道描述一个彩色像素。其实,这里也一样,是兼容了多声道的情况。

但是,我们只要一个通道就好。所以需要通过tf.squeeze(audio, axis=-1)对数据进行降一个维度,把[[-0.00238037][-0.0038147 ]]变为[-0.00238037, -0.0038147 ]。这,才是一个纯粹的波形。嗯,这样才能交给傅里叶先生进行分析。

tf.signal.stft里面的参数,是指取小样的规则。就是从总波形里面,每隔多久取多少小样本进行分析。分析之后,我们也是可以像绘制波形一样,把分析的频谱结果绘制出来的。

音频数据处理与神经网络训练

看不懂上面的图没有关系,这很正常,非常正常,极其正常。因为,我即便用了一万多字,50多张图,专门做了详细的解释。但是依然,有20%左右的读者还是不明白。

不过,此时,你需要明白,一段声音的特性是可以通过科学的方法抽取出来的。这,就够了。

把特性抽取出来之后,我们就交给人工智能框架去训练了。

2.3 音频数据的预处理

上面,我们已经成功地获取到一段音频的重要灵魂:频谱。

下面,就该交给神经网络模型去训练了。

在正式交给模型之前,其实还有一些预处理工作要做。比如,给它切一切毛边,叠一叠,整理成同一个形状。

音频数据处理与神经网络训练

正如计算机只能识别0和1,很多框架也是只能接收固定的结构化数据。

举个简单的例子,你在训练古诗的时候,有五言的和七言的。比如:“床前明月光”和“一顿不吃饿得慌”两句。那么,最终都需要处理成一样的长短。要么前面加0,要么后边加0,要么把长的裁短。总之,必须一样长度才行。

床前明月光〇〇
一顿不吃饿得慌
蜀道难〇〇〇〇

那么,我们的音频数据如何处理呢?我们的音波数据经过短时傅里叶变换之后,格式是这样的:

<tf.Tensor: shape=(86, 129, 1), dtype=float32, numpy=
array([[[4.62073803e-01],
        ...,
        [2.92062759e-05]],

       [[3.96062881e-01],
        [2.01166332e-01],
        [2.09505502e-02],
        ...,
        [1.43915415e-04]]], dtype=float32)>

这是因为我们11146长度的音频,经过tf.signal.stftframe_step=128分割之后,可以分成86份。所以我们看到shape=(86, 129, 1)。那么,如果音频的长度变化,那么这个结构也会变。这样不好。

因此,我们首先要把音频的长度规范一下。因为采样率是16000,也就是1秒钟记录16000次音频数据。那么,我们不妨就拿1秒音频,也就是16000个长度,为一个标准单位。过长的,我们就裁剪掉后面的。过短的,我们就在后面补上0。

我说的这一系列操作,反映到代码上,就是下面这样:

waveform = tf.squeeze(audio, axis=-1)
input_len = 16000
waveform = waveform[:input_len]
zero_padding = tf.zeros([16000] - tf.shape(waveform),dtype=tf.float32)
waveform = tf.cast(waveform, dtype=tf.float32)
equal_length = tf.concat([waveform, zero_padding], 0)
spectrogram = tf.signal.stft(equal_length, frame_length=255, frame_step=128)
spectrogram = tf.abs(spectrogram)
spectrogram = spectrogram[..., tf.newaxis]

这时候,再来看看我们的频谱数据结构:

<tf.Tensor: shape=(124, 129, 1), dtype=float32, numpy=
array([[[4.62073803e-01],
        ...,
        [2.92062759e-05]],
...
        [0.00000000e+00],
        ...,
        [0.00000000e+00]]], dtype=float32)>

现在,不管你输入任何长短的音频,最终它的频谱都是shape=(124, 129, 1)。从图上我们也可以看出,不足的就算后面补0,也得凑成个16000长度。

音频数据处理与神经网络训练

下面,真的要开始构建神经网络了。

2.4 构建模型和训练

依照老张的要求……我现在不想提他,因为我的手指被绿萝电的还有点发麻。

依照要求……他要四种命令,分别是:前进、停止、左转、右转。那么,我就搞了四种音频,分别放在对应的文件夹下面。

音频数据处理与神经网络训练

从文件夹读取数据、将输入输出结对、按照比例分出数据集和验证集,以及把datasets划分为batch……这些操作,在TensorFlow中已经很成熟了。而且,随着版本的更新,越来越成熟。体现在代码上,就是字数越来越少。此处我就不说了,我会把完整代码上传到github,供诸君参考。

下面,我重点说一下,本例子中,实现语音分类,它的神经网络的结构,以及模型训练的配置。

import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras import models

model = models.Sequential([
       layers.Input(shape= (124, 129, 1)),
       layers.Resizing(32, 32),
       layers.Normalization(),
       layers.Conv2D(32, 3, activation='relu'),
       layers.Conv2D(64, 3, activation='relu'),
       layers.MaxPooling2D(),
       layers.Dropout(0.25),
       layers.Flatten(),
       layers.Dense(128, activation='relu'),
       layers.Dropout(0.5),
       layers.Dense(4),
])
model.compile(
       optimizer=tf.keras.optimizers.Adam(),
       loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
       metrics=['accuracy']
)

其实,我感觉人工智能应用层面的开发,预处理和后处理比较难。中间的模型基本上都是有固定招式的。

第1层layers.Input(shape= (124, 129, 1))叫输入层,是训练样本的数据结构。就是我们上一节凑成16000之后,求频谱得出的(124, 129)这个结构。

最后一层layers.Dense(4),是输出层。我们搞了“走”,“停”,“左”,“右”4个文件夹分类,最终结果是4类,所以是4

头尾基本固定后,这个序列Sequential就意味着:吃音频文件,然后排出它是4个分类中的哪一种。

那么中间我们就可以自己操作了。Normalization是归一化。Conv2D是做卷积。MaxPooling2D是做池化。Dropout(0.25)是随机砍掉一定比例(此处是25%)的神经网络,以保证其健壮性。快结束时,通过Flatten()将多维数据拉平为一维数据。后面给个激活函数,收缩神经元个数,准备降落。最后,对接到Dense(4)

这就实现了,将前面16000个音频采样点,经过一系列转化后,最终输出为某个分类。

音频数据处理与神经网络训练

最后,进行训练和保存模型。

model = create_model()
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath='model/model.ckpt',
       save_weights_only=True,
       save_best_only=True)
history = model.fit(
       train_ds,
       validation_data=val_ds,
       epochs=50,
       callbacks=[cp_callback]
)

filepath='model/model.ckpt'表示训练完成后,存储的路径。save_weights_only=True只存储权重数据。save_best_only=True意思是只存储最好的训练的结果。调用训练很简单,调用model.fit,传入训练集、验证集、训练轮数、以及训练回调就可以啦。

2.5 加载模型并预测

上一节中,我们指定了模型的保存路径,调用model.fit后会将结果保存在对应的路径下。这就是我们最终要的产物:

音频数据处理与神经网络训练

我们可以加载这些文件,这样就让我们的程序具备了多年功力。可以对外来音频文件做预测。

model = create_model()
if os.path.exists('model/model.ckpt.index'):
       model.load_weights('model/model.ckpt')  
labels = ['go', 'left', 'right', 'stop']
# 音频文件转码
audio = get_audio_data('mysound.wav')
audios = np.array()
predictions = model(audios)
index = np.argmax(predictions[0])
print(labels[index])

上面代码中,先加载了历史模型。然后,将我录制的一个mysound.wav文件进行预处理,方式就是前面说的凑成16000,然后通过短时傅里叶解析成(124, 129)结构的频谱数据。这也是我们训练时的模样。

最后,把它输入到模型。出于惯性,它会顺势输出这是'go'分类的语音指令。尽管这个模型,从来没有见过我这段动听的嗓音。但是它也能识别出来,我发出了一个包含'go'声音特性的声音。

以上,就是利用TensorFlow框架,实现声音分类的全过程。

音频分类项目开源地址:github.com/hlwgy/sound

再次提醒大家:要求TensorFlow 2.6(2021年10之后版) + python 3.9。因为,里面用了很多新特性。旧版本是跑不通的,具体体现在TensorFlow各种找不到层。

三、我们的合作

我带着成果去找老张。老张沉默了一会儿,不说话。

我说,老张啊,你就说吧。你不说话,我心里没底,不知道会发生啥。

老张说,兄弟啊,其实语音小车这个项目,没啥创意。我昨天才知道,我们车间老王,三年前,自己一个人,就做出来过了。说完,老张又沉默了。

我安慰他说,没关系的。这个不行,你就再换一个呗。

老张猛然抬起头,眼睛中闪着光,他说:兄弟,宇宙飞船相关的软件,你搞得定吗?!火星车也行。

我不紧不忙地关闭服务,并把电脑收进包里。

我穿上鞋,然后拿上包。打开门,回头跟老张说了一句:兄弟,三个月内,我们先不联系了吧。

我是ITF男孩,在掘金是TF男孩,带你从IT视角看世界。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

Hugging News: 新推理API功能、模型分类页面、Flash Attention 2和ChatGPT教程

2023-11-25 20:37:14

AI教程

浅谈使用正则化防止过拟合(上)

2023-11-25 20:48:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索