2019-07-16 09:48:07 weixin_43612049 阅读数 185

暑假实习,收获挺大,感谢老师般的范院!

实习项目是去做一款轻量级、个性化的语音识别软件,于是补了很多语音识别的算法。我把做的一个PPT粘到这里(做工一般,个人较菜),也希望对大家有所帮助~

注:不是特别详细,可以通过此PPT了解语音识别的概况和最新进展等

 

 

 

 

上面有一部分是我个人见解,如有问题,欢迎指正~~~

2018-12-01 22:57:29 Tonywu2018 阅读数 2168

最近在做脑磁图的信号处理方面的工作,在网上查阅信号处理相关工作的资料时,看到了CMU的王赟博士关于语音识别技术的介绍,个人感觉讲的很精彩,同时,语音识别技术已经渗入到我们生活的方方面面,抱着学习者的心态,把王博士关于语音识别技术的讲解写一篇文章,文章主要是根据王博士的PPT和我自己的一些理解组成,我个人并不是做语音识别方向,所以难免会有些理解不到位或者不准确的地方,如果有什么错误,希望大家指正。下面开始进行语音识别技术的介绍。

目录

1.背景

2.孤立词识别

2.1 特征提取

2.2 动态弯算法

2.3 GMM(Gaussian mixture model)

2.4 HMM(Hidden markov model)

2.5 EM训练算法

2.6 语音识别基本方程

3.连续语音识别

3.1 语言模型

3.2 大词汇量

3.3 语音识别系统结构

3.4 评价指标:WER

4. 潘多拉魔盒

4.1 上下文有关模型

4.2 区分式训练

4.3 说话人适应

4.4 二次打分

5.结语

参考文献

1.背景

在讲解每一个知识或者技术之前,我们通常先说到它的概念,当然语音识别技术也不例外。那么什么是语音识别呢,语音识别就是把语音转换成文字。科大讯飞的输入法就是一个典型的语音识别的例子,我们可以在不方便打字又不想让别人听到自己声音的时候,就可以使用这种方法,它可以把语音信号转化成文字。全球有很多种语言,怎么让不同语言的信号转换成其对应的文字呢。这就衍生了语音识别的一些相关课题。元数据识别:就是无论说话人说的是中文还是外语,我都可以将它转换成对应的汉子或者外国文字,而且更加神奇的是,它还知道在一句话的什么地方该加标点符号,这都属于元数据的识别范畴;语音增强与分离:在很多语音中挣钱并分离出自己需要的语音信号,最典型的的例子就是鸡尾酒宴会,意思就是在这个宴会上会有很多声音音乐声,不同的人的说话声,这时我想从这些混杂的语音中提取小明的声音以及他说话的内容,这时我们就可以用到语音增强与分离技术,这个可以用SVD实现;语音合成与转换:语音合成是语音识别的逆过程,也就是说语音合成是把文字转化成语音信号;自然语言处理的应用场景也非常多,像Siri或者win10上的小娜都是基于NLP的。说完概念,接下来说一说语音识别的应用。

语音识别技术在我们生活中见得非常多,比如语音助手、导航系统等等。这里就不多介绍。说完这些,我们就将进入语音识别技术的前世今生的前世部分。

这是两个带着红领巾的少先队员,为什么以这个图片为背景呢,我猜测可能和语音识别技术的历史有关。因为语音识别技术的前世是在20世纪80年代兴起的,而那时,也就是上世纪八九十年代,带着红领巾的少先队员正是风靡全国,曾经作为一名少先队员,我骄傲。既然说到这里,我就大致说下语音识别技术的发展史:1952年贝尔实验室的Davis等人研究成功了世界上第一个能识别10个英文数字发音的实验系统;20世纪70年代孤立词识别取得实质性的进展;1987年李开复开发世界上第一个‘非特定人连续语音识别系统’,(是的,就是那个创新工场的李开复,人家不仅能当总裁,还是一个科技大牛,不得不服呀,而且,王博士跟李开复是一个实验室的),用统计的方法提升了语音识别率;2011年微软的DNN深度神经网络模型在语音识别领域取得成功。具体的发展史可以看参考文献1中的小视频。

2.孤立词识别

每一个单词都有模板,用于待预测语音与它进行比较,如上图所示,右边蓝色的语音信号到底是说的Yes还是No。我们直观的看一下,可能感觉它是Yes的可能性更大,因为Yes的信号波形中有两个包,No只有一个包,而待预测的信号也是有两个包,从波形上我们可以大概判断出待预测信号是Yes的可能性更大一些,当然这种看波形的方法实不可取的,更是不科学的。那么我们应该怎么做呢,就是计算待预测信号与模板信号的距离,其实也就是相似程度,距离越短,相似程度越高。但是信号的距离该怎么比较呢,是通过把信号转换成特征向量,然后比较向量之间的欧式距离或者其他的距离,接下来,就来到了特征提取的部分。

2.1 特征提取

对于一段语音信号,我们通常是一段一段的分析,我们提取一帧信号,它的长度通常为20~50ms,一帧信号要至少包含2~3个周期,那你怎么知道20~50ms就能有至少2~3个周期呢,因为人说话的频率是在100Hz左右,所以语音信号的周期就是10ms,微观上我们感觉一帧信号特别长,竟然包含几个周期,但其实,50ms也就是0.05s,比一眨眼的的时间还短。

接下来我们就需要分析这一帧信号,对一帧信号做傅里叶变换,这样可以得到信号的频域的信息。有关傅里叶变换的知识可以看参考文献2,讲的非常通俗易懂。经过傅里叶变换之后,我们会得到这一帧信号的频谱,它由两部分组成,其中蓝色的线表示的是精细结构反应的是音高,在汉语中比较常用,用来识别汉字的声调,但是在外语中用处相对较小;红色的线表示的是包络,也就是频谱的大致幅度,它反映的是音色,包含主要的信息。可以看到,包络的峰要比精细结构少很多,只是在500Hz、1700Hz和2500Hz的地方有峰值。像精细结构这样的多峰的波形分析起来比较复杂,其实可以直接用包络表示这段语音信号,虽然不能丝毫不差的表示,但是它包含了这段信号的大部分信息。但是,我们通常不会直接用包络表示语音信息,那么我们应该怎么做呢,就是对精细结构进行滤波,继续往下看。

我们对一帧信号进行三角滤波,中间那副图片中的蓝线表示的就是精细结构,红线表示的就是三角滤波器。可以看到,左边的三角滤波器比较密,这是跟人耳的频率特性相关的,人耳对低频信号比较敏感,所在低频部分的分辨率较高,高频部分的分辨率就相对较低。经过三角滤波后,我们得到最下边的成为滤波器组输出的图像,可以看出,Filterbank output的峰的个数相对精细结构也变少了,我们再一看,它和频谱的包络在形状上很相似,实际上,他就是频谱包络的一种近似。最下边这幅图的横坐标表示三角滤波器的标号,此示例中用了40个三角滤波器,纵坐标表示信号的能量,可以看出频谱精细结构在500Hz处有个高峰,所以在500Hz处的三角滤波器中的能量比较大,500Hz大约对应第12个三角滤波器,在下图可以看到,横坐标在12处有个高峰。其实这个Filterbank output就基本可以作为这一帧语音的特征了,但是我们还通常会进一步做数学变换来达到数据压缩的目的,继续往下看。

通常情况下我们会对Filterbank output做一个对数变换或者离散余弦变换,这样就可以吧Filterbank output压缩为一个13维的特征向量,称为MFCC。经过一系列处理之后,我们得到了一帧语音信号的特征向量,那么怎么对整个一段语音信号进行处理呢,我们继续往下看。

对于一段语音信号,我们通常利用类似于滑动窗口的方法沿着时间的维度一帧帧的将信号堆叠起来,窗宽就是一帧信号的长度,两个窗口之间通常会有10ms的重叠以防信息的泄漏,这样把一帧帧信号按列堆叠起来,形成了中间的语谱图,横坐标为帧的序号,纵坐标为评率,右边的Colorbar表示信号的能量,自下向上能量越来越大,对语谱图进行数学变换之后,我们可以得到MFCC序列,它就表示语音信号中的有用特征。接下来看看特征提取的一些特点。

在语音识别领域中MFCC序列是最常用的特征,他主要描述的是频谱包络,也就是音色。它的优点就是排除基频也就是精细结构;一帧语音信号通常会有成百上千个采样点,而MFCC序列只需要一个13维的特征向量就可以表示这一帧信号的主要特征信息,所以有维度低的特点;符合听觉就是指在低频处三角滤波器密集而在高频处三角滤波器稀疏。万物都有两面性,MFCC序列有优点就会有缺点,它一次只能看一帧也就是20~50ms的信号,即视野小;还有就是受噪声、回声影响等。关于它的改进部分就不多说了。

2.2 动态弯算法

在文章的开头说过,需要计算待预测语音与模板语音之间的距离,从而判断它想要表达的内容。经过特征提取之后我们将语音信号编程了一个个13维的特征向量,那么我们该怎么计算两个特征序列之间的距离呢。这里用到了DWT算法,将待识别语音中的每一帧与模板中最相似的一帧匹配,同时要保证顺序不要改变。因为一个词或者一句话顺序改变,意思也会完全改变。在上图中我们用一个竖条表示一帧语音信号的特征向量,通过DTW算法将待识别语音的特征向量与模板特征向量对齐之后,计算待识别语音中的每一帧语音与模板中每一帧的欧式距离,然后总距离为所有帧的欧式距离之和。通过计算待识别语音与Yes模板和No模板的欧式距离,取两者中距离小的那个为最终识别结果即可。其实到此,孤立词识别问题就已经解决了,但是人总是不满足的, 总是想找到更好的方法,那么我们继续往下看。

2.3 GMM(Gaussian mixture model)

我们知道,同一个人或者不同的人说一个汉字或者单词的发音是存在区别的,那么我一个词比如Yes只录一个模板的话,那么最后的识别正确率会降低。这就要求我们需要多录几个模板,在上图中,中间的五个特征向量为待识别语音,上下分别为待识别语音的模板,那么经过DTW算法之后,我们把对齐的几个特征向量整合在一块,这样原来的模板就变成了右边的有五个状态的模型,然后我们就让待识别语音与模型中的每个状态进行对齐,这样的话,识别的准确率会大大提升。接下来我们需要使用GMM来拟合每个状态中特征向量的分布,顾名思义,GMM其实就是多个高斯分布叠加而成的一种模型,如右下角的图形就是在二维空间下的GMM,可以看到,它的形状有点像地理中的等高线,这其实是一个轮廓图,经过GMM后,可以得出特征向量的概率密度并以此代替欧式距离。训练好GMM后,我们怎么用模型识别未知语音呢,继续往下看。

我们首先用DTW算法把待识别语音与模型中的状态对齐,计算每一个特征向量在模型中的概率密度,并以此来代替向量间的欧式距离。然后把每一帧的概率密度(这里一帧和一个特征向量是一样的,都是用一个竖线表示)相乘得到了在模型下待识别语音的概率,这里假设各帧之间是独立的。最后取P(待识别语音|模型)最大的模型为识别结果。还是以文章开头的例子为说明,计算待识别语音中的每一帧分别在Yes模型和No模型中的每一状态中的概率密度,最后相乘得到P(待识别语音|Yes模型)和P(待识别语音|No模型),取两者中较大的值对应的模型就是待识别语音最后的结果。

2.4 HMM(Hidden markov model)

接下来就轮到了前世中的主角闪亮登场了,也就是隐马尔可夫模型(HMM),它将模型完全概率化,概率可以为我们提供更多的信息,比如我想知道明天是晴天还是阴天或者下雨,告诉你结果,不如告诉你这三者中每一个发生的概率,其实我们也经常在天气预报中听到,某某地区的降水概率是多少,这显然比直接告诉我们明天是下雨或者晴天包含的信息更多。HMM在GMM的基础上增加了转移概率。什么是转移概率呢,如上面的图中所示,初始状态为第一个状态,那么它的下一状态还为第一状态的概率为0.7,下一状态为第二状态的概率为0.3,这里的0.7和0.3就是从当前状态转移到下一状态的概率,也就是转移概率。我们可以看到,待识别语音的每一帧与模型中的状态对齐之间也有个概率,也就是0.016,0.028这些值,这些值可以由GMM计算得出,也就是观测概率。我们把GMM测得的观测概率和HMM的转移概率相乘,就得到了在某模型下,语音和对齐方式的联合概率。接下来看看,HMM的特点。

HMM主要有两个特点,第一个就是特征序列由隐状态产生,隐状态也就是上面说到的五个状态,为什么称之为隐状态呢,是因为每帧特征向量和状态之间的对齐方式是未知的;另一个特点就是马尔可夫性,这里就不多说了。有关GMM,HMM,包括下面将要说的EM的相关知识,我推荐大家可以看一下悉尼科技大学的徐亦达老师在优酷的一个自频道,里面详细的讲解了概率统计的一些知识,并且每一个概念都有详细的公式推导,值得一看,教学视频见参考文献3。说完模型的特点,接下来说一下模型的参数,实际上HMM是有三个参数的:转移概率,观测概率以及初始概率。但是上面示例中的HMM是单向的,所以初始状态默认为第一个状态,所以,模型的参数就变成了两个:转移概率和观测概率。

接下来就看看HMM在语音识别领域中是如何使用的。HMM的三大问题:第一个问题就是求值问题,也就是给定模型参数和待识别语音,求概率,因为对齐方式是事前不知道了,所以我们就需要枚举每种对齐方式,由前面讲到的公式P(语音,对齐方式|模型) = GMM观测概率*HMM转移概率可以算出联合概率P(语音,对齐方式|模型),然后对所有对齐方式求和,联合概率就变成了边缘概率P(语音|模型),但是对齐方式是多种多样的,对枚举每一种对齐方式是很困难的,所以这里就引入了动态规划算法中的前向算法,个人对动态规划的算法不了解,所以这里就不多进行说明;第二个问题就是解码问题,也就是给定模型参数和语音,求最佳对齐方式的问题,那么什么才是最佳的对齐方式呢,最佳对齐方式就是使联合概率P(语音,对齐方式|模型)最大的那种对齐方式,这里用到了动态规划的算法中的Viterbi,这个算法其实就是DTW算法的升级版本,最佳对齐方式的概率可以作为总概率的近似,这里说明一下它的意思,比如我有1000种对齐方式,那么最佳对齐方式只是其中的一种,如果假设对齐方式服从均匀分布的话,当然这时候也就没有了最佳而言,因为每一种对齐方式的概率都是一样的都是0.1%,这显然跟总概率相差甚远,实际上并不是这样了,通常情况下最佳对齐方式的概率跟非最佳对齐方式的概率不是一个数量级,也就是说我最佳概率可能为90%,而你其他999中非最佳的概率和才占10%,所以,这种情况下,可以把最佳概率作为总概率的近似;最后一个问题就是训练问题,训练问题就是给定语音和模型结构,求模型参数的问题。将在下面的内容中详细的讲解。

2.5 EM训练算法

最大期望算法(Expectation-maximization algorithm,EM)在统计中被用于寻找,依赖于不可观察的隐性变量的概率模型中,参数的最大似然估计。我们事先是不知道对齐方式的,但是我们可以随机假设一种对齐方式,假设Yes这个词的发音一共有15帧信号,那么我们可以这样分割,前五帧代表Y的发音,中间五帧代表e的发音,最后五帧代表s的发音,然后以这种对齐方式求得模型的参数,然后再反过来更新对齐方式,这里没有再使用DTW,而是使用Viterbi或者Forward-backward算法,一直这样不断更新下去,直到收敛为止。

2.6 语音识别基本方程

最后讲一下语音识别的基本方程。这个方程其实就是语音识别技术要解决的问题,它是求得在已知X也就是待识别语音的条件下它被识别为任意一个单词W的概率,比如我们说段语音,它可以被识别为Yes也可以被识别为No或者其他,语音识别要做的就是找出被识别为某一个词最大的概率,也就是W^{*}P(W|X)是条件概率,我们把它经过贝叶斯公式变换一下得到\frac{P(X|W)P(W)}{P(X)},其中P(X|W)称为likelihood也就是似然函数,在这里它是指的声学模型,根据这个式子P(W|X) = \frac{P(X|W)P(W)}{P(X)}而言,要想使P(W|X)最大,我们要做的无非就是让分子尽量大,分母尽量小。因为给定了X以后P(X)就可以认为是固定了,那么最大化式子P(W|X) = \frac{P(X|W)P(W)}{P(X)},就变成了最大化式子P(W|X) = P(X|W)P(W),那么就需要让似然函数也就是声学模型P(X|W)和先验概率P(W)尽量大,声学模型我们可以用GMM和HMM训练得到,而且上一张PPT中,EM训练算法可以最大化似然函数也就是P(X|W)。那么什么是先验概率呢,先验概率就是一个人说某一个词或字的概率,比如我们一天中说“我吃米饭“”的次数比较多,那么它们的概率就比较大,而说“饭米吃我”的次数极少,那么它们的概率就小,那么这些概率就是所谓的先验概率,当似然函数也就是声学模型不能给我们提供足够的信息的时候,也就是P(X|W)很小的时候,我们就会通过先验概率P(W)来判断这个语音说的是什么,对于一段待识别语音,我们会更倾向与把它识别为“我吃米饭”而不是“饭米吃我”,当然这个我举的这个例子是一句话了,不是单个的孤立词,但是意思是一样的。那么,到目前为止,有关孤立词识别的内容就全部讲完了,接下来,将讲解连续语音识别的相关内容。

3.连续语音识别

什么是连续语音识别呢,我们把上面的孤立词都变成句子就是连续语音识别了。W和W^{*}就不再表示一个词,而是表示一句话;同样的孤立词的似然也就是声学模型P(X|W)也变成了句子的声学模型,它可由一个个单词的声学模型串联起来,但是一定要保证顺序。最后的孤立词的先验概率P(W)也变成了句子的先验概率,也就是我们上面举的例子,“我吃米饭”与“饭米吃我”,哪一个更像我们正常人说的话,那么我们也称作句子的先验概率为语言模型。接下来,我们将详细介绍语言模型。

3.1 语言模型

在语言模型中,会经常用到链式法则,其实就是概率论中的乘法公式,即某一句话出现的概率等于第一个字出现概率乘以已知第一个字出现的条件下第二个字出现的概率乘以已知前两个字出现的概率下第三个字出现的概率依次类推。我们以“皮卡皮卡丘”这句话为例,链式法则用公式表示就是P(皮卡皮卡丘) = P(皮)*P(卡|皮)*P(皮|皮卡)*P(卡|皮卡皮)*P(丘|皮卡皮卡)。但是通常情况下,当我们说皮卡后,下一个字是皮的概率很小,也就是P(皮|皮卡)很小,而且不容易计算。为了解决问题,提出了n-gram模型,这是一种什么样的存在呢,也就是说每个词只与前n-1个词有关,当每个词只与前1个词有关时,我们称之为Bigram,这其实就跟Markov模型是一样的了,我们怎么计算这样的概率呢,这需要搜集大量的语料,以P(丘|卡)为例,我们需要搜集卡*出现的次数,然后搜集卡丘出现的次数,用卡丘出现的次数除以卡*的次数就是P(丘|卡);同样的,当每个词只与前两个词有关的时候,我们称之为Trigram。还有一些最大熵和神经网络的语言模型,它们比较复杂,我本人也不太懂,所以不多介绍。接下来,我们主要看下Bigram模型。

正如我上面所说的。Bigram实际上就是马尔可夫模型,下一个词只与当前词有关,可以可到,这里它不再像孤立词中的HMM一样,这里的模型它是双向,也称为是遍历的,它们之间的状态可以相互转移,上图为例,“皮”转移到“卡”的概率为0.5,转移到“丘”的概率为0.3,还是“皮”的概率为0.2。那么我们把语言模型看做一个马尔可夫模型有什么好处呢,其实这样的话,它就可以与单词的声学模型进行复合。什么意思呢,我们以上图中下边这个状态转移图为例进行简要的说明。如上图所示,把每个字都分成了3个状态,前两个状态只能在这个顺序字内转移,也就是说皮1不能直接到皮3或者到卡以及丘的内部状态中,但是第三个状态可以在字间或者字内转移。以皮3为例,假设皮3转移到自身的概率为0.6,那么它转移到其它转态的概率为0.4,这里的0.4包括字内转态转移和字间转态转移,因为字间转移只能由第三个状态完成,在这里也就是皮字转移到其它字只能由皮3完成,我们在上边那个三角形的转移图中可以看到,皮字转移到卡的概率为0.5,那么0.5*0.4 = 0.2就是皮3转移到卡1的概率;同样的0.3*0.4 = 0.12就是皮3转移到丘1的概率,0.4*0.2 = 0.08就是皮3转移到皮1的概率。因为语音信号的顺序问题,比如“卡”字的发音是按照卡1---卡2---卡3这样的顺序完成的,所以字间转移,也就是第三转态只能转移到本字或者其他字的第一状态。我们可以将待识别语音在复合模型中遍历,让待识别语音跟复合模型中的状态进行匹配,计算状态的观测概率,寻找最佳路径,然后将最佳路径上的单词按照顺序排列起来就得到了连续语音信号的识别。我们通常说的语音都是大词汇量的,那么我们该怎么去识别大词汇量的语音呢,继续往下看。

3.2 大词汇量

我们前边讲到的是为每一个单词训练单独的HMM,也就是每一个单词录不同的模板组成模型。但是语言库中的汉字或者单词都是上万级别的,如果为每个汉字或者单词都录入几个不同的模板的话,这样的工作量就太大了,所以这种方法是不可取的。我们知道汉语是按照拼音进行发音的,我们可以为字母表中的拼音训练HMM,同样的我们可以为英语中的音标训练HMM,这样我们就把每个单词训练的HMM变为了为每个因素训练HMM。这样之前原来的复合模型就变成了上图中更加庞大的一个复合模型,我我们得到了这个模型,那么我们应该怎么训练它呢,请继续往下看。

训练的过程和之前的训练是相同的,它在音素串内部的HMM仍然是单向的,我们还是采用EM算法。这里就不多说了,接下来就是解码的过程,当给定一门语言的解码器(这包括语言模型,词典(词典的作用就是把音素拼接为一个单词)和声学模型)和一条语音,我们就可以使用Viterbi算法求得最佳路径,最佳路径其实还是回归到概率问题,概率最大的路径就是最佳路径,路径上的单词就是识别的结果。那么我们就完成了大词汇量的连续语音信号的识别问题,接下来我们看看语音识别系统的一个整体结构框架。

3.3 语音识别系统结构

首先是将一段语音信号经过特征提取的操作后把信号变成了一个MFCC序列,特征提取也称为前端,那么后端主要是由解码器组成的,解码器又包括声学模型、词典和语言模型。声学模型也就是似然,它描述的是单词或者音素的发音情况,主要由GMM和HMM来完成,词典可以把音素拼接起来组成单词或者汉字,语言模型就是把单词整合成符合人类说话习惯的连续的语音,也就是把单词整合成一句话,最后将结果输出就是我们最终想要得到的识别结果。模型都有评价指标,这个模型到底好不好,我们该怎么去评价它呢,接下来我们就讲解语音识别系统的评价指标。

3.4 评价指标:WER

语音识别系统常用的额评价指标就是词错误率,英文是word error rate,简写为WER。那么怎么计算呢,PPT中写的很详细,我在这里就不多说了。例子也不说了。

我们可以看到,这张PPT中的对齐方式跟上一张不太一样,但是最终的词错误率是一样的,所以最优对齐方式不一定是唯一的。词错误率有一个缺陷就是它只能发现词是对是错,却不能说明词错误的程度,比如我说“小狗”,它识别成了“小巩”或者识别成“小猫”的词错误率都是50%,但是很明显,把“小狗”识别成“小猫”的错误程度更大,但是语音识别中还是通常使用词错误率作为评价指标而不会去在意那些细节性的东西。还有一点就是WER可能高于100%,这是因为你错的词的个数比正确的答案句子的长度还要多,这就会导致WER高于100%,如上边的例子所示。

这两幅PPT主要是讲解的WER的发展史,这里就不多做介绍。

4. 潘多拉魔盒

这么长时间内语音识别的框架没有发生改变,并不是说技术没有进步,只是人们把框图中的每个人部分更加优化了,解决的方法更好了。下面主要讲后端部分的优化。

4.1 上下文有关模型

上下文有关模型实际上就是在说同样的音素在前后音素发音不同时,中间的音素的发音也有微小的不同,上边的PPT以five和nine为例说明了这个问题,在英语中大约有50个音素,每个因素有3个状态的话,这样也就是有150个状态,但是有了上下文有关音素之后,这个音素受它的上一个音素、下一个音素以及它本身这三个音素的影响,每一个音素有50种情况,那么一种有50*50*50种情况,也就是125000种情况,这显然是非常多的。于是就有了上下文聚类的一种方法,这样最终的状态数会在几千的数量级,这对于我们来说还是可以接受的。

4.2 区分式训练

4.3 说话人适应

4.4 二次打分

 

5.结语

至此,语音识别技术前世今生之前世的内容就讲完了,下一篇文章将继续讲解语音识别技术前世今生之今生的内容。

参考文献

1、语音识别技术的前世今生     https://www.bilibili.com/video/av19158521/

2、傅里叶变换            https://zhuanlan.zhihu.com/p/19763358

3、徐亦达自频道        http://i.youku.com/i/UMzIzNDgxNTg5Ng==?spm=a2hzp.8253869.0.0

2018-12-04 20:27:38 rickyzhang2008 阅读数 578

本文首发于:算法社区 dspstack.com,转发请注明出处。

前言#

动态时间规整算法,Dynamic Time Wraping,缩写为DTW,是语音识别领域的一个基础算法。

算法的提出#

DTW的提出是为了解决或尽量解决在语音识别当中的孤立词识别不正确的问题。该问题简单描述为:在识别阶段,将输入语音的特征矢量时间序列依次与模板库中的每个模板进行相似度比较,最后将相似度最高者作为识别结果输出。但是,由于语音信号具有相当大的随机性,即使是同一个人在不同时刻所讲的同一句话、发的同一个音,也不可能具有完全相同的时间长度。而在进行模板匹配时,这些时间长度的变化会影响测度的估计,从而降低识别率。对此,日本学者 板仓(Itakura)将动态规划(DP)算法的概念用于解决孤立词识别时的说话速度不均匀的难题,提出了著名的动态时间规整算法或称动态时间伸缩算法(DTW)。

算法的内容#

DTW的目标是从不同时间跨度的两个数据求出它们之间的最小总累计距离,所以首先我们要找出输入矢量和参考矢量之间的对应关系,从而根据对应的矢量来求出模板之间的最小累计距离。在求累计距离的每一步中,需要满足以下条件的规整函数:

  • 边界条件

    w(1) = 1,w(N) = M (3-1)

    即规整函数起点为(1,1),终点为(N,M),

  • 连续条件

    w(n + 1) = w(n) + 0/1/2,如果 w(n) <> w(n - 1) 成立 (3-2)

       w(n + 1) = w(n) + 1/2,如果 w(n) == w(n - 1) 成立 (3-3)

Tip:

  • 式(3-2)意思是 w() 的当前值和前一个 w() 值不相等,说明已经加过 1 或 2 ,则 w(n + 1) 的加上的值可为 0;式(3-3)则刚刚相反,w(n + 1) 的加上的值不能为 0,因为 w(n) 已经使用过 0;本质上,规整函数限制了最小累加距离的路径走向。0/1/2代表了路径走的步数,要么是当前矢量本身,要么是当前矢量的前一列的步数为 1和 2 的矢量(或点)。
  • n 的值与 N 相对应

使用规整函数的最小累计距离递推公式:
DTW(n,m) = d((n,m)) + min { DTW(n - 1, m) * g(n - 1, m),DTW(n - 1, m - 1), DTW(n - 1, m - 2)} (3-4)
其中:

g(n - 1,m) = 1,如果 w(n - 1) <> w(n - 2) (3-5)

g(n - 1,m) = ∞,如果w(n - 1) == w(n - 2) (3-6)

Tip:这里的只能取前一列的矢量(点)的数据
根据式(3-2)和式(3-3),文字解释为:当 DTW(n - 1, m) 不是来自 DTW(n - 2, m) 时,则去判断 DTW(n - 1, m) [本身,步数为 0 ],DTW(n - 1, m - 1) [步数为 1 ],DTW(n - 1, m - 2) [步数为 2 ]

优点和注意点#

时间复杂度#

DTW算法的优势在于:它解决了数据长度不同的两数据序列的差异度表示方式。从计算的角度来说,式(3-4)可看出,横轴每向前增加一步,仅参考前一列的累计距离,所以在计算时只保留前一列的累计距离即可,不必保留所有数据。这样可以降低算法的时间复杂度;其原理在于,一个参考数据只与其距离相近的数据会比较相似,距离过远的数据关系不大,所以就没必要计算参考数据与对比数据的所有距离,而DTW算法本身就是在矩阵中运算的,其一般的计算点关系如图
file

距离指标选择#

相邻矢量间距离指标的好坏绝对影响DTW算法的效果。在孤立词识别当中,先用矢量量化技术,然后再对各分量使用欧拉
距离来度量和计算;由于DTW算法可应用不同的领域,所以不同的领域距离指标是不一样的,甚至一般的统计距离:欧拉距离、Minkowski距离、Mahalanobis距离以及兰氏距离等用在所碰到的问题上,达不到想要的效果。所以,此时就需要根据实际数据的特征来构造距离(这里的距离已经不是一般意义上的长度等)指标,去衡量两个数据的相似程度

数据点约束#

虽说根据规整函数可以使计算复杂度降低,但是从递推公式可知,要想知道终点的累计最短距离,还是要不断计算前面的累计距离,那么如何才能更进一步的降低计算时间复杂度呢?答案就是对计算数据的范围进行约束,下图是平行四边形约束
file
也就是说,计算的数据点坐标必须落在平行四边形内部,否则就不用计算,至于平行四边形的形状可以根据实际数据来调试,一般不会相差很大,主要取决于平行四边形邻边的角度,即斜率

计算例子#

最后,给出一个简单的例子,讲下DTW的计算过程。
时间序列为:
d1 = {1,3,3,5,2},d2 = {0,2,3,6,4,1}
第一列:
DTW(1,1) = 1 + 0 = 1;
DTW(1,2) = 1 + min {DTW(0,2), DTW(0,1), DTW(0,0)} = 1 + 0 = 1;
DTW(1,3) = 2 + min {DTW(0,3), DTW(0,2), DTW(0,1)} = 2 + 0 = 2;
DTW(1,4) = 5 + min {DTW(0,4), DTW(0,3), DTW(0,2)} = 5 + 0 = 5;
DTW(1,5) = 3 + min {DTW(0,5), DTW(0,4), DTW(0,3)} = 3 + 0 = 3;
DTW(1,6) = 0 + min {DTW(0,6), DTW(0,5), DTW(0,4)} = 0 + 0 = 0;
第二列:
DTW(2,1) = 3 + min {DTW(1,1), DTW(1,0), DTW(1,-1)} = 3 + 1 = 4;
DTW(2,2) = 1 + min {DTW(1,2), DTW(1,1), DTW(1,0)} = 1 + 1 = 2;
DTW(2,3) = 0 + min {DTW(1,3), DTW(1,2), DTW(1,1)} = 0 + 1 = 1;
DTW(2,4) = 3 + min {DTW(1,4), DTW(1,3), DTW(1,2)} = 3 + 1 = 4;
DTW(2,5) = 1 + min {DTW(1,5), DTW(1,4), DTW(1,3)} = 1 + 2 = 3;
DTW(2,6) = 2 + min {DTW(1,6), DTW(1,5), DTW(1,4)} = 2 + 0 = 2;(这列符合w(n-1) == w(n-2))
第三列:
DTW(3,1) = 3 + min {DTW(2,1), DTW(2,0), DTW(2,-1)} = 3 + 4 = 7;
DTW(3,2) = 1 + min {DTW(2,2), DTW(2,1), DTW(2,0)} = 1 + 2 = 3;
DTW(3,3) = 0 + min {DTW(2,3), DTW(2,2), DTW(2,1)} = 0 + 1 = 1;(这列符合w(n-1) == w(n-2))
DTW(3,4) = 3 + min {DTW(2,4), DTW(2,3), DTW(2,2)} = 3 + 1 = 4;
DTW(3,5) = 1 + min {DTW(2,5), DTW(2,4), DTW(2,3)} = 1 + 1 = 2;
DTW(3,6) = 2 + min {DTW(2,6)∞, DTW(2,5), DTW(2,4)} = 2 + 3 = 5;
第四列:
DTW(4,1) = 5 + min {DTW(3,1), DTW(3,0), DTW(3,-1)} = 5 + 7 = 12;(这列符合w(n-1) == w(n-2))
DTW(4,2) = 3 + min {DTW(3,2), DTW(3,1), DTW(3,0)} = 3 + 3 = 6;(这列符合w(n-1) == w(n-2))
DTW(4,3) = 2 + min {DTW(3,3)
∞, DTW(3,2), DTW(3,1)} = 2 + 3 = 5;
DTW(4,4) = 1 + min {DTW(3,4), DTW(3,3), DTW(3,2)} = 1 + 1 = 2;
DTW(4,5) = 1 + min {DTW(3,5), DTW(3,4), DTW(3,3)} = 1 + 1 = 2;
DTW(4,6) = 4 + min {DTW(3,6), DTW(3,5), DTW(3,4)} = 4 + 2 = 6;
第五列:
DTW(5,1) = 2 + min {DTW(4,1), DTW(4,0), DTW(4,-1)} = 2 + ∞ = ∞ ;
DTW(5,2) = 0 + min {DTW(4,2), DTW(4,1), DTW(4,0)} = 0 + 12 = 12;
DTW(5,3) = 1 + min {DTW(4,3), DTW(4,2), DTW(4,1)} = 1 + 5 = 6;
DTW(5,4) = 4 + min {DTW(4,4), DTW(4,3), DTW(4,2)} = 4 + 2 = 6;(这列符合w(n-1) == w(n-2))
DTW(5,5) = 2 + min {DTW(4,5), DTW(4,4), DTW(4,3)} = 2 + 2 = 4;(这列符合w(n-1) == w(n-2))
DTW(5,6) = 1 + min {DTW(4,6), DTW(4,5), DTW(4,4)} = 1 + 2 = 3;
最终以表格形式给出计算结果:
file

DTW(5,6) 就是最终的累计最短距离,也就是两个数据的差异度表示

本文首发于:算法社区 dspstack.com,转发请注明出处。

2019-09-08 21:58:23 david_tym 阅读数 726

前面的博客里说过最近几个月我从传统语音(语音通信)切到了智能语音(语音识别)。刚开始是学语音识别领域的基础知识,学了后把自己学到的写了PPT给组内同学做了presentation(语音识别传统方法(GMM+HMM+NGRAM)概述)。一段时间后老板就布置了具体任务:在我们公司自己的ARM芯片上基于kaldi搭建一个在线语音识别系统,三个人花三个月左右的时间完成。由于我们都是语音识别领域的小白,要求可以低些,就用传统的GMM-HMM来实现。说实话接到这个任务我们心里是有点没底的,不知道能不能按时完成,毕竟我们对语音识别不熟,对kaldi不熟。既然任务下达了,硬着头皮也要上,并尽最大努力完成。我本能的先在网上用百度/google搜了搜,看有没有一些经验可供参考,好让我们少走弯路。遗憾的是没搜到有价值的东西。没办法,我们只能根据自己以前的经验摸索着前进。最终我们按计划花了不到三个月的时间完成了嵌入式平台上在线语音识别系统的搭建。虽然只是demo,但是为后面真正做商用的产品打下了良好的基础,累积了不少的经验。今天我就把我们怎么做的分享出来,给也想做类似产品的朋友做个参考。

 

既然作为一个项目来做,就要有计划,分几个阶段完成这个项目。我在学习语音识别基础知识时对kaldi有一个简单的了解(在做语音识别前就已知kaldi的大名,没办法这几年人工智能(AI)太热了。智能语音作为人工智能的主要落地点之一,好多都是基于kaldi来实现的。我是做语音的,自然会关注这个热门领域的动态)。根据对kaldi的简单了解,我把项目分成了三个阶段,第一阶段是学习kaldi,对kaldi有一个更深的认识,同时搞清楚基于kaldi做方案后面有哪些事情要做,计划花一个月左右的时间完成。第二阶段是设计软件架构、写代码、训练模型等,也是花一个月左右的时间完成。第三阶段是调试,提升识别率,还是花一个月左右的时间完成。计划的时间会根据实际情况做微调。

 

1,第一阶段

第一阶段就是学习kaldi。由于我们是三个人做这个项目,我就把学习任务分成三块:数据准备和MFCC、GMM-HMM模型训练、解码网络创建和解码。在其他两位同学挑好模块后剩下的解码网络创建和解码就有我来学习了。学习过程就是看网上文章、看博客和看kaldi代码、脚本的过程。学完后大家搞清楚了后面有哪些事情要做,同时做了PPT给组内同学讲,让大家共同提高。解码相关的见我前面的文章(基于WFST的语音识别解码器 )。Kaldi中解码有两种类型:offline(多用于模型调试等)和online(多用于在线识别等),其中online也有两种方式,一种是通过PortAudio从MIC采集语音数据做在线语音识别,另一种是通过读音频WAV文件的方式做在线语音识别。我们要做的是在线语音识别,这两个就是很好的参考,尤其是通过PortAudio从MIC采集方式的,很有必要弄明白运行机制。于是我根据网上的博客基于thchs30搭建了PC上的在线识别来调试,基本上搞清楚了代码的运行机制。Kaldi中设定采样率为16kHZ,每帧25ms(其中帧移10ms),每27帧为一组集中做MFCC特征提取和解码,这样处理一组的语音时长是285ms(25+(27-1)*10=285),共4560(16*285=4560)个采样点。每次处理完一组后就从buffer中再取出一组做MFCC和解码,解码后看有没有识别的字出来,有的话就打印出来。

 

2,第二阶段 

第一阶段主要是学习,第二阶段就要真正干活了。我们在Linux上开发,先制定系统搭建完成后的目标:设备用数据线连在PC上,能在线实时识别英文数字0—9(选识别这些是因为网上有现成的英国人说的音频源,我们可以省去录音频源的工作,好节约时间),即人对着设备说出英文数字0—9后PC屏幕上能实时打印出来,识别率接近GMM-HMM模型下的较好值。大家的任务还是沿袭第一阶段的。学习数据准备和MFCC的同学先数据准备相关的工作,如标注等,好给模型训练的同学用,然后移植kaldi中MFCC相关的代码。学习模型训练的同学先开始模型训练的准备工作,等要准备的数据好了后就开始训练。我负责整个软件架构的设计,同时还要把kaldi中的绝大部分(除了MFCC)移植进我们系统中。通过对kaldi的学习,使我对怎么设计这个在线语音识别的软件架构有了更深的认识。语音识别分两个阶段,即训练阶段和识别阶段。训练阶段就是得到模型给识别阶段用。它相对独立,我们就基于kaldi来训练模型,最终得到final.mdl等文件给识别阶段的软件用(在初始化时读取这些文件得到解码网络)。识别阶段的软件主要分两部分,声音采集和识别(包括特征提取和解码)。这样系统就有两个thread,一个是声音采集thread(audio capture thread),它基于ALSA来做,负责声音的采集和前处理(如噪声抑制),另一个是识别thread(kaldi process thread),负责MFCC和解码。两个thread通过ring buffer交互数据,同时要注意数据的保护。这样系统的软件架构框图如下:

 

大家对软件架构讨论觉得没什么问题后我就开始写代码搭建软件框架了。在 Linux中创建thread等都是一些套路活。Audio capture thread里先做初始化,包括ALSA的配置以及前处理模块的初始化等。然后就每隔一定时间通过ALSA_LIB的API完成一次音频数据的采集工作,读完数据后就做前处理,处理好后把音频数据放进ring buffer中,同时激活kaldi process thread,让kaldi process thread开始干活。Kaldi thread也是先做一些初始化的工作,然后睡下去等待激活。激活后先从ring buffer里取语音数据,然后做MFCC和decoder。完成后又睡下去等待下次再被激活。搭建软件框架时kaldi相关的代码还没被移植进去,kaldi process thread里仅仅把从ring  buffer里拿到的语音数据写进PCM文件,然后用CoolEdit听,声音正常就说明软件框架基本成型了。刚开始时audio capture thread里也没加前处理模块,调试时把从ALSA里获取的数据写进PCM文件听后发现有噪声,就加了噪声抑制(ANS)模块。这个模块用的是webRTC里的。webRTC里的三大前处理模块(AEC/ANS/AGC)几年前我就用过,这次拿过来简单处理一下就用好了,去噪效果也挺好的。ANS一个loop是10ms,而前面说过kaldi里在线识别解码一次处理一组27帧是285ms,我就取两者的最小公倍数570ms作为audio capture thread的loop时间。从ALSA取到语音数据后分57(570/10 = 57)次做噪声抑制,再把抑制后的语音数据写进ring buffer。Kaldi thread激活后还是每次取出285ms语音数据做处理,只不过要取两次(570/285 = 2)。

 

软件架构搭好后就开始移植kaldi代码了。Kaldi代码量大,不可能也没必要全部移植到我们系统里,只需要移植我们需要的就可以了。怎样才能移植我们需要的代码呢?考虑后我用了如下的方法:先把在线解码相关的代码移植进去,然后开始不停的编译,报什么错提示缺什么就加什么,直到编译通过。这种方法保证了把需要的文件都移植进系统了,但有可能某些文件中的函数没用到,即到文件级还没到函数级。由于时间紧,这个问题就暂时不管了。移植过程更多的是一个体力活,需要小心细致。在移植过程中遇到问题就去网上搜,最后都圆满解决了。Kaldi主要用到了三个开源库:openfst、BLAS、LAPACK。BLAS和LAPACK我用的常规方法,即到官网上下载编译后生成库,然后把库和头文件放到系统的”/usr/lib”和“/use/include”下,让其他代码用。kaldi支持的有BALS库有 ATLAS / CLAPACK / openBLAS / MKL等。在X86的Ubuntu PC上跑kaldi时就用的Intel的MKL,在ARM上就不能用了,需要用其他的几种之一。我评估下来用了openBLAS,主要因为三点:1)它是BSD的;2)它支持多种架构(ARM/X86/MIPS/….),是开源库里性能最好的(各种架构里都嵌了很多的汇编代码),被多家著名公司使用,如IBM/ARM/nvidia/huawei等;3)它有多个编译选项可供选择,比如单线程/多线程选择、设定线程数等。BLAS的早期代码都是用fortran写的,后来用C对其进行了封装,所以系统还要加上对fortran的支持。对openFST,我发现用到的代码并不多,也就没用常规的方法,而是直接把用到的代码移植进系统。我移植好编译没问题后另一个同学把剩下的MFCC以及和ALSA接口(用ALSA接口替代kaldi里的PortAudio接口)相关的也移植进去了。这样移植工作就算结束了。对比了下移植进系统的kaldi代码和kaldi里SRC下的代码,应该是只用了其中一小部分。下图显示了移植进系统的kaldi文件(没列出相关的头文件)。同时负责模型训练的同学也有了一个初步的模型生成的文件,把这些文件放进系统里就可以跑起来了,人说话后PC屏幕上就有词打印出来,不过不正确。这也正常呀,因为还没调试呢!

 

 

3,第三阶段

第三阶段就是调试。第二阶段结束后说话就有词出来,但都是错的,需要排查定位问题。在线语音识别系统从大的角度可以分两块:模型和代码实现。首先我们需要定位是模型的问题还是代码实现的问题,先从模型排查。在第一阶段时利用thchs30大致搞清楚了在线解码的机制,是用模型tri1调的,当时识别率很差。现在要关注识别率了,把模型换成了tri2b,识别率有所提高。这说明kaldi里的在线解码的代码是没有问题的,识别率差问题出在模型。况且全球这么多人在用kaldi,如果在线解码有问题应该早就fix了。所以我们决定把我们生成的模型文件放进thchs30里来验证模型是否有问题。为了排除从MIC输入的音频数据有噪声等的干扰,先用读文件的方式验证。把我们的模型文件放进去后发现基本识别不正确,这说明模型是有问题的。负责模型的同学去调查,发现用于训练的音源都是8K采样的,但是在线解码用的都是16K采样的,这是我们自己挖的坑,用重采样程序把8K的全部转成16K的,这个坑也就填好了,但是识别率依旧不好。又发现训练集全是英国人的发音,而测试集是我们中国人的发音,有一定口音的,最好用我们中国人自己的发音作为训练集。于是我们自己又录了用于训练的音源,为了加大训练的数据,又请好多其他人录了音源。训练后得到了新的模型,再放到thchs30里面验证,识别率有六七成了,这说明模型的大方向对了,为了提高识别率,模型还需要继续调试。

 

接下来就要看代码部分是否有问题了。把新生产的模型放进我们自己的系统,并且用从音频文件都数据的方式(我们的系统既可以从MIC采集数据也可以从音频文件读数据,从音频文件读数据是为了debug)来替代从MIC采集到的数据(这样做是为了排除噪声等因素的干扰)来看代码是否有问题。运行下来发现识别率依旧很差,这说明我们的代码也是有问题的。在第二阶段我已经调试过部分代码,确保了在kaldi process thread里从PCM ring buffer里拿到的音频数据是没有问题的。还有两方面需要调试,一是送进MFCC的PCM数据要是OK的,二是我们的在线解码机制要跟kaldi里的在线解码机制完全一样。一很快就调试好了。二是先再深入研究吃透kaldi里的在线解码机制,改正我们与它不一样的地方,经过两三天调试后识别率跟thchs30里的差不多了,这说明我们的代码经过调试后也有一个好的base了,后面就要开始调性能了。

 

前面是通过从音频文件中读取数据来做在线识别的,数据相对干净些。现在要从MIC读取音频数据做真正在线识别了,试下来后识别率明显偏低,这说明我们的前处理还没完全做好(前面调试时只加了ANS模块)。我把前处理后的音频数据dump出来用CoolEdit听,的确有时候音质不好,于是我又把webRTC中的AGC模块加上去,再次dump出前处理后的音频数据听,多次听后都感觉音质正常。再来运行加了AGC后的从MIC采集音频数据的在线识别,识别率果然有了明显的提升。前处理能做的都做了,要想再提高识别率,就要靠模型发力了。做模型的同学一边请更多的人录音源来训练,一边尝试各种模型,最终用的是tri4b,有了一个相对不错的识别率。由于我们用的是GMM-HMM,如今主流的语音识别中已不再使用,老板就觉得没有必要再调了,后面肯定会用主流的模型的,但是整个嵌入式上的在线语音识别软件代码尤其软件架构和音频采集还是有用的,后面就要基于这些代码做真正的产品。

 

对语音识别领域的资深人士来说,这个嵌入式在线语音识别系统还很稚嫩。但通过搭这个系统,让我们对语音识别领域有了多一点的感性认识,也有了一个良好的开端,给老板以信心,并且可以继续做下去。这次工程上的事情偏多,后面希望更深入的做下去,累积更多的语音识别领域的经验。搭这个系统没有任何可供参考的资料,纯粹是根据我们以往的经验摸索着搭出来的。做的产品可能不一样,但很多解决问题的思路都是一样的。如果有朋友也搭过嵌入式上的在线语音识别系统,欢迎探讨,搭出一个更好的在线语音识别系统。

2020-02-12 18:07:05 weixin_44532659 阅读数 181

分享:
在 interspeech上,ESPnet的作者们分享了**一篇基于端到端的语音处理的PPT,**这篇PPT内容包含了ASR,TTS,NLU,MT等,题目为《Advanced Methods for Neural End-to-End Speech Processing》

在这篇PPT中,作者们把所有遇到的问题都通过ESPnet来完成,并且提供了大量的案例研究和实践教程,作者们希望通过这篇PPT可以让语音处理的研究和开发变得更容易。

特别值得注意的是:
在ASR这里,基本分享了目前主流的端到端模型,比如:CTC,RNNT,TRANSFORMER等。同时,也提供了一个colab的地址可以学习:https://colab.research.google.com/github/espnet/interspeech2019-tutorial/blob/master/notebooks/interspeech2019_asr/interspeech2019_asr.ipynb

可通过以下几种方式获取PPT:
全部PPT链接:
https://pan.baidu.com/s/1C7jgHUH_wuGXKmnvid2EYw
提取码:d470

语音控制PPT

阅读数 3

博文 来自: weixin_43202566
没有更多推荐了,返回首页