刚入门ASR的时候一直能听到HMM模型的相关字眼,这里就补一下用GMMs-HMMs进行语音识别的原理,虽然这个方法很古老,而且已经近乎被神经网络所取代,但它背后的思想仍然值得我们去了解和学习~
笔者看了一些教程,包括课程讲义、博客还有一些工具书,总算大致理清了思路,相信至少在文章逻辑上比一些没有相关背景设定、无厘头、不知所云的教程要好,原理上至少也是能用到的都有介绍。而有些地方太深入的原理笔者也没有去细究,感兴趣的读者根据笔者的逻辑自己去深入探索即可。
整体的行文思路按照“结果导向”,需要什么数学知识和原理再去介绍什么,而不是上来就用GMM和HMM进行轰炸,导致前后脱节。在数学原理上,也是尽量做到通俗易懂,加入自己的理解,而不是甩一堆高深莫测的公式。
文章目录
一. 最简单的场景:孤立词识别
1.1 整体思路
孤立词识别是语音识别中最简单的场景,即一个音频文件里面只包含一个词的语音。训练和识别的整体思路为:
(1)训练阶段:对于每个词,用不同人录制的这个词的音频特征作为训练样本,构建一个生成模型
P
(
X
∣
W
)
P(X|W)
P(X∣W),
W
W
W表示词,
X
X
X表示音频文件提取出的特征(如FBank、MFCC等,参见这篇博客)。
(2)识别阶段:给定一段音频特征,经过上面学习到的每个词的
P
(
X
∣
W
)
P(X|W)
P(X∣W)模型,看哪个词生成这段音频特征的概率最大,取最大的那个词作为识别词。
形象化的图如下:
这里的重点就是 P ( X ∣ W ) P(X|W) P(X∣W)这个生成模型的建立。因为对于每个孤立词都是分别采用一个生成模型来独立建模的,所以后面介绍的原理都是以一个孤立词为例讲解。
1.2 模型结构
对于这种时间序列的建模,自然就是采用HMM模型。其结构图如下:
不大张旗鼓地讲那么多关于HMM没用的东西,就看这个图来讲解。它的构建思路就是认为音频特征是由一些隐藏状态生成的,这些状态内部会有转移,并且满足马尔可夫性,状态个数是超参数,需要自己设置(一般3~5个)。
对于除了开始( s I s_I sI)和结束( s E s_E sE)这两个状态之外的状态,都有两种转移选择,要么就转向自己,要么就转向下一个状态。因为音频特征的序列长度往往要比状态个数多,所以这里要有转向自己的,因此每个状态可能对应多个音频特征的“帧”。
以这个图里面的3个状态和对齐为例,生成模型可展开为: 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(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)
a
i
j
a_{ij}
aij:
P
(
s
j
∣
s
i
)
P(s_j|s_i)
P(sj∣si),状态转移概率
(2)
b
j
(
x
t
)
b_j(x_t)
bj(xt):
P
(
x
t
∣
s
j
)
P(x_t|s_j)
P(xt∣sj),输出生成概率
其中 b j ( x t ) b_j(x_t) bj(xt)一般采用GMM进行建模,这也就是为什么叫GMM-HMM。直接给出GMM的公式:
b j ( x t ) = ∑ m = 1 M c j m N ( x t ; μ j m , Σ j m ) 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 / 2 e x p ( − 1 2 ( 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−μ))
即认为输出生成概率由高斯混合模型生成,先根据概率 c j m c_{jm} cjm选择一个高斯模型,然后根据这个模型的概率分布 N ( x t ; μ j m , Σ j m ) N(x_t; \mu_{jm}, \Sigma_{jm}) N(xt;μjm,Σjm)生成 x t x_t xt。
总结一下,要学习的参数有:
- a i j a_{ij} aij:HMM的状态转移概率矩阵
- c j m c_{jm} cjm:各状态对应的GMM中的混合权重(如果是单高斯建模,则不用学习)
- μ j m \mu_{jm} μjm:各状态对应的GMM中各高斯分量的均值向量
- Σ j m \Sigma_{jm} Σjm:各状态对应的GMM中各高斯分量的协方差矩阵
建立好这样的模型之后,后面就是如何根据样本进行训练了。
1.3 训练过程
这里的训练,确切来讲应该叫参数估计,用一定的算法来估计参数,使其能够拟合数据分布(即最大化数据的似然概率)。下面循序渐进地来讲这种参数估计算法:
1.3.1 单样本、单高斯
这是一种最简单的情况,假设一个词的训练样本只有一个音频文件,并且对于输出概率 b j ( x t ) b_j(x_t) bj(xt)采用单高斯建模,即 m = 1 m=1 m=1, c j m = 1 c_{jm}=1 cjm=1。此时需要估计的参数为:
- a i j a_{ij} aij:HMM的状态转移概率矩阵
- μ j \mu_{j} μj:各状态对应的单高斯模型的均值向量
- Σ j \Sigma_{j} Σj:各状态对应的单高斯模型的协方差矩阵
这里进行参数估计的一大难点就是:各个状态对应哪些音频帧是不知道的,不像1.2里面给出的那个图那样具有对齐信息。所以输出概率 b j ( x t ) b_j(x_t) bj(xt)根本不知道具体去拟合哪些音频帧数据。
为了解决这个问题,引入一种软对齐策略,即给出各个音频帧属于各个状态的概率(各个时刻处于各个状态的概率) γ t ( j ) = P ( s t = j ∣ X ) \gamma_t(j) = P(s_t = j|X) γt(j)=P(st=j∣X),此时对于各个单高斯模型的参数估计就可以写为:
μ ^ j = ∑ t = 1 T γ t ( j ) x t ∑ t = 1 T γ 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 = 1 T γ t ( j ) ( x t − μ ^ j ) ( x t − μ ^ j ) T ∑ t = 1 T γ 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 = 1 T z j t x t ∑ t = 1 T z j t \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 = 1 T z j t ( x t − μ ^ j ) ( x t − μ ^ j ) T ∑ t = 1 T z j t \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
z j t = 1 i f x t ∈ s j e l s e 0 z_{jt} = 1 \ \ \ if \ \ x_t \in s_j \ \ else \ \ 0 zjt=1 if xt∈sj else 0
但是这里没有 z j t z_{jt} zjt这种“硬对齐”信息,所以要用“软对齐”的概率 γ t ( j ) \gamma_t(j) γt(j)来代替。
同样的,对于转移概率 a k j a_{kj} akj,也引入软对齐转移概率 ξ t ( i , j ) = P ( s t = i , s t + 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),表示在 t t t 时刻的状态在 s i s_i si,而在 t + 1 t+1 t+1 时刻的状态为 s j s_j sj的概率。此时转移概率可以估计为:
a ^ i j = ∑ t = 1 T ξ t ( i , j ) ∑ k = 1 N ∑ t = 1 T ξ 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)
即状态 s i s_i si转到状态 s j s_j sj(在各个时刻下求和)的概率除以状态 s i s_i si转到其他所有状态 s k s_k sk(在各个时刻下求和)的概率。
综上,在进行参数估计前,需要先拿到 γ 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
(
x
1
,
x
2
,
.
.
.
,
x
t
,
s
t
=
j
)
\alpha_t(j) = P(x_1, x_2, ..., x_t, s_t = j)
αt(j)=P(x1,x2,...,xt,st=j),即已经输出
x
1
,
x
2
,
.
.
.
,
x
t
x_1, x_2, ..., x_t
x1,x2,...,xt,并且在时刻
t
t
t 处在状态
s
j
s_j
sj的概率。
(2)
β
t
(
j
)
\beta_t(j)
βt(j):后向概率,展开为
β
t
(
j
)
=
P
(
x
t
+
1
,
x
t
+
2
,
.
.
.
,
x
T
∣
s
t
=
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),在时刻
t
t
t 处在状态
s
j
s_j
sj时,后续输出为
x
t
+
1
,
x
t
+
2
,
.
.
.
,
x
T
x_{t+1}, x_{t+2}, ..., x_T
xt+1,xt+2,...,xT的概率。
在给出了这两个概率时,就可以计算 γ t ( j ) \gamma_t(j) γt(j)和 ξ t ( i , j ) \xi_t(i, j) ξt(i,j),如下:
γ t ( j ) = P ( s t = j ∣ X ) = 1 α T ( s E ) α 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 ( s t = i , s t + 1 = j ∣ X ) = α t ( i ) a i j b j ( x t + 1 ) β t + 1 ( j ) α T ( s E ) \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 ( s E ) \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
(
s
I
)
=
1
\alpha_0(s_I) = 1
α0(sI)=1,
α
0
(
j
)
=
0
i
f
j
!
=
s
I
\alpha_0(j) = 0 \ \ \ if \ \ j \ \ != \ s_I
α0(j)=0 if j != sI
(2)迭代:
α
t
(
j
)
=
∑
i
=
1
N
α
t
−
1
(
j
)
a
i
j
b
j
(
x
t
)
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
(
s
E
)
=
∑
i
=
1
N
α
T
(
i
)
a
i
E
P(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
)
=
a
i
E
\beta_T(i) = a_{iE}
βT(i)=aiE
(2)迭代:
β
t
(
i
)
=
∑
j
=
1
N
a
i
j
b
j
(
x
t
+
1
)
β
t
+
1
(
j
)
f
o
r
t
=
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) for t =T−1,...,1
(3)终止:
P
(
X
)
=
β
0
(
s
I
)
=
∑
j
=
1
N
a
I
j
b
j
(
x
1
)
β
1
(
j
)
=
α
T
(
s
E
)
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算法):
- 初始化:
笔者看到的有:对于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
^
i
j
\hat a_{ij}
a^ij进行估计。
其中每一步的计算公式,前面都已经详细说明了。
1.3.2 扩展到高斯混合模型
一般在对于输出概率分布的建模,都会采用GMM方法。理论上,足够的单高斯模型的混合可以拟合任意的分布。1.3.1的单高斯建模只是一种特例,目的还是为了方便扩展到GMM的场景。
这里需要估计的参数就要加上GMM的混合权重,即:
- a k j a_{kj} akj:HMM的状态转移概率矩阵
- c j m c_{jm} cjm:各状态对应的GMM中的混合权重
- μ j m \mu_{jm} μjm:各状态对应的GMM中各高斯分量的均值向量
- Σ j m \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,属于状态 s j s_j sj以及第 m m m个高斯分量的概率。
笔者猜测的 γ t ( j , m ) \gamma_t(j, m) γt(j,m)计算公式为:(因为没找到相关的资料,如果有错误,还请指出~)
γ t ( j , m ) = P ( s t = j , c t = m ∣ X ) = P ( s t = j ∣ X ) P ( c t = m ∣ s t = j , X ) = 1 α T ( s E ) α t ( j ) β t ( j ) 1 ( 2 π ) D / 2 ∣ Σ j m ∣ 1 / 2 e x p ( − 1 2 ( x t − μ j m ) T Σ j m − 1 ( x t − μ j m ) ) \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))
相应的,对于 μ ^ j m \hat \mu_{jm} μ^jm和 Σ ^ j m \hat \Sigma_{jm} Σ^jm的参数估计就要改为:
μ ^ j m = ∑ t = 1 T γ t ( j , m ) x t ∑ t = 1 T γ 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
Σ ^ j m = ∑ t = 1 T γ t ( j , m ) ( x t − μ ^ j m ) ( x t − μ ^ j m ) T ∑ t = 1 T γ 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 ^ j m = ∑ t = 1 T γ t ( j , m ) ∑ m ′ = 1 M ∑ t = 1 T γ 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)
即处于状态 j j j 和分量 m m m 的概率,除以处于状态 j j j 和其他所有分量的概率。
而前向概率 α t ( j ) \alpha_t(j) αt(j)、后向概率 β t ( j ) \beta_t(j) βt(j) 和 ξ t ( i , j ) \xi_t(i, j) ξt(i,j)的计算公式不变,转移概率 a i j a_{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组概率,对
μ
^
j
m
\hat \mu_{jm}
μ^jm、
Σ
^
j
m
\hat \Sigma_{jm}
Σ^jm、
c
^
j
m
\hat c_{jm}
c^jm和
a
^
i
j
\hat a_{ij}
a^ij进行估计。
1.3.3 扩展到多个训练样本
前面1.3.1和1.3.2讨论的都是只有一个训练样本的情况,实际上对于每个词,都会有很多个音频文件与之对应,从而提升模型建模的鲁棒性。
假设某个词一共有 R R R 个音频文件,则前面的 x t x_t xt就要改为 x t r x_t^r xtr,表示第 r r r 个训练样本中的第 t t t 帧。此时EM的流程变为:
(1)E步:对每个样本都计算出:
α
t
r
(
j
)
\alpha_t^r(j)
αtr(j)、
β
t
r
(
j
)
\beta_t^r(j)
βtr(j)、
γ
t
r
(
j
,
m
)
\gamma_t^r(j, m)
γtr(j,m)和
ξ
t
r
(
i
,
j
)
\xi_t^r(i, j)
ξtr(i,j)。
(2)M步:对于参数的估计需要在全部的训练样本上进行,具体为
μ ^ j m = ∑ r = 1 R ∑ t = 1 T γ t r ( j , m ) x t r ∑ r = 1 R ∑ t = 1 T γ t r ( 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
Σ ^ j m = ∑ r = 1 R ∑ t = 1 T γ t r ( j , m ) ( x t r − μ ^ j m ) ( x t r − μ ^ j m ) T ∑ r = 1 R ∑ t = 1 T γ t r ( 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 ^ j m = ∑ r = 1 R ∑ t = 1 T γ t r ( j , m ) ∑ r = 1 R ∑ m ′ = 1 M ∑ t = 1 T γ t r ( 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 ^ i j = ∑ r = 1 R ∑ t = 1 T ξ t r ( i , j ) ∑ r = 1 R ∑ k = 1 N ∑ t = 1 T ξ t r ( 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 识别过程
识别的过程,就是给定一段音频 X X X,对于每个词的模型 P ( X ∣ W ) P(X|W) P(X∣W),都计算出其生成 X X X的概率,然后取最大的那个 W = a r g m a x W P ( X ∣ W ) W = argmax_W P(X|W) W=argmaxWP(X∣W)作为识别出的孤立词。
这里的 P ( X ∣ W ) P(X|W) P(X∣W)实际在前面已经提到了,就是 α T ( s E ) \alpha_T(s_E) αT(sE),但一般在识别过程中,不会对所有路径的概率计算总和,而是会选择最大的那一条概率作为最终的概率。这其实就是HMM中经典的维特比算法。
定义概率 V t ( j ) V_t(j) Vt(j),表示在时刻 t t t 到达状态 s j s_j sj的所有路径中概率的最大值,对应到1.3中第一张路径图,即从起始的紫色圆圈到某个青色圆圈的所有路径概率中,最大的那一个。同时定义回溯指针 b t t ( j ) bt_t(j) btt(j),即时刻 t t t 到达状态 s j s_j sj的所有路径中概率最大的那一条路径对应的前一个状态,便于之后进行状态回溯。
与前向概率 α t ( j ) \alpha_t(j) αt(j)类似,对于 V t ( j ) V_t(j) Vt(j)和 b t t ( j ) bt_t(j) btt(j)的计算也是采用动态规划进行迭代计算,具体的计算方式为:
(1)初始化:
V
0
(
s
I
)
=
1
V_0(s_I) = 1
V0(sI)=1
V 0 ( j ) = 0 i f j ! = s I V_0(j) = 0 \ \ \ if \ \ j \ \ != \ s_I V0(j)=0 if j != sI
b t 0 ( j ) = 0 bt_0(j) = 0 bt0(j)=0
(2)迭代:
V
t
(
j
)
=
a
r
g
m
a
x
i
=
1
N
V
t
−
1
(
j
)
a
i
j
b
j
(
x
t
)
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)
b
t
t
(
j
)
=
a
r
g
m
a
x
i
=
1
N
V
t
−
1
(
j
)
a
i
j
b
j
(
x
t
)
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
∗
=
V
T
(
s
E
)
=
max
i
=
1
N
V
T
(
i
)
a
i
E
P^* = V_T(s_E) = \max_{i=1}^N V_T(i) a_{iE}
P∗=VT(sE)=i=1maxNVT(i)aiE
s T ∗ = b t T ( s E ) = a r g m a x i = 1 N V T ( i ) a i E s_T^* = bt_T(s_E) = argmax_{i=1}^N V_T(i) a_{iE} sT∗=btT(sE)=argmaxi=1NVT(i)aiE
下面两个图是拆分出的中间某一步的迭代计算过程:
最后的识别词为: W = a r g m a x W P ( X ∣ W ) = a r g m a x W V T W ( s E ) 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),其中 X X X是音频特征, S S S是其对应的句子。训练是希望能最大化 P ( S ∣ X ) P(S|X) P(S∣X),识别是希望能找到 a r g m a x S P ( 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)识别阶段:给定一段音频特征,用构建好的生成模型和语言模型,得到识别出的句子
a
r
g
m
a
x
S
P
(
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模型,如下图:
这里的句子“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组概率,对 μ ^ j m \hat \mu_{jm} μ^jm、 Σ ^ j m \hat \Sigma_{jm} Σ^jm、 c ^ j m \hat c_{jm} c^jm和 a ^ i j \hat a_{ij} a^ij进行估计;
- 重复整个流程,直到所有的句子遍历完成。
在训练完成之后,每个音素的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, ..., T
for state i <= N
将状态i的token复制到与其连接的所有不是结束状态的状态j中
更新token的score为:score += log a_ij + log b_j(y_t)
end
for 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为这个新建的WLR
end
扔掉所有的原始token
for state i <= N+1
保留状态i中所有tokens中最大score的那个token,扔掉其他所有的tokens
end
将结束状态的token复制到开始状态中
返回结束状态的token
识别结果:根据token的model id以及path id一层一层向前回溯,得到整个句子
上面的过程能够得到 a r g m a x S P ( X ∣ S ) argmax_S P(X|S) argmaxSP(X∣S)的句子,识别的目标是 a r g m a x S P ( 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}))
,找了一个比较形象的图:
2.4.2 扩展到对音素建模的情况
有了上面的对词建模的识别过程之后,既可以很容易地扩展到对音素建模的情况,只是这里要多加一层从“音素->词”的传递,通过发音词典即可完成,比如下图:
可以将一个词的所有音素的HMM状态都串起来,作为一整个HMM(改一下 a i j a_{ij} aij矩阵的连接方式应该就可以)。而后进行与2.4.1一样的识别过程即可。
三. 延伸:上下文建模
3.1 三音素建模
前面讨论的都是对单音素进行HMM建模的方式,但实际上一个音素的发声是依赖于其上下文(即邻居音素)的,只用一个模型对其进行建模,信息量难免会有丢失。因此,应当在建模时考虑到这种上下文信息。
一种较普遍的做法是,对三音素(tripone)进行建模。对于音素
x
x
x,假设它左边的音素是
l
l
l,右边的音素是
r
r
r,那么音素
x
x
x 的三音素形式就可以表示为
l
−
x
+
r
l-x+r
l−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个音素,那么可能的三音素就会有 4 0 3 = 64000 40^3=64000 403=64000个,在实际情况下,可能有 50000 50000 50000个是真实存在的。那么,要对这 50000 50000 50000个三音素进行三状态的HMM建模,假设用 10 10 10个混合分量的GMM模型,那么一共需要 50000 ∗ 3 ∗ 10 = 1.5 M 50000 * 3 * 10 = 1.5M 50000∗3∗10=1.5M的高斯模型。假设用39维的音频特征,那么每个高斯模型需要约 800 800 800个参数,总的参数量就高达 120 M 120M 120M。这是十分惊人的,需要有大量的数据来进行拟合,同时还要保证每个三音素都有足够的数据,这点比较困难。
为了对其进行优化,通常会采用共享参数的方式,减少建模的参数量,方便训练。共享可以发生在不同的层面:
- 共享高斯模型:所有状态都用同样的高斯模型,只是混合权重不一样(捆绑混合物)
- 共享状态:允许不同的HMM模型使用一些相同的状态(状态聚类)
- 共享模型:相似的三音素使用同样的HMM模型(三音素泛化)
所有的方法都是数据驱动的,下面进行一些简单介绍:(笔者没有过多细究这部分,只是大概了解了原理,对此感兴趣的读者可以前往传送门查找细节)
3.2.1 共享状态
这部分的重点,其实是对状态进行聚类。找到相似的一堆状态,然后让不同的HMM模型之间共享这些状态,比如下图:
具体怎么找这些相似的状态?可以采用自顶向下的拆分,建立决策树来聚类,看下面这个形象的图:
顶层节点是所有中间音素为/iy/
的三音素,然后通过问各种问题,比如左边是鼻音吗?右边是浊音吗?等等,将这些三音素进行划分,最后在同一个叶子节点里面的所有三音素的中间状态就可以共享。
3.2.2 共享模型
这部分的重点,其实就是对三音素进行泛化,找到一堆相似的三音素,用同一个泛化的三音素来表示,有点儿像词干的感觉。看下面这个图:
这里就是将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课程讲义,考虑上下文的建模