Press "Enter" to skip to content

张海腾:语音识别实践教程

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

作者:张海腾,标贝科技,Datawhale优秀学习者

 

 


为智能
语音交互相关的从业者,今天以天池学习赛:
《零基础入门语音识别:
食物声音识别》为例,带大家梳理一些自动语音识别技术(ASR)关的知识,同时给出线上可运行的完整代码实践,供大家练习。

 

实践背景

 

赛题名称:零基础入门语音识别-食物声音识别

 

实践地址:
(文末阅读全文或复制链接)
https://tianchi.aliyun.com/competition/entrance/531887/information

 

语音相关知识点梳理

 

一些在我司常听到的关键词

 

语音不像文本,可以看得见,仅有对应的音频,需要对语音有一个“可以看见”的过程,于是有了下列的几种音频文件的表示方法:

 

1)波形图

 

语音的保存形式可用波形图展现,可以看作是上下摆动的数字序列,每一秒的音频用16000个电压数值表示,采样率即为16kHz。

2)采样点

 

采样点是对波形图的放大,可以看到的更细的单位

 

 

3)频谱图

 

可以变为频谱图,颜色代表频带能量大小,语音的傅立叶变换是按帧进行,短的窗口有着高时域和低频域,长时窗口有低时域和高频域

 

 

4)基本单位

 

对于语音而言,基本单位是帧(对应文本的token),一帧即是一个向量,整条语音可以表示为以帧为单位的向量组。帧是由ASR的前端声学特征提取模块产生,提取的技术设计“离散傅立叶变换”和”梅尔滤波器组“

 

整体解决思路

 

在我的理解认知中,对于ASR的解决方法可以分为两种,一种是声学模型加语言模型的组合,另外一种是端到端的解决方式。

第一种方式:

 

路线的个人理解大约是,有一个音频,先有声学模型,将对应的音频信号处理为对应的声学特征,再有语言模型,将声学特征的结果得到概率最大的输出字符串。

 

在上图中,代表的是声学特征向量,代表输出的文本序列,在(2.1)中,代表的是声学模型,代表的是语言模型

 

第二种方式:

 

端到端的解决手段,个人印象中在吴恩达的课程里提到,ASR在CTC提出后有一个较大的提升。个人理解是在CTC之前,seq2seq的建模方式比较难处理输出序列远短于输入序列的情况,以及在不同帧出现的相同音素的输出

 

其他术语

 

声学模型:
常用的话,包括了H
MM,GMM,DN N-HM的声学模型。

 

语言模型:
常用的语言模型这里包括了n-gram语言模型以及RNN的语言模型。

 

解码器:
最终目的是取得最大概率的字符输出,解码本质上是一个搜索问题,并可借助加权有限状态转换器(Weighted Finite State Transducer,WFST) 统一进行最优路径搜索。

 

端到端的方法:
seq2seq+ 损失函数, RNN Transducer, Transforme,这里需要补充的话 应该要去看李宏毅2020年的人类语言处理的课程。

 

完整实践代码

 

本代码已经部署到天池DSW实验平台上,可直接免配置环境运行,对于DSW不熟悉的学习者可参考: 小白如何用免费GPU跑天池算法大赛!

 

赛题介绍:
有20种不同食物的咀嚼声音,给出对应的音频,对声音的数据进行建模
,判断是哪种食物的咀嚼声音

 

Baseline思路

将对应的音频文件,使用librosa转化为梅尔谱作为输入的特征,用CNN对梅尔谱的特征进行建模分类预测。

 

Bas
eline

址:
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12
586969.1002.3.78ac14e9eP5wk4&postId=198902

 

数据获取

 

# 下载数据集
!wget [http://tianchi-competition.oss-cn-hangzhou.aliyuncs.com/531887/train_sample.zip](http://tianchi-competition.oss-cn-hangzhou.aliyuncs.com/531887/train_sample.zip)
!wget [http://tianchi-competition.oss-cn-hangzhou.aliyuncs.com/531887/test_a.zip](http://tianchi-competition.oss-cn-hangzhou.aliyuncs.com/531887/test_a.zip)
# 解压并删除压缩包
!unzip -qq train_sample.zip
!\rm train_sample.zip
!unzip -qq test_a.zip
!\rm test_a.zip

 

基本库及模型框架导入

 

环境要求:

 

TensorFlow的版本:2.0 +

 

keras

 

sklearn

 

librosa

 

#基本库
import pandas as pd 
import numpy as np
from sklearn.model_selection import train_test_split #数据分割
from sklearn.metrics import classification_report #评价
from sklearn.model_selection import GridSearchCV #网格搜索,最优模型选择
from sklearn.preprocessing import MinMaxScaler #特征标准化

 

# 加载深度学习框架
# 搭建分类模型所需要的库
from tensorflow.keras.models import Sequential 
from tensorflow.keras.layers import Conv2D, Flatten, Dense, MaxPool2D, Dropout
from tensorflow.keras.utils import to_categorical 
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC

 

# 安装librosa Librosa是一个用于音频、音乐分析、处理的python工具包。
# 一些常见的时频处理、特征提取、绘制声音图形等功能应有尽有,功能十分强大
!pip install librosa --user 

 

# 其他库
#音频处理
import librosa
import librosa.display
#文件处理
import glob
import os

 

特征提取以及数据集的建立

 

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()}

 

#特征提取
from tqdm import tqdm #提示进度条
def extract_features(parent_dir, sub_dirs, max_file=10, file_ext="*.wav"):#提取特征以及标签
    c = 0
    label, 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]

 

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以及类别的label
temp = extract_features(parent_dir,sub_dirs,max_file=100)

 

temp = np.array(temp)#列表转换成矩阵
data = temp.transpose()#矩阵转置

 

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

 

#数据集划分
#训练集的大小 750
#测试集的大小 250
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state = 1, stratify=Y)
#random_state 是随机数的种子。

 

建立模型

 

1、搭建CNN网络

 

model = Sequential()#多个网络层的线性堆叠
# 输入的大小
input_dim = (16, 8, 1)
model.add(Conv2D(64, (3, 3), padding = "same",  = "tanh", input_shape = input_dim))# 卷积层
# 输入shape
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))
#为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时按一定概率(rate)随机断开输入神经元,Dropout层用于防止过拟合。
model.add(Flatten()) # 展开
model.add(Dense(1024, activation = "tanh"))
model.add(Dense(20, activation = "softmax")) # :20个units输出20个类的概率
# 编译模型,设置损失函数,优化方法以及评价标准
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

 

model.summary()

 

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

 

2、预测测试集

 


#提取测试集数据特征
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/')

 

#输出测试结果
preds = 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
!wc -l submit.csv
#利用wc指令我们可以计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据。

 

 

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注