失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 零基础入门语音识别-食物声音识别[Task 1]

零基础入门语音识别-食物声音识别[Task 1]

时间:2023-12-01 19:44:01

相关推荐

零基础入门语音识别-食物声音识别[Task 1]

Task1 食物声音识别之Baseline学习

作为零基础入门语音识别的新人赛,本次任务不涉及复杂的声音模型、语言模型,希望大家通过两种baseline的学习能体验到语音识别的乐趣。

任务说明:我们提供了两种Baseline供大家学习,基本要求是大家跟着提示将两条Baseline跑通。

代码:/notebook-ai/detail?spm=5176.12586969.1002.3.750a204aZZZHfu&postId=198902,本文的代码均来自此链接,且运行结果与链接一致,这里就不再贴出每一步骤的运行结果。此Task的目的是只是了解运行环境设定,代码及其使用函数的作用的概述,初步跑通结果对解决问题建立初步的信心。对具体的问题解决思路,方法选择将在后续的Task中完成。

目前在天池当中跑出的结果与上述代码一致。跑分的结果如下:

1 基于CNN的分类模型:

参考图片分类的形式,将不同声音的频谱做分类。这种形式虽然不能准确识别人说话的文本,但对于本次任务中区分不同类别的声音任务是足够的。

1.1 环境准备

TensorFlow的版本:2.0 +keras1sklearn2librosa3

安装音频处理库

!pip install librosa --user

加载基本库

# 基本库import pandas as pdimport numpy as np

加载Sklearn库

# Sklearn库from sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_reportfrom sklearn.model_selection import GridSearchCVfrom sklearn.preprocessing import MinMaxScaler

sklearn.model_selection这个模块主要是对数据的分割,以及与数据划分相关的功能。其中train_test_split()函数是用来随机划分样本数据为训练集和测试集的,当然也可以人为的切片划分。而GridSearchCV的名字其实可以拆分为两部分,GridSearch和CV,即网格搜索和交叉验证。这两个名字都非常好理解。网格搜索,搜索的是参数,即在指定的参数范围内,按步长依次调整参数,利用调整的参数训练学习器,从所有的参数中找到在验证集上精度最高的参数,这其实是一个训练和比较的过程。

sklearn.metrics里面会有很多的评价指标,即用于检验机器学习模型效果的定量指标。

sklearn.preprocessing这个模块主要是对数据进行预处理的,主要功能可以概括成标准化和归一化。MinMaxScaler是一个归一化的函数,可以将范围所放在指定的范围区间内

加载深度学习框架

from tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Conv2D, Flatten, Dense, MaxPool2D, Dropoutfrom tensorflow.keras.utils import to_categorical

from sklearn.ensemble import RandomForestClassifierfrom sklearn.svm import SVC

sklearn.ensemble库,支持众多集成学习算法和模型。RandomForestClassifier是非常具有代表性的Bagging集成算法,它的所有基评估器都是决策树,分类树组成的森林就叫做随机森林分类器,回归树所集成的森林就叫做随机森林回归器。

sklearn.svm库支持向量机,主要用于分类,回归和异常检测。SVC全称是C-Support Vector Classification,是一种基于libsvm的支持向量机,由于其时间复杂度为O(n^2),所以当样本数量超过两万时难以实现。

加载其他库

# 其他库import osimport librosaimport librosa.displayimport glob

os是python环境下对文件,文件夹执行操作的一个模块。

librosa.display模块并不默认包含在librosa中,使用时要单独引入,是用来做音频特征的显示。

glob是UNIX程序,用来匹配路径文件名

1.2 数据准备

采用wget函数在网络上下载需要的训练样本以及测试样本,并用unzip函数来解压,其中-qq的含义是使解压缩尽可能安静,只报告错误,然后用\rm命令删除.zip文件

# 获取训练样本!wget http://tianchi-competition.oss-cn-/531887/train_sample.zip# 解压训练样本!unzip -qq train_sample.zip!\rm train_sample.zip# 获取测试样本!wget http://tianchi-competition.oss-cn-/531887/test_a.zip# 解压测试样本!unzip -qq test_a.zip!\rm test_a.zip

1.3 特征提取及数据集建立

做好上述准备以后,我们开始进入数据的特征提取,首先建立特征值矩阵,以及标签矩阵,给不同的声音建立类别名称以及唯一的数字编号与之对应,然后将数字编号和类别调换位置,从矩阵的角度看是逆矩阵,具体代码如下:

feature = []label = []# 建立类别标签,不同类别对应不同的数字。label_dict = {'aloe': 0, 'burger': 1, 'cabbage': 2,'candied_fruits':3, 'carrots': 4, 'chips':5,'chocolate': 6, 'drinks': 7, 'fries': 8, 'grapes': 9, 'gummies': 10, 'ice-cream':11,'jelly': 12, 'noodles': 13, 'pickles': 14, 'pizza': 15, 'ribs': 16, 'salmon':17,'soup': 18, 'wings': 19}label_dict_inv = {v:k for k,v in label_dict.items()}

定义一个函数extract_features对输入的路径下的音频文件提取特征值

为了方便知道处理的状态,这里采用了一个Tqdm 库,它是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator)。

glob.glob()函数返回所有匹配的文件路径列表。这里就是获取路径下的所有.wav文件列表。[:max_file]表示最多11个文件即[0:10],这里是函数自限定,也可以由输入函数定义。

os.path.join()函数用来连接两个或更多的路径名组件: 1.如果各组件名首字母不包含’/’,则函数会自动加上; 2.如果有一个组件是一个绝对路径,则在它之前的所有组件均会被舍弃; 3.如果最后一个组件为空,则生成的路径以一个’/’分隔符结尾。在下述代码中,最后的就是parent_dir\sub_dir*.wav

split()函数是通过指定分隔符对字符串进行切片,下面代码中fn.split(’/’)[-2]代表的意思是在字符串fn中以自负’/'进行切片,在切片的数组中选择从右到左第二个赋给label_name

alist.extend(blist)函数的意思是将参数中的blist添加到alist的数组中。

librosa.load()函数用途是读取文件,可以是wav、mp3等格式。audio, sample_rate = librosa.load(file_name, res_type=‘kaiser_fast’) 表示采用更快的方法读取音频文件,并将res_type设置成’kaiser_fast’。.load函数返回的是音频的时间序列和音频的采样率

librosa.feature.melspectrogram(y=X,sr=sample_rate).T将梅尔频谱的转置进行转置,并用np.mean(a, axis=0),其中axis=0表示压缩行,对各列求均值,返回 1* n 矩阵,并将得到的mels矩阵加入特征值数组中

# 进度条显示工具from tqdm import tqdmdef extract_features(parent_dir, sub_dirs, max_file=10, file_ext="*.wav"):c = 0label, feature = [], []for sub_dir in sub_dirs:for fn in tqdm(glob.glob(os.path.join(parent_dir, sub_dir, file_ext))[:max_file]): # 遍历数据集的所有文件# segment_log_specgrams, segment_labels = [], []#sound_clip,sr = librosa.load(fn)#print(fn)label_name = fn.split('/')[-2]label.extend([label_dict[label_name]])X, sample_rate = librosa.load(fn,res_type='kaiser_fast')mels = np.mean(librosa.feature.melspectrogram(y=X,sr=sample_rate).T,axis=0) # 计算梅尔频谱(mel spectrogram),并把它作为特征feature.extend([mels])return [feature, label]

将目录设置为网络获取的训练样本的地址,并创建声音分类路径名称数据,将其作为输入带入到上面定义的extract_features函数中,可以得到训练样本的音频数据的特征值矩阵

# 自己更改目录parent_dir = './train_sample/'save_dir = "./"folds = sub_dirs = np.array(['aloe','burger','cabbage','candied_fruits','carrots','chips','chocolate','drinks','fries','grapes','gummies','ice-cream','jelly','noodles','pickles','pizza','ribs','salmon','soup','wings'])# 获取特征feature以及类别的labeltemp = extract_features(parent_dir,sub_dirs,max_file=100)

将特征矩阵创建为ndarray数组,这是利用了numpy相较于python的array (对象实际为指向内存位置的指针)的计算的优势,并将得到的数组进行转置。

temp = np.array(temp)data = temp.transpose()

np.vstack函数的作用是按垂直方向(行顺序)堆叠数组构成一个新的数组,

# 获取特征X = np.vstack(data[:, 0])# 获取标签Y = np.array(data[:, 1])print('X的特征尺寸是:',X.shape)print('Y的特征尺寸是:',Y.shape)

在Keras库中:to_categorical就是将类别向量转换为二进制(只有0和1)的矩阵类型表示。其表现为将原有的类别向量转换为独热编码的形式。下面的代码将Y的矩阵变为Y.shape*Y中不重复的个数

# 在Keras库中:to_categorical就是将类别向量转换为二进制(只有0和1)的矩阵类型表示Y = to_categorical(Y)

'''最终数据'''print(X.shape)print(Y.shape)

调用train_test_split函数用以划分样本特征集,这个函数的输入有

输入:

X = 所要划分的样本特征集Y = 所要划分的样本结果random_state = 随机数组,这里赋值为1那么每次执行所用的随机数组都是相同的stratify = 保持分类的比例与所要划分的样本结果相同

返回的是按照样本结果比例随机抽取的训练和测试样本特征集以及样本结果。

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state = 1, stratify=Y)print('训练集的大小',len(X_train))print('测试集的大小',len(X_test))

将训练和测试特征集按照[-1,16,8,1]形式重新组合成矩阵,其中-1的作用是在这一维度上不做限制,numpy会根据数据的总数以及其他维度的列数信息自动计算出这一维度的列数。而为什么是这样的重组矩阵呢?后面会说。

X_train = X_train.reshape(-1, 16, 8, 1)X_test = X_test.reshape(-1, 16, 8, 1)

1.4 搭建CNN网络

构建一个默认的sequential模型

定义输入数据的维度(通道,高,长)

Cov2D函数是用来,用法是Conv2d(filters, kernel_size, padding=‘same’, activation = “tanh”, input_shape = input_dim),其中

filters卷积核的数量,决定输出数据的最后一维(channels)的大小。

kernel_size卷积核的大小(形状),决定输出数据 height, width 维度的大小。

padding等于’valid’时卷积核在输入数据形状(height, width)最大范围内进行移动,等于’same’时如果(height, width)没有恰好完全符合设定的kernel_size和strides,超出的部分将会填充0.

activation激活函数,默认为none,为线性,在这种情况下,每一层的输出都是上一层的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这与一个隐藏层的效果相当(这种情况就是多层感知机MPL)。但当我们需要进行深度神经网络训练(多个隐藏层)的时候,如果激活函数仍然使用线性的,多层的隐藏函数与一层的隐藏函数作用的相当的,就失去了深度神经网络的意义,所以引入非线性函数作为激活函数。

input_shape输入数据形状

Maxpool2D池化,作用是将生成的卷积核进一步池化成Maxpool2D函数中定义的池大小的矩阵,就是在取卷积核中变换成池矩阵时,例如99卷积核矩阵的对应33的池矩阵,则需要计算每个3*3卷积核矩阵中的最大值并将其赋予池矩阵的1个值

Dropout可以让模型训练时,随机让网络的某些节点不工作(输出置零),也不更新权重(但会保存下来,下次训练得要用,只是本次训练不参与bp传播),其他过程不变。我们通常设定一个dropout radio=p,即每个输出节点以概率p置0(不工作,权重不更新),假设每个输出都是独立的,每个输出都服从二项伯努利分布p(1-p),则大约认为训练时,只使用了(1-p)比例的输出,相当于每次训练一个子网络。测试的时候,可以直接去掉Dropout层,将所有输出都使用起来,为此需要将尺度对齐,即比例缩小输出 r=r*(1-p)。需要注意的是训练的时候需要dropout,测试的时候直接去掉。

Flatten层用来将输入“压平”,即把多维的输入一维化。但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。

Dense层就是所谓的全连接神经网络层,这里定义了一个有1024个节点,使用tanh激活函数的神经层。同时也定义了一个有20个节点,使用softmax激活函数的神经层。

pile方法用于在配置训练方法时,告知训练时用的优化器、损失函数和准确率评测标准。这里adam是优化器,损失函数为categorical_crossentropy,亦称作多类的对数损失,注意使用该目标函数时,需要将标签转化为形如(nb_samples, nb_classes)的二值序列。

model = Sequential()# 输入的大小input_dim = (16, 8, 1)model.add(Conv2D(64, (3, 3), padding = "same", activation = "tanh", input_shape = input_dim))# 卷积层model.add(MaxPool2D(pool_size=(2, 2)))# 最大池化model.add(Conv2D(128, (3, 3), padding = "same", activation = "tanh")) #卷积层model.add(MaxPool2D(pool_size=(2, 2))) # 最大池化层model.add(Dropout(0.1))model.add(Flatten()) # 展开model.add(Dense(1024, activation = "tanh"))model.add(Dense(20, activation = "softmax")) # 输出层:20个units输出20个类的概率# 编译模型,设置损失函数,优化方法以及评价标准pile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

输出模型各层的参数状况

model.summary()

model.fit()函数的作用是利用输入的训练集特征值和训练集标签,迭代epochs出20次,并且每个batch的大小为15,并设置用当前的训练集特征值和训练集标签来评价每次迭代后的损失。

# 训练模型model.fit(X_train, Y_train, epochs = 20, batch_size = 15, validation_data = (X_test, Y_test))

1.5 预测测试集

用上述处理训练集的方法处理测试集,因为对于测试集并没有子目录和文件数量限制,所以定义一个新的extract_features函数用来提取测试集的特征值

# 训练模型def extract_features(test_dir, file_ext="*.wav"):feature = []for fn in tqdm(glob.glob(os.path.join(test_dir, file_ext))[:]): # 遍历数据集的所有文件X, sample_rate = librosa.load(fn,res_type='kaiser_fast')mels = np.mean(librosa.feature.melspectrogram(y=X,sr=sample_rate).T,axis=0) # 计算梅尔频谱(mel spectrogram),并把它作为特征feature.extend([mels])return feature

将测试集的实际文件夹代入上述函数,得到测试集的特征值

X_test = extract_features('./test_a/')

X_test = np.vstack(X_test)predictions = model.predict(X_test.reshape(-1, 16, 8, 1))

reds = np.argmax(predictions, axis = 1)preds = [label_dict_inv[x] for x in preds]path = glob.glob('./test_a/*.wav')result = pd.DataFrame({'name':path, 'label': preds})result['name'] = result['name'].apply(lambda x: x.split('/')[-1])result.to_csv('submit.csv',index=None)

列示出测试集下所有的音频文件行数

!ls ./test_a/*.wav | wc -l

显示文件的行数,其中包含了total的一行,所以比上述的音频文件行数多一行

!wc -l submit.csv

2 基于LSTM的分类模型:[未实现]

通过数据预处理、特征提取、划分数据集以及训练模型等步骤给声音数据做分类。

Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。 ↩︎

Scikit-learn(sklearn)是机器学习中常用的第三方模块,对常用的机器学习方法进行了封装,包括回归(Regression)、降维(Dimensionality Reduction)、分类(Classfication)、聚类(Clustering)等方法。 ↩︎

Librosa是一个用于音频、音乐分析、处理的python工具包,一些常见的时频处理、特征提取、绘制声音图形等功能应有尽有,功能十分强大。 ↩︎

如果觉得《零基础入门语音识别-食物声音识别[Task 1]》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。