本系列是编者学习吴恩达deeplearning.ai 深度学习系列课程的笔记。
编者有一定机器学习基础,也看过Andrew的机器学习课程。由于Andrew的课程非常通俗易懂,笔记中会省略那些帮助理解的例子、图形等,只把最终的观点、解释、结论和解决方案做整理,以供日后查看,另外编者也在某些地方,结合原版论文做出了一些批注和补充,所以称之为精华版。
本篇是Course5,主要讲述递归神经网络RNN的基础和应用,包括GRU,LSTM,双向RNN,深度RNN。应用层面包括利用RNN做简单语言模型和序列生成,利用Word2Vec和GloVe模型做词嵌入和情绪分类;利用encoder-decoder模型和注意力机制做机器翻译。
5. 序列模型
5.1 递归神经网络RNN
5.1.1-5.1.2 序列模型简介
序列模型的输入样本或输出样本是一个序列,可能是一组单词,一个音频曲线,一个视频流等等,就表示第i个样本的第3个元素。
本章以输入一组单词,识别其中名字实体为例介绍RNN。
- one-hot表示:如果序列是一组单词,可以首先构建一个词汇表,比如10000个有序单词,然后序列中每一个单词就对应一个位置的1,其余位置全0.
5.1.3-5.1.5 递归神经网络RNN
常规DNN表现不佳,原因有三:
- 输入输出经常是不等长的,这对设计网络结构来说比较糟糕,用0-padding效果也不好;
- 输入规模太大了:有很多词,每个词one-hot表示;
- 不同位置学到的特征不能共享。
下面两张图表示了RNN的基本结构。每个位置的隐层激励,都用于下一个位置的训练,可初始化为0向量。每个位置层数不多,只有两层,一个隐层一个输出层。
- 隐层激励用tanh偏多,ReLU偏少;
- 输出层激励看分类需求,sigmoid,softmax都可以;
- 本例中输出各个单词是名字的概率。
- 损失函数:单位置损失函数和logistic回归的损失函数一致;整个序列样本的损失函数是各位置累加。
- 反向传播:RNN反向传播又称为通过时间的反向传播,因为求微分时需要从后面位置逐步往前做链式法则。
上面这种典型模型只用于输入输出长度相等,其它序列模型也可以用RNN:
- 一对一:就是一个普通网络,没啥用;
- 一对多:比如音乐生成,后续位置的输入,用前面位置的输出代替;
- 多对一:比如情感判断,只在最后位置输出;
- 多对多(等长):比如命名实体判断;
- 多对多(不等长):比如机器翻译,用前面一部分当编码器,后面一部分当解码器,输出结果。
5.1.6 语言模型
语言模型就是预测一句话出现的概率是多少?
比如语音检测中,用户说一句话,可能有几句话都有可能都类似的发音,就可以通过语言模型,判定每一句话出现的概率是多少,然后选用概率最高的那个。
要训练这样一个模型,训练集就是一个语料库,有各种各样的句子。
- 如下图这样一个句子,要训练模型,第一个输入是0向量,预测第一个词是Cats的概率是多少;
- 然后第二个位置输入是Cats,预测在第一个词是Cats的前提下,第二个词是average的概率是多少;
- 同样,第三个位置输入是average,预测在前两个词是Cats average的前提下,第三个词是fifteen的概率是多少;
- 依次类推,最后一个词可以是句子结束符,中间不认识的词用同一个word代替。
- 最终整个句子的概率就是各个输出相乘的结果。
- 由于每个位置的输出都是词汇表那么长的一个softmax输出,损失函数可以直接用softmax的代价函数。整个序列样本的损失函数就是各位置损失相加。
5.1.7 序列生成
训练好语言模型之后,就可以用它生成一些句子。具体方法是:
- 第一个位置输入0向量,得到一个softmax输出,实际上是一个词汇表各单词的分布;
- 根据这个分布,得到第一个词,然后输入网络,再得到一个分布;依次类推;
- 直到最后得到了句子结束符为止。
5.1.8 RNN梯度消失
由于序列模型一般深度都非常的深,自然存在着梯度消失和梯度爆炸的问题。
- 梯度爆炸还好解决,只要在训练时用程序做下监控,过大时按比例缩放下就好。
- 但是梯度消失就不太好处理了。这意味着序列后面的位置极少收到序列前面位置的影响,条件概率的条件很弱。
5.1.9 GRU(Gated Recurrent Unit)
GRU就是用来解决RNN梯度消失导致的长期依赖较弱的情况的。
其每一层的内部组件如下图所示。设置一个memory cell用来在层间传递,在GRU中,这个meomory cell和普通RNN一样,就是隐层激励值。
- 是一个更新门,可以理解为其只会取0或1(Sigmoid激励函数);如果取0,则之前的激励就不更新,如果取1就做大幅度更新;
- 是正常的一种隐层激励,和普通RNN一样。
- 由于激励是多维的,因此也是一个多维的0-1向量,指示哪些知识应该被记住,哪些应该被更新。
- 事实上还有一个,称为关系门,决定在所占权重的一个门,图中简略掉了。
5.1.10 LSTM(Long short-term memory)
LSTM相比于GRU还要更复杂一些,他有三个门,更新门,遗忘门复杂弄记忆单元;输出门将记忆单元转换为激励单元。
-
LSTM和GRU一样,同样善于捕捉长期依赖,但是GRU门更少,复杂度低,算的更快。
5.1.11 双向RNN
之前的RNN只能根据句子中某个词之前的句子片段来判断当前词是否是一个名字,但有时候句子后面部分才是最重要的,因此同时需要前面部分句子的影响和后面句子的影响,才能准确学习当前词是否是一个名字词。
- 解决方案:在每个前向块后面补一个后向块,整个网络变成了一个有向无环图(DAG),输出预测由前向、后向两部分激励值共同构成。
- 中间的每个激励块都可以是GRU或者LSTM。
-
劣势是只能用完整的句子做训练或预测,只有句子一部分就不行了,在语音检测中会有劣势。
5.1.12 深度RNN
- 在序列的每个位置上再垂直叠1-2层,参数空间翻倍增长;
- 每个基本块(神经元)可以是普通RNN块,也可以是GRU,LSTM,需要接受两个方向的输入,对两个方向进行输出;如果是双向深度RNN,就是4个方向了。
-
在输出之后,可以再做一个较深的预测模型,这个就不属于RNN的范畴了。
5.2 NLP和词嵌入
5.2.1-5.2.8 词嵌入
基本概念
- 之前的one-hot表示不仅占用空间过大,最麻烦的是各个词之间没有任何关系(内积为0)。词关系在语义理解中是十分重要的,比如apple, orange; man, woman; king,queen这样的词对,相互之间是具有强关系的,如果能捕捉到,对于名字识别等语义任务有很大帮助。
- 为了解决这个问题,可以考虑设置一系列特征,比如颜色、是否是水果、性别、地位等等,让每个词得到对应特征的关联因子,因而得到一个向量。这个向量是对应特征空间中的一个点,因此也称为词嵌入。
使用方法
- 由于世界上的单词就那么多,特征也是基本固定的,因此可以先从网上大量的语料库中学习词嵌入,训练集是非常丰富的。当然直接拿到别人训练好的嵌入模型也完全ok。
- 然后把这些模型迁移到我们任务中较小的训练集上,比如只有10k个单词,每个单词可能用300维嵌入向量即可紧密表示。这时就构成了一个嵌入式矩阵E(300*10k),也是模型的学习任务。
- 注意E乘某个词的one-hot向量正好是这个词的嵌入向量。
- 接下来可以用我们的训练集微调这些嵌入向量,当然如果你的训练集很小,不调也没事。
这种词嵌入向量,和CV中对照片用CNN编码很类似,但唯一的区别在于,编码的CNN一旦训练好之后,可以用于编码任何图片;但是词嵌入的模型不会再看见什么新词了,词汇都是有限的。
特性
词嵌入有一个非常重要的特性就是类比推理:
- 举个例子:man,woman; king,queen都只在性别上有差异,如果某个特征是性别的话,那我们同时对成对向量做差,相减之后的向量应该非常相近(可以用cosine similarity)。
- 通过这种性质就可以做词推理:比如给出man-woman的逻辑,给出king,就可以找到queen。
学习词嵌入
这个模型非常简单,把嵌入矩阵E当参数,然后每次把需要推理预测的位置的前后几个单词放进模型,先经过E取嵌入,然后进入网络隐层,最后进入Softmax分类层,表示对当前模型所有输出中每一个的概率。因为训练集知道要预测位置的单词是什么。所以可以计算代价并反向传播。
- 不同的学习任务,可以采用不同的上下文:一般来说,E都作为网络参数,在训练其它学习任务时捎带训练出来的。如果要训一个语言模型(句子前面推后面),上下文可以用前面几个词;要做名字识别,前后几个词也可以。这方面并不固定。
a) Word2Vec (skip-gram模型)
学习词嵌入还有更简单的方式:用一个词当上下文词,然后随机从周边几个词里面抽一个当目标词,用一个网络训练,其任务是用上下文词预测目标词。当然这个任务和网络本身没什么用,但是在训练这个任务的过程中,得到的参数矩阵E(嵌入矩阵)是非常有用的。
这个网络什么结构呢?
- 输入一个词one-hot向量,通过嵌入矩阵E(全是参数),得到嵌入向量e,然后进入softmax层预测目标词。结构极其简单。
- 有一个问题是,如果词汇表太大的话,每次做softmax激励计算复杂性太高。解决方案就是做分层预测,每次只预测目标词所在区间,然后分层处理。【分层softmax】
b) 负采样
刚才我们提到了word2vec最麻烦的事情就是softmax激励计算复杂性太高,分层处理是一种优化方案,但仍然不够好,负采样就是来优化这一问题的。
负采样的基本思路就是用词对预测标签(二分类)。具体思路为:
- 训练集构建:从句子中抽一个词(上下文词),再抽一个周围的词(目标词),记为正样本;再从字典里随机抽K个词(目标词),记为负样本。
- “网络”结构:上下文词经E得到嵌入向量,目标词也经过一定变换,相乘之后进入一个logistic回归分类器。
- 负采样方法:f是词汇的词频,以这种概率分布进行采样。
c) GloVe(global vectors for word representation)
GloVe算法是基于词汇表中两两伴随出现的频率设计的代价函数,运用了整体的统计特性。对于出现频率越高的词对,代价权重就越大。
【编者:这里Andrew没有详述代价函数推导来源,我从知乎https://zhuanlan.zhihu.com/p/42073620上获得了一些更深的理解,仅供参考。】
5.2.9 情绪分类
基于文本进行情绪分类任务的主要挑战在于训练集很难获得,一般也比较少。但是基于嵌入词向量,即使在较少的训练集上,也能获得不错的效果。
一个简单的模型
将所有词的嵌入向量取平均扔进softmax进行训练,能获得不错的效果。但是问题在于没有考虑词序,比如lacking good taste, good service, good env. 由于good重复了很多次,平均的时候主要考虑good了,会因此判断错误。
RNN模型
那如何能考虑语句前后的关键词,以及长短期的语义依赖呢?自然是RNN(包括GRU,LSTM)所擅长的事情。相比于简单模型,它没有把每个词的权重均一化,而是互相考虑影响,存在一定记忆,因而效果更好。
5.2.10 词嵌入除偏
目前从广泛的语料库中学到的嵌入词向量是有偏见的:
- 比如Man-programmer,模型推测woman-会输出homemaker。
虽然说这种偏见是来源于语料库,也就是来源于人类社会,但是我们还是不希望模型中带有这类偏见。
有一种方法可以减轻偏见,这里我们统一以性别偏见为例:
- 定位偏见:将he-she,male-female,带有性别对立的词做差,然后做均值(论文中使用了SVD,处理方法类似),得到一个向量在嵌入空间中作为一个偏见基;
- 无偏斜词中立化:对于那些没有明显性别对立的词比如programmer,doctor等,把它们的嵌入向量向非bias基上进行投影,消除偏斜基上的差异。
- 偏斜词均衡化:对于那些有性别对立的词,比如girl-boy, grandmother-grandfather等词,让他们距离非偏斜轴等距(它们应该分布在非偏斜轴两侧,偏斜的绝对值应该是近似相等的)
5.3 序列到序列模型
5.3.1 基础模型
基本的问题比如机器翻译:法语到英语这样。(本节都以机器翻译作为例子讲解)
基本模型有两种:
- RNN模型:先来一组encoder,再来一组decoder,都是RNN模型(many-to-one, one-to-many)
- CNN-RNN模型:用CNN做encoder,再来一组RNN decoder。
5.3.2-5.3.6 encoder-decoder 编码器解码器模型
把机器翻译看做一个条件语言模型
- 语言模型就是在5.1中提到的概率模型,即按照概率生成一个随机的句子。反过来我们也可以评估生成的句子出现的概率。
- 注意语言模型第一次输入是0向量,但是机器翻译不是,机器翻译在语言模型前面还有一个encoder,用来输出一个激励值,这个激励值作为语言模型的初始输入激励向量。从概率上讲,生成的句子成了条件概率。即在输入句子的条件下,输出句子出现的概率。
- 再次注意,我们要做的是在所有可能的句子里面,找到一个概率最大的,而不是像语言模型中随机生成句子一样使用贪婪算法。
集束搜索(Beam Search)
算法
由于在词典中找一个句子,整体搜索空间太大,因此只能寻找近似方法。beam search就是一个效率和效果之间的trade-off,无论是在性能上还是方法上。
具体来说,假设beam width = 3:
- 在将输入语句做encode之后,第一次进入RNN,softmax分类之后,取概率最大的3个词做备选;
- 以这三个词分别作为输入进入下一层的单元,然后输出三个softmax结果,从这3*10000个pair(假设词汇表有10000个单词)中选择概率最大的3个pair;
- 然后依次类推,每一层最后只保留三个子句。
注意如果beam width=1的话,就变成了贪婪搜索,和5.1的语言模型生成是同一种方法了。beam width经常选用100-1000.
公式修正
- 问题1:概率收缩
- 描述:由于子句出现的概率是不断相乘的,每次乘的都是1个小于1的数,所以越乘越小,存储精度越低;
- 解决方案:对乘积概率取log,然后对每一层的概率都对数相加。
- 问题2:短句子偏好
- 同样,由于概率越乘越小(取对数后越加也是越小),所以如果算法要找概率较大的句子,会更倾向于短句子。
- 解决方案:长度归一化,在得到概率加和后(是负数)除以句子长度,补偿长句子。
误差分析
如果模型最后得到的翻译结果和人类的翻译结果不一致,就是翻译错了,如何做误差分析呢?
- 直接让模型跑两个句子的概率,如果人类的概率更大,那说明beam search没找到,应该让beam width调大一些;如果错误句子的概率更大,说明RNN有问题,应该提升RNN。
Bleu得分
Bleu得分用于评估机器翻译的质量。给定两个人类参考翻译,Bleu得分实际上会计算机翻和人翻的相似度来作为质量得分。
具体计算方式如下:
- 将机翻的句子每n=2个连续的单词组拿出来,计数;
- 对于每个2单词组,去参考翻译里找,是否有,有的话最大计数是多少(比如说in the可能在某个参考翻译中出现3次,那最大计数就是3);
- 以最大计数为上界,命中的单词组积分,然后除以总的2单词组个数,就是n=2时的得分;
- n=1...4的分数都算一下,然后求和平均得到;
- ,BP是短句子惩罚项;如果机翻的句子没有参考翻译的句子长,得分也要被压缩
5.3.7-5.3.8 注意力机制
encoder-decoder模型的问题就是对于太长的句子它全都要先记忆一次,然后进行输出。这和人类的翻译过程是不一样的,人记不住那么多,一般是一段一段翻译的。所以这个模型在句子变得较长的时候bleu score反而会下降。
注意力机制和encoder-decoder模型最大的普通就在于其每一个输出都是受encoder模块各个神经元激励值加权和影响的,如下图:
- 对于一个输出神经元,各输入神经元权重加和为1;
- 成对的方格代表双向RNN,其激励值可以拼在一起成为一个激励值单元;
- 问题就在于如何确定每一步的权重,权重受和各个输入神经元的激励值控制,这个问题可以单独交给一个小的神经网络来控制。
5.3.9 语音识别
即语音->文字的问题。
- 注意力机制模型就是语音识别的一种解决方案,输入就是每个时间点每个频率的值。
- CTC模型也是一种方法,它使用输入输出等长的many-to-many RNN模型。这样输出的是带有很多重复字母和空白的,只要把这些折叠掉,就可以得到正确输出。
5.3.10 触发字检测
即“Hey, Siri问题”。
- 类似于CTC模型,只不过输出的都是0或1;只要是关键字,就输出1,否则是0。为了解决1比较少的问题,可以在现有的1后面补一些1.