内容提要
- 项目分析
- 预备知识
- 项目实战
一、项目分析
1、问题提出
数字0-9是我们生活中常见的10个基数,在医院、银行、饭店等场所,由于资源和人手的受限,人们必须排队等候服务,叫号系统应运而生。
任何一个数字,都是由10个基数构成的,英文叫号系统在播报序号时,如果能将对应的阿拉伯数字及时展示在大屏上,这对于以非英语为母语的顾客而言,无疑是一个很好的福音,能帮助他们不会因语言障碍而错过漫长的排队。因此,有必要借助于机器来实现英语数字的识别。
下面,我们利用语音特征提取技术和卷积神经网络模型,对数字语音进行识别以解决上述问题。
2、解决方案
为识别出一段语音中的数字,一种简单的实现方法是首先将语音进行分段切分,按说话的停顿切分出每个单词,然后提取每个单词的语音特征。
其次构建一个多层CNN分类器,利用0-9的语音样本集对模型进行训练,得到满足精度要求的模型。
最后利用训练好的模型逐个对提取的单词语音特征进行分类,看它属于0-9中的那个数字,然后将分类出的数字组合起来就得到最终的识别结果。
二、预备知识
语音识别过程中,要用到语音检测模块webrtcvad、语音特征提取模块python_speech_features和飞桨框架paddlepaddle。
1、webrtcvad模块
webrtcvad模块是一个语音活动检测器的python接口,通过VAD(Voice Activity Detection)算法将音频数据分类为有声或无声,据此来判断语音的开始和结束,也就能从数字语音中切分出一个个的单词。
【案例1】切分音频文件中有效的语音数据
编程前要使用如下命令先安装webrtcvad模块:
pip3 install webrtcvad
然后编程完成语音切分,实现的源代码(case1.ipynb)如下。
1 import scipy.io.wavfile as wav
2 import webrtcvad
3 import numpy as np
4 samp_rate, signal_data = wav.read('data/1_5.wav')
5 vad = webrtcvad.Vad(mode=3)
6 signal= np.pad(signal_data,(0,160-(signal_data.shape[0]%int(samp_rate*0.02))),'constant')
7 lens = signal.shape[0]
8 signals = np.split(signal, lens//int(samp_rate*0.02))
9 audio = [];audios = []
10 for signal_item in signals:
11 if vad.is_speech(signal_item,samp_rate):
12 audio.append(signal_item)
13 elif len(audio)>0 and (not vad.is_speech(signal_item,samp_rate)):
14 audios.append(np.concatenate(audio, 0))
15 audio= []
代码行4 是读取音频文件1_5 .wav 的采样频率和音频数据, 代码行5 是构建一个激进模式为3 的声音分类对象vad , 代码行6 是以帧长为20ms 来分帧, 对最后一帧长度不足160 的部分进行补0 , 然后在代码行8 中对语音信号进行分割, 形成一个个长度为160 的数据帧。
代码11一15行是对分割后的数据帧进行判断, 如果是语音帧,则在变量audio 中进行累计, 如果不是语音帧且语音结束, 则将语音帧统一转换成一维数组audios 输出。
audios 的内容如下图所示。
从上图可以看出, 英文数字1 和5 的语音信号被成功切分出来, 分别保存到两个一维数组中。
2、python_speech_features模块
有效音频信号被切分出来后,如何辨别这些音频信号具有独特的特征呢?
这时就要用到python_speech_features模块,该模块提供了计算一个音频信号的梅尔频率倒谱系数MFCC(Mel Frequency Cepstral Coefficients)特征和一阶、二阶差分系数的方法。
MFCC特征向量描述了一帧语音的静态特征,一阶差分系数、二阶差分系数描述了帧之间的动态信息,三者的结合就比较完整地描述了音频信号的全部特征。
【案例2】先使用如下命令安装python_speech_features模块。
pip3 install python_speech_features
然后编写如下代码,实现语音特征的提取。
1 from python_speech_features import mfcc,delta
2 wav_feature = mfcc(audios[0],8000)
3 d_mfcc_feat = delta(wav_feature,1)
4 d_mfcc_feat2 = delta(wav_feature,2)
5 feature = np.concatenate([wav_feature.reshape(1,-1,13),d_mfcc_feat.reshape(1,-1,13),
d_mfcc_feat2.reshape(1,-1,13)], 0)
6 if feature.shape[1]>64:
7 feature = feature[:,:64,:]
8 else:
9 feature = np.pad(feature,((0,0),(0,64-feature.shape[1]),(0,0)),'constant')
10 feature = feature.transpose((2,0,1))
11 feature = feature[np.newaxis,:]
代码行2计算案例1中切分出来的第一个语音信号audios[0]的MFCC特征值,代码行3-4分别计算这些语音帧的一阶、二阶差分系数。
代码行5将所有特征值转换成三维矩阵并合并,合并后的三维矩阵含有3个多行13列的二维矩阵。
代码行6-9对每个二维特征矩阵的行数(高度)进行截取或填充,不足64行的在后面填充0,以保证经处理后的特征矩阵feature是一个3通道64×13的矩阵。
代码行10将特征矩阵feature进行转置,把原来的矩阵形状(0,1,2)转变为(2,0,1),即三维数组中某一元素原来的索引坐标(x,y,z)调换为(z,x,y),变换后的矩阵则是一个13通道3×64的矩阵。
代码行11是把特征矩阵feature增加一个新的维度,由原来的三维变成四维,特征矩阵转换的目的是满足网络模型对输入数据体的要求。提取后的特征feature如图所示。
3、paddlepaddle框架
Paddlepaddle(飞桨)是百度公司提供的开源开放的一个产业级深度学习框架,有全面的官方支持的工业级应用模型,涵盖自然语言处理、计算机视觉、推荐引擎等多个领域,并开放多个领先的预训练模型。
飞桨同时支持稠密参数和稀疏参数场景的大规模深度学习并行训练,支持千亿规模参数、数百个节点的高效并行训练。
另外,飞桨拥有多端部署能力,支持服务器端、移动端等多种异构硬件设备的高速推理,预测性能有显著优势。目前PaddlePaddle已经实现了API的稳定和向后兼容,具有完善的中英双语使用文档。
飞桨的应用框架如图所示:
示意图的上半部分是从开发、训练到部署的全流程工具,下半部分是预训练模型、各领域的开发套件和模型库等模型资源。
飞桨除提供用于模型研发的基础框架外,还推出了一系列的工具组件,来支持深度学习模型从训练到部署的全流程。
由此可见,利用百度飞桨能节省编写大量底层代码的精力,用户只需关注模型的逻辑结构即可。
同时,深度学习工具简化了计算,降低了深度学习入门门槛,这对于学习者来说,无疑是个很大的福音。另外,利用飞桨具备灵活移植性的特点,可将代码部署到CPU/GPU/移动端上,选择具有分布式性能的深度学习工具会使模型训练更高效,省去了部署和适配环境的烦恼。
【案例3】搭建一个房价预测的神经网络模型。
1 import paddle.fluid as fluid
2 from paddle.fluid.dygraph import Linear
3 class Regressor(fluid.dygraph.Layer):
4 def __init__(self):
5 super().__init__()
6 self.fc=Linear(input_dim=1,output_dim=1,act=None)
7 def forward(self,inputs):
8 x=self.fc(inputs)
9 return x
代码行1导入飞桨的主包fluid,目前大部分的实用函数均在paddle.fluid包内。
代码行2从动态图的类库dygraph中导入全连接线性变换类Linear。
代码行3-9是定义一个线性回归网络Regressor,其中中间只有一个全连接层fc,输入维度为1(房屋面积),输出维度为1(房屋价格),因为模型只是一个线性回归模型,所以定义激活函数为None,通过前向计算函数来构建网络结构,实现前向计算过程,并返回预测结果,在本引例中是返回房价预测结果。
三、项目实战
3.1 提取音频的语音特征数据
我们事先录制了一段单声道、8k、16bit的数字语音audio.wav,为方便提取语音特征,减少代码冗余和提高代码的可移植性。
我们将上一节中的案例1和案例2的代码封装在类VioceFeature中,通过调用语音切分方法vad和特征提取方法get_mfccw来完成音频文件的特征数据提取任务,为后续进一步的语音识别做好数据准备工作。
新建文件task1.ipynb,根据任务目标,按照以下步骤和操作,完成任务一。
任务目标:
提取音频文件audio.wav的语音特征数据,按后续语音识别网络模型的输入数据格式要求,得到一个形状为(n,13,3,64)的特征数据矩阵,其中n指音频中包含的数字个数。
完成步骤:
(1)设计特征提取类VioceFeature
(2)提取语音特征数据
1、设计特征提取类VioceFeature
定义类VioceFeature,主要包含两个成员方法vad和get_mfcc,分别对应音频切分和特征数据提取功能,具体的代码见前面的案例1和案例2。
为方便模块的调用,需要在jupyter环境中将类VioceFeature另存为VioceFeature.py文件,操作方法如图所示。
2、提取语音特征数据
在文件task1.ipynb中调用模块VioceFeature,编写以下代码,得到满足网络模型输入格式的特征数据。
1 from VioceFeature import *
2 voicefeature=VioceFeature()
3 audios,samp_rate=voicefeature.vad('data\\audio.wav')
4 features = []
5 for audio in audios:
6 feature = voicefeature.get_mfcc(audio, samp_rate)
7 features.append(feature)
8 features = np.concatenate(features, 0).astype('float32')
代码行1导入VioceFeature模块中所有类,代码行2创建对象voicefeature,代码行3调用对象voicefeature的方法vad完成音频切分。
代码行4-8对切分出的音频数据集audios,采用MFCC算法进行特征提取,提取后的结果保存在矩阵变量features中。执行如下命令查看features的矩阵形状如图所示。
3.2 构建语音数字识别神经网络模型
前面已经提到,利用多层卷积神经网络不仅能进行图像分类,也可以完成语音识别。
因为我们可以根据任务一提取到的每个英文数字发音的特征数据,通过普通的二维卷积进行处理,将其分类到0-9十个类别上。
根据任务目标,按照以下步骤和操作,完成任务二。
任务目标:
设计一个多层卷积神经网络模型,对其进行训练并保存最优模型。
完成步骤:
(1)定义多层神经网络模型
(2)模型训练及保存最优训练模型
1、定义多层神经网络模型
该模型就是一个分类器,它的输入就是n×13×3×64的四维语音矩阵,它的输出是10维向量,即Y=(y0,y1,…,y9),第i维是语音片段被分类为第i个数字的概率,如Y=(0,1,…,0),则表示该语音片段对应的数字是1。
为简化网络模型,我们采用多层卷积神经网络CNN和全连接层来构架一个分类器,其网络结构如图所示。
在上图中,每两层卷积层为一个块,前一层负责提取特征,后一层负责下采样,经过6层卷积操作后,形成1×8×64单通道特征输出,再经过2层的全连接进行分类,最终得到识别结果。
模型的实现代码如下。
1 class AudioCNN(fluid.dygraph.Layer):
2 def __init__(self):
3 super().__init__()
4 self.conv1 = Conv2D(num_channels=13,num_filters=16,filter_size=3,
stride=1,padding=1)
5 self.conv2 = Conv2D(16,16,(3,2),(1,2),(1,0))
6 self.conv3 = Conv2D(16,32,3,1,1)
7 self.conv4 = Conv2D(32,32,(3,2),(1,2),(1,0))
8 self.conv5 = Conv2D(32,64,3,1,1)
9 self.conv6 = Conv2D(64,64,(3,2),2)
10 self.fc1 = Linear(input_dim=1*8*64,output_dim=128,act='relu')
11 self.fc2 = Linear(128,10,act='softmax')
12 # 定义前向网络
13 def forward(self, inputs, labels=None):
14 out = self.conv1(inputs)
15 out = self.conv2(out)
16 out = self.conv3(out)
17 out = self.conv4(out)
18 out = self.conv5(out)
19 out = self.conv6(out)
20 out = reshape(out, [-1,8*64])
21 out = self.fc1(out)
22 out = self.fc2(out)
23 if labels is not None:
24 loss = softmax_with_cross_entropy(out, labels)
25 acc = accuracy(out, labels)
26 return loss, acc
27 else:
28 return out
代码行4定义的二维卷积层的输入通道数与输入数据的通道格式一致(=13),采用16个卷积核、卷积大小即滤波器尺寸为3×3、步长为1、填充尺寸为1进行特征提取。
在代码行5中,紧接着利用尺寸为3×2的滤波器,按水平、垂直方向步长分别为1和2、无填充来实现下采样的效果。
代码6-9又完成两组特征提取和下采样操作,代码行10对主要对卷积后的特征数据进行降维,形成一个1×128的向量,最后在代码行11完成分类操作。
代码行13-28是定义网络的前向计算过程,其中代码行14-22采用初始化函数__init__中定义好的网络层依次对输入数据inputs进行前向处理,代码行23-28是返回处理后的结果,如果样本带有标签,则计算分类误差loss和分类精度acc,否则,直接返回分类结果out。
2、模型训练及保存最优训练模型
语音样本集采用Free-Spoken-Digit-Dataset语音集,该语句集一共有3000条数据,从百度官网:https://aistudio.baidu.com/aistudio/datasetdetail/23050下载。
模型的训练过程定义主要包括以下几个方面:
(1)以动态图dygraph 的guard函数指定运行训练的机器资源,表明在with作用域下的程序均执行在本机的CPU|GPU资源上,程序会以飞桨动态图的模式实时执行。
(2)创建定义好的模型AudioCNN实例,并将模型的状态设置为训练。
(3)加载训练数据和测试数据。
(4)设置训练迭代次数,启动模型迭代训练。在迭代过程中,可以观察到模型的训练误差和训练精度。
(5)最后保存训练好的模型。
模型训练的代码具体可参考百度官网https://aistudio.baidu.com/aistudio/projectdetail/797250,模型训练完成后,通过以下代码来保存模型,以备测试或校验的程序调用。
fluid.save_dygraph(optimizer.state_dict(), 'final_model')
然后就可以利用模型来测试数字语音的识别效果了。
3.3 利用训练好的模型来识别语音
通过前面的任务1已经获取了英文数字的语音特征,并在任务2中对构建的神经网络模型进行了训练,下面就利用保存的模型对语音特征数据完成分类工作,将分类结果进行合并,从而最终完成对语音的识别任务。根据任务目标,按照以下步骤和操作,完成任务3。
任务目标:
利用训练好的CNN模型,对语音特征数据进行分类识别,得到语音文件audio.wav的识别结果。
完成步骤:
(1)配置模型识别的机器资源
(2)加载模型参数给模型实例
(3)将提取的特征样本输入模型,得到识别结果
1、配置模型识别的机器资源
从前面的模型定义和训练来看,我们训练好最后的模型所花销的时间相对还是很短的,主要原因是我们所使用的AudioCNN卷积神经网络比较简单。
但现实生活中,我们可能会遇到更复杂的机器学习、深度学习任务,需要运算速度更高的硬件(GPU、TPU),甚至同时使用多个机器共同训练一个任务(多卡训练和多机训练)。
但本案例是在普通的电脑上训练和预测,所以通过以下语句进行模型运行的资源配置。
with fluid.dygraph.guard(place=fluid.CPUPlace()):
2、加载模型参数给模型实例
首先要构造一个模型实例model,然后将前面训练好的模型final_model参数加载到模型实例中。
加载完毕后,还需将模型的状态调整为校验状态eval,是因为模型在训练过程中要同时支持正向计算和反向传导梯度,此时的模型比较臃肿,而校验eval后的模型只需支持正向计算,此时模型的实现简单且性能较高。
对应的代码如下。
1 model = AudioCNN()
2 params_dict, _ = load_dygraph('data/final_model')
3 model.set_dict(params_dict)
4 model.eval()
代码行1是构建神经网络类AudioCNN的一个实例model,代码行2是加载目录data下训练好的模型final_model,代码行3给模型model加载参数,代码行4完成对模型的校验,模型只用于预测。
3、将提取的特征样本输入模型,得到识别结果
在任务一中我们提取出英文数字音频的语音特征features,下面就基于该特征值,利用训练好的模型进行语音识别,实现的代码如下。
1 features =to_variable(features)
2 out = model(features)
3 result = ' '.join([str(num) for num in np.argmax(out.numpy(),1).tolist()])
4 print('语音数字的识别结果是:',result)
代码行1将多维的矩阵转换成Paddle支持的张量Tensor类型,代码行2将特征数据features作为模型的输入来预测识别结果。
由于模型的输出out仍是一个张量类型,故在代码行3中对其进行numpy转换,变成一个二维数组,然后按行求各行中的最大值的下标,因为下标值与预测的数字值是一一对应的,故最后的拼接结果result实际就是识别的数字,识别的结果如图所示。
可以看到,CNN模型准确识别出语音文件audio.wav的内容,说明卷积神经网络的确可用于语音的识别,能获得较好的识别效果。
更多精彩内容请持续关注本站!