语音识别
语音识别基础
Ø 特征提取
(https://asr.pub/posts/feature_extraction/)
预加重的目的是提升高频部分,使信号的频谱变得平坦,保持在低频到高频的整个频带中,能用同样的信噪比求频谱。同时,也是为了消除发生过程中声带和嘴唇的效应,来补偿语音信号受到发音系统所抑制的高频部分,也为了突出高频的共振峰。这里说的高频低频是指时域上采样点分帧后的频率,针对每一帧,作傅利叶变换得到频域的N个分量,这N个分量按照频率为横坐标,振幅为纵坐标。如果没有噪音,这N个分量振幅值应该随着频率增大逐渐递减,但因为噪音存在,在某一高频段处,振幅值出现反常,变得很大,导致信噪比很小,而预加重就是为了把高频段的信号都放大,从而增大高频段的信噪比
l 为什么要分帧加窗
语音信号处理需要弄清楚语音中各个频率成分的分布。做这件事情的数学工具是傅里叶变换。傅里叶变换要求输入信号是平稳的。而语音在宏观上来看是不平稳的——你的嘴巴一动,信号的特征就变了。但是从微观上来看,在比较短的时间内,嘴巴动得是没有那么快的,语音信号就可以看成平稳的,就可以截取出来做傅里叶变换了。这就是为什么语音信号要分帧处理,截取出来的一小段信号就叫一「帧」。
那么一帧有多长呢?帧长要满足两个条件:
从宏观上看,它必须足够短来保证帧内信号是平稳的。前面说过,口型的变化是导致信号不平稳的原因,所以在一帧的期间内口型不能有明显变化,即一帧的长度应当小于一个音素的长度。正常语速下,音素的持续时间大约是 50~200 毫秒,所以帧长一般取为小于 50 毫秒。
从微观上来看,它又必须包括足够多的振动周期,因为傅里叶变换是要分析频率的,只有重复足够多次才能分析频率。语音的基频,男声在 100 赫兹左右,女声在 200 赫兹左右,换算成周期就是 10 毫秒和 5 毫秒。既然一帧要包含多个周期,所以一般取至少 20 毫秒。
这样,我们就知道了帧长一般取为 20 ~ 50 毫秒,20、25、30、40、50 都是比较常用的数值。
取出来的一帧信号,在做傅里叶变换之前,要先进行「加窗」的操作,即与一个「窗函数」相乘 加窗的目的是让一帧信号的幅度在两端渐变到 0。渐变对傅里叶变换有好处,可以让频谱上的各个峰更细,不容易糊在一起(术语叫做减轻频谱泄漏。
加窗的代价是一帧信号两端的部分被削弱了,没有像中央的部分那样得到重视。弥补的办法是,帧不要背靠背地截取,而是相互重叠一部分。相邻两帧的起始位置的时间差叫做帧移,常见的取法是取为帧长的一半,或者固定取为 10 毫秒。
对一帧信号做傅里叶变换,得到的结果叫频谱。语音的频谱,常常呈现出「精细结构」和「包络」两种模式。「精细结构」就是蓝线上的一个个小峰,它们在横轴上的间距就是基频,它体现了语音的音高——峰越稀疏,基频越高,音高也越高。「包络」则是连接这些小峰峰顶的平滑曲线(红线),它代表了口型,即发的是哪个音。包络上的峰叫共振峰,图中能看出四个,分别在 500、1700、2450、3800 赫兹附近。有经验的人,根据共振峰的位置,就能看出发的是什么音。
对每一帧信号都做这样的傅里叶变换,就可以知道音高和口型随时间的变化情况,也就能识别出一句话说的是什么了。
l Fbank特征和MFCC特征对比
fbank只是缺少mfcc特征提取的dct倒谱环节,其他步骤相同。
fbank的不足:相邻的特征高度相关(相邻滤波器组有重叠),因此当我们用HMM对音素建模的时候,几乎总需要首先进行倒谱转换,通过这样得到MFCC特征。
计算量:MFCC是在FBank的基础上进行的,所以MFCC的计算量更大
特征区分度:FBank特征相关性较高,MFCC具有更好的判别度,所以大多数语音识别论文中用的是MFCC,而不是Fbank。而端到端的方法基本都用的Fbank
l 为什么有DCT
其中DCT的实质是去除各维信号之间的相关性,将信号映射到低维空间。离散余弦变换(discrete cosine transform,DCT)是傅里叶变换的一个变种,好处是结果是实数,没有虚部。DCT还有一个特点是,对于一般的语音信号,这一步的结果的前几个系数特别大,后面的系数比较小,可以忽略。上面说了一般取40个三角形,所以DCT的结果也是40个点;实际中,一般仅保留前12~20个,这就进一步压缩了数据。
Ø 语音数据增强
(https://blog.csdn.net/qq_36999834/article/details/109851965)
音量增强
速度增强(基于一维的插值算法)
失真增强
音调增强(频率)
移动增强
噪声增强(加噪)
SpecAugment: 在语谱图上做数据增强。 语谱图直观的表示语音信号随时间变化的频谱特性。任一给定频率成分在给定时刻的强弱用相应点的灰度或色调的浓淡来表示。用语谱图分析语音又称为语谱分析。
时间扭曲:随机的选择一个点出来,然后将它放在距离当前位置为W的地方,左边,右边都可以。那这个距离怎样选择呢,沿着波形线,我们从一个从0到时间扭曲参数(自己设置的)的正态分布里面去选择。
时域遮盖
频域遮盖
Ø 声学模型和语言模型
l 声学模型
声学模型其实就是可以识别单个音素的模型(音素a的模型可以判定一小段语音是否是a);
语言模型表示一个个词串(如何结合了词典,就可以成为一个个音素串)它们在语料库中出现的概率大小(比如,不合语法的词串(句子)概率接近0,很合乎语法的词串概率大);
解码器就是基于Viterbi算法在HMM模型上搜索生成给定观测值序列(待识别语音的声学特征)概率最大的HMM状态序列,再由HMM状态序列获取对应的词序列,得到结果结果。
如果你只做单个音素识别,(语音很短,内容只是音素),那么只用声学模型就可以做到,不用语言模型。做法就是在每个音素的声学模型上使用解码器做解码(简单的Viterbi算法即可)。
但是,通常是要识别一个比较长的语音,这段语音中包含了很多词。这就需要把所有可能的词串,结合词典展开为音素串,再跟音素的声学模型结合,可以得到解码图(实际上可以看成很多很多HMM模型连接而成),然后在这个解码图上实施Viterbi算法,得到最佳序列,进而得到识别结果。
l 语言模型
N-Gram
语言模型把语言(词的序列)看作一个随机事件,并赋予相应的概率来描述其属于某种语言集合的可能性。给定一个词汇集合 V,对于一个由 V 中的词构成的序列S = ⟨w1, · · · , wT ⟩ ∈ Vn,统计语言模型赋予这个序列一个概率P(S),来衡量S符合自然语言的语法和语义规则的置信度。用一句简单的话说,就语言模型就是计算一个句子的概率大小的这种模型。有什么意义呢?一个句子的打分概率越高,越说明他是更合乎人说出来的自然句子。
N-Gram是一种基于统计语言模型的算法。它的基本思想是将文本里面的内容按照字节进行大小为N的滑动窗口操作,形成了长度是N的字节片段序列。
每一个字节片段称为gram,对所有gram的出现频度进行统计,并且按照事先设定好的阈值进行过滤,形成关键gram列表,也就是这个文本的向量特征空间,列表中的每一种gram就是一个特征向量维度。
该模型基于这样一种假设,第N个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积。这些概率可以通过直接从语料中统计N个词同时出现的次数得到。常用的是二元的Bi-Gram和三元的Tri-Gram。
n-gram模型用于评估语句是否合理:
NNLM
NNLM 即 Neural Network based Language Model,它由四层组成,输入层、嵌入层、隐层和输出层。模型接收的输入是长度为n的词序列,输出是下一个词的类别。首先,输入是单词序列的index序列,例如单词 I 在字典(大小为|V|)中的index是10,单词 am 的 index 是23, Bengio 的 index 是65,则句子“I am Bengio”的index序列就是 10, 23, 65。嵌入层(Embedding)是一个大小为∣V∣×K的矩阵,从中取出第10、23、65行向量拼成3×K的矩阵就是Embedding层的输出了。隐层接受拼接后的Embedding层输出作为输入,以tanh为激活函数,最后送入带softmax的输出层,输出概率。
NNLM最大的缺点就是参数多,训练慢。另外,NNLM要求输入是定长n,定长输入这一点本身就很不灵活,同时不能利用完整的历史信息。
RNNLM
其结构实际上是用RNN代替NNLM里的隐层,RNN可以接受任意长度输入、利用完整的历史信息。同时,RNN的引入意味着可以使用RNN的其他变体,像LSTM等等,从而在时间序列建模上进行更多更丰富的优化。
Ø NLP中的subword算法及实现(Tokenization)
(https://zhuanlan.zhihu.com/p/198964217?utm_source=wechat_session&utm_medium=social&utm_oi=642320524580229120)
(https://zhuanlan.zhihu.com/p/112444056?utm_source=wechat_session&utm_medium=social&utm_oi=642320524580229120)
l token
token是指,句子经过分词或者subword模型得到的序列,该序列的元素就是token,如果分词算法将得到的结果是[ '在', '纽', '约', '生', '活' ],那么‘在’就是token,’在’的embedding是‘在’这个token的特征表示。
l word、subword和character
在NLP任务中,神经网络模型的训练和预测都需要借助词表来对句子进行表示。传统构造词表的方法,是先对各个句子进行分词,然后再统计并选出频数最高的前N个词组成词表。通常训练集中包含了大量的词汇,以英语为例,总的单词数量在17万到100万左右。出于计算效率的考虑,通常N的选取无法包含训练集中的所有词。因而,这种方法构造的词表存在着如下的问题:
- 实际应用中,模型预测的词汇是开放的,对于未在词表中出现的词(Out Of Vocabulary, OOV),模型将无法处理及生成;
- 词表中的低频词/稀疏词在模型训练过程中无法得到充分训练,进而模型不能充分理解这些词的语义;
- 一个单词因为不同的形态会产生不同的词,如由"look"衍生出的"looks", "looking", "looked",显然这些词具有相近的意思,但是在词表中这些词会被当作不同的词处理,一方面增加了训练冗余,另一方面也造成了大词汇量问题。
一种解决思路是使用字符粒度来表示词表,虽然能够解决OOV问题,但单词被拆分成字符后,一方面丢失了词的语义信息,另一方面,模型输入会变得很长,这使得模型的训练更加复杂难以收敛。
针对上述问题,Subword(子词)模型方法横空出世。它的划分粒度介于词与字符之间,比如可以将”looking”划分为”look”和”ing”两个子词,而划分出来的"look",”ing”又能够用来构造其它词,如"look"和"ed"子词可组成单词"looked",因而Subword方法能够大大降低词典的大小,同时对相近词能更好地处理。
目前有三种主流的Subword算法,它们分别是:Byte Pair Encoding (BPE), WordPiece和Unigram Language Model。
l Byte Pair Encoding(BPE)
Byte Pair Encoding (BPE) 是一种压缩算法,它属于自下而上的算法(由少变多)。
BPE是一种数据压缩的方式,它将字符串中最常见的一对连续字符数据替换成该字符串中不存在的字符串,后续再通过一个词表重建原始的数据。BPE的处理过程可以理解为一个单词的再拆分过程。如"loved","loving","loves"这三个单词,其本身的语义都是”爱”的意思。BPE通过训练,能够把上面的3个单词拆分成”lov”,”ed”,”ing”,”es”几部分,这样可以把词的本身的意思和时态分开,有效的减少了词表的数量。
- 获取subword词表的流程(learn-bpe)
- 准备语料,分解成最小单元,比如英文中26个字母加上各种符号,作为原始词表
- 根据语料统计相邻字符对出现的频次
- 挑出频次最高的相邻字符对,比如"t"和"h",合并组成"th",加入词表,训练语料中所有该相邻字符对都进行融合
- 重复2和3操作,直至词表中单词的数量达到期望,或下一个最高频的字节对出现频率为1
编码和解码(apply-bpe及其逆过程)
得到subword词表后,对该词表按照子词长度由大到小排序。编码时,对于每个单词,遍历排好序的字词词表寻找是否有token是当前单词的子字符串,如果有,则该token是表示单词的tokens之一。从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。 最终,该过程将迭代所有tokens,并将所有子字符串替换为tokens。 如果仍然有子字符串没被替换但所有token都已迭代完毕,则将剩余的子词替换为特殊token,如unk。(注:当输入也是文本数据时,需要对每个单词做编码,将单词表示成bpe产生的token集合(在字典中挑选能表示该单词的字词),此时就用到了该编码过程。在标签数据也是文本数据时,每个单词也需要被表示成bpe产生的token集合。这是因为模型的输出单元是单个token,每个单词是由好几个token表示的,所以模型的几个输出(token)连接起来才是该单词)
而解码是编码的逆过程,主要应用在得到翻译输出怎么将subword恢复为word
l WordPiece
Google的Bert模型在分词的时候使用的是WordPiece算法。与BPE算法类似,WordPiece算法也是每次从词表中选出两个子词合并成新的子词。与BPE的最大区别在于,如何选择两个子词进行合并:BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表。
看到这里,你可能不清楚WordPiece是如何选取子词的。这里,通过形式化方法,能够清楚地理解WordPiece在合并这一步是如何作出选择的。假设句子 由n个子词组成,ti表示子词,且假设各个子词之间是独立存在的,则句子的语言模型似然值等价于所有子词概率的乘积:
假设把相邻位置的x和y两个子词进行合并,合并后产生的子词记为z,此时句子似然值的变化可表示为:
从上面的公式,很容易发现,似然值的变化就是两个子词之间的互信息。简而言之,WordPiece每次选择合并的两个子词,他们具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性,它们经常在语料中以相邻方式同时出现。
wordpiece算法中subword词表的学习跟BPE也差不多:
- 准备语料,分解成最小单元,比如英文中26个字母加上各种符号,作为原始词表
- 利用上述语料训练语言模型
- 从所有可能的subword单元中选择加入语言模型后能最大程度地增加训练数据概率的单元作为新的单元
- 重复上步骤,直至词表大小达到设定值或概率增量低于某一阈值
l unigram language model
语言模型作为NLP的大厦根基,也是unigram分词的基础。在wordpiece算法中,其实已经用到了language modeling,在选择token进行合并的时候目标就是能提高句子的likelihood。而unigram分词则更进一步,直接以最大化句子的likelihood为目标来直接构建整个词表。
首先,了解一下怎么样在给定词表的条件下最大化句子的likelihood。 给定词表及对应概率值: {"你":0.18, "们":0.16, "好":0.18, "你们":0.15},对句子”你们好“进行分词:
- 划分为"你@@" "们@@" "好" 的概率为18*0.16*0.18=0.005184
- 划分为"你们@@" "好@@" 的概率为15*0.18=0.027
明显看出后一种分词方式要比前一种好,当然在真实的案例下词表可能有几万个token,直接罗列各种组合的概率显然不可能,所以需要用到Viterbi算法。因此在给定词表的情况下,可以
1.计算每个token对应的概率;
2.找到一个句子最好的分词方式
但是在词表没有确定的情况下,同时要优化词表和词表里每个token的概率很难做到。unigram分词使用逐步迭代的方式来求解,具体步骤如下:
- 首先初始化一个很大的词表
- 重复以下步骤直到词表数量减少到预先设定的阈值:
- 保持词表不变,用EM算法来求解每个token的概率
- 对于每一个token,计算如果把这个token从词表中移除而导致的likelihood减少值,作为这个token的loss
- 按loss从大到小排序,保留前n%(原文中为80%)的token。
初始化词表可以用不同的方法,一个比较直接的办法就是用所有长度为1的token加上高频出现的ngram来作为起始词表。
l SentencePiece
如何使用上述子词算法?一种简便的方法是使用SentencePiece,它是谷歌推出的子词开源工具包,其中集成了BPE、ULM子词算法。除此之外,SentencePiece还能支持字符和词级别的分词。更进一步,为了能够处理多语言问题,sentencePiece将句子视为Unicode编码序列,从而子词算法不用依赖于语言的表示。
Ø word2vec
基于神经网络的分布表示又称为词向量、词嵌入,神经网络词向量模型与其它分布表示方法一样,均基于分布假说,核心依然是上下文的表示以及上下文与目标词之间的关系的建模。语言模型正好具有捕捉上下文信息的能力。那么构建上下文与目标词之间的关系,最自然的一种思路就是使用语言模型语言模型statistical language model。就是给你几个词,在这几个词出现的前提下来计算某个词出现的(事后)概率。CBOW也是统计语言模型的一种,顾名思义就是根据某个词前面的C个词或者前后C个连续的词,来计算某个词出现的概率。Skip-Gram Model相反,是根据某个词,然后分别计算它前后出现某几个词的各个概率。
以“我爱北京天安门”这句话为例。假设我们现在关注的词是“爱”,C=2时它的上下文分别是“我”,“北京天安门”。CBOW(完形填空)模型就是把“我” “北京天安门” 的one hot表示方式作为输入,也就是C个1xV的向量,分别跟同一个VxN的大小的系数矩阵W1相乘得到C个1xN的隐藏层hidden layer,然后C个取平均所以只算一个隐藏层。这个过程也被称为线性激活函数(这也算激活函数?分明就是没有激活函数了)。然后再跟另一个NxV大小的系数矩阵W2相乘得到1xV的输出层,这个输出层每个元素代表的就是词库里每个词的事后概率。输出层需要跟ground truth也就是“爱”的one hot形式做比较计算loss。Skip gram(造句)训练过程类似,只不过输入输出刚好相反。CBOM和skip gram本质上都是在做极大似然估计,CBOM是在上下文已知的条件下,出现中心词的概率是模型参数的函数,最大化对应的似然函数(各个时刻的累计和)。skip-gram是在中心词已知的条件下,分别出现上下文每个单词的概率是模型参数的函数,最大化对应的似然函数(各个时刻的累计和)。
训练trick:
- 负采样缓解sofrmax计算量大的问题。本质就是用sigmoid代替softmax,只挑选一部分负样本计算交叉熵损失,正样本正常计算。
- 层次softmax。构造霍夫曼树
l Hierarchical Softmax
Hierarchical Softmax
(B站视频)
根据语料库统计词频,构造霍夫曼树(霍夫曼树的好处是,一般得到霍夫曼树后我们会对叶子节点进行霍夫曼编码,由于权重高的叶子节点越靠近根节点,而权重低的叶子节点会远离根节点,这样我们的高权重节点编码值较短,而低权重值编码值较长。这保证的树的带权路径最短)。
Ø 位置编码
不同时间步位置编码不同,且不同特征维度,位置编码也不同。
为什么用sin和cos做位置编码:我们希望i处的位置向量Pi与j处的位置向量Pj的内积是(j-i)的函数(为什么是内积,因为计算attention的score时,两个向量的内积时最简单的算法。当满足这个条件时,i和j的相似度就和位置也有关系了)。而cos和sin函数正好有这个性质:
cos(i-j) = cosi*cosj+sini*sinj([cosi,sini]*[cosj,sinj]T)
令Pi = [sini, cosi]; Pj = [sinj,cosj],那么Pi和Pj的内积= cosi*cosj+sini*sinj
模型
l 端到端
https://blog.csdn.net/wudibaba21/article/details/110946194
Seq2seq
CTC
l GMM-HMM
https://blog.csdn.net/fandaoerji/article/details/44853853
l RNN
普通神经网络的前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的,比如某个单词的意思会因为上文提到的内容不同而有不同的含义,RNN就能够很好地解决这类问题。RNN的核心思想即是将数据按时间轴展开,每一时刻数据均对应相同的神经单元,且上一时刻的结果能传递至下一时刻。至此便解决了输入输出变长且存在上下文依赖的问题。
隐藏层St的取值不仅仅取决于Xt,还取决于St-1。用公式表达:St = f(U*Xt+W*St-1);其中St时t时刻隐藏层的输出,Xt是t时刻的书,U和W分别是两个权重矩阵。
RNN的求导
(https://zhuanlan.zhihu.com/p/101322965)
如果是做序列标注任务(语音识别也算),在每个time step,我们每一个时刻都会预测一个结果,并得到一个损失值,那么最终的损失将会是所有时刻损失之和。St的计算包括了St-1和W,而St-1的计算又包括了St-2和W,所以计算St对W的导数时,需要一层一层的向下求导。
l LSTM
(https://zhuanlan.zhihu.com/p/50915723)
LSTM是一种特殊的RNN,主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。简单来说,就是相比普通的RNN,LSTM能够在更长的序列中有更好的表现。
LSTM内部主要有三个阶段,对应三个门:
- 忘记阶段。这个阶段主要是对上一个节点传进来的信息进行选择性忘记。简单来说就是会 “忘记不重要的,记住重要的”。具体来说是通过计算得到的Zf(f表示forget)来作为忘记门控,来控制上一个状态的Ct-1 哪些需要留哪些需要忘。
- 选择记忆阶段。将这个阶段的输入有选择性地进行“记忆”。主要是会对输入Xt 进行选择记忆。哪些重要则着重记录下来,哪些不重要,则少记一些。当前的输入内容由前面计算得到的z 表示。而选择的门控信号则是由zi (i代表information)来进行控制。
将上面两步得到的结果相加,即可得到传输给下一个状态的 Ct 。也就是上图中的第一个公式。
- 输出阶段。这个阶段将决定哪些将会被当成当前状态的输出。主要是通过Zo 来进行控制的。并且还对上一阶段得到的Co 进行了放缩(通过一个tanh激活函数进行变化)。
与普通RNN类似,输出 yt 往往最终也是通过 ht 变化得到。
l RNN梯度消失和LSTM怎么缓解该问题的
“LSTM 能解决梯度消失/梯度爆炸”是对 LSTM 的经典误解。这里我先给出几个粗线条的结论,详细的回答以后有时间了再扩展:
1、首先需要明确的是,RNN 中的梯度消失/梯度爆炸和普通的 MLP 或者深层 CNN 中梯度消失/梯度爆炸的含义不一样。MLP/CNN 中不同的层有不同的参数,各是各的梯度;而 RNN 中同样的权重在各个时间步共享,最终的梯度 g = 各个时间步的梯度 g_t 的和。
2、由 1 中所述的原因,RNN 中总的梯度是不会消失的。即便梯度越传越弱,那也只是远距离的梯度消失,由于近距离的梯度不会消失,所有梯度之和便不会消失。RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。
3、LSTM 中梯度的传播有很多条路径, 这条路径上只有逐元素相乘和相加的操作,梯度流最稳定;但是其他路径(例如 )上梯度流与普通 RNN 类似,照样会发生相同的权重矩阵反复连乘。
4、LSTM 刚提出时没有遗忘门,或者说相当于 ft=1 ,这时候在 直接相连的短路路径上, ,从而这条路径上的梯度畅通无阻,不会消失。类似于 ResNet 中的残差连接。
5、但是在其他路径上,LSTM 的梯度流和普通 RNN 没有太大区别,依然会爆炸或者消失。由于总的远距离梯度 = 各条路径的远距离梯度之和,即便其他远距离路径梯度消失了,只要保证有一条远距离路径(就是上面说的那条高速公路)梯度不消失,总的远距离梯度就不会消失(正常梯度 + 消失梯度 = 正常梯度)。因此 LSTM 通过改善一条路径上的梯度问题拯救了总体的远距离梯度。
6、同样,因为总的远距离梯度 = 各条路径的远距离梯度之和,高速公路上梯度流比较稳定,但其他路径上梯度有可能爆炸,此时总的远距离梯度 = 正常梯度 + 爆炸梯度 = 爆炸梯度,因此 LSTM 仍然有可能发生梯度爆炸。不过,由于 LSTM 的其他路径非常崎岖,和普通 RNN 相比多经过了很多次激活函数(导数都小于 1),因此 LSTM 发生梯度爆炸的频率要低得多。实践中梯度爆炸一般通过梯度裁剪来解决。
7、对于现在常用的带遗忘门的 LSTM 来说,6 中的分析依然成立,而 5 分为两种情况:其一是遗忘门接近 1(例如模型初始化时会把 forget bias 设置成较大的正数,让遗忘门饱和),这时候远距离梯度不消失;其二是遗忘门接近 0,但这时模型是故意阻断梯度流的,这不是 bug 而是 feature(例如情感分析任务中有一条样本 “A,但是 B”,模型读到“但是”后选择把遗忘门设置成 0,遗忘掉内容 A,这是合理的)。当然,常常也存在 f 介于 [0, 1] 之间的情况,在这种情况下只能说 LSTM 改善(而非解决)了梯度消失的状况。
8、最后,别总是抓着梯度不放。梯度只是从反向的、优化的角度来看的,多从正面的、建模的角度想想 LSTM 有效性的原因。
l Encoder-Decoder框架
虽然LSTM确实能够解决序列的长期依赖问题,但是对于很长的序列(长度超过30),LSTM效果也难以让人满意,这时我们需要探索一种更有效的方法,即注意力机制(attention mechanism)。在介绍注意力机制前,我们先了解一种常用的框架:Encoder-Decoder框架。
在上文的讨论中,我们均考虑的是输入输出序列等长的问题,然而在实际中却大量存在输入输出序列长度不等的情况,如机器翻译、语音识别、问答系统等。这时我们便需要设计一种映射可变长序列至另一个可变长序列的RNN网络结构,Encoder-Decoder框架呼之欲出。
Encoder-Decoder框架是机器翻译(Machine Translation)模型的产物,其于2014年Cho et al.在Seq2Seq循环神经网络中首次提出。在统计翻译模型中,模型的训练步骤可以分为预处理、词对齐、短语对齐、抽取短语特征、训练语言模型、学习特征权重等诸多步骤。而Seq2Seq模型的基本思想非常简单一一使用一个循环神经网络读取输入句子,将整个句子的信息压缩到一个固定维度(注意是固定维度,下文的注意力集中机制将在此做文章)的编码中;再使用另一个循环神经网络读取这个编码,将其“解压”为目标语言的一个句子。这两个循环神经网络分别称为编码器(Encoder)和解码器(Decoder),这就是 encoder-decoder框架的由来。
l 注意力机制
注意力机制的主要亮点在于对于seq2seq模型中编码器将整个句子压缩为一个固定长度的向量 C ,而当句子较长时其很难保存足够的语义信息,而Attention允许解码器根据当前不同的翻译内容,查阅输入句子的部分不同的单词或片段,以提高每个词或者片段的翻译精确度。具体做法为解码器在每一步的解码过程中,将查询编码器的隐藏状态。对于整个输入序列计算每一位置(每一片段)与当前翻译内容的相关程度,即权重。再根据这个权重对各输入位置的隐藏状态进行加权平均得到“context”向量(Encoder-Decoder框架向量 C ),该结果包含了与当前翻译内容最相关的原文信息 。同时在解码下一个单词时,将context作为额外信息输入至RNN中,这样网络可以时刻读取原文中最相关的信息,而不必完全依赖于上一时刻的隐藏状态。对比seq2seq,Attention本质上是通过加权平均,计算可变的上下文向量 C 。
l Transformer
(https://www.zhihu.com/question/337886108/answer/893002189)
l Conformer
基于Transformer模型(善于捕捉长序列依赖)和CNN(局部信息,边缘以及形状)的优势,以及它们的缺陷Transformer(提取细粒度的局部特征图案的能力较弱)CNN(局部连接的限制是需要更多地层或者参数量才能去捕捉全局信息)。
Transformer在提取长序列依赖的时候更有效,而卷积则是擅长提取局部特征。Conformer将两者结合起来。
Ø CTC
传统的语音识别的声学模型训练,对于每一帧的数据,需要知道对应的label才能进行有效的训练,在训练数据之前需要做语音对齐的预处理。而语音对齐的过程本身就需要进行反复多次的迭代,来确保对齐更准确,这本身就是一个比较耗时的工作。与传统的声学模型训练相比,采用CTC作为损失函数的声学模型训练,是一种完全端到端的声学模型训练,不需要预先对数据做对齐,只需要一个输入序列和一个输出序列即可以训练。这样就不需要对数据对齐和一一标注,并且CTC直接输出序列预测的概率,不需要外部的后处理。既然CTC的方法是关心一个输入序列到一个输出序列的结果,那么它只会关心预测输出的序列是否和真实的序列是否接近(相同),而不会关心预测输出序列中每个结果在时间点上是否和输入的序列正好对齐。CTC引入了blank(该帧没有预测值),每个预测的分类对应的一整段语音中的一个spike(尖峰),其他不是尖峰的位置认为是blank。对于一段语音,CTC最后的输出是spike(尖峰)的序列,并不关心每一个音素持续了多长时间。
l 个人理解
- CTC是一种损失函数
我想说的第一个就是CTC是一种损失函数,它对网络结构的改变就是在最后输出层多加了一类,即多加了一个节点,其他部分与声学模型的结构可以完全一样。一句话表明CTC作为损失函数所优化的目标就是使所有可能映射到正确label的序列概率之和最大。怎么理解呢?语音识别本质上是一个序列标注的问题,但是其最大的难点在于输入的长度T是远大于输出的长度U的。如果T=U,那么问题就很简单了,每帧输出都有label,直接对每帧使用交叉熵损失函数(CE)来进行优化,然后对所有帧求和,比如词性标注任务。那么T>U怎么办呢?这里很自然的一个想法就是把U变长了,我理解传统方法和CTC就是两种不同的变长U的方法。(基于注意力机制的端到端模型是把T变短,思路不一样)
第一种传统语音识别中的办法是将标签中的字重复,因为很显然一个字的持续时间不止一帧,所以我们可以把好几帧的label都标注为同一个字,最终让U和T一样长。但是按照上述以字作为建模单元的方法效果会非常差,因为一帧的语音包含的信息非常有限,我们希望每一帧的label应该是能反映该帧声学信息的单元。于是我们需要把每个标签按照发音学特性划分成颗粒度更小的单元,比如中文标签是字,那就继续划分为音节,音素,状态。实验发现划分到状态一级是比较理想的。经过一系列操作之后,输入仍然会比输出长,要想使T=U,另外一个问题就是无法确定每个状态对应的是哪几帧。于是传统语音识别方法中有一个强制对齐的过程得到每个状态的位置信息,即每一帧对应的状态级标签是什么,最后使得T=U。之后便可以训练一个帧级别的分类器(GMM或者DNN)得到每帧的类别后验概率分布了,也就是传统方法中的声学模型部分。
第二种办法就是CTC的方法,除了可以将标签中的字进行重复以外,CTC方法在长度为U的序列中间插入一些符号使得输入输出一样长。这个符号就是空白(blank)类别。加入空白类别的好处是我们可以不需要对字的建模单元继续切分了,因为在对当前帧无法判断或者判断不准确的时候,我们可以输出一个空白标签,由于网络结构一般使用RNN等递归神经网络,每一帧都会保留前面帧的历史信息,当到达某个字的结束帧时,网络完全能够获得这个字的所有声学信息,因此可以给出比较准确的label。所以CTC方法可以使用更大的建模单元。但是加入空白标签和重复标签的序列是有很多种的,每一个T=U的序列作为label都可以和网络的输出概率分布计算一个交叉熵的损失函数,所以CTC的损失函数就是将所有这些可以使T=U的序列作为label之后计算的损失函数进行求和。由于这样的序列有很多,使用常规计算方法时间复杂度很高,所以使用动态规划即很多回答中提到的前向后向算法计算这个损失函数。在解码时,直接把空白和重复去掉就能得到识别的文本序列了。
- 端到端具体指什么(CTC算不算端到端?)
首先我想说我认为CTC并不能算端到端的方法(个人看法),但是它具有很多端到端模型的特性,并且向大家展现了端到端方法的可行性,为端到端方法提供了很好的指导。为什么CTC不算端到端模型呢?我觉得有以下几个原因:
依赖语言模型
首先我们回顾一下端到端语音识别方法的定义,它是指能够使用一个单一的神经网络直接将输入的语音特征(如果更严格的话是wav)转化为输出文本的网络,它能够将声学模型,语言模型和发音模型融合在一起,简化传统语音识别中的复杂过程。尽管如此,端到端模型还是会依赖语言模型的,因为通常带有文本的语音数据的数据量往往是远小于纯文本的数据量的,尽管端到端模型能够学习到语言模型特性,但是由于数据量的原因,使用一个更大的文本数据集训练的更好的语言模型会对端到端模型有所提升。而CTC方法对于语言模型的依赖并不单单是数据量的原因,CTC中每帧的输出是相互独立的,这是CTC方法本身的一个缺陷。换句话说,CTC外加语言模型的收益会远大于端到端模型(RNN-T,LAS)。它对语言模型的依赖与端到端模型对语言模型的依赖不一样,依赖的程度也不同。
有时需要词典
端到端模型方法中不需要发音词典是因为可以使用更大的建模单元,比如汉字,网络能够直接输出我们所需要的单元,因此不需要发音词典。CTC方法当然也可以使用更大的建模单元,但是对于中文的语音识别,会发现基于音素的建模单元会或得更好的效果,因此在这种情况下,CTC方法需要同时使用发音词典和语言模型,解码则需要基于WFST构建解码网络,与传统的语音识别方法类似,并没有达到端到端方法的效果。
l CTC损失
(https://xiaodu.io/ctc-explained/)
建立从预测结果(编码,有blank,重复的词)到句子(解码后)的多对一关系。即有多个路径能得到同一个句子。在每一时刻输出是条件独立的情况下,预测为某一句子的概率就等于多个解码后为该句子的预测结果概率之和。CTC使用前向和后向算法,通过动态规划计算每条路径的概率。再反向传播计算梯度时,也能利用前向概率和后向概率计算梯度,进而减少计算成本。
l CTC的解码方式
(https://blog.csdn.net/weixin_42615068/article/details/93767781)
Ø 解码
l 贪心搜索 (greedy search)
Greedy search 是在每一步选择概率最大的输出值,这样就可以得到最终解码的输出序列。然而,CTC网络的输出序列只对应了搜索空间的一条路径,一个最终标签可对应搜索空间的N条路径,所以概率最大的路径并不等于最终标签的概率最大,即不是最优解,这是因为最终标签的概率是所有可达路径的概率和。
l 束搜索(Beam Search)
贪心搜索的性能非常受限, 这种方法忽略了一个输出可能对应多个对齐结果。很多时候,如果我们能拿到nearbest的路径,后续可以利用其他信息来进一步优化搜索的结果。束搜索能近似找出 top 最优的若干条路径。
基本原理是通过t i − 1 中beamsize 个序列,每个序列分别连接t i中所有节点(字典大小),得到 beamsize个新序列及对应的score,然后按照score从大到小的顺序选出前beamSize个序列,依次推进。
l Prefix Search Decoding
CTC Prefix Search Decoding本质是贪心算法,每一次搜索都会选取“前缀概率”最大的节点扩展,直到找到最大概率的目标label,它的核心是利用动态规划算法计算“前缀概率”。下面先通过一个简单的例子来介绍CTC Prefix Search Decoding的大致过程。
如上图例子,CTC Prefix Search搜索过程:
- 初始化最佳序列l*为空集,最佳序列的概率p(l*)。把根节点放入到扩展集合中,初始化它的前缀概率为1.0,初始化p(l*)=0。
- 从扩展集合中选取前缀概率最大的节点扩展,扩展子节点a和b,计算a和b的前缀概率(上图中第一层节点a和b的前缀概率分别为0.7和0.2),如果前缀概率大于p(l*)则将其加入到扩展集合。同时,计算结束节点的概率(上图中第一层节点$的概率为0.1),如果结束节点的概率大于p(l*),则将其对应的label设置为最佳序列l*,同时更新p(l*)。
- 继续搜索,重复步骤2,直到扩展集合为空,即搜索结束,输出最终解码的l*和概率p(l*)。(上图中最终,l*=ab, p(l*)=0.3)
l 前缀束搜索(Prefix Beam Search)
基本的思想是将记录prefix的时候不在记录raw sequence(模型在每个时间点输出的字符组成的字符串),而是记录去掉blank和duplicate的sequence。前缀束搜索(Prefix Beam Search)方法,可以在搜索过程中不断的合并相同的前缀。
prefix beam search基本思想:
- 在每个时刻t,计算所有当前可能输出的规整字符串(即去除连续重复和blank的字符串)的概率。
- 因为t时刻的字符串长度最长为t,所有候选的当前所有可能的解码字符串个数是C^t,其中C是字符词典大小。 该值随着t的增大而增大,
- 用beam search的方法,在每个时刻选取最好的N个路径,从而将每个时间点t上的搜索空间变为为常数。
针对超多类别的分类问题
Ø 人脸识别的loss
L-softmax;A-softmax;AM-softmax
A-Softmax与L-Softmax的最大区别在于A-Softmax的权重归一化了,而L-Softmax则没的。A-Softmax权重的归一化导致特征上的点映射到单位超球面上,而L-Softmax则不没有这个限制,这个特性使得两者在几何的解释上是不一样的。如图10所示,如果在训练时两个类别的特征输入在同一个区域时,如下图10所示。A-Softmax只能从角度上分度这两个类别,也就是说它仅从方向上区分类,分类的结果如图11所示;而L-Softmax,不仅可以从角度上区别两个类,还能从权重的模(长度)上区别这两个类,分类的结果如图12所示。在数据集合大小固定的条件下,L-Softmax能有两个方法分类,训练可能没有使得它在角度与长度方向都分离,导致它的精确可能不如A-Softmax。
- 点赞
- 收藏
- 关注作者
评论(0)