语音识别食用指南
- 循环神经网络 RNN
- 单层RNN
- 双向RNN
- 多层RNN
- 前向传播
- 反向传播
- LSTM 记忆增强
- GRU 记忆增强
- 意念打字
- 自然语言处理
- 词嵌入
- 类比推理
- 矩阵表
- Word2Vec
- 负采样
- GLOVE模型
- 情感分类
- 序列模型和注意力机制
- 基础模型
- 维特比算法
- 集束搜索
循环神经网络 RNN
之前描述的全连接神经网络(FCN)和卷积神经网络(CNN),其目标数据的样本是不分先后的。
比如下面这句话:
- 早上好天气,适合(去打球\睡懒觉)。
既然“天气不错”,那么做出 “去打球” 这个决策的可能性应该高于“睡懒觉”。
为了获取这一类序列特征,在此基础上,具备记忆机制的循环网络模型,逐渐演进到现在,成为更有效的循环神经网络模型(RNN)。
RNN 是一种特别适合识别带有时间先后顺序的数据的算法模型(比如像是文本、语音信息,还有一些具有前后因果关系的图像数据)。
我们平常用的很多中英文翻译软件(如微信的语音转文字),就使用到了这个算法。
单层RNN
早上好天气,适合(去打球\睡懒觉)。
输入到 RNN 中,会被切分成依次排成的序列:
P.S. 如果有做分词的,那
- 输出:
上图中,
:第 t 层元素的激活值(a)
:第 t 个元素
:x 作为输入来计算 h 时的参数
:上一步 h 作为输入来计算 h 时的参数
:上一步 h 作为输入来计算 y 时的参数
:一般初始化为零向量,是人为添加的
- ···
第 1 次处理过程输入是 ,得到激活值
。
第 2 次处理过程输入是 ,得到激活值
。
也就是说,网络再对第 2 个字()预测的时候,会参考第 1 个字(
)的信息。
这样一次处理过程,也叫一个时间步,每次输入不同,但后一个时间步的输入是前一个时间步的输出。
双向RNN
如果遇到,下文反过来推理上文的场景就需要双向 RNN,例如:
- 早上难得好天气,适合(去打球/睡懒觉),可别叫醒我。
综合上下文,既然有 “别叫醒我” 跟在后面,显然 “睡懒觉” 的可能性大幅提高。
在这一类需要结合上下文,在正序、倒序两个方向上做推理的场景中,可以通过双向 RNN 结构,加入逆向推理机制。
:表示正向
:表示反向
同正方向序列上的权值参数一样,逆序列也有权参 ,相应的参数量和隐藏节点个数扩展到单向模型的两倍。
俩者的区别只在于,计算激活值的方向不同, 是从
到
,
是从
到
。
- ···
双向 RNN 就不能实现实时检测了,需待整个序列输入完毕后才能预测。
多层RNN
如果遇到更复杂的序列信息,还有什么更进一步的措施来捕捉更丰富的数据特征呢?
CNN 卷积神经网络模型,是靠增加卷积核个数或者增加 Conv-Pool 单元的数量,用更深的卷积过滤器,以及层级更多的网络处理层来捕捉更丰富的特征。
与之对应,RNN 的多层模型,可以通过组合叠加更多的隐藏层来抽取深层的序列特征。
下图是,一个三层四个时间步的 RNN:
- ···
- ···
前向传播
前向传播:根据一个输入 x 和一个输入 h(激活值)会生成一个激活值 h 和预测值 y。
第 l 层输出:
最后一层输出:
以上就是前向传播所有表达式。
将 合二为一,拼接成一个新的
如 ,可以改成
。
反向传播
每一个时间步都有自己的损失,整个序列的损失就是将每一个时间步的损失累计起来。
反向传播具体图示:在原图上所有箭头,反向,就是反向传播。
LSTM 记忆增强
RNN 记性不好,无法记住间隔远的时间步,这就导致无法根据上下文信息来生成语法正确的句子。
记性不好的原因,在于梯度消失(很深的神经网络就容易造成梯度爆炸、消失)。
随着 RNN 的时间步越来越多,就会产生梯度爆炸、消失。
- 梯度爆炸:用梯度修剪即可
- 梯度消失:需要通过 LSTM、GRU 来增强记忆力
LSTM:Long Short Term Memory,长短期记忆。
因为一层比一层的激活值大,那层数深了,积累到一定程度就会发生梯度爆炸。
而如果我们能让前面的激活值直接传递到后面去,就可以避免梯度消失。
LSTM 的方法:在计算激活值时,会引入一些开关,让这些开关来控制当前激活值是否需要被传递到后面去。
RNN 传递的状态只有 ,LSTM 在此基础上,引入了
。
在每个时间步上,先求 :
再把
- 输入门
- 遗忘门
- 输出门
- 准单元
最后,计算单元状态和隐状态:
- 单元状态:
- 隐状态:
整个控制过程,是一系列公式:
逐个解释:
:RNN 计算激活值公式,输入是前一时间步的激活值
、本时间步的输入
,与参数
但为了提高 RNN 的记性,不能直接做成激活值,还需要经过三个公式的判断,或成为激活值,或忽略。
这三个变量,又称为:
- 更新门
- 记忆门
- 输出门
那 LSTM 是如何控制激活值是否被传递到下一时间步到呢?俩个公式:
某些情况下 代表前一时间步的激活值(
)。
如果此时更新门为 ,记忆门、输出门为1,那
,等同
。
本时间步激活值 = 前一时间步激活值,相当于,前面时间步激活值传递到了下一时间步,且激活值没有增大,就避免了梯度消失。
神经网络通过控制
- 更新门全部打开,记忆门全部关闭,那本时间步就不会与前一时间步的激活值有任何瓜葛
- 更新门、记忆门都各自打开一半,本时间步激活值就是本时间步激活值的一半 + 前一时间步激活值的一半。
LSTM 单个时间步的模型结构:
P.S. 加号是梯度直接传递,乘号是梯度与叉路因子相乘。
上图是单个时间步的结构图,多个时间步排列在一起就构成了 RNN。
通过这样的机制,就可以把前面的激活值传递到后面。
比如,小明今天他去打球了,小明和小黑今天他们去打球了。
因为 RNN 记性不好,那他就忘了今天谁去打球了,一个人用他,俩个人用他们,那到底是他还是他们?通过逻辑门的控制,就可以把小明、小明和小黑的激活值直接传送到后面,就知道用他、他们了。
GRU 记忆增强
GRU 其实是 LSTM 的简化版,计算量会少些,但灵活性没 LSTM 好。
LSTM 公式:
GRU 公式:
对比俩者,发现控制逻辑门的公式少了一个,逻辑门的数量也从 3 个变成了 2 个,GRU 传递的是 ,而不是
,但最后的公式指明了俩者相等。
意念打字
脑控打字至少要分成3步:1.采集并翻译脑电信号;2.转换成指令;3.打字。
RNN 解决的是第 2 步。
在训练的时候,研究人员会请受试者大声阅读大量的句子,比如说读一些小孩子的故事书,像《睡美人》《爱丽丝梦游仙境》这些经典小人书,而后他们把脑电信号和语言的转化工作分成两步。
- 第一步,解锁发声动作。一个人说话的时候,TA的下巴、喉咙、嘴唇和舌头的运动和发出的声音有很清晰的对应关系。比如张大口,喉咙发出声音,可能就在说 “啊”。
- 第二步,解码脑信号与发生动作的关系。同样是运用人工智能的算法,通过发声动作这个中介,大脑的想法就探测出来了,发现了哪一组信号对应哪个器官运动的组合。
当数据有复杂时间结构的时候,RNN 绝对是个利器。
放在这里,RNN 可以解码声道咬合关节运动,把这些运动转化成句子。
自然语言处理
词嵌入
让神经网络理解词之间的关系,如男人和女人、苹果和橘子、汽车和卡车。
我们会用到一种叫词嵌入,会赋予词汇属性,如性别、高贵、年龄、食物。
- man (男人)性别特性为 -1
- woman(女人)性别特性为 1
- king(皇帝)性别特性为 -0.95(女皇帝很少如武则天)
- Queen(皇后)性别特性为 0.97(有变性)
- apple(苹果)性别特性为 0(中性)
通过这些特性,我们就可以发现关系紧密的词。
词嵌入让神经网络举一反三。如:
- Robert Lin is an apple farmer.(Robert Lin 是一个种苹果的农民)
- Sally Johnson is a durian cultivator.(Sally Johnson是一个榴莲培育家)
但是神经网络词汇表里没有 Sally Johnson 这个词,神经网络不知道是人名,还是公司名。
但通过前一句 Robert Lin 是一个种苹果的农民,神经网络就可以根据苹果和榴莲都是水果的关系,推断出 Sally Johnson 是一个人名。
具体来说,迁移学习 + 词嵌入技术。
我们在网上下载训练完成的词嵌入模型,再通过迁移学习应用到自己的项目中。
类比推理
创造类比本身就是难度很高的活动,一般人做不来:
- 首先要有足够的知识、信息储备,才能在理解新事物的时候找到真正合适的、最恰当的那个 “参照物”;
- 之所以能找到最恰当的,不仅仅是找到最“像”的那个,还要仔细搞清楚“不像”的地方究竟有哪些,以免在传递信息的时候出现偏差……
于是,创造一个 “精妙的类比” 是很非常复杂的过程,所花费的精力不知要比听者理解所需要的精力多不知道多少倍…… 极为恰当的类比,常常只不过是 “妙手偶得之”,而想要再找一个 “旗鼓相当” 的就几乎不可能了。
要说信息储备,神经网络在这方面独一无二,那我们用词嵌入实现类推:
- 男人 -> 女人
- 皇帝 -> ?
词嵌入模型眼中词与词之间的关系变得更深了,以至于你可以做一些逻辑上的加减法运算。比如说
- 巴黎 - 法国 + 意大利 = ?
你猜等于什么?罗马。
巴黎跟法国的最常见关系是法国的首都,巴黎 - 法国,大约就是去除法国元素但是保留首都元素;再加上意大利,那就是首都加意大利,也就是意大利首都罗马。
类似的例子还有
- 科学家 - 爱因斯坦 + 毕加索 = 画家
- Windows - 微软 + Google = 安卓
如果我们用:
- 男人 - 女人 =
- 皇帝 - 皇后 =
发现俩个向量式子的结果一样,这也就是类比的逻辑所在。
第 1 个元素代表性别,也就是说,男人和女人、皇帝和皇后最大的区别在于性别。
所以,就产生了:
- 男人 - 女人 = 皇帝 - 皇后
矩阵表
在没有词嵌入前,我们会整理一份词典,所有词都有一个索引位置。
如 man 是 5391,woman 是 9853,king 是 4914。
这种一对一的对应的词汇关系,存储和访问是最优的,但无法向词嵌入那样表示词语之间的相似性。
神经网络已经知道:
I want a glass of orange juice.
之后再给神经网络一个句子:
I want a glass of apple __.
咱肯定想到也可以填 juice 啊,因为你知道 orange 与 apple 都是水果,TA们在某种意义上有相似性。
但如果我们使用的是 one-hot 形式(以上形式)对词进行编码的话,我们完全无法根据词向量来计算词与词之间的相似性,而两个 one-hot 词向量的内积永远也等于 0。
词嵌入就在此基础上,引入了特性来描述不同词汇之间的相似性。
立场对人工智能来说,只是一组多维度的坐标值(向量)而已。
简单来说就是把个体所有的属性全部列出来打分,好比吃甜豆腐脑 = 10,吃咸豆腐脑 = -10,民主党 = 10,共和党 = -10 等等,而且这个立场向量不需要手工录入,只要把您所有的言行,写过的文章给机器学习一下,就能得出比咱们自己的感知更细致的立场。
这个过程就是建立一个矩阵表。
词嵌入是这么建立矩阵表的:
:one-hot 向量编号,每个单词对应一个 one-hot 向量,如 I 对应 9665。
- E 是嵌入矩阵表,可以通过构建自然语言模型,运用梯度下降算法得到。最开始矩阵表的特性都是随机值,这些特性会被神经网络不断训练。
- e 是词嵌入向量,每一个 one-hot 向量与词嵌入矩阵表运算后,得到对应的词嵌入向量。
这些词嵌入向量会传递给后面的神经网络,最终通过 softmax 来预测结果。
如果预测错了,损失就大,神经网络就会调整参数 w、b 以及矩阵表 E 来使损失越来越小。
Word2Vec
Word2Vec:把单词转换为词嵌入向量的模型。
转换的基本原理,就是上面的。
具体实现 Word2Vec 有俩种方法:
- CBOW
- Skip gram
假设算例是:I want a glass of orange juice.
如果我们要预测 juice,那需要将前 4 个单词 a glass of orange 输入到模型中训练。
- 这 4 个单词称为上下文词的 context words
Skip gram 独特一些,是从句子中随机选取一个单词作为 context words,如 orange。
再从 orange 附近随机选,假设选了 juice。
- 输入x:orange(context)
- 标签y:juice(target)
附近的选取范围是自定义的,如设置为 4,就是在 orange 前后 4 个单词内随机选取。
context-target 不仅仅会有 orange-juice,还有很多词对,orange-glass 等等。
- context word:句子中选一个词
- target word:词周围选一个词
有了大量的词对后,就用之前的方法训练词嵌入矩阵。
我们将 content word 对应的 one-hot 向量 作为输入 x,这个向量会与词嵌入矩阵表 E 进行矩阵相乘,而后得到相应的词嵌入向量
。
最终 softmax 会给出预测值,如果这个预测值与 target word 不一致,模型就会更新矩阵表数值。
不断学习下,最终词嵌入矩阵表每个单词都有合理的特性。
负采样
输入一个 context word,softmax 来预测词库中每个单词是 target word 的概率。
softmax 计算量很大,我们可以用 sigmoid 代替 softmax。
构建出一个词对后,用 sigmoid 判断这对词是否有关联,如 orange-juice。
如果存在关联,标签
标签为 1 的词对称为正采样,为 0 称为负采样。
如何选取负样本?
- 一般小数据集,选择 5-20 个负采样。
- 一般大数据集,选择 2-5 个负采样。
GLOVE模型
GLOVE 也是训练词嵌入向量的模型,词与词之间共现的统计数据可作为训练词嵌入向量的重要依据。
- 先用语料库统计词的共现矩阵
- 再基于共现矩阵和模型学习词向量
共现矩阵是啥呢?
设共现矩阵为 X,其元素为 。
:在语料库中,单词
和 单词
共同出现在一个窗口中的次数。
情感分类
情感分类任务就是看一段文本,然后分辨这个人是否喜欢他们在讨论的这个东西,这是NLP中最重要的模块之一,经常用在许多应用中。
情感分类一个最大的挑战就是可能标记的训练集没有那么多,但是有了词嵌入,即使只有中等大小的标记的训练集,你也能构建一个不错的情感分类器,让我们看看是怎么做到的。
输入x是一段文本,而输出y是你要预测的相应情感。比如餐馆评价:
- “The dessert is excellent.”(甜点很棒),并给出了四星的评价;
- “Service was quite slow”(服务太慢),两星评价;
- “Good for a quick meal but nothing special”(适合吃快餐但没什么亮点),三星评价;
- “Completely lacking in good taste, good service and good ambiance.”(完全没有好的味道,好的服务,好的氛围),给出一星评价。
如果你能训练一个从x到y的映射,基于这样的标记的数据集,那么你就可以用来搜集大家对你运营的餐馆的评价。
咱们来看看是怎么实现情感分类的:
每个单词的 one-hot 向量与词嵌入矩阵相乘后,得到词嵌入向量。
对所有词嵌入向量求平均值,再输入到 softmax 中,softmax 会给出 5 份概率(1-5星)。
但平均值有个缺点,忽略句子中词汇的顺序。
如 Completely lacking in good taste, good service and good ambiance.(完全没有好的味道,好的服务,好的氛围),给出一星评价。
虽然也有 lacking(缺乏),但句子中出现多个 good,所以整个句子的平均值就会偏好评。
为了解决这个问题,我们会配合对能识别顺序的 RNN 一起。
将句子中的每个词一步步的输入到 RNN 中去,这个模型就学会在 good 前面还有一个 lacking。
序列模型和注意力机制
基础模型
从最基础的模型 seq2seq 开始,seq2seq 是 RNN 后面再接一个 RNN。
比如,我们把法文翻译成英文:
- 法文:Jane visite I’Afrique en septembre.
- 英文:Jane is visiting Africa in September.
我们用 seq2seq 模型实现翻译。
- Encoder编码网络:第一个 RNN,将法文一步步输入到 RNN 中
- Decoder解码网络:第二个 RNN,将英文一步步输出来
seq2seq 不仅可以文字翻译,而且还可以图片翻译,给图片自动添加描述。
将网络中最后的 softmax 单元去掉后,就是一个编码网络。
最后的 4096 维的向量就代表了猫的图片。
将这个向量再输入到后面的 RNN 中,后面的 RNN 会一步步的输出文字描述这张图,如 A cat sitting on a chair.
维特比算法
今天通信中使用最广泛的算法维特比算法。
维特比算法从本质上讲,是把一种指数复杂度的问题,变成了线性的复杂度。
这可能是所有的计算机算法中复杂度下降最大的改进。
今天,除了语音识别、机器翻译、拼音转汉字、汉语的分词等用到了维特比算法,各种语言的拼写纠错,基因的测序,通信的解码都要用到ta。
语音识别基本上分为三步:
- 第一步是对声波进行信号处理,得到一些语音特征;
- 第二步是通过语音特征,识别出相应的音节;
- 第三步是根据音节,合成出完整的语句。
我们就写从第二步到第三步的过程。
为了简化问题,我们假定第一步和第二步都是完美的,也就是说识别出的音节是 100% 正确的。
当然在现实的各种识别系统中,能做到 70% 的单个音节识别率就非常好了。
事实上我们人的识别率也就是这个水平,我们人之所以能够听清楚完整的句子,完全靠的是对上下文的理解,而非音节识别得准。
在汉语中,即使知道了读音,也就是我们小学学的拼音,要找到准确的对应的汉字还有一些困难,因为汉语中平均一个读音对应十几个汉字,即使你的四声读音非常准,耳朵也听得非常准。
一音多字这个问题,在语音识别上会产生指数爆炸的灾难性后果。比如一个音对应6个字,从理论上讲,两个音的组合就可能对应36个字的组合,一个长度只有十个字的短句子,10个拼音所对应的全部汉字串,能组合出6的十次方,也就是6000万种可能性。如果句子更长,组合数还会快速增长。
当然,可能有人会说考虑到一些字会组成常用的词,那么情况并不会这么糟糕。这种考虑在语音识别中的确是需要的,通过这种精简,可以大大减少排列组合的数量。但是,即使每一两个拼音,只对应少数几个单字词或者双字词,当句子稍微长一点,组合数量还是大得惊人。我们假定在考虑了组词之后每个拼音平均对应两个字,10个音节(拼音)可能的组合只有一千多种,比6000万种好了很多,但是,如果一句话的长度到了20个音节,可能的组合数量又达到了百万种。语音识别其实是一个任务,就是需要在这上百万的候选中找到最合理、最可能的语句。
上述问题在通信中也会遇到,是一个标准的解码问题,那么怎么解决这个问题呢?将一百万种可能性一一评估显然不是一个好办法。对此维特比想到了一个特别妙的解码办法。为了比较清楚地说明问题,我把一句话的拼音和相应的汉字,做一个编号,按照下面的方式放到一张表中。
假如一个句子有 20 个音节,每个音节可能对应6个字,那么我们就用一个长为 20,深度为 6 的网格将每个可能的字填入其中。
由于一个句子的音节按顺序从头到尾构成一个时间序列,因此,为了便于描述,我们用t表示音节的时间次序,将第一个音节标记为时间 t=1,最后一个音节标记为时间 t=20。
接下来,我们把每一个音节可以对应的汉字填入到相应的那一列的六个网格中,并且从 1 到 6 挨个标记。
比如第一个音节对应的字,编号就是从 W11,W12,到 W16,第二个拼音对应的字,编号为 W21,W22,到 W26。
编号中第一个数字是时间 t,后一个数字是某个拼音所对应的六个候选汉字的序号。因此,到了最后一个拼音,即 t=20,相应的字就是 W201,W202,到 W206。
在上面的表格中,填入了第一个音和第六个音的情况。
所谓的解码就是在这样的网格中,找一条从 t=1 到 t=20 的路径,路径中经过的点,就是方格中的一个个字,比如W13,W24,到W201,等等。
上图给出了一条路径。当然,对于一个20个音节的句子,这样的路径有三千六百万亿条。解码的过程就是找到一条最合理的路径的过程,我们把它称为寻找最佳路径。
对于这样路径密密麻麻的网格,维特比注意到这样一个现象:不论有多少条路径,最佳的那一条路径在某个特定时刻,只有六种可能。
比如在 t=6(也就是第六个音节)的时刻,必须经过 W61,W62,W63…W66,这六个字中的一个。
这样一来在第六个音节之前不论有多少条路径,真正有可能是最佳路径的候选路径,只可能有六条候选。
接下来,进入到第七个音节,由于这个音节也对应了 6 个汉字,和前面六条路径组合出 6x6=36 种可能性。
但是,由于在第七个音节处,最佳的路径也只可能有六种(和 t=6 的情况一样)。因此,我们只需要对第七个音节的六个候选,W71,W72,W73…W76,每个候选保留一个最佳路径即可,这样一共也是六条。
以此类推,在每个时刻,我们只需要保留六条路径,将这个步骤一直走到 t=20 最后的时刻。
如果对于上述内容你一时难以理解,也没有关系,只要记住,在任何时刻,只要保留六条路径就好。
集束搜索
Beam Search(集束搜索)是一种启发式图搜索算法,通常用在图的解空间比较大的情况下,为了减少搜索所占用的空间和时间,在每一步深度扩展的时候,剪掉一些质量比较差的结点,保留下一些质量较高的结点。
这样减少了空间消耗,并提高了时间效率,但缺点就是有可能存在潜在的最佳方案被丢弃,因此Beam Search算法是不完全的,一般用于解空间较大的系统中,如机器翻译系统,语音识别系统。
因为这些系统中的数据集可能非常大,而且结果也没有唯一正确的解,系统用最快的方式找到最接近正确的解才是系统的目标。。
Beam Search(集束搜索)使用广度优先策略建立搜索树,在树的每一层,按照启发代价对节点进行排序,仅留下预先确定的个数的节点,仅这些节点在下一层次继续扩展,其他节点就被剪掉了。如果集束宽度无穷大,那该搜索就是宽度优先搜索(BFS)。
在自动翻译中,所谓预先确定的个数的节点:
- 集束搜索会根据解码网络给出的句子中每个词的组合概率来挑选最佳方案。因为一句话的翻译有很多种,所以翻译结果也很多种。
- 为了实现最佳翻译,集束搜索会让每个时间步会选出 N 个概率最大的单词。N 是一个超参数,如果 N 为 3,时间步就会选出概率第一、第二、第三,三个单词。
- N 是一个超参数,
比如,翻译 Someone like you.
- 翻译一:像你一样的人
- 翻译二:有人喜欢你
- 翻译三:某人喜欢你
在翻译第 1 个词 someone 时,选择概率最大的 3 个词:
- 有人
- 某人
在第 2 个词 like 时,选择概率最大的 3 个词:
- 像
- 喜欢
- 一样