在语音辨识中输入是语音信号,输出是对应的文字。语音表示为一个序列向量(长度为 T \text{T} T,维度为 d \text{d} d)。输出的Text
一般表示为一个Token
序列,长度为 N \text{N} N, V \text{V} V个不同的token
所构成,通常 T > N \text{T} > \text{N} T>N。
在实际运用的过程中,有时候也会从声音信号直接得到word embeddings
,或者连着翻译(Translation
)一起做了,或者引入意图分类( Intent classification
,常见于客服电话这些),或者加上槽填充(Slot Filling
,填槽指的是为了让用户意图转化为用户明确的指令而补全信息的过程,我的理解是:比如在Chatbot
中引导用户给出明确指令。)
拿到一个语音信号我们一般是如何处理的呢?也就是如何将语音信号表示为一个序列向量(长度为 T \text{T} T,维度为 d \text{d} d)。或者称之为提取它的声学特征(acoustic feature
),下图是一种解决办法的流程:
拿到一段语音信号之后首先需要对其进行初步处理,一般25ms
的时间窗取出一个frame
(对应就有400
个采样点(16KHz
);使用MFCC
会得到39
维向量、filter bank
输出是80
维),然后开始滑动,类似滤波操作,一般每个时间窗的间隔为10ms
,那么1s
内就有100
个frame
。更多的学习资料可以参考:
如何处理每个frame
呢?如下图所示:
先输入声音信号,经过离散傅里叶变换(DFT
) 变成频谱图,经过多个不同的filter bank
(古圣先贤们设计出来的)处理后, 得到一系列向量,通过对数变换,再经过离散余弦变换(DCT
),使用再结合MFCC
方法得到向量。当然在实际过程中很多学者也不是都采取需要将上述过程全部走完得到MFCC
输出的方式,2019
年大部分工作都是在filter bank
输出之后就将其作为特征输出。
输出问文字部分,文字的表示方法不同大体可以分为以下五种:
phoneme
,发音的基本单位):这里相当于音标,然通过Lexicon
(词典)将phoneme
转换成word
。而这个词典需要语言学家事先定义好,因此是这种方法的弊端。Grapheme
,书写的基本单位),也就是说通过字母,标点,空格这些来表示一个词。比如one_punch_man
这个词中N=13
, V=26+
;如果是中文,则由单个汉字组成文本,比如:“一”,“拳”,“超”,“人”,N=4
, V≈4000
。Morpheme
,语素,可以传达意思的最小单位,小于词,大于字母),例如英文中:unbreakable
可以拆成 “un“ ”break“ ”able”。语素的获取主要有两种方式,一种是由语言学家给出,另一种是统计单词中某些字母的组合,从而进行估计。V
一直可表示为256
(8字节)。 2019
年使用最多的是grapheme
这种字母的表现形式。
传统的神经网络模型无法解决变长的序列学习问题。主流的有两种解决思路:1. Encoder-Decoder
;2. Connectionist Temporal Classification
(CTC)。
LAS
是15
年比较流行的几个动词叠在一起的叫法,本质还是Seq2seq
。
属于Encoder-Decoder
,只不过在神经网络中加了attention
机制,主要是提取特征,过滤掉杂讯信号。LAS
主要可以分三点来展开:Listen
, Attend
和Spell
。
Listen
部分主要是时序声音信号的处理,可以采用循环神经网络(RNN
)、或者卷积神经网络(CNN
)的方式来处理,而目前主流的方法都是在循环处理部分引入self-attention
机制。而声音的时序信号往往太长,相邻的声音讯号在RNN
序列中带有重复信息,因此在这里做下采样能够大大减少运算量但对实际效果影响会比较小。作者这里采用了Pyramin RNN
这样一种下采样(Down Sampling
)的技术。
Down Sampling大体上可以分为4
种:
Pyramin RNN
:用于LAS
中,如果没有Pyramin RNN
,LAS
是train
不起来的,所以起到了至关重要的作用,主要是将RNN的输出的相邻两个节点进行相加。Pooling Over Time
:用于RNN
中,将RNN
输出相邻两个中只取一个。首次提出是Bahdanau. et al.
,在ICASSP’16
上提出。Time-Delay DNN
:Time-Delay DNN
也被称作时延神经网络,早在1989
年就已经被提出。主要目的有两个:1)使用不变性对模式进行分类,以及 2)在网络的每一层建模上下文。 不变移位分类意味着分类器在分类之前不需要显式分割。对于时间模式(例如语音)的分类,TDNN
因此避免了在对声音进行分类之前必须确定声音的起点和终点。
对于TDNN
中的上下文建模,每一层的每个神经单元不仅从下一层的激活/功能接收输入,而且从单元输出及其上下文的模式接收输入。对于时间信号,每个单元随时间从下面的单元接收激活模式作为输入。将TDNN
应用于二维分类(图像,时频图)时,可以在坐标空间中进行平移不变性训练,并避免在坐标空间中进行精确分割。
用于CNN
的一维卷积中,可以值取CNN
输出的第一个和最后一个,与Dilated CNN
(空洞卷积)类似。
Waibel, A., Hanazawa, T., Hinton, G. E., Shikano, K., & Lang, K. J. (1989). Phoneme recognition using time-delay neural networks. IEEE Transactions on Acoustics, Speech, and Signal Processing, 37(3), 393-404.
Peddinti, V., Povey, D., & Khudanpur, S. (2015). A time delay neural network architecture for efficient modeling of long temporal contexts… conference of the international speech communication association.
Truncated Self-Attention
:以往的attention
是注意在整个sequence
上面,而truncated self-attention
只考虑部分sequence
,因为语音的sequence
非常长。具体多长是一个需要调整的参数,也就是说只注意一个窗口内的元素,至于这个窗口是多大是一个待调整的参数。 对于attention
不了解的可以参考以下文章:
这里对其数学部分进行简要描述:
首先我们有一个序列: x 1 , ⋯ , x i x_{1},\cdots,x_{i} x1,⋯,xi,是我们的输入信号,首先经过一个学习权重矩阵 W W W 得到输出: a 1 , ⋯ , a i a_{1},\cdots, a_{i} a1,⋯,ai (即 a i = W x i a^{i}=Wx^{i} ai=Wxi)。再将每个 a i a_{i} ai 乘以三个不同的权重参数: W q W^{q} Wq, W k W^{k} Wk, W v W^{v} Wv,得到query
q i q^{i} qi 、key
k i k^{i} ki 和value
v i v^{i} vi。
q i = W q a i k i = W k a i v i = W v a i q^{i} =W^{q}a_{i}\\ k^{i} =W^{k}a_{i}\\ v^{i} =W^{v}a_{i} qi=Wqaiki=Wkaivi=Wvai
每个query
q q q 对每个key
k k k 做attention
(相当于对每个输入乘以权重系数,用于表示输出更注重于哪个输入,用这种乘以权重系数的方法表述注意力机制):
α 1 , i = q 1 ⋅ k i / d \alpha_{1,i} = q^{1} \cdot k^{i} / \sqrt{d} α1,i=q1⋅ki/d
也就是Attention
是吃两个向量,输出这两个向量有多匹配(输出一个分数),可以看到上述方法是采用Scaled Dot-Product Attention
的方式得到。 d d d 是 q q q 和 k k k 的维度,除以 d \sqrt{d} d是相当于归一化的处理,用于减少方差。
除了Dot-product Attention
的方法还有Additive Attention
的方法,即二者相加再经过tanh
函数和权重矩阵得到最后的 α \alpha α结果,可以如下文献中找到具体算法:
到此我们可以得到对输入 i i i个向量 x i x_{i} xi的全部注意力 α 1 , 1 ⋯ , α 1 , i \alpha_{1,1} \cdots, \alpha_{1,i} α1,1⋯,α1,i,再将其送入Softmax
层得到 α ^ 1 , i \hat{\alpha}_{1,i} α^1,i:
α ^ 1 , i = exp ( α 1 , i ) / ∑ j exp ( α 1 , j ) \hat{\alpha}_{1, i}=\exp \left(\alpha_{1, i}\right) / \sum_{j} \exp \left(\alpha_{1, j}\right) α^1,i=exp(α1,i)/j∑exp(α1,j)
再将 α ^ 1 , i \hat{\alpha}_{1,i} α^1,i与 v i v^{i} vi相乘再求和得到第一个输出: b 1 = ∑ i α ^ 1 , i v i b_{1}=\sum_{i}\hat{\alpha}_{1,i}v^{i} b1=∑iα^1,ivi。由此可以看出attention
通过 α ^ \hat{\alpha} α^决定看哪些信息,不看哪些信息。用 q 2 q_{2} q2 做attention
,依此类推可以计算得到 b 2 b_{2} b2。而上述算法其实就是一堆矩阵运算,因此可以用GPU
加速。(当然上述的attention
过程可以做多份,也就是类似的 q q q, k k k, v v v做多份,只要是由不同的参数矩阵 W W W所生成出来的就可以,这种叫做Multi-head Self-attention
。对于输入有先后关系的数据需要引入位置编码(Positional Encoding
)将位置信息送入)。
LAS
中的做法与之有些许不同:
编码器和解码器中间的注意层会有一个 z 0 z^{0} z0参数作为要搜索的Query
,而编码器输出的隐层嵌入每位置的向量 h i h^{i} hi是要注意的Key
。我们用 z 0 z^{0} z0和每一个 h i h^{i} hi去计算注意力得分(点积或加性的方式):
之后 c 0 c^{0} c0 会是decoder
的输入。
c 0 c^{0} c0 和 z 0 z^{0} z0(关键字) 作为 解码器RNN
的第一个输入,然后输出一个token
的分布,取max
可以得到第一个token
:
再拿 z 1 z_{1} z1与原编码器的隐层向量做注意力,得到一个新的注意力分布 c 1 c^{1} c1,再次输入给RNN
,得到第二个token
,直到终止符
。
由于解码中RNN
的输出会影响下一个点的输出,因此有了Greedy Decoding
和Beam Search
两种方式。这里考虑RNN
输出只有两种情况的例子对其举例说明。若采用Greedy Decoding
的方式:
会得到ABB
的输出结果,但是BBB
的置信分数确是最高的。Beam Search
的方式是说每次找K条路径进行搜索,窗口大小为K
的贪心搜索。从每个节点我们都保留K
个最好的路径,一直往下。
如果在训练RNN
的时候,第二个节点用第一个节点真实的标签进行训练的话,这种技术叫做Teacher Forcing
:
之前的注意力阶段,我们每次是用解码器的输出隐层去与编码器的输出做注意力。除此以外,还有另一种做注意力的方式。我们把解码器的隐层 z t z_{t} zt拿出来与 h i h_{i} hi做注意力得到 c t c^{t} ct。这个 c t c^{t} ct不是保留到下一个时间才使用,在当前时间点立刻使用。我们把 z t z_{t} zt和 c t c^{t} ct丢给解码器RNN
,得到新分布 z t + 1 z_{t+1} zt+1。这两种注意力的区别在,注意力得到的结果是下一个时间使用还是当前时间使用。在LAS
中作者采用了全都要的方式:
语音识别是否非要用注意力不可呢?注意力最早是用在Seq2Seq
翻译上解决源语言与目标语言的对齐问题。这个弹性很大的注意力,在语音上用会有种杀鸡焉用牛刀的感觉。因为语音上,每次注意跳跃是有限的。而不会出现像机器翻译那样,开头注意到结尾的的大跳跃情况。我们可以用Location-aware attention
来优化。我们的注意力不能够随便乱跳,而是要考虑前一个时间步得到的注意力权重影响。我们把 t t t之前的注意力权重 α 0 \alpha_{0} α0到 α t − 1 \alpha_{t-1} αt−1的向量,做一个线性映射后再输入给解码RNN。 这样模型就能学到,每解码出一个token
,注意力就要往右移动一点。
LAS
模型需要在海量数据集上训练,和一些调参技巧,与传统方法相比才会有显著提升。但LAS
有另一个好处是,它的模型参数可以比传统方法变得很小。
LAS
的注意力可视化出来发现,即便没有用Location-aware attention
,模型也可以自己学到这样的注意规律。
LAS
虽然神通广大,但它也有一些问题。我们期待我们的模型可以做online
的识别,即能够一边听,一边做语音识别。而不是模型听完整句话后,等上一秒,模型才输出辨识结果。往后要讲的模型,就是解决LAS
的这个问题。
CTC
是能够做到on-line
的语音辨识算法,因为是Online
的算法,所以其RNN
输入是单向的。在RNN
输出后接一个线性模型用于预测token
的分布。
RNN
中输入语音信号 x i x^{i} xi,得到输出 h i h^{i} hi,再经过一个线性模型,相当于乘以参数矩阵 W W W再经过Softmax
层得到token
的分布。由于在语音信号中当前的音频 x i x^{i} xi 可能并没有对应的token
,往往是多个 x i x^{i} xi才能得到一个token
,所以预测的token
类别要多一个空的类别,用 ϕ \phi ϕ定义,表示模型并不知道要输出什么。
CTC
没有下采样(down sampling
) 因此输入 T T T 个acoustic features
,会输出得到 T T T 个tokens
,这 T T T 个tokens
会包含 ϕ \phi ϕ,并且我们需要合并相同的tokens
,并移除 ϕ \phi ϕ。如果我们得到的输出为 ϕ ϕ d d ϕ e ϕ e ϕ p p \phi \phi \mathrm{d} \mathrm{d} \phi \mathrm{e} \phi \mathrm{e} \phi \mathrm{p} \mathrm{p} ϕϕddϕeϕeϕpp,那我们最终得到的输出就是deep
。
这也会存在一个问题,就是输入的语音信号往往是会比输出的token
要多的,那此时的label
就会有很多种情况,比如 ϕ d d d ϕ e ϕ e ϕ p p \phi \mathrm{d} \mathrm{d} \mathrm{d} \phi \mathrm{e} \phi \mathrm{e} \phi \mathrm{p} \mathrm{p} ϕdddϕeϕeϕpp也代表是deep
,这样的话标注就会变的非常困难。CTC
的训练过程中穷举所有的label
,做cross-entropy
。
token
采用词汇的方式也会有效果,可参考下文。
CTC
的算法也存在一些问题,比如后面输出的token
并不知道前面token
所发生的事情,即线性模型的解码器是独立工作的,如果训练的时候label
还穷举所有可能的话,就会存在前面如果输出了正确的token
d \mathrm{d} d和 ϕ \phi ϕ 之后,后面的输出并不知道这一点可能会再次输出 d \mathrm{d} d 导致重复。也就是我们需要考虑线性模型这一层的前后依赖关系。
在RNN-T
之前,有一个算法是用来解决线性模型这一层的前后依赖关系的,Recurrent Neural Aligner
(RNA)由Sak, H
在2017年提出:
主要工作是将线性模型那一层神经网络换成了RNN
:
在此基础之上,如果我们让解码的RNN
是吃一个输入,输出多个token
,比如th
只是一个音。对于CTC
而言,它只能先输出t
再在下一个时间点解码输出h
,而我们希望它直接输出t
和h
。这种输入一个隐层,预测多个tokens
的模型便是RNN-T
:
如上图所示,这种方式会有 T T T 个 ϕ \phi ϕ 的输出。RNN-T
与CTC
同样会遇到训练数据难标注,标注多标准的问题。我们会穷举所有可能的标注给模型训练。
Neural Transducer
与之前所作方法的改变在输入 h i h^{i} hi 上,其输入不再是单个 h i h^{i} hi ,而是一把 h i h^{i} hi,然后做attention
:
首先我们会让声学特征通过编码器产生隐层输出,再对一个窗口的小范围隐层做注意力后,再输出给解码的RNN
。若窗口大小内信息已经用完了,解码的RNN
就会输出空类别。接下来我们再移动窗口,对下一个窗口内的隐层重复刚才的操作。
MoChA
是一种动态的窗口移动算法(dynamically shift the window
),能够动态地去调整窗口到底放在哪里会比较好。
输入z0
和隐层h1
,输出yes/no
,表示要不要把窗口开头放在此处位置。如果不要,就往右移动窗口,再检查下一个位置的隐层h2。一旦确定放置窗口,就对窗口内的隐层向量做注意力,解码出的token
不会包含空类别。
HMM
(Hidden Markov Model) 与上述这种End-To-End
的神经网络方法有什么区别呢?以前神经网络不像现在这么流行的时候是如何来做这样一件事情的呢?
语音辨识要做的事情就是输入一个向量 X X X,输出产生一串token
Y Y Y。传统的HMM
采用统计的方法,穷举所有可能的 Y Y Y,找到一个可以使得 P ( Y ∣ X ) P(Y|X) P(Y∣X)最大的那一个 Y Y Y,即: Y ∗ = arg max Y P ( Y ∣ X ) Y^{*}=\arg \max _{Y} P(Y | X) Y∗=argmaxYP(Y∣X),也就得到了最终的结果。这种算法通常被称作解码(decode
),根据贝叶斯定律我们有以下等式变换:
Y ∗ = arg max Y P ( Y ∣ X ) = arg max Y P ( X ∣ Y ) P ( Y ) P ( X ) = arg max Y P ( X ∣ Y ) P ( Y ) \begin{array}{l} \mathrm{Y}^{*}=\arg \max _{\mathrm{Y}} P(\mathrm{Y} | X) \\ \qquad \begin{array}{l} =\arg \max _{\mathrm{Y}} \frac{P(X | \mathrm{Y}) P(\mathrm{Y})}{P(X)} \\ =\arg \max _{\mathrm{Y}} P(X | \mathrm{Y}) P(\mathrm{Y}) \end{array} \end{array} Y∗=argmaxYP(Y∣X)=argmaxYP(X)P(X∣Y)P(Y)=argmaxYP(X∣Y)P(Y)
前面这项 P ( X ∣ Y ) P(X|Y) P(X∣Y)是Acoustic Model
,HMM
可以建模,后面那项 P ( Y ) P(Y) P(Y)是Language Model
,有很多种建模方式。
输出的 Y Y Y可以有很多不同的粒度,比如拿音标,字或词。但这些单位,对HMM
的隐变量来说,都太大了。所以我们需要为 P ( X ∣ Y ) P(X|Y) P(X∣Y)建模,变成为 P ( X ∣ S ) P(X|S) P(X∣S)建模。 S S S为状态,是人定义的。它是比音素Phoneme
还要小的单位。序列中的每一个音素,都会受到前后音素单位的影响。我们会用一个Tri-phone
,把当前的每一个音素,都加上它前后的音素,相当于把原来的音素切得更细。这样d
后面的uw
,和y
后面的uw
表达出来就会是不同的单位。
HMM
建模过程中有两种概率:一种是转移概率(Transition Probability
),如 P ( b ∣ a ) P(b|a) P(b∣a),即当前状态转移到其它状态或不转移状态的概率,另一种是输出概率(Emission Probability
),如 P ( x ∣ " t − d + u w 1 " ) P(x|"t-d+uw1") P(x∣"t−d+uw1"),即该状态输出某个样子的声学特征向量的概率。 假设每一个状态,它产生出来的声学特征向量有一个固定的分布。我们可以用高斯混合模型,或者GMM
来表示这个概率。
这便是为什么我们要用比Phoneme
还要小的单位来表示状态。因为我们要假设每个状态输出出来的分布稳定。为什么我们不用字符单位来当作状态呢?c
这个字母它的发音不是固定的。它在很多时候是发"ke",但它在h后面就发音"ch"。这样就不适合拿来当作HMM
的状态。
假设我们已经用给定好的数据算好了Emission Probability
和Transition Probability
,我们还是算不出 P ( X ∣ S ) P(X|S) P(X∣S)的概率。假设我们有3个状态abc
。我们需要知道把它变成aabbcc的序列才可以和声学特征对齐一样,但是会存在很多各种不同可能的对齐方式,比如 h = a b b b b c h = abbbbc h=abbbbc。给定的候选对齐状态不同,算出来产生的声学特征的概率 P ( X ∣ h ) P(X|h) P(X∣h)也就会不一样。因此就需要穷举所有可能,找到它产生与观测 X X X的声学特征概率最大,最一致的对齐方式。关键技术在Alignment
。
P θ ( X ∣ S ) = ? ∑ h ∈ a l i g n ( S ) P ( X ∣ h ) \mathrm{P}_{\theta}(X | S)=? \sum_{h \in align(S)} P(X | h) Pθ(X∣S)=?h∈align(S)∑P(X∣h)
Tandem
:之前的声学特征用的是MFCC
做的,深度学习在做的是,输入一个MFCC
,预测它属于哪个状态的概率。DNN-HMM Hybrid
:HMM
中有一个高斯混合模型。我们想把它用DNN
取代掉。高斯混合模型做的事情是给定一个状态,预测声学特征向量的分布,即 P ( x ∣ a ) P(x|a) P(x∣a)。DNN
是训练一个State
的分类器,计算给定一个声学特征下,它是某个状态的概率,即 P ( a ∣ x ) P(a|x) P(a∣x)。基于贝叶斯定律,可以得到 P ( x ∣ a ) = P ( a ∣ x ) P ( x ) / P ( a ) P(x|a) = P(a|x)P(x) / P(a) P(x∣a)=P(a∣x)P(x)/P(a)。 P ( a ) P(a) P(a)可以通过在训练资料中统计得到。 P ( x ) P(x) P(x)可以忽略。 我们要如何训练一个状态分类器呢?它的输入是一个声学特征(Acoustic features
),输出是它是某个状态的概率。做这样的任务我们需要标注数据(每个声学特征和状态之间的对应关系),但是我们并没有这样的标注数据。过去的做法是训练一个HMM-GMM
,那这个粗糙的模型去做找出一个概率最大的对齐。然后再根据声学特征与状态之间的对齐数据,去训练状态分类器。
接着,我们再拿这个训练好的状态分类器,替换掉原来的HMM-GMM
再对数据对齐,来训练出一个更好的状态分类器。我们反复重复这个过程。用训练得到的DNN
去对数据做对齐,再用对齐的数据去训练一个新的DNN
。
Yu, D., Xiong, W., Droppo, J., Stolcke, A., Ye, G., Li, J., & Zweig, G. (2016). Deep Convolutional Neural Networks with Layer-Wise Context Expansion and Attention… conference of the international speech communication association.
Saon, G., Kurata, G., Sercu, T., Audhkhasi, K., Thomas, S., Dimitriadis, D., … & Hall, P. (2017). English Conversational Telephone Speech Recognition by Humans and Machines… conference of the international speech communication association.
对于端对端的语音识别系统,比如说LAS
,你可以想成它可以直接计算 P ( Y ∣ X ) P(Y|X) P(Y∣X)。对于序列中的第一个 c 0 c_{0} c0,它通过RNN
计算 P ( a ∣ c 0 ) P(a|c_{0}) P(a∣c0)的概率。生成出 a a a之后,它又计算 P ( b ∣ a , c 0 , c 1 ) P(b|a,c_{0},c_{1}) P(b∣a,c0,c1)的概率……我们把这些每步计算的概率连乘起来就可以得到 P ( Y ∣ X ) P(Y|X) P(Y∣X)。训练的时候,是找一个最优的模型参数,来让 P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X)越大越好。而测试(解码)的时候,则是用Beam Search
来遍历所有可能的 Y Y Y,来让 P ( Y ∣ X ) P(Y|X) P(Y∣X)越大越好,如下图所示:
对于CTC
和RNN-T
是无法直接计算 P ( Y ∣ X ) P(Y|X) P(Y∣X)的,我们需要额外的alignment
(使得输出、输入长度对齐,一样),也就是我们需要先计算 P ( h ∣ X ) P(h | X) \quad P(h∣X),其中 h = a ϕ b ϕ → a b h=a \phi b \phi \rightarrow a b h=aϕbϕ→ab
P ( Y ∣ X ) = ∑ h ∈ align ( Y ) P ( h ∣ X ) \mathrm{P}(\mathrm{Y} | X)=\sum_{h \in \text {align}(Y)} P(h | X) P(Y∣X)=h∈align(Y)∑P(h∣X)
那如何穷举所有可能的alignment
?如何把所有的alignment
加起来,如何来训练 θ ∗ = arg max θ log P θ ( Y ^ ∣ X ) \theta^{*}=\arg \max _{\theta} \log \mathrm{P}_{\theta}(\hat{Y} | X) θ∗=argmaxθlogPθ(Y^∣X)?若采用梯度下降算法,那得对其求梯度 ∂ P θ ( Y ^ ∣ X ) ∂ θ = ? \frac{\partial \mathrm{P}_{\theta}(\hat{Y} | X)}{\partial \theta}=? ∂θ∂Pθ(Y^∣X)=?,如何计算?当模型训练好了,又要怎么做推断 Y ∗ = arg max Y P ( Y ∣ X ) Y^{*}=\arg \max _{Y} P(Y | X) Y∗=argmaxYP(Y∣X) 呢?
alignment
:假设输入有6
个声学特征向量,输出是三个以英文字母为最小单位的tokens
,“c”, “a”, 和"t"。虽然实际中不会用字符为最小单位,但这里我们只是为了方便比较。对于HMM
,它们为隐变量状态。我们需要对cat
分别做一些重复,变成ccaaat
或者 caaaat
等等。而对于CTC
,它有两种选择,一个是重复,另外一个是加上空类别,变成 c ϕ a a t t c\phi aatt cϕaatt或者 ϕ c a ϕ t ϕ \phi ca\phi t\phi ϕcaϕtϕ等等。对于RNN-T
,它要加上T
个 ϕ \phi ϕ。它的运作是每次解码器输出一个 ϕ \phi ϕ的时候,它就读下一个声学特征。因此它需要输出6
次 ϕ \phi ϕ,才能把所有的声学特征读完。变成 c ϕ ϕ ϕ a ϕ ϕ t ϕ c\phi \phi \phi a\phi \phi t\phi cϕϕϕaϕϕtϕ 或者 c ∅ ∅ a ∅ ∅ t ∅ ∅ c∅∅a∅∅t∅∅ c∅∅a∅∅t∅∅等等,每一种就是一个alignment
。但对于LAS
来说,它完全不需要考虑对齐的问题。用隐马尔可夫链来可视化三种模型的对齐方式,就可以直观地看到他们的关联:
alignments
总和:假设 h = ϕ h=\phi h=ϕ с ϕ ϕ \phi \phi ϕϕ а ϕ \phi ϕ t ϕ ϕ \phi \phi ϕϕ,此条alignment
概率计算公式为: P ( h ∣ X ) = P ( ϕ ∣ X ) × P ( c ∣ X , ϕ ) × P ( ϕ ∣ X , ϕ c ) ⋯ P(h | X) =P(\phi | X) \times P(c | X, \phi) \times P(\phi | X, \phi c) \cdots P(h∣X)=P(ϕ∣X)×P(c∣X,ϕ)×P(ϕ∣X,ϕc)⋯ RNN-T
与CTC
不同的地方在,它另外训练了一个解码的RNN
。这个RNN
会把解码出来的token
当作输入,去影响它接下来的输出。一开始我们没有任何输入,就先输入一个让它产生一个向量。一开始产生的是 l 0 l_{0} l0,我们把编码产生的 h 1 h_{1} h1,与 l 0 l_{0} l0一起输入给解码器MLP
,让它产生一个概率 p 1 , 0 p_{1,0} p1,0,表示输入第一个隐层,没产生任何token
时,RNN-T
产生出的概率分布。它是 ϕ \phi ϕ放在句首的概率。接下来我们要算,有了这个 ϕ \phi ϕ之后,产生下一个token
为c
的概率。上面的解码RNN
也不会造成任何影响。它只在解码器MLP
生成了实在的token
,它才会往下计算。但产生 ϕ \phi ϕ是表示当前由编码器输出的隐层 h 1 h_{1} h1的信息模型已经用尽了。接下来我们要计算下一个token
的隐层 h 2 h_{2} h2。我们把 h 2 h_{2} h2和之前的 l 0 l_{0} l0一起输入编码器MLP
,输出得到概率 p 2 , 0 p_{2,0} p2,0。由于我们解码出了实在的token
c c c。往后一步这个 c c c就会输入给上面的RNN
,来计算一个新的 l 1 l_{1} l1。 l 1 l_{1} l1和 h 2 h_{2} h2一起丢给解码器MLP
就会得到概率 p 2 , 1 p_{2,1} p2,1。以此类推… 这些输出的概率全部相乘,就是最终 P ( h ∣ X ) P(h|X) P(h∣X)的概率。
RNN-T
它神奇的地方是,它把 token
与token
之间的关系,独立用 RNN
来表示。这刚好是HMM
的独立观测假设。
HMM
是用向前和向后传播算法来计算所有候选对齐的概率分数。RNN-T
与HMM
所用的方法,是一模一样的。我们定义 α i , j α_{i,j} αi,j为,已经读了第 i i i个声学特征且输出了第 j j j个token
的所有对齐分数之和。比如说 α 4 , 2 α_{4,2} α4,2可以从 α 4 , 1 α_{4,1} α4,1和 α 3 , 2 α_{3,2} α3,2转移过来,那么就有 α 4 , 2 = α 4 , 1 p 4 , 1 + α 3 , 2 p 3 , 2 α_{4,2} = α_{4,1} p_{4,1} + α_{3,2}p_{3,2} α4,2=α4,1p4,1+α3,2p3,2。如此一来,我们就很容易推出动态规划的递推式 α i , j = α i − 1 , j p i − 1 , j + α i , j − 1 p j − 1 , i α_{i,j} = α_{i-1,j}p_{i-1,j} + α_{i,j-1}p_{j-1,i} αi,j=αi−1,jpi−1,j+αi,j−1pj−1,i。这样我们只需要遍历一个 T × N T \times N T×N的网格,我们就能算出所有的分数之和。有了所有候选对齐的概率分数之和,我们就得到了 P ( Y ∣ X ) P(Y|X) P(Y∣X)。
∂ P ( P ∣ X ) ∂ θ = ? ∂ p 4 , 1 ( a ) ∂ θ ∂ P ( P ∣ X ) ∂ p 4 , 1 ( a ) + ∂ p 3 , 2 ( ϕ ) ∂ θ ∂ P ( P ∣ X ) ∂ p 3 , 2 ( ϕ ) + ⋯ \frac{\partial P(P | X)}{\partial \theta}=? \quad \frac{\partial p_{4,1}(a)}{\partial \theta} \frac{\partial P(P | X)}{\partial p_{4,1}(a)}+\frac{\partial p_{3,2}(\phi)}{\partial \theta} \frac{\partial P(P | X)}{\partial p_{3,2}(\phi)}+\cdots ∂θ∂P(P∣X)=?∂θ∂p4,1(a)∂p4,1(a)∂P(P∣X)+∂θ∂p3,2(ϕ)∂p3,2(ϕ)∂P(P∣X)+⋯
对于前一项,每个箭头对参数 θ \theta θ的偏微分,计算方式就是经典的BPTT
时序的反向传播。一开始最右边的结果计算和标签的损失,反向传播传到编码器,再传到上面的解码器RNN
。
对于后一项,整个 P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X)对每个箭头的偏微分,我们要先用之前的动态规划算法得到 P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X)。算的时候,要把包含当前 p i , j p_{i,j} pi,j和不包含当前 p i , j p_{i,j} pi,j分开来计算。对于前面包含 p i , j p_{i,j} pi,j的,求导后就只剩非 p i , j p_{i,j} pi,j的概率相乘求和。对于第二项没有包含 p i , j p_{i,j} pi,j的求导,它就没了。我们把第一项再整理一下,就可以得到最终的计算式。得到 P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X)后除以当前箭头 p i , j p_{i,j} pi,j的概率。
这时,我们再引入另一个辅助变量 β i , j β_{i,j} βi,j。它与 α i , j α_{i,j} αi,j很像,它表示从第 i i i个声学特征开始且输出到第 j j j个token
的所有候选对齐分数之和。 β 4 , 2 β_{4,2} β4,2如图所示,它表示已经产生了4个声学特征和输出两个token
的情况下,它们当前位置走到结尾为止的所有路径的分数总和。 β i , j β_{i,j} βi,j刚好是 α i , j α_{i,j} αi,j的反过来。前面 α i , j α_{i,j} αi,j对应着 HMM
的正向传播算法,这里 β i , j β_{i,j} βi,j对应着 HMM
的反向传播算法。通过动态规划算法,于是我们有递推式, β i , j = β i + 1 , j p i , j + β i , j + 1 p i , j β_{i,j} = β_{i+1,j}p_{i,j} + β_{i,j+1}p_{i,j} βi,j=βi+1,jpi,j+βi,j+1pi,j。
当我们可以算 α i , j α_{i,j} αi,j 和 β i , j β_{i,j} βi,j 之后,我们就可以计算出,所有包含 p 4 , 1 ( a ) p_{4,1}(a) p4,1(a)的分数总和。如图示, P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X) 的计算方式可以改写为,所有从起始位置到 (4,1) 的候选对齐路径的分数和 α 4 , 1 α_{4,1} α4,1 乘上 p 4 , 1 ( a ) p_{4,1}(a) p4,1(a)后,再乘上所有从位置 (4,2) 到终点的候选对齐路径的分数和 β 4 , 2 β_{4,2} β4,2。这样我们把它再除以 p 4 , 1 p_{4,1} p4,1,就消掉了 p 4 , 1 p_{4,1} p4,1。这样 P ( Y ^ ∣ X ) P(\hat{Y}|X) P(Y^∣X) 对某个箭头概率 p i , j p_{i,j} pi,j 的偏微分就可以改写为 α i , j β i , j + 1 α_{i,j}β_{i,j+1} αi,jβi,j+1。带入最终的式子后,就能计算全部候选对齐的得分,对模型参数的梯度。然后反向传播更新模型参数进行训练。
训练好模型之后,我们要进行推断,即遍历所有可能的候选 Y Y Y,来使得模型输出的概率 P ( Y ∣ X ) P(Y|X) P(Y∣X)最大,从而找到最优的解码 Y Y Y。但现实中遍历所有可能候选 Y Y Y不大容易。我们只能退一步求其次,通过贪心近似估计的方法。我们不把所有的候选对齐分数加起来,而是比每一个 Y Y Y中,分数最高的那个对齐方式。概率最高的对齐方式叫作 h ∗ h^{*} h∗。我们要探究 h ∗ h_{*} h∗它背后的 Y ∗ Y^{*} Y∗是什么。
实际中要怎么找一个概率最高的对齐方式呢?RNN-T
每一个时间步都会跑出一个概率分布。我们把每个概率分布中,概率最大的那个token
取出来,就是 h ∗ h^{*} h∗的一个近似。由于取当前概率最大的未能让整个路径最大的。如果我们想要得到更好的近似,就用beam search
,加大K
。在精度和计算效率上进行折中和平衡。
比较一下这三个模型。在解码部分,LAS
和RNN-T
会考虑前面的时序对当前时序的影响。而CTC
没有考虑之前的时间步,已经生成出来的token
。在对齐部分,因为中间的注意力层,LAS
不用显示地考虑对齐。由于注意力一次要看全部,这也导致它不能在线学习。而CTC
和RNN-T
没有注意力层,RNN
一步一步地对输入解码,让它可以在线学习。但缺点是需要把输入和输出进行对齐。而且针对需要对齐的训练,会比较麻烦。
Language Modeling(LM)
用于估测一段token sequence
出现的机率。比如在HMM
中 Y ∗ = arg max Y P ( X ∣ Y ) P ( Y ) Y^{*}=\arg \max _{Y} P(X | Y) P(Y) Y∗=argmaxYP(X∣Y)P(Y)中的 P ( Y ) P(Y) P(Y)就是LM
。而LAS
虽然是对条件概率 P ( X ∣ Y ) P(X|Y) P(X∣Y)建模,看起来不需要 P ( Y ) P(Y) P(Y),但我们实际上很容易得到 P ( Y ) P(Y) P(Y)的分布。我们让 P ( Y ) P(Y) P(Y)去乘上 P ( Y ∣ X ) P(Y|X) P(Y∣X)来像HMM
一样解码,能让表现变得更好。而且,计算条件概率我们只需要成对的资料,会比计算联合概率容易得多。
某一个 token sequence
y 1 , y 2 , ⋯ , y n y_{1}, y_{2}, \cdots , y_{n} y1,y2,⋯,yn 可能在训练数据中出现的概率为0,但是我们并不能说这个句子在现实生活中出现的机率是0。N-gram
语言模型将其拆分成比较小的窗口的概率连乘: P ( y 1 , y 2 , ⋯ , y n ) = P ( y 1 ∣ B O S ) P ( y 2 ∣ y 1 ) ⋯ P ( y n ∣ y n − 1 ) P(y_{1},y_{2},\cdots,y_{n})=P(y_{1}|BOS)P(y_{2}|y_{1})\cdots P(y_{n}|y_{n-1}) P(y1,y2,⋯,yn)=P(y1∣BOS)P(y2∣y1)⋯P(yn∣yn−1)。但是这种方式也会存在一些问题,就是当原始训练数据中有某个词汇 y k y_{k} yk后面接某个特定的词汇 y k + 1 y_{k+1} yk+1时,其概率为0
,如果连乘起来,整个序列出现的概率也就会0
。这是因为数据稀疏导致的。此时可以采用 language model smoothing
的方法,赋予其一个较低的值。
在深度学习之前,用的是一种从推荐系统中来的 Continuous LM
。通过对矩阵分解的方式来解决上述问题,概率为0
的问题。通过这种方式,它就会自动把0
补成学到的参数。矩阵分解参考:经典机器学习系列之【个性化推荐之协同过滤】。
L = ∑ ( i , j ) ( v i ⋅ h j − n i j ) 2 L=\sum_{(i, j)}\left(v^{i} \cdot h^{j}-n_{i j}\right)^{2} L=∑(i,j)(vi⋅hj−nij)2也可以采用Deep Learning
的方法来做。 h j h^{j} hj 作为输入, v i v^{i} vi作为参数, n i j n_{i j} nij作为标签。
NN-based LM
最早是想要取代 N-gram
的语言模型。它训练目标是通过输入前面的词,来预测后面的词出现的概率。
有了NN-based LM
就自然会进入 RNN-based LM
。它可以解决输入序列较长的问题。
RNN
有各式各样的变形。曾经人们的想法是,把 RNN
尽可能地做复杂,看能不能做出更强的语言模型。甚至还有人用 Nerual Turning Machine
改一改来做语言模型。近几年也有研究表明,LSTM
加上合适的优化器和正则项就可以表现得很好。也不见得需要用非常神妙的奇技淫巧。
之后就是拿这些语言模型与LAS
结合,也有很多种方法:
【1】李宏毅-基于深度学习的人类自然语言处理
【2】https://zhuanlan.zhihu.com/p/124327822