基于GMMs-HMMs的语音识别原理
刚入门ASR的时候一直能听到HMM模型的相关字眼,这里就补一下用GMMs-HMMs进行语音识别的原理,虽然这个方法很古老,而且已经近乎被神经网络所取代,但它背后的思想仍然值得我们去了解和学习~
笔者看了一些教程,包括课程讲义、博客还有一些工具书,总算大致理清了思路,相信至少在文章逻辑上比一些没有相关背景设定、无厘头、不知所云的教程要好,原理上至少也是能用到的都有介绍。而有些地方太深入的原理笔者也没有去细究,感兴趣的读者根据笔者的逻辑自己去深入探索即可。
整体的行文思路按照“结果导向”,需要什么数学知识和原理再去介绍什么,而不是上来就用GMM和HMM进行轰炸,导致前后脱节。在数学原理上,也是尽量做到通俗易懂,加入自己的理解,而不是甩一堆高深莫测的公式。
文章目录
- 一. 最简单的场景:孤立词识别
- 1.1 整体思路
- 1.2 模型结构
- 1.3 训练过程
- 1.3.1 单样本、单高斯
- 1.3.2 扩展到高斯混合模型
- 1.3.3 扩展到多个训练样本
- 1.4 识别过程
- 二. 扩展到通用场景:连续语音识别
- 2.1 整体思路
- 2.2 模型结构
- 2.3 训练过程
- 2.4 识别过程
- 2.4.1 较简单的情况:假设是对词进行HMM建模
- 2.4.2 扩展到对音素建模的情况
- 三. 延伸:上下文建模
- 3.1 三音素建模
- 3.2 优化:参数共享
- 3.2.1 共享状态
- 3.2.2 共享模型
- 传送门:
一. 最简单的场景:孤立词识别
1.1 整体思路
孤立词识别是语音识别中最简单的场景,即一个音频文件里面只包含一个词的语音。训练和识别的整体思路为:
(1)训练阶段:对于每个词,用不同人录制的这个词的音频特征作为训练样本,构建一个生成模型P(X∣W)P(X|W)P(X∣W),WWW表示词,XXX表示音频文件提取出的特征(如FBank、MFCC等,参见这篇博客)。
(2)识别阶段:给定一段音频特征,经过上面学习到的每个词的P(X∣W)P(X|W)P(X∣W)模型,看哪个词生成这段音频特征的概率最大,取最大的那个词作为识别词。
形象化的图如下:
![](/assets/blank.gif)
这里的重点就是P(X∣W)P(X|W)P(X∣W)这个生成模型的建立。因为对于每个孤立词都是分别采用一个生成模型来独立建模的,所以后面介绍的原理都是以一个孤立词为例讲解。
1.2 模型结构
对于这种时间序列的建模,自然就是采用HMM模型。其结构图如下:
![](/assets/blank.gif)
不大张旗鼓地讲那么多关于HMM没用的东西,就看这个图来讲解。它的构建思路就是认为音频特征是由一些隐藏状态生成的,这些状态内部会有转移,并且满足马尔可夫性,状态个数是超参数,需要自己设置(一般3~5个)。
对于除了开始(sIs_IsI)和结束(sEs_EsE)这两个状态之外的状态,都有两种转移选择,要么就转向自己,要么就转向下一个状态。因为音频特征的序列长度往往要比状态个数多,所以这里要有转向自己的,因此每个状态可能对应多个音频特征的“帧”。
以这个图里面的3个状态和对齐为例,生成模型可展开为:P(X∣W)=P(x1∣s1)P(s1∣s1)P(x2∣s1)P(s1∣s1)P(x3∣s1)P(s2∣s1)P(x4∣s2)P(s2∣s2)P(x5∣s2)P(s3∣s2)P(x6∣s3)P(X|W)=P(x_1|s_1)P(s_1|s_1)P(x_2|s_1)P(s_1|s_1)P(x_3|s_1)P(s_2|s_1)P(x_4|s_2)P(s_2|s_2)P(x_5|s_2)P(s_3|s_2)P(x_6|s_3)P(X∣W)=P(x1∣s1)P(s1∣s1)P(x2∣s1)P(s1∣s1)P(x3∣s1)P(s2∣s1)P(x4∣s2)P(s2∣s2)P(x5∣s2)P(s3∣s2)P(x6∣s3)。
这里主要有两部分概率需要学习:
(1)aija_{ij}aij:P(sj∣si)P(s_j|s_i)P(sj∣si),状态转移概率
(2)bj(xt)b_j(x_t)bj(xt):P(xt∣sj)P(x_t|s_j)P(xt∣sj),输出生成概率
其中bj(xt)b_j(x_t)bj(xt)一般采用GMM进行建模,这也就是为什么叫GMM-HMM。直接给出GMM的公式:
bj(xt)=∑m=1McjmN(xt;μjm,Σjm)b_j(x_t) = \sum_{m=1}^M c_{jm} N(x_t; \mu_{jm}, \Sigma_{jm})bj(xt)=m=1∑McjmN(xt;μjm,Σjm)
N(x;μ,Σ)=1(2π)D/2∣Σ∣1/2exp(−12(x−μ)TΣ−1(x−μ))N(x_; \mu, \Sigma) = \frac{1}{(2\pi)^{D/2}|\Sigma|^{1/2}}exp(-\frac{1}{2}(x - \mu)^T \Sigma^{-1}(x - \mu))N(x;μ,Σ)=(2π)D/2∣Σ∣1/21exp(−21(x−μ)TΣ−1(x−μ))
即认为输出生成概率由高斯混合模型生成,先根据概率cjmc_{jm}cjm选择一个高斯模型,然后根据这个模型的概率分布N(xt;μjm,Σjm)N(x_t; \mu_{jm}, \Sigma_{jm})N(xt;μjm,Σjm)生成 xtx_txt。
总结一下,要学习的参数有:
- aija_{ij}aij:HMM的状态转移概率矩阵
- cjmc_{jm}cjm:各状态对应的GMM中的混合权重(如果是单高斯建模,则不用学习)
- μjm\mu_{jm}μjm:各状态对应的GMM中各高斯分量的均值向量
- Σjm\Sigma_{jm}Σjm:各状态对应的GMM中各高斯分量的协方差矩阵
建立好这样的模型之后,后面就是如何根据样本进行训练了。
1.3 训练过程
这里的训练,确切来讲应该叫参数估计,用一定的算法来估计参数,使其能够拟合数据分布(即最大化数据的似然概率)。下面循序渐进地来讲这种参数估计算法:
1.3.1 单样本、单高斯
这是一种最简单的情况,假设一个词的训练样本只有一个音频文件,并且对于输出概率bj(xt)b_j(x_t)bj(xt)采用单高斯建模,即 m=1m=1m=1,cjm=1c_{jm}=1cjm=1。此时需要估计的参数为:
- aija_{ij}aij:HMM的状态转移概率矩阵
- μj\mu_{j}μj:各状态对应的单高斯模型的均值向量
- Σj\Sigma_{j}Σj:各状态对应的单高斯模型的协方差矩阵
这里进行参数估计的一大难点就是:各个状态对应哪些音频帧是不知道的,不像1.2里面给出的那个图那样具有对齐信息。所以输出概率bj(xt)b_j(x_t)bj(xt)根本不知道具体去拟合哪些音频帧数据。
为了解决这个问题,引入一种软对齐策略,即给出各个音频帧属于各个状态的概率(各个时刻处于各个状态的概率)γt(j)=P(st=j∣X)\gamma_t(j) = P(s_t = j|X)γt(j)=P(st=j∣X),此时对于各个单高斯模型的参数估计就可以写为:
μ^j=∑t=1Tγt(j)xt∑t=1Tγt(j)\hat \mu_j = \frac{\sum_{t=1}^T \gamma_t(j) x_t}{\sum_{t=1}^T \gamma_t(j)}μ^j=∑t=1Tγt(j)∑t=1Tγt(j)xt
Σ^j=∑t=1Tγt(j)(xt−μ^j)(xt−μ^j)T∑t=1Tγt(j)\hat \Sigma_j = \frac{\sum_{t=1}^T \gamma_t(j) (x_t - \hat \mu_j) (x_t - \hat \mu_j) ^ T}{\sum_{t=1}^T \gamma_t(j)}Σ^j=∑t=1Tγt(j)∑t=1Tγt(j)(xt−μ^j)(xt−μ^j)T
这个地方不好理解的话,再说得深入一些:
如果最理想情况下给出了各个状态对应的音频帧,像1.2节的图那样。则每个状态对应的高斯模型需要拟合的数据就确定了,此时根据最大似然概率,均值和方差分别应该估计为:
μ^j=∑t=1Tzjtxt∑t=1Tzjt\hat \mu_j = \frac{\sum_{t=1}^T z_{jt} x_t}{\sum_{t=1}^T z_{jt}}μ^j=∑t=1Tzjt∑t=1Tzjtxt
Σ^j=∑t=1Tzjt(xt−μ^j)(xt−μ^j)T∑t=1Tzjt\hat \Sigma_j = \frac{\sum_{t=1}^T z_{jt} (x_t - \hat \mu_j) (x_t - \hat \mu_j) ^ T}{\sum_{t=1}^T z_{jt}}Σ^j=∑t=1Tzjt∑t=1Tzjt(xt−μ^j)(xt−μ^j)T
zjt=1ifxt∈sjelse0z_{jt} = 1 \ \ \ if \ \ x_t \in s_j \ \ else \ \ 0zjt=1ifxt∈sjelse0
但是这里没有zjtz_{jt}zjt这种“硬对齐”信息,所以要用“软对齐”的概率γt(j)\gamma_t(j)γt(j)来代替。
同样的,对于转移概率akja_{kj}akj,也引入软对齐转移概率ξt(i,j)=P(st=i,st+1=j∣X)\xi_t(i, j) = P(s_t = i, s_{t+1} = j|X)ξt(i,j)=P(st=i,st+1=j∣X),表示在 ttt 时刻的状态在sis_isi,而在 t+1t+1t+1 时刻的状态为sjs_jsj的概率。此时转移概率可以估计为:
a^ij=∑t=1Tξt(i,j)∑k=1N∑t=1Tξt(i,k)\hat a_{ij} = \frac{\sum_{t=1}^T\xi_t (i, j)}{\sum_{k=1}^N \sum_{t=1}^T \xi_t (i, k)}a^ij=∑k=1N∑t=1Tξt(i,k)∑t=1Tξt(i,j)
即状态sis_isi转到状态sjs_jsj(在各个时刻下求和)的概率除以状态sis_isi转到其他所有状态sks_ksk(在各个时刻下求和)的概率。
综上,在进行参数估计前,需要先拿到γt(j)\gamma_t(j)γt(j)和ξt(i,j)\xi_t(i, j)ξt(i,j)这两个概率。
那么,这两个软对齐概率要怎么计算呢?此时就需要用到HMM中经典的前向-后向概率计算公式。引入两个概率:
(1)αt(j)\alpha_t(j)αt(j):前向概率,展开为αt(j)=P(x1,x2,...,xt,st=j)\alpha_t(j) = P(x_1, x_2, ..., x_t, s_t = j)αt(j)=P(x1,x2,...,xt,st=j),即已经输出x1,x2,...,xtx_1, x_2, ..., x_tx1,x2,...,xt,并且在时刻 ttt 处在状态sjs_jsj的概率。
(2)βt(j)\beta_t(j)βt(j):后向概率,展开为βt(j)=P(xt+1,xt+2,...,xT∣st=j)\beta_t(j) = P(x_{t+1}, x_{t+2}, ..., x_{T} | s_t = j)βt(j)=P(xt+1,xt+2,...,xT∣st=j),在时刻 ttt 处在状态sjs_jsj时,后续输出为xt+1,xt+2,...,xTx_{t+1}, x_{t+2}, ..., x_Txt+1,xt+2,...,xT的概率。
在给出了这两个概率时,就可以计算γt(j)\gamma_t(j)γt(j)和ξt(i,j)\xi_t(i, j)ξt(i,j),如下:
γt(j)=P(st=j∣X)=1αT(sE)αt(j)βt(j)\gamma_t(j) = P(s_t = j|X) = \frac{1}{\alpha_T(s_E)} \alpha_t(j) \beta_t(j)γt(j)=P(st=j∣X)=αT(sE)1αt(j)βt(j)
ξt(i,j)=P(st=i,st+1=j∣X)=αt(i)aijbj(xt+1)βt+1(j)αT(sE)\xi_t (i, j) = P(s_t = i, s_{t+1} = j|X) = \frac{\alpha_t(i) a_{ij} b_j(x_{t+1}) \beta_{t+1} (j)}{\alpha_T(s_E)}ξt(i,j)=P(st=i,st+1=j∣X)=αT(sE)αt(i)aijbj(xt+1)βt+1(j)
这里的αT(sE)\alpha_T(s_E)αT(sE)实际上就是P(X)P(X)P(X)。
综上,在计算γt(j)\gamma_t(j)γt(j)和ξt(i,j)\xi_t(i, j)ξt(i,j)这两个概率之前,需要先计算αt(j)\alpha_t(j)αt(j)和βt(j)\beta_t(j)βt(j)。
那么如何计算前向和后向概率呢?先放一张图:
![](/assets/blank.gif)
这是一个典型的所有可能路径的示意图,αt(j)\alpha_t(j)αt(j)即计算从起点的紫色圆圈到其中某个青色圆圈的所有路径的概率和,βt(j)\beta_t(j)βt(j)即计算从某个青色圆圈到终点的紫色圆圈的所有路径的概率和。计算方式采用动态规划算法,迭代进行:
具体地,对于αt(j)\alpha_t(j)αt(j),计算方法为:
(1)初始化:α0(sI)=1\alpha_0(s_I) = 1α0(sI)=1,α0(j)=0ifj!=sI\alpha_0(j) = 0 \ \ \ if \ \ j \ \ != \ s_Iα0(j)=0ifj!=sI
(2)迭代:αt(j)=∑i=1Nαt−1(j)aijbj(xt)1<=j<=N,1<=t<=T\alpha_t(j) = \sum_{i=1}^N \alpha_{t-1}(j) a_{ij} b_j(x_t) \ \ \ 1 <= j <= N, 1<=t<=Tαt(j)=∑i=1Nαt−1(j)aijbj(xt)1<=j<=N,1<=t<=T
(3)终止:P(X)=αT(sE)=∑i=1NαT(i)aiEP(X) = \alpha_T(s_E) = \sum_{i=1}^N \alpha_T(i) a_{iE}P(X)=αT(sE)=∑i=1NαT(i)aiE
下图是迭代计算过程中拆分出的某一步的示意图,展示了动态规划的计算方法。
![](/assets/blank.gif)
同样地,对于βt(i)\beta_t(i)βt(i),计算方法为:
(1)初始化:βT(i)=aiE\beta_T(i) = a_{iE}βT(i)=aiE
(2)迭代:βt(i)=∑j=1Naijbj(xt+1)βt+1(j)fort=T−1,...,1\beta_t(i) = \sum_{j=1}^N a_{ij} b_j(x^{t+1}) \beta_{t+1}(j) \ \ \ for \ \ t \ \ = T-1, ..., 1βt(i)=∑j=1Naijbj(xt+1)βt+1(j)fort=T−1,...,1
(3)终止:P(X)=β0(sI)=∑j=1NaIjbj(x1)β1(j)=αT(sE)P(X) = \beta_0(s_I) = \sum_{j=1}^N a_{Ij}b_j(x^1)\beta_1(j) = \alpha_T(s_E)P(X)=β0(sI)=∑j=1NaIjbj(x1)β1(j)=αT(sE)
下图是迭代过程中拆分出的某一步的示意图:
![](/assets/blank.gif)
到这里,所有在参数估计中需要用到的概率都已经计算完毕了,下面展示整个流程。
整个参数估计的流程,其实还是需要迭代的,这里采用EM算法(一种对含有隐变量模型进行参数估计的迭代算法,在HMM的场景里,也叫前向-后向算法或Baum-Welch算法):
- 初始化:
笔者看到的有:对于GMM,一种做法是用所有数据进行估计,另一种是用K-Means先聚类一波;对于HMM的转移概率,是用的平均。
- 对于每一次迭代:
(1)E步:用上一步估计出的参数,迭代计算前向概率αt(j)\alpha_t(j)αt(j)和后向概率βt(j)\beta_t(j)βt(j),进而计算软对齐概率γt(j)\gamma_t(j)γt(j)和ξt(i,j)\xi_t(i, j)ξt(i,j)。
(2)M步:基于E步计算的4组概率,对μ^j\hat \mu_jμ^j、Σ^j\hat \Sigma_jΣ^j和a^ij\hat a_{ij}a^ij进行估计。
其中每一步的计算公式,前面都已经详细说明了。
1.3.2 扩展到高斯混合模型
一般在对于输出概率分布的建模,都会采用GMM方法。理论上,足够的单高斯模型的混合可以拟合任意的分布。1.3.1的单高斯建模只是一种特例,目的还是为了方便扩展到GMM的场景。
这里需要估计的参数就要加上GMM的混合权重,即:
- akja_{kj}akj:HMM的状态转移概率矩阵
- cjmc_{jm}cjm:各状态对应的GMM中的混合权重
- μjm\mu_{jm}μjm:各状态对应的GMM中各高斯分量的均值向量
- Σjm\Sigma_{jm}Σjm:各状态对应的GMM中各高斯分量的协方差矩阵
对于GMM的参数估计中,与之前HMM是同样的道理。因为在音频帧对应到某个状态后,还是不知道其是GMM中的哪个分量生成的。所以还是需要一种软对齐概率,具体表现在γt(j)\gamma_t(j)γt(j)上,此时需要改为γt(j,m)\gamma_t(j, m)γt(j,m),表示在时刻t,属于状态sjs_jsj以及第mmm个高斯分量的概率。
笔者猜测的γt(j,m)\gamma_t(j, m)γt(j,m)计算公式为:(因为没找到相关的资料,如果有错误,还请指出~)
γt(j,m)=P(st=j,ct=m∣X)=P(st=j∣X)P(ct=m∣st=j,X)=1αT(sE)αt(j)βt(j)1(2π)D/2∣Σjm∣1/2exp(−12(xt−μjm)TΣjm−1(xt−μjm))\gamma_t(j, m) = P(s_t = j, c_t = m |X) = P(s_t = j | X) P(c_t = m | s_t = j, X) = \frac{1}{\alpha_T(s_E)} \alpha_t(j) \beta_t(j) \frac{1}{(2\pi)^{D/2}|\Sigma_{jm}|^{1/2}}exp(-\frac{1}{2}(x_t - \mu_{jm})^T \Sigma_{jm}^{-1}(x_t - \mu_{jm}))γt(j,m)=P(st=j,ct=m∣X)=P(st=j∣X)P(ct=m∣st=j,X)=αT(sE)1αt(j)βt(j)(2π)D/2∣Σjm∣1/21exp(−21(xt−μjm)TΣjm−1(xt−μjm))
相应的,对于μ^jm\hat \mu_{jm}μ^jm和Σ^jm\hat \Sigma_{jm}Σ^jm的参数估计就要改为:
μ^jm=∑t=1Tγt(j,m)xt∑t=1Tγt(j,m)\hat \mu_{jm} = \frac{\sum_{t=1}^T \gamma_t(j, m) x_t}{\sum_{t=1}^T \gamma_t(j, m)}μ^jm=∑t=1Tγt(j,m)∑t=1Tγt(j,m)xt
Σ^jm=∑t=1Tγt(j,m)(xt−μ^jm)(xt−μ^jm)T∑t=1Tγt(j,m)\hat \Sigma_{jm} = \frac{\sum_{t=1}^T \gamma_t(j, m) (x_t - \hat \mu_{jm}) (x_t - \hat \mu_{jm}) ^ T}{\sum_{t=1}^T \gamma_t(j, m)}Σ^jm=∑t=1Tγt(j,m)∑t=1Tγt(j,m)(xt−μ^jm)(xt−μ^jm)T
GMM的混合参数估计如下:
c^jm=∑t=1Tγt(j,m)∑m′=1M∑t=1Tγt(j,m′)\hat c_{jm} = \frac{\sum_{t=1}^T \gamma_t(j, m)}{\sum_{m'=1}^M \sum_{t=1}^T \gamma_t(j, m')}c^jm=∑m′=1M∑t=1Tγt(j,m′)∑t=1Tγt(j,m)
即处于状态 jjj 和分量 mmm 的概率,除以处于状态 jjj 和其他所有分量的概率。
而前向概率αt(j)\alpha_t(j)αt(j)、后向概率βt(j)\beta_t(j)βt(j) 和 ξt(i,j)\xi_t(i, j)ξt(i,j)的计算公式不变,转移概率aija_{ij}aij的估计公式也不变。
那么EM的流程就变为:
(1)E步:用上一步估计出的参数,迭代计算前向概率αt(j)\alpha_t(j)αt(j)和后向概率βt(j)\beta_t(j)βt(j),进而计算软对齐概率γt(j,m)\gamma_t(j, m)γt(j,m)和ξt(i,j)\xi_t(i, j)ξt(i,j)。
(2)M步:基于E步计算的4组概率,对μ^jm\hat \mu_{jm}μ^jm、Σ^jm\hat \Sigma_{jm}Σ^jm、c^jm\hat c_{jm}c^jm和 a^ij\hat a_{ij}a^ij进行估计。
1.3.3 扩展到多个训练样本
前面1.3.1和1.3.2讨论的都是只有一个训练样本的情况,实际上对于每个词,都会有很多个音频文件与之对应,从而提升模型建模的鲁棒性。
假设某个词一共有 RRR 个音频文件,则前面的xtx_txt就要改为xtrx_t^rxtr,表示第 rrr 个训练样本中的第 ttt 帧。此时EM的流程变为:
(1)E步:对每个样本都计算出:αtr(j)\alpha_t^r(j)αtr(j)、βtr(j)\beta_t^r(j)βtr(j)、γtr(j,m)\gamma_t^r(j, m)γtr(j,m)和ξtr(i,j)\xi_t^r(i, j)ξtr(i,j)。
(2)M步:对于参数的估计需要在全部的训练样本上进行,具体为
μ^jm=∑r=1R∑t=1Tγtr(j,m)xtr∑r=1R∑t=1Tγtr(j,m)\hat \mu_{jm} = \frac{\sum_{r=1}^R \sum_{t=1}^T \gamma_t^r(j, m) x_t^r}{\sum_{r=1}^R \sum_{t=1}^T \gamma_t^r(j, m)}μ^jm=∑r=1R∑t=1Tγtr(j,m)∑r=1R∑t=1Tγtr(j,m)xtr
Σ^jm=∑r=1R∑t=1Tγtr(j,m)(xtr−μ^jm)(xtr−μ^jm)T∑r=1R∑t=1Tγtr(j,m)\hat \Sigma_{jm} = \frac{\sum_{r=1}^R \sum_{t=1}^T \gamma_t^r(j, m) (x_t^r - \hat \mu_{jm}) (x_t^r - \hat \mu_{jm}) ^ T}{\sum_{r=1}^R \sum_{t=1}^T \gamma_t^r(j, m)}Σ^jm=∑r=1R∑t=1Tγtr(j,m)∑r=1R∑t=1Tγtr(j,m)(xtr−μ^jm)(xtr−μ^jm)T
c^jm=∑r=1R∑t=1Tγtr(j,m)∑r=1R∑m′=1M∑t=1Tγtr(j,m′)\hat c_{jm} = \frac{\sum_{r=1}^R \sum_{t=1}^T \gamma_t^r(j, m)}{\sum_{r=1}^R \sum_{m'=1}^M \sum_{t=1}^T \gamma_t^r(j, m')}c^jm=∑r=1R∑m′=1M∑t=1Tγtr(j,m′)∑r=1R∑t=1Tγtr(j,m)
a^ij=∑r=1R∑t=1Tξtr(i,j)∑r=1R∑k=1N∑t=1Tξtr(i,k)\hat a_{ij} = \frac{\sum_{r=1}^R \sum_{t=1}^T\xi_t^r (i, j)}{\sum_{r=1}^R \sum_{k=1}^N \sum_{t=1}^T \xi_t^r (i, k)}a^ij=∑r=1R∑k=1N∑t=1Tξtr(i,k)∑r=1R∑t=1Tξtr(i,j)
在训练完成后,就是如何利用训练好的模型对新音频进行识别了。
1.4 识别过程
识别的过程,就是给定一段音频XXX,对于每个词的模型P(X∣W)P(X|W)P(X∣W),都计算出其生成XXX的概率,然后取最大的那个W=argmaxWP(X∣W)W = argmax_W P(X|W)W=argmaxWP(X∣W)作为识别出的孤立词。
这里的P(X∣W)P(X|W)P(X∣W)实际在前面已经提到了,就是αT(sE)\alpha_T(s_E)αT(sE),但一般在识别过程中,不会对所有路径的概率计算总和,而是会选择最大的那一条概率作为最终的概率。这其实就是HMM中经典的维特比算法。
定义概率Vt(j)V_t(j)Vt(j),表示在时刻 ttt 到达状态sjs_jsj的所有路径中概率的最大值,对应到1.3中第一张路径图,即从起始的紫色圆圈到某个青色圆圈的所有路径概率中,最大的那一个。同时定义回溯指针btt(j)bt_t(j)btt(j),即时刻 ttt 到达状态sjs_jsj的所有路径中概率最大的那一条路径对应的前一个状态,便于之后进行状态回溯。
与前向概率αt(j)\alpha_t(j)αt(j)类似,对于Vt(j)V_t(j)Vt(j)和btt(j)bt_t(j)btt(j)的计算也是采用动态规划进行迭代计算,具体的计算方式为:
(1)初始化:
V0(sI)=1V_0(s_I) = 1V0(sI)=1
V0(j)=0ifj!=sIV_0(j) = 0 \ \ \ if \ \ j \ \ != \ s_IV0(j)=0ifj!=sI
bt0(j)=0bt_0(j) = 0bt0(j)=0
(2)迭代:
Vt(j)=argmaxi=1NVt−1(j)aijbj(xt)V_t(j) = argmax_{i=1}^N V_{t-1}(j) a_{ij} b_j(x_t)Vt(j)=argmaxi=1NVt−1(j)aijbj(xt)
btt(j)=argmaxi=1NVt−1(j)aijbj(xt)bt_t(j) = argmax_{i=1}^N V_{t-1}(j) a_{ij} b_j(x_t)btt(j)=argmaxi=1NVt−1(j)aijbj(xt)
(3)终止:
P∗=VT(sE)=maxi=1NVT(i)aiEP^* = V_T(s_E) = \max_{i=1}^N V_T(i) a_{iE}P∗=VT(sE)=i=1maxNVT(i)aiE
sT∗=btT(sE)=argmaxi=1NVT(i)aiEs_T^* = bt_T(s_E) = argmax_{i=1}^N V_T(i) a_{iE}sT∗=btT(sE)=argmaxi=1NVT(i)aiE
下面两个图是拆分出的中间某一步的迭代计算过程:
![](/assets/blank.gif)
![](/assets/blank.gif)
最后的识别词为:W=argmaxWP(X∣W)=argmaxWVTW(sE)W = argmax_W P(X|W) = argmax_W V_T^W(s_E)W=argmaxWP(X∣W)=argmaxWVTW(sE)。
至此,对于孤立词识别的全部内容就已经介绍完毕!
二. 扩展到通用场景:连续语音识别
2.1 整体思路
连续语言识别是比较通用的场景,即一个音频文件里面包含一个连续的句子,而不是一个词,其难点在于不知道每个词对应音频文件的起止位置。如果有这种标注好的切分,那么仍然可以沿用前面的孤立词识别方式进行训练和识别,但这样着实费时费力,而且会有人工误差。那么,能不能在没有切分的情况下,对一整段音频,识别一整个句子?当然可以,下面将详细介绍。
与孤立词识别类似,这里是希望构建一个判别模型P(S∣X)P(S|X)P(S∣X),其中XXX是音频特征,SSS是其对应的句子。训练是希望能最大化P(S∣X)P(S|X)P(S∣X),识别是希望能找到argmaxSP(S∣X)argmax_S P(S|X)argmaxSP(S∣X)。对于P(S∣X)P(S|X)P(S∣X)的建模通常会通过贝叶斯公式转为P(S∣X)≈P(X∣S)P(S)P(S|X) \approx P(X|S)P(S)P(S∣X)≈P(X∣S)P(S)来处理,其中P(X∣S)P(X|S)P(X∣S)即为生成模型,P(S)P(S)P(S)为语言模型(这里不涉及到语言模型的细节)。
其训练和识别的整体思路为:
(1)训练阶段:对于所有的句子,构建生成模型P(X∣S)P(X|S)P(X∣S),最大化每个句子的似然概率。
(2)识别阶段:给定一段音频特征,用构建好的生成模型和语言模型,得到识别出的句子argmaxSP(X∣S)P(S)argmax_S P(X|S)P(S)argmaxSP(X∣S)P(S)。
2.2 模型结构
对于生成模型P(X∣S)P(X|S)P(X∣S)的构建,可以使用“嵌入训练(embedded training)”的方式。
理想情况下,在词表比较小的时候,可以对每个词进行一个HMM建模(与之前孤立词识别一样),而后将整个句子中所有词的HMM状态都串起来,作为一个超长的HMM,其训练方式与1.3节的一样。
但在真实场景中,词表往往很庞大,此时如果对所有的词建模,HMM模型将非常多。所以,一般都是将句子转成音素串(可以将音素理解为音标,一个词会对应一条音素序列)进行处理,音素表往往会小很多,这样对每个音素建模较为简便。
总结一下,在连续语音场景中,是为每个音素建立一个HMM模型,将句子转为音素串之后,将句子中所有音素对应的HMM状态都串在一起(中间的开始和结束状态会去掉),成为一个超长的HMM模型,如下图:
![](/assets/blank.gif)
这里的句子“six quid”转为音素串为“/s/ /ih/ /k/ /s/ … /d/”,每个“beg mid end”是对一个音素的3状态HMM建模。
2.3 训练过程
在串成2.2节中超长的HMM之后,训练的方式就与1.3节中的类似了,只不过1.3节中是每次对一个HMM模型进行训练,这里是一次对很多个HMM模型进行并行训练。
其训练流程为:
- 获取下一个句子;
- 转成音素串后,按照2.2节构建成超长的HMM模型,当成一个HMM模型来处理;
- E步:迭代计算前向概率αt(j)\alpha_t(j)αt(j)和后向概率βt(j)\beta_t(j)βt(j),进而计算软对齐概率γt(j,m)\gamma_t(j, m)γt(j,m)和ξt(i,j)\xi_t(i, j)ξt(i,j);
- M步:基于E步计算的4组概率,对μ^jm\hat \mu_{jm}μ^jm、Σ^jm\hat \Sigma_{jm}Σ^jm、c^jm\hat c_{jm}c^jm和 a^ij\hat a_{ij}a^ij进行估计;
- 重复整个流程,直到所有的句子遍历完成。
在训练完成之后,每个音素的HMM模型也就训练好了。
2.4 识别过程
识别过程与孤立词识别的差别就比较大了,因为要考虑“音素->词->句子”的层级传递。这里介绍一个比较常用的识别算法——“token passing算法”,属于剪枝的维特比译码算法的一种。
PS:其实这个算法本身不难理解,但因为相关资料比较少,而且说的都比较模糊。笔者也是看了很久才明白原理,希望能用通俗的方式把它呈现出来,细节上如果有不周到的地方,还望指出~
2.4.1 较简单的情况:假设是对词进行HMM建模
虽然2.2节和2.3节介绍的内容都是对音素建模,但从音素到句子要经过“音素->词->句子”的两层识别传递。这里为了方便更好地讲明白原理,先假设之前构建和训练的都是针对每个词的HMM模型,这样只需要经过“词->句子”的一层识别传递,更容易理解原理。后面再进一步扩展到对音素建模的情况。
首先需要声明的是,因为只针对训练样本中有的词构建了HMM模型,所以在识别时,只能识别这些已有的词。假设词表中只有“one”、“two”和“three”这三个词,那么识别的示意图可以画成下面这样:
![](/assets/blank.gif)
其实还是与之前1.4节孤立词识别同样的方法,只不过这里是并行对所有词进行识别。同时,在孤立词识别过程中,如果到达了结束状态,则识别就结束了;但在这里,如果到达了结束状态,还是要继续识别下一个词,所以在图里是一个循环。
在每个HMM内部,还是采用维特比识别方法,用动态规划,在每个时刻对于每个状态选择一条最大概率的路径。因为是并行的,那么在某个时刻,可能同时会有多个词到达结束状态,分别对应着一段已识别出的句子(路径),然后又都要同时再进行下一个词的识别。这里为了避免多余的计算,采用与维特比识别一样的思路,只需要取一个最大概率的句子,而扔掉其他的。那么,在这个过程中,就需要记录概率和路径,这个就叫“token”。看下面这个图:
![](/assets/blank.gif)
每个“token”里面存储score和WLR,前者是这条路径的概率,后者是“word link record”,记录当前的路径(包括score:分数;path id:它是从之前哪个WLR过来的;model id:当前识别出的词是哪个;time:时间等)。这样在某个时刻,每条到达结束状态的路径,都会有一个对应的token,从这些“tokens”里面选一个具有最大score的token保留,扔掉其他所有的token后,继续下一步的识别。
写成伪代码就是:
先定义两个特殊的token:(1)“start token”(P=1,score=logP=0,WLR=null),(2)“null token”(P=0,score=logP=-inf,WLR=null)
假设时间长度为T,状态数目为N+2,其中0和N+1分别表示开始状态和结束状态初始化:在开始状态中放入“start token”,在其他状态中放入“null token”
迭代:for time t = 1, ..., Tfor state i <= N将状态i的token复制到与其连接的所有不是结束状态的状态j中更新token的score为:score += log a_ij + log b_j(y_t)endfor state i in (与结束状态连接的状态集合)将状态i的原始token复制到结束状态更新token的score为:score += log a_iE创建一个新的WLR,其path id指向token的WLR,model id为这个token对应的识别词,time为t,score为当前token的分数更新token的WLR为这个新建的WLRend扔掉所有的原始tokenfor state i <= N+1保留状态i中所有tokens中最大score的那个token,扔掉其他所有的tokensend将结束状态的token复制到开始状态中返回结束状态的token
识别结果:根据token的model id以及path id一层一层向前回溯,得到整个句子
上面的过程能够得到argmaxSP(X∣S)argmax_S P(X|S)argmaxSP(X∣S)的句子,识别的目标是argmaxSP(X∣S)P(S)argmax_S P(X|S)P(S)argmaxSP(X∣S)P(S)。语言模型可以通过两种方式加进去:
- rescore:即重打分排序,这就需要前面保留token的时候,要保留NBest(参考beam search的思想),然后对NBest识别出来的句子,通过LM进行rescore即可。
- fusion:直接融合进去,每次识别出一个词之后,可以用语言模型,根据它之前的句子和这个词,得到当前这个词的分数,一起加到token的计算里面,具体加入的位置可以是
更新token的score为:score += log a_iE + (LM分数 log P(w_t|w_1, ..., w_{t-1}))
,找了一个比较形象的图:
![](/assets/blank.gif)
2.4.2 扩展到对音素建模的情况
有了上面的对词建模的识别过程之后,既可以很容易地扩展到对音素建模的情况,只是这里要多加一层从“音素->词”的传递,通过发音词典即可完成,比如下图:
![](/assets/blank.gif)
可以将一个词的所有音素的HMM状态都串起来,作为一整个HMM(改一下aija_{ij}aij矩阵的连接方式应该就可以)。而后进行与2.4.1一样的识别过程即可。
三. 延伸:上下文建模
3.1 三音素建模
前面讨论的都是对单音素进行HMM建模的方式,但实际上一个音素的发声是依赖于其上下文(即邻居音素)的,只用一个模型对其进行建模,信息量难免会有丢失。因此,应当在建模时考虑到这种上下文信息。
一种较普遍的做法是,对三音素(tripone)进行建模。对于音素 xxx,假设它左边的音素是 lll,右边的音素是 rrr,那么音素 xxx 的三音素形式就可以表示为 l−x+rl-x+rl−x+r。比如don't ask
就可以表示为sil sil-d+oh d-oh+n oh-n+t n-t+ah t-ah+s ah-s+k s-k+sil sil
这样的三音素串。
这样在建模的时候就是对类似d-oh+n
这样的三音素(三个音素当成一个音素)进行建模。
3.2 优化:参数共享
只考虑单音素的情况下,音素表可能比较小,方便建模。但如果考虑三音素的情况,音素表可能就呈指数级增长了。
简单计算一下,假设单音素表有40个音素,那么可能的三音素就会有403=6400040^3=64000403=64000个,在实际情况下,可能有500005000050000个是真实存在的。那么,要对这500005000050000个三音素进行三状态的HMM建模,假设用101010个混合分量的GMM模型,那么一共需要50000∗3∗10=1.5M50000 * 3 * 10 = 1.5M50000∗3∗10=1.5M的高斯模型。假设用39维的音频特征,那么每个高斯模型需要约800800800个参数,总的参数量就高达120M120M120M。这是十分惊人的,需要有大量的数据来进行拟合,同时还要保证每个三音素都有足够的数据,这点比较困难。
为了对其进行优化,通常会采用共享参数的方式,减少建模的参数量,方便训练。共享可以发生在不同的层面:
- 共享高斯模型:所有状态都用同样的高斯模型,只是混合权重不一样(捆绑混合物)
- 共享状态:允许不同的HMM模型使用一些相同的状态(状态聚类)
- 共享模型:相似的三音素使用同样的HMM模型(三音素泛化)
所有的方法都是数据驱动的,下面进行一些简单介绍:(笔者没有过多细究这部分,只是大概了解了原理,对此感兴趣的读者可以前往传送门查找细节)
3.2.1 共享状态
这部分的重点,其实是对状态进行聚类。找到相似的一堆状态,然后让不同的HMM模型之间共享这些状态,比如下图:
![](/assets/blank.gif)
具体怎么找这些相似的状态?可以采用自顶向下的拆分,建立决策树来聚类,看下面这个形象的图:
![](/assets/blank.gif)
顶层节点是所有中间音素为/iy/
的三音素,然后通过问各种问题,比如左边是鼻音吗?右边是浊音吗?等等,将这些三音素进行划分,最后在同一个叶子节点里面的所有三音素的中间状态就可以共享。
3.2.2 共享模型
这部分的重点,其实就是对三音素进行泛化,找到一堆相似的三音素,用同一个泛化的三音素来表示,有点儿像词干的感觉。看下面这个图:
![](/assets/blank.gif)
这里就是将s-iy+l
和f-iy+l
泛化为(s,f)-iy+l
,将t-iy+n
和t-iy+m
泛化为t-iy+(n,m)
,模型个数缩减了一半。
具体怎么找这些相似的三音素?可以采用自底向上的合并,比较具有不同三音素的异音素(allophone)模型,合并相似的那些。(PS:这句话笔者是翻译过来的,,只能意会)
传送门:
HTK Book:HTK工具包的说明文档(需要先注册才能看),第一章的教程对于整体脉络的把握很清晰
HMM acoustic modelling: HMMs and GMMs:英国爱丁堡大学的ASR课程讲义,主攻HMM和GMM模型和孤立词识别,在数学原理上很详细
HMM acoustic modelling: Context-dependent phone modelling:英国爱丁堡大学的ASR课程讲义,考虑上下文的建模
基于GMMs-HMMs的语音识别原理相关推荐
- 基于DTW算法的语音识别原理与实现
[摘 要]以一个能识别数字0-9的语音识别系统的实现过程为例,阐述了基于DTW算法的特定人孤立词语音识别的基本原理和关键技术.其中包括对语音端点检测方法.特征参数计算方法和DTW算法实现的详细讨论,最 ...
- 语音识别的原理_语音识别原理_语音识别原理框图 - 云+社区 - 腾讯云
广告关闭 腾讯云双11爆品提前享,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高满返5000元! 深入浅出地介绍了基于hmm的语音识别的原理,不注重公式的细节推导而是着重阐述公式背 ...
- 基于LD3320的嵌入式语音识别系统设计
摘要:语音交互系统是比较人性化的人机操作界面,它需要语音识别系统的支持.LD3320就是这样一款语音识别芯片.介绍了该芯片的工作原理及应用,给出了LD3320与微处理器的硬件接口电路及软件程序.随着高 ...
- 一种基于地图导航的语音识别管理系统的制作方法
本发明涉及语音识别技术领域,具体为一种基于地图导航的语音识别管理系统. 背景技术: 随着GPS技术的不断发展,给人们的出行带来了很大的便利,人们可以根据GPS导航到达指定的地方. 现有的在对地图资源的 ...
- 软件包应用分享|基于RT-Thread的百度语音识别(二)
本期分享来自RT-Thread的社区小伙伴霹雳大乌龙,如果你也有文章愿意分享/希望获得官方的写作指导,可以发送文章/联系方式邮件至邮箱:xuqianqian@rt-thread.com 回顾往期: 软 ...
- 【AI 全栈 SOTA 综述 】这些你都不知道,怎么敢说会 AI?【语音识别原理 + 实战】
章目录 前言 语音识别原理信号处理,声学特征提取识别字符,组成文本声学模型语言模型词汇模型 语音声学特征提取:MFCC和LogFBank算法的原理 实战一 ASR语音识别模型系统的流程基于HTTP协议 ...
- 实战:基于tensorflow 的中文语音识别模型 | CSDN博文精选
作者 | Pelhans 来源 | CSDN博客 目前网上关于tensorflow 的中文语音识别实现较少,而且结构功能较为简单.而百度在PaddlePaddle上的 Deepspeech2 实现功能 ...
- 基于SPI的数据报过滤原理与实现
基于SPI的数据报过滤原理与实现 作者: TOo2y 一)个人防火墙技术概述 随着网络安全问题日益严重,广大用户对网络安全产品也越来越关注.防火墙作为一种网络安全工具,早已受到大家的青睐.在PC机上使 ...
- 一种基于说话人识别和数字语音识别的身份认证方法与流程
本发明属于语音处理技术领域,具体涉及到对数字语音序列进行说话人识别和语音识别,确定说话人身份的身份认证方法. 背景技术: 说话人识别也称为声纹识别,可以从说话人发出的声音中提取其个性特征,从而识别出当 ...
- android基于plt/got的hook原理
目录 概述 简单示例 ELF文件格式初探 装载.动态链接与重定位 PLT与GOT 如何定位基址? 如何修改呢? 解析基址和偏移 思考和小结 概述 我们日常开发中编写的C/C++代码经过NDK进行编译和 ...
最新文章
- 图像传感器与信号处理——光学系统
- 简单的ftp服务器(客户端、服务器端、socket)
- python 无头模式_Python + Selenium(二十五)无头模式 headless
- java 常用集合list与Set、Map区别及适用场景总结
- android peopleactivity.java,Android面试基础篇---Activity(上)
- IBM中低端存储解决方案
- Onvif协议:IPC客户端开发之图像抓拍
- 关于矩阵乘法的记忆方法
- 【腾讯TMQ】众测白皮书
- iVMS-4200 Vs区别_菲尔·杰克逊揭示了迈克尔·乔丹和科比·布莱恩特之间的关键区别...
- 内核态与用户态通信之eventfd使用
- 华为机试-字符串子序列
- 【阅读笔记】低照度图像增强-《Fast efficient algorithm for enhancement of low lighting video》
- 原神改文件换服务器,原神B服怎么转成官服
- 如何解决百度云下载大文件限速问题
- 将应用程序添加到鼠标右键发送到
- 大端与小端字节数据详解
- 遇到问题--hadoop---cdh--SERVICE_MONITOR_LOG_DIRECTORY_FREE_SPACE has become bad
- word页面顺序倒过来_Word文字倒过来将每一个文字颠倒显示即更改文字显示方向...
- Vissim-Python二次开发笔记
热门文章
- Django Rest Framework - 实例PartyDemo 之 API文档
- 打造视听游戏新体验:TCL T7G真高刷电视II发布
- (转)工行国际e卡申请,充值(美元)图文介绍 工行国际e卡申请,充值(美元)图文介绍
- python中scipy.optimize_浅谈SciPy中的optimize.minimize实现受限优化问题
- 【论文】期刊和会议如何查询、期刊级别分类和顶会概念一文精析
- certutil证书管理命令
- BUAA-OO 第一单元总结
- 【万里征程——Windows App开发】画笔和图像
- Mysql查看表的建表语句DDL
- Java集合工具类Collections(一)