刚入门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(XW)WWW表示词,XXX表示音频文件提取出的特征(如FBank、MFCC等,参见这篇博客)。
(2)识别阶段:给定一段音频特征,经过上面学习到的每个词的P(X∣W)P(X|W)P(XW)模型,看哪个词生成这段音频特征的概率最大,取最大的那个词作为识别词。

形象化的图如下:

这里的重点就是P(X∣W)P(X|W)P(XW)这个生成模型的建立。因为对于每个孤立词都是分别采用一个生成模型来独立建模的,所以后面介绍的原理都是以一个孤立词为例讲解。

1.2 模型结构

对于这种时间序列的建模,自然就是采用HMM模型。其结构图如下:

不大张旗鼓地讲那么多关于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(XW)=P(x1s1)P(s1s1)P(x2s1)P(s1s1)P(x3s1)P(s2s1)P(x4s2)P(s2s2)P(x5s2)P(s3s2)P(x6s3)

这里主要有两部分概率需要学习:

(1)aija_{ij}aijP(sj∣si)P(s_j|s_i)P(sjsi),状态转移概率
(2)bj(xt)b_j(x_t)bj(xt)P(xt∣sj)P(x_t|s_j)P(xtsj),输出生成概率

其中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=1McjmN(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

总结一下,要学习的参数有:

  1. aija_{ij}aij:HMM的状态转移概率矩阵
  2. cjmc_{jm}cjm:各状态对应的GMM中的混合权重(如果是单高斯建模,则不用学习)
  3. μjm\mu_{jm}μjm:各状态对应的GMM中各高斯分量的均值向量
  4. Σjm\Sigma_{jm}Σjm:各状态对应的GMM中各高斯分量的协方差矩阵

建立好这样的模型之后,后面就是如何根据样本进行训练了。

1.3 训练过程

这里的训练,确切来讲应该叫参数估计,用一定的算法来估计参数,使其能够拟合数据分布(即最大化数据的似然概率)。下面循序渐进地来讲这种参数估计算法:

1.3.1 单样本、单高斯

这是一种最简单的情况,假设一个词的训练样本只有一个音频文件,并且对于输出概率bj(xt)b_j(x_t)bj(xt)采用单高斯建模,即 m=1m=1m=1cjm=1c_{jm}=1cjm=1。此时需要估计的参数为:

  1. aija_{ij}aij:HMM的状态转移概率矩阵
  2. μj\mu_{j}μj:各状态对应的单高斯模型的均值向量
  3. Σ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=jX),此时对于各个单高斯模型的参数估计就可以写为:

μ^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=1Tzjtt=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=1Tzjtt=1Tzjt(xtμ^j)(xtμ^j)T

zjt=1ifxt∈sjelse0z_{jt} = 1 \ \ \ if \ \ x_t \in s_j \ \ else \ \ 0zjt=1ifxtsjelse0

但是这里没有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=jX),表示在 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=1Nt=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,...,xTst=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=jX)=α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=jX)=α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)

那么如何计算前向和后向概率呢?先放一张图:

这是一个典型的所有可能路径的示意图,α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&lt;=j&lt;=N,1&lt;=t&lt;=T\alpha_t(j) = \sum_{i=1}^N \alpha_{t-1}(j) a_{ij} b_j(x_t) \ \ \ 1 &lt;= j &lt;= N, 1&lt;=t&lt;=Tαt(j)=i=1Nαt1(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

下图是迭代计算过程中拆分出的某一步的示意图,展示了动态规划的计算方法。

同样地,对于β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=T1,...,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)

下图是迭代过程中拆分出的某一步的示意图:

到这里,所有在参数估计中需要用到的概率都已经计算完毕了,下面展示整个流程。

整个参数估计的流程,其实还是需要迭代的,这里采用EM算法(一种对含有隐变量模型进行参数估计的迭代算法,在HMM的场景里,也叫前向-后向算法或Baum-Welch算法):

  1. 初始化:

笔者看到的有:对于GMM,一种做法是用所有数据进行估计,另一种是用K-Means先聚类一波;对于HMM的转移概率,是用的平均。

  1. 对于每一次迭代:

(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Σ^ja^ij\hat a_{ij}a^ij进行估计。

其中每一步的计算公式,前面都已经详细说明了。

1.3.2 扩展到高斯混合模型

一般在对于输出概率分布的建模,都会采用GMM方法。理论上,足够的单高斯模型的混合可以拟合任意的分布。1.3.1的单高斯建模只是一种特例,目的还是为了方便扩展到GMM的场景。

这里需要估计的参数就要加上GMM的混合权重,即:

  1. akja_{kj}akj:HMM的状态转移概率矩阵
  2. cjmc_{jm}cjm:各状态对应的GMM中的混合权重
  3. μjm\mu_{jm}μjm:各状态对应的GMM中各高斯分量的均值向量
  4. Σ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=mX)=P(st=jX)P(ct=mst=j,X)=αT(sE)1αt(j)βt(j)(2π)D/2Σjm1/21exp(21(xtμjm)TΣjm1(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&#x27;=1}^M \sum_{t=1}^T \gamma_t(j, m&#x27;)}c^jm=m=1Mt=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}Σ^jmc^jm\hat c_{jm}c^jma^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=1Rt=1Tγtr(j,m)r=1Rt=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=1Rt=1Tγtr(j,m)r=1Rt=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&#x27;=1}^M \sum_{t=1}^T \gamma_t^r(j, m&#x27;)}c^jm=r=1Rm=1Mt=1Tγtr(j,m)r=1Rt=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=1Rk=1Nt=1Tξtr(i,k)r=1Rt=1Tξtr(i,j)

在训练完成后,就是如何利用训练好的模型对新音频进行识别了。

1.4 识别过程

识别的过程,就是给定一段音频XXX,对于每个词的模型P(X∣W)P(X|W)P(XW),都计算出其生成XXX的概率,然后取最大的那个W=argmaxWP(X∣W)W = argmax_W P(X|W)W=argmaxWP(XW)作为识别出的孤立词。

这里的P(X∣W)P(X|W)P(XW)实际在前面已经提到了,就是α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=1NVt1(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=1NVt1(j)aijbj(xt)
(3)终止:
P∗=VT(sE)=max⁡i=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

下面两个图是拆分出的中间某一步的迭代计算过程:

最后的识别词为:W=argmaxWP(X∣W)=argmaxWVTW(sE)W = argmax_W P(X|W) = argmax_W V_T^W(s_E)W=argmaxWP(XW)=argmaxWVTW(sE)

至此,对于孤立词识别的全部内容就已经介绍完毕!

二. 扩展到通用场景:连续语音识别

2.1 整体思路

连续语言识别是比较通用的场景,即一个音频文件里面包含一个连续的句子,而不是一个词,其难点在于不知道每个词对应音频文件的起止位置。如果有这种标注好的切分,那么仍然可以沿用前面的孤立词识别方式进行训练和识别,但这样着实费时费力,而且会有人工误差。那么,能不能在没有切分的情况下,对一整段音频,识别一整个句子?当然可以,下面将详细介绍。

与孤立词识别类似,这里是希望构建一个判别模型P(S∣X)P(S|X)P(SX),其中XXX是音频特征,SSS是其对应的句子。训练是希望能最大化P(S∣X)P(S|X)P(SX),识别是希望能找到argmaxSP(S∣X)argmax_S P(S|X)argmaxSP(SX)。对于P(S∣X)P(S|X)P(SX)的建模通常会通过贝叶斯公式转为P(S∣X)≈P(X∣S)P(S)P(S|X) \approx P(X|S)P(S)P(SX)P(XS)P(S)来处理,其中P(X∣S)P(X|S)P(XS)即为生成模型,P(S)P(S)P(S)为语言模型(这里不涉及到语言模型的细节)。

其训练和识别的整体思路为:
(1)训练阶段:对于所有的句子,构建生成模型P(X∣S)P(X|S)P(XS),最大化每个句子的似然概率。
(2)识别阶段:给定一段音频特征,用构建好的生成模型和语言模型,得到识别出的句子argmaxSP(X∣S)P(S)argmax_S P(X|S)P(S)argmaxSP(XS)P(S)

2.2 模型结构

对于生成模型P(X∣S)P(X|S)P(XS)的构建,可以使用“嵌入训练(embedded training)”的方式。

理想情况下,在词表比较小的时候,可以对每个词进行一个HMM建模(与之前孤立词识别一样),而后将整个句子中所有词的HMM状态都串起来,作为一个超长的HMM,其训练方式与1.3节的一样。

但在真实场景中,词表往往很庞大,此时如果对所有的词建模,HMM模型将非常多。所以,一般都是将句子转成音素串(可以将音素理解为音标,一个词会对应一条音素序列)进行处理,音素表往往会小很多,这样对每个音素建模较为简便。

总结一下,在连续语音场景中,是为每个音素建立一个HMM模型,将句子转为音素串之后,将句子中所有音素对应的HMM状态都串在一起(中间的开始和结束状态会去掉),成为一个超长的HMM模型,如下图:

这里的句子“six quid”转为音素串为“/s/ /ih/ /k/ /s/ … /d/”,每个“beg mid end”是对一个音素的3状态HMM建模。

2.3 训练过程

在串成2.2节中超长的HMM之后,训练的方式就与1.3节中的类似了,只不过1.3节中是每次对一个HMM模型进行训练,这里是一次对很多个HMM模型进行并行训练。

其训练流程为:

  1. 获取下一个句子;
  2. 转成音素串后,按照2.2节构建成超长的HMM模型,当成一个HMM模型来处理;
  3. 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)
  4. M步:基于E步计算的4组概率,对μ^jm\hat \mu_{jm}μ^jmΣ^jm\hat \Sigma_{jm}Σ^jmc^jm\hat c_{jm}c^jma^ij\hat a_{ij}a^ij进行估计;
  5. 重复整个流程,直到所有的句子遍历完成。

在训练完成之后,每个音素的HMM模型也就训练好了。

2.4 识别过程

识别过程与孤立词识别的差别就比较大了,因为要考虑“音素->词->句子”的层级传递。这里介绍一个比较常用的识别算法——“token passing算法”,属于剪枝的维特比译码算法的一种。

PS:其实这个算法本身不难理解,但因为相关资料比较少,而且说的都比较模糊。笔者也是看了很久才明白原理,希望能用通俗的方式把它呈现出来,细节上如果有不周到的地方,还望指出~

2.4.1 较简单的情况:假设是对词进行HMM建模

虽然2.2节和2.3节介绍的内容都是对音素建模,但从音素到句子要经过“音素->词->句子”的两层识别传递。这里为了方便更好地讲明白原理,先假设之前构建和训练的都是针对每个词的HMM模型,这样只需要经过“词->句子”的一层识别传递,更容易理解原理。后面再进一步扩展到对音素建模的情况。

首先需要声明的是,因为只针对训练样本中有的词构建了HMM模型,所以在识别时,只能识别这些已有的词。假设词表中只有“one”、“two”和“three”这三个词,那么识别的示意图可以画成下面这样:

其实还是与之前1.4节孤立词识别同样的方法,只不过这里是并行对所有词进行识别。同时,在孤立词识别过程中,如果到达了结束状态,则识别就结束了;但在这里,如果到达了结束状态,还是要继续识别下一个词,所以在图里是一个循环。

在每个HMM内部,还是采用维特比识别方法,用动态规划,在每个时刻对于每个状态选择一条最大概率的路径。因为是并行的,那么在某个时刻,可能同时会有多个词到达结束状态,分别对应着一段已识别出的句子(路径),然后又都要同时再进行下一个词的识别。这里为了避免多余的计算,采用与维特比识别一样的思路,只需要取一个最大概率的句子,而扔掉其他的。那么,在这个过程中,就需要记录概率和路径,这个就叫“token”。看下面这个图:

每个“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(XS)的句子,识别的目标是argmaxSP(X∣S)P(S)argmax_S P(X|S)P(S)argmaxSP(XS)P(S)。语言模型可以通过两种方式加进去:

  1. rescore:即重打分排序,这就需要前面保留token的时候,要保留NBest(参考beam search的思想),然后对NBest识别出来的句子,通过LM进行rescore即可。
  2. fusion:直接融合进去,每次识别出一个词之后,可以用语言模型,根据它之前的句子和这个词,得到当前这个词的分数,一起加到token的计算里面,具体加入的位置可以是更新token的score为:score += log a_iE + (LM分数 log P(w_t|w_1, ..., w_{t-1})),找了一个比较形象的图:

2.4.2 扩展到对音素建模的情况

有了上面的对词建模的识别过程之后,既可以很容易地扩展到对音素建模的情况,只是这里要多加一层从“音素->词”的传递,通过发音词典即可完成,比如下图:

可以将一个词的所有音素的HMM状态都串起来,作为一整个HMM(改一下aija_{ij}aij矩阵的连接方式应该就可以)。而后进行与2.4.1一样的识别过程即可。

三. 延伸:上下文建模

3.1 三音素建模

前面讨论的都是对单音素进行HMM建模的方式,但实际上一个音素的发声是依赖于其上下文(即邻居音素)的,只用一个模型对其进行建模,信息量难免会有丢失。因此,应当在建模时考虑到这种上下文信息。

一种较普遍的做法是,对三音素(tripone)进行建模。对于音素 xxx,假设它左边的音素是 lll,右边的音素是 rrr,那么音素 xxx 的三音素形式就可以表示为 l−x+rl-x+rlx+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.5M50000310=1.5M的高斯模型。假设用39维的音频特征,那么每个高斯模型需要约800800800个参数,总的参数量就高达120M120M120M。这是十分惊人的,需要有大量的数据来进行拟合,同时还要保证每个三音素都有足够的数据,这点比较困难。

为了对其进行优化,通常会采用共享参数的方式,减少建模的参数量,方便训练。共享可以发生在不同的层面:

  1. 共享高斯模型:所有状态都用同样的高斯模型,只是混合权重不一样(捆绑混合物)
  2. 共享状态:允许不同的HMM模型使用一些相同的状态(状态聚类)
  3. 共享模型:相似的三音素使用同样的HMM模型(三音素泛化)

所有的方法都是数据驱动的,下面进行一些简单介绍:(笔者没有过多细究这部分,只是大概了解了原理,对此感兴趣的读者可以前往传送门查找细节)

3.2.1 共享状态

这部分的重点,其实是对状态进行聚类。找到相似的一堆状态,然后让不同的HMM模型之间共享这些状态,比如下图:

具体怎么找这些相似的状态?可以采用自顶向下的拆分,建立决策树来聚类,看下面这个形象的图:

顶层节点是所有中间音素为/iy/的三音素,然后通过问各种问题,比如左边是鼻音吗?右边是浊音吗?等等,将这些三音素进行划分,最后在同一个叶子节点里面的所有三音素的中间状态就可以共享。

3.2.2 共享模型

这部分的重点,其实就是对三音素进行泛化,找到一堆相似的三音素,用同一个泛化的三音素来表示,有点儿像词干的感觉。看下面这个图:

这里就是将s-iy+lf-iy+l泛化为(s,f)-iy+l,将t-iy+nt-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的语音识别原理相关推荐

  1. 基于DTW算法的语音识别原理与实现

    [摘 要]以一个能识别数字0-9的语音识别系统的实现过程为例,阐述了基于DTW算法的特定人孤立词语音识别的基本原理和关键技术.其中包括对语音端点检测方法.特征参数计算方法和DTW算法实现的详细讨论,最 ...

  2. 语音识别的原理_语音识别原理_语音识别原理框图 - 云+社区 - 腾讯云

    广告关闭 腾讯云双11爆品提前享,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高满返5000元! 深入浅出地介绍了基于hmm的语音识别的原理,不注重公式的细节推导而是着重阐述公式背 ...

  3. 基于LD3320的嵌入式语音识别系统设计

    摘要:语音交互系统是比较人性化的人机操作界面,它需要语音识别系统的支持.LD3320就是这样一款语音识别芯片.介绍了该芯片的工作原理及应用,给出了LD3320与微处理器的硬件接口电路及软件程序.随着高 ...

  4. 一种基于地图导航的语音识别管理系统的制作方法

    本发明涉及语音识别技术领域,具体为一种基于地图导航的语音识别管理系统. 背景技术: 随着GPS技术的不断发展,给人们的出行带来了很大的便利,人们可以根据GPS导航到达指定的地方. 现有的在对地图资源的 ...

  5. 软件包应用分享|基于RT-Thread的百度语音识别(二)

    本期分享来自RT-Thread的社区小伙伴霹雳大乌龙,如果你也有文章愿意分享/希望获得官方的写作指导,可以发送文章/联系方式邮件至邮箱:xuqianqian@rt-thread.com 回顾往期: 软 ...

  6. 【AI 全栈 SOTA 综述 】这些你都不知道,怎么敢说会 AI?【语音识别原理 + 实战】

    章目录 前言 语音识别原理信号处理,声学特征提取识别字符,组成文本声学模型语言模型词汇模型 语音声学特征提取:MFCC和LogFBank算法的原理 实战一 ASR语音识别模型系统的流程基于HTTP协议 ...

  7. 实战:基于tensorflow 的中文语音识别模型 | CSDN博文精选

    作者 | Pelhans 来源 | CSDN博客 目前网上关于tensorflow 的中文语音识别实现较少,而且结构功能较为简单.而百度在PaddlePaddle上的 Deepspeech2 实现功能 ...

  8. 基于SPI的数据报过滤原理与实现

    基于SPI的数据报过滤原理与实现 作者: TOo2y 一)个人防火墙技术概述 随着网络安全问题日益严重,广大用户对网络安全产品也越来越关注.防火墙作为一种网络安全工具,早已受到大家的青睐.在PC机上使 ...

  9. 一种基于说话人识别和数字语音识别的身份认证方法与流程

    本发明属于语音处理技术领域,具体涉及到对数字语音序列进行说话人识别和语音识别,确定说话人身份的身份认证方法. 背景技术: 说话人识别也称为声纹识别,可以从说话人发出的声音中提取其个性特征,从而识别出当 ...

  10. android基于plt/got的hook原理

    目录 概述 简单示例 ELF文件格式初探 装载.动态链接与重定位 PLT与GOT 如何定位基址? 如何修改呢? 解析基址和偏移 思考和小结 概述 我们日常开发中编写的C/C++代码经过NDK进行编译和 ...

最新文章

  1. 图像传感器与信号处理——光学系统
  2. 简单的ftp服务器(客户端、服务器端、socket)
  3. python 无头模式_Python + Selenium(二十五)无头模式 headless
  4. java 常用集合list与Set、Map区别及适用场景总结
  5. android peopleactivity.java,Android面试基础篇---Activity(上)
  6. IBM中低端存储解决方案
  7. Onvif协议:IPC客户端开发之图像抓拍
  8. 关于矩阵乘法的记忆方法
  9. 【腾讯TMQ】众测白皮书
  10. iVMS-4200 Vs区别_菲尔·杰克逊揭示了迈克尔·乔丹和科比·布莱恩特之间的关键区别...
  11. 内核态与用户态通信之eventfd使用
  12. 华为机试-字符串子序列
  13. 【阅读笔记】低照度图像增强-《Fast efficient algorithm for enhancement of low lighting video》
  14. 原神改文件换服务器,原神B服怎么转成官服
  15. 如何解决百度云下载大文件限速问题
  16. 将应用程序添加到鼠标右键发送到
  17. 大端与小端字节数据详解
  18. 遇到问题--hadoop---cdh--SERVICE_MONITOR_LOG_DIRECTORY_FREE_SPACE has become bad
  19. word页面顺序倒过来_Word文字倒过来将每一个文字颠倒显示即更改文字显示方向...
  20. Vissim-Python二次开发笔记

热门文章

  1. Django Rest Framework - 实例PartyDemo 之 API文档
  2. 打造视听游戏新体验:TCL T7G真高刷电视II发布
  3. (转)工行国际e卡申请,充值(美元)图文介绍 工行国际e卡申请,充值(美元)图文介绍
  4. python中scipy.optimize_浅谈SciPy中的optimize.minimize实现受限优化问题
  5. 【论文】期刊和会议如何查询、期刊级别分类和顶会概念一文精析
  6. certutil证书管理命令
  7. BUAA-OO 第一单元总结
  8. 【万里征程——Windows App开发】画笔和图像
  9. Mysql查看表的建表语句DDL
  10. Java集合工具类Collections(一)