CS224N(3.14)Future of NLP + Deep Learning

今天是该课程的最后一节课,介绍了使用未标注数据集进行NLP学习的方法,以及谈了谈NLP未来的发展方向。下面主要介绍使用未标注数据集进行NLP学习的方法。 我们知道,在机器翻译领域,特别缺少标注好的语料集。目前世界上有上千种语言,但用得最多的只有十几种。对于那些使用人数很少的语言,它们和其他语言之间标注好的翻译句子就更少了。如何使用少量标注集,甚至不用标注集,就能实现机器翻译功能,是NLP领域一个很有前景的发展方向。 之前的很多工作使用pre-training来提高机器翻译模型的性能。具体方法是,先在源语言和目标语言的语料集上分别训练一个语言模型,这是无监督的,这个语言模型可以学到不同词的含义。然后在翻译模型中,用源语言的语言模型初始化Encoder权重,用目标语言的语言模型初始化Decoder权重。使用pre-training的模型相比于不使用pre-training的模型的BLEU大概有2分的提高。 Pre-training的问题是,由于预训练是在两种语言上独立进行的,两种语言在预训练期间没有交互过程。 下面的Back-Translation比较有意思。比如我们要训练一个英语到法语的翻译器,初始化的时候让模型随便从英语翻译成法语。同时,训练一个法语到英语的翻译器,把上一个翻译器输出的法语翻译成英语。有点像练功的时候左右互搏,也有点像AlphaGo自己教自己下棋,随着训练的进行,两个模型的翻译能力都得到了提升。 当然这有一些问题,如果两个模型一开始都一无所知,则可能前一个模型的输出是随机的,后一个模型的输入是随机的,模型根本学不到任何知识,无法收敛。所以更好的做法是,有少量的标注数据,两个模型先在标注数据上学到一个比较差的模型;然后用这个比较差的模型左右互搏,相当于可以粗略的标注一部分新数据;然后又在标注数据上训练;如此循环往复。实验结果表明,使用Back-Translation和大量无标注数据集之后,翻译模型的性能有大幅提升。 Back-Translation要求我们还是需要少量的标注数据,用来启动左右互搏的过程。那么如果有两种语言X和Y,我们没有X和Y的任何翻译好的句子pair,但依然想翻译它们,怎么办。这时候,可以先从简单的单词翻译做起。在训练X和Y各自的词向量时,可以把它们的词向量映射到同一个空间,则空间中相近的词的含义也相近。对于X中的一个词x,只需要在词向量空间中选与x最接近的Y中的词y,则y可以作为x的翻译。这种把多种语言的词向量统一对齐到一个空间之后的词向量称为跨语言的词向量,也就是说从这个空间中取一个词向量,虽然它的含义是固定的,但可以转换成任意一种语言的具体的词。 那么,关键问题就是怎样把X和Y的词向量对齐。我们知道word2vec有一个很好的特点就是,它训练出来的词向量能够保持比较好的空间结构。举例来说,对于X中的词x和Y中的词y,即使它们的含义很接近,但如果直接把x和y放到同一个空间中,它们的距离可能还是比较大,因为X和Y的词向量坐标系可能就不一样。但是,对于X中的两个词x1和x2,如果它们分别对应Y中的两个词y1和y2,则在X空间中,x1和x2的距离应该与Y空间中y1和y2的距离相近,也就是说两个空间的整体结构是一样的。如下图所示,X和Y对齐的过程就是找出它们的变换矩阵W。由于我们只希望将它们的坐标系进行变换对齐,并不想改变里面的数据分布,所以变换矩阵W最好是正交的。 学习矩阵W的过程也很有意思,这里介绍了一种对抗学习的方式。有一个生成器,用来生成矩阵W。有一个判别器,它想要区分一个词向量是Y中的词向量,还是X中的词向量经过W变换得到的。起初,由于X和Y的空间分布不同,W也是随机的,判别器可以很容易地区分Y和WX。但是随着对抗的进行,W越来越准,最后WX和Y重合了,此时判别器傻傻分不清楚,它只能随机猜,有50%的几率猜对。所以学习矩阵W的过程就是让判别器懵圈的过程。 上述是非监督的词与词的翻译,怎样由此得到非监督的句子与句子的翻译呢? 我们首先使用上述的跨语言的词向量,然后使用相同的encoder-decoder来编码和解码两种语言。解码的时候,设置一个标志位,告诉decoder要把目标含义用哪种语言表达出来,就是下图的<Fr>标签。 然后有两个训练目标,第一个目标是对源句子进行微小的打乱,然后让encoder恢复原来的句子。第二个目标是使用上文提到的back translation进行左右互搏(所以好像还是需要少量标注集?)。 这种方法有效是因为输入的词向量是跨语言词向量,输入一个英文句子就相当于输入了一个法文句子。又因为使用的encoder是相同的,所以英文句子在进行第一个目标训练时,隐含学到了将法语翻译为英语的能力。 上述的非监督词与词、句子与句子的翻译,只有在源语言和目标语言比较像的情况下才能取得比较好的效果,比如英语、法语、德语比较像,可以用。但英语和土耳其语差别比较大,这种非监督方法的效果就比较差。语言像不像涉及到很多方面,比如语法结构、句子结构、用词顺序等等。

March 26, 2020 · 1 min

CS224N(3.5)Multitask Learning

今天介绍NLP中的多任务学习。 我们知道预训练和参数共享在CV中很重要,很多模型都会在ImageNet上预训练,然后迁移到具体的任务中进行微调。这种方式能够成功的原因是很多CV任务几乎都是以分类为基础任务,分类相当于CV的积木(building block),所以在ImageNet上训练的CNN分类模型迁移到其他CV任务中能起到很好的提升效果。 NLP中虽然也有一些预训练模块,比如预训练词向量,然后用到具体的NLP任务中,但也仅仅是将词向量作为下游模型的输入。在NLP中,并没有一个基础模型(包括模型的结构、权重等),能把整个基础模型迁移到下游任务进行微调,现在都是针对不同的问题设计专门的网络结构,比如POS、NER、NMT等,处于不同任务各自为政的局面。 在NLP中,一个统一的多任务基础模型可以很方便地进行模型迁移、模型部署,并且对这个基础模型的不断改进,可以不断提高下游模型的性能,有可能达到持续学习、持续提升的目的,而如果每个新的子模型都重新学习的话,相当于利用不到基础模型长期学习到的知识。 但是,NLP领域有很多任务,比如序列标注、命名实体识别是一类;对整个句子的分类是一类;还有就是seq2seq的机器翻译、自动摘要等,要怎样将这些不同的任务统一到一个模型中呢? 可以把所有NLP任务统一成QA任务。如下左图所示,机器翻译转换为QA任务就是问这个句子翻译成另一种语言是什么句子,句子情感分类就是问这个句子表达的是积极还是消极的含义等等,所以所有NLP任务都可以转换为QA任务。PPT中列出了10个NLP任务都可以转换为QA任务,所以这个统一的基础任务相当于十项全能选手(decaNLP)。 设计这样一个十项全能的NLP模型,需要满足如下三个条件。1. 必须对十项任务一视同仁,也就是喂给模型的数据不能包含这条数据具体是哪个任务,不能告诉模型这条数据是要做NMT,另一条数据要做QA等。2. 模型也不能包含针对不同任务的特殊模块,模型必须学会自己辨别不同的任务类型,并且进行内部切换,执行不同的操作。3. 此外,模型还应具备执行这10个任务之外的任务的能力,即zero shot learning。 如下图是这个多任务QA模型的一个全貌。首先给一个上下文Context,然后给一个问题Question,最后模型输出答案。输出答案的时候,每次产生一个词,这个词可能来自Question、Context、或者Vocabulary,所以有一个指针开关,指向这个词可能的来源分布。 模型细节如下图所示,基本上是以前学过的模块,这里只是把它们组合起来了。 输入:固定词向量GloVe和charCNN,然后用bi-LSTM进行编码(Initial Encoding),注意这个bi-LSTM是Question和Context共享的,共享一套参数。输出就是Question和Context的每个词的隐藏层特征向量,图中Question有3个词,所以Initial Encoding后面有一个3*4的矩阵,3行就表示Question中的3个词,Context有4个词,也是类似的。 Coattention:以Question为例,Question自己的bi-LSTM输出经过一个attention,然后拼上Context的bi-LSTM输出再过一个attention,所以相当于Question和Context进行了co-attention。Context的也类似。 Transformer:Coattention出来后经过一层bi-LSTM、两层Transformer、再经过一层bi-LSTM,得到Question和Context最终的编码。这里的bi-LSTM就是Question和Context独立的了。 输出:Question和Context的Final Encoding输出又经过Attention,综合Question、Context和Vocabulary得到输出词的分布。 在训练阶段,PPT底部还提供了Answer,供误差反传。 收集的10个数据集,虽然不同任务的评价指标不同,但他们的值理论上都在0~100之间,所以模型总的得分是10个子任务各自得分的累加和。 性能表如下,看起来在大多数任务上,针对该任务单独训练的模型比Multitask模型的性能要好,但是在QA Zero shot relation extraction(QA-ZRE)任务上,Multitask具有比较大的优势。 训练技巧。不同的训练方式可能对模型的性能产生影响,这里介绍两种方式,一种是Fully joint,另一种是Anti-Curriculum。Fully joint,有10个不同的数据集需要训练,训练的时候,从每个数据集中抽一部分数据出来,组成一个batch进行训练,而不是依次训练第一个、第二个数据集这样子。Anti-Curriculum,训练的时候先训练难的任务,比如先在机器翻译上训练,再在句子情感分类上训练,因为情感分类任务相对简单,如果先在这上面训练的话,很容易达到局部最优,到时候就很难爬出来再针对机器翻译训练了;而如果先在更难的机器翻译上训练的话,得到泛化能力更强的模型后再在情感分类上训练就容易得多了。仅仅是训练方式的不同,Anti-Curriculum比Fully joint的测试得分会有所提高。 但是多任务模型的性能依然弱于单任务模型,发现主要在机器翻译数据集上,多任务模型的性能较大地差于单任务模型,可能是因为机器翻译的输出词大多数不在Question和Context中,而是在Vocabulary中。作者近期又有很多改进,使得多任务一个模型的总性能已经很接近多个单任务模型的性能总和了。 最后,愿景,希望把decaNLP变成NLP的ImageNet-CNN模型,以后大家的模型都可以基于decaNLP进行fine-tune和改进。

March 14, 2020 · 1 min

CS224N(2.26)Coreference Resolution

今天介绍的内容是指代消解(Coreference Resolution)。指代(mention)是指句子中出现的名词、名词短语、代词等,指代消解就是把指向同一实体(entity)的指代聚类到一起的过程。比如下面的两句话,蓝色的词就是很多的指代,需要找出来哪些指代是指向同一个实体。比如Barack Obama、his、He都是指奥巴马。 下面的例子是比较简单的情况,事实上,在真实的语言中,情况更加复杂,比如有些指代可能表示多个实体。比如“他们”,可能同时指代了“小明”和“小王”,这种指代对于现在的NLP模型来说比较难,暂时不考虑。 指代消解有很多应用场景,比如知道代词的含义之后,能更好地理解全文,进而能更准确的进行机器翻译,另外,在对话系统中进行指代消解能产生更正确的回答句子。 指代消解主要有两个步骤。第一步是指代识别(mention detection),即找出句子中所有的指代,这一步相对简单。第二步才是进行真正的指代消解(coreference resolution),这一步比较难,也是本节课的主要内容。 指代(Mention)是指句子中的一个短语(span),它可以是代词、也可以是命名实体、还可以是名词短语。指代识别的方法有多种,比如词性标注(POS)、命名实体识别(NER)、语法分析器(parser)等。 指代识别虽然相比于指代消解更简单,但也不是那么简单,它也有它自己的一些难题。比如下面列出的几个例子其实就不是指代,但用POS、NER等方法有可能把它们找出来,导致找出过多的指代(over-generates)。 一个简单的方法是,指代识别阶段尽量保召回率,保留所有找到的可能是指代的词,都参与后期的指代消解。如果一个指代没有找到它的共同指代(coreference),则说明这个指代是孤立的(singleton mention),有可能是指代识别阶段找到的不是指代的词,直接把它扔掉即可。 指代消解发展至今,经历了四种不同的方法,分别是Rule-based、Mention pair、Mention Ranking和Clustering。下面分别介绍每一种方法。 1976年Hobbs提出了基于规则的朴素算法,被后人称为Hobbs算法。该方法有9个步骤,包含了很多规则,非常繁琐,这里就不具体介绍了。Hobbs算法虽然是基于规则的,但在当时取得了不错的效果,现在也常常作为该领域的baseline模型。但是因为该方法是基于规则的,有很多指代消解没法解决。比如下图的两组例子,它们的句子结构完全相同,但指代不同,基于规则的Hobbs算法如果正确消解了第一个句子的指代,则肯定无法正确消解第二个句子的指代。 很明显,要想同时正确消解两个句子的指代,必须像人一样理解句子的含义。所以,指代消解也可以作为图灵测试之外的实验,用来测试AI的智力。如果AI能100%准确完成指代消解,AI的智力也就达到了人类的水平。 比较简单的Mention pair的方法。该方法把指代消解问题转化为一个二分类问题。从左到右遍历句子,每找到一个指代,就把它和前面找到的每个指代作为一个pair,问分类器这个pair是否指代同一个实体,如果是的话,就把它们连起来。二分类的损失就是交叉熵。很简单的一个模型。 该模型在测试的时候,设定一个概率阈值,比如把所有概率超过0.5的指代pair都连起来,然后所有连在一起的指代作为一个聚类。如果一个指代不与任何其他指代连接,则它可能不是一个指代。这种方法有over-cluster的风险,即万一有一根线连错了,则会导致原本属于两个cluster的所有指代归为一个类。 Mention pair模型的不足:对于很长的句子,可能会找到很多指代,而一个指代的先行词(antecedent)往往只有一个,但mention pair方法却把它和它前面的所有指代都pair进行指代消解。比如下面的例子,最后的he明确的指代是紧邻它的前一个Nader,但却需要把he和前面的4个指代都pair进行消解,无形中增加了计算量。可能的解决方法是只让模型预测一个先行词,也就是mention ranking方法。 Mention ranking的方法。每个指代同时和前面所有指代打分,用softmax归一化,找出概率最大的先行词,添加一条连边。注意需要添加一个NA节点,因为有的指代可能第一次出现,前面没有先行词,或者这个指代根本就不是一个真正的指代。 Mention ranking在训练时的损失函数如下:i:对于每一个指代;j:看看其前面的所有指代,最大化i所指代的j的概率\(1(y_{ij}=1)p(m_j,m_i)\)。 测试的时候与mention pair类似,把有边连的指代聚类到一起。 前面的内容都是假设我们计算好了任意两个指代是coreference的概率,那么,如何来计算这个概率呢?主要有三种方法,分别是Non-neural statistical classifier、Simple neural network和More advanced model using LSTMs, attention。下面分别介绍这三种方法。 A. Non-neural statistical classifier。统计机器学习方法,抽取每个指代的各种特征,然后用机器学习分类器来计算两个指代是coreference的概率。这里面的特征包括人称、性别一致性,语义相容性等等,如下图所示。 B. Neural Coref Model。输入是候选先行词和当前指代词的词向量,还需要加入一些额外的特征(Additional Feature),也就是上面统计机器学习方法里用到的一些特征。中间是FFNN,即全连接网络,最后输出两个指代是coreference的概率。 C. End-to-end Model。end2end模型是目前指代消解的SOTA模型,它把指代识别和指代消解两个任务融合到一起,用一个模型来解决。 它的网络结构如下左图所示。由于该模型同时完成了指代识别和指代消解两个任务,所以它需要枚举句子中任意两个词之间的span是否是一个指代,以及这个指代与其他指代是否是coreference的关系。 具体来说,对于每个词i,输入网络中的是这个词的词向量以及charCNN词向量拼接起来的向量。然后送入bi-LSTM中,提取bi-LSTM两个LSTM的隐藏层向量,拼接起来作为词i的特征向量\(x^*\)。枚举包含词i的起始位置START(i)和终止位置END(i),由起始位置到终止位置构成一个span,假设这个span可能是一个指代。一个span由向量\(g_i\)表示,它包含4个部分,分别是:span的起始和终止位置的特征向量\(x^*_{START(i)}\)和\(x^*_{END(i)}\),包含这个span的起始和终止位置的特征;这个span的attention表示向量\(\hat{x_i}\),提取这个span表示的核心信息,比如the black cat中cat是核心信息;以及一些额外的特征\(\phi(i)\)。 一个span的attention表示向量\(\hat{x_i}\)计算公式如下右图。span中每个词的特征向量经过全连接网络FFNN,得到一个attention打分,softmax归一化得到归一化的attention打分分布,最后加权平均起来。 ...

March 8, 2020 · 1 min

CS224N(2.21)Transformers and Self-Attention For Generative Models

今天介绍大名鼎鼎的Transformer,它于2017年出自谷歌的论文《Attention Is All You Need》(https://arxiv.org/pdf/1706.03762.pdf),用Attention实现机器翻译模型,并取得了新的SOTA性能。 传统的机器翻译模型一般是结合RNN和Attention,可以看我之前的博客介绍:CS224N(1.31)Translation, Seq2Seq, Attention。虽然RNN+Attention的组合取得了不错的效果,但依然存在一些问题。由于RNN是序列依赖的模型,难以并行化,训练时间较长;且当句子很长时由于梯度消失难以捕捉长距离依赖关系。虽然相继推出的LSTM和GRU能一定程度上缓解梯度消失的问题,但这个问题依然存在。而且LSTM和GRU难以解释,我们根本不知道当前timestep依赖远的词多一点还是近的词多一点。 Transformer的思想很激进,它完全抛弃了RNN,只保留Attention,从其论文标题可见一斑。RNN无法并行化的根本原因是它的正向和反向传播是沿着句子方向(即水平方向),要想实现并行化,肯定不能再走水平方向了。于是,Transformer完全抛弃水平方向的RNN,而是在垂直方向上不断叠加Attention。由于每一层的Attention计算只和其前一层的Attention输出有关,所以当前层的所有词的Attention可以并行计算,互不干扰,这就使得Transformer可以利用GPU进行并行训练。 具体来说,Transformer的结构如下图所示。我们知道end2end的机器翻译模型一般都是Encoder+Decoder的组合,Encoder对源句子进行编码,将编码信息传给Decoder,Decoder翻译出目标句子。Transformer也不例外,下图左边即为Encoder,右边即为Decoder。 Encoder的每一层有两个子层组成,包括Self-Attention和Feed-forward neural network (FFNN)。FFNN就是常规的全连接网络,没什么可说的,下面重点介绍Self-Attention。 Encoder Self-Attention的结构如下图所示,由于此时是Encoder阶段,对于每个词,都能看到句子中所有其他的词(对应到RNN里面就是可以用双向的RNN)。假设我们想要抽取第二个词”represent”的特征表示。首先,对第二个词的词向量\(e_2\)进行线性变换,即乘以矩阵\(matmul_Q\),得到Query,这就是标准Attention中的Query。其次,对周围所有的其他词,比如\(e_1\),也进行线性变换,变换矩阵为\(matmul_K\),得到很多的Key。然后,Query和所有Key做点积,并用softmax归一化,得到了Query在周围词上的Attention score distribution。接着,周围词乘以另一个线性变换矩阵\(matmul_V\),变换为Value。最后,Value和Attention score distribution进行加权求和,并加上\(e_2\)自己,送给FFNN。图中右下角的公式中的分母只是个缩放因子。 回顾一下,一个标准的Attention包括三个向量:Q、K、V,其中Q为用来查询的Query,K表示被查询的Key,V表示被查询的Value。其中的K和V来源相同,只是经过了不同的变换。形象描述就是:计算Q在K上分配的注意力\(QK^T\),然后从V中取出这部分注意力的值\(softmax(\frac{QK^T}{\sqrt{d_k}})V\)。 Self-Attention的优点。因为每个词都和周围所有词做attention,所以任意两个位置都相当于有直连线路,可捕获长距离依赖。而且Attention的可解释性更好,根据Attention score可以知道一个词和哪些词的关系比较大。易于并行化,当前层的Attention计算只和前一层的值有关,所以一层的所有节点可并行执行self-attention操作。计算效率高,一次Self-Attention只需要两次矩阵运算,速度很快。 Transformer的Decoder部分每一层有三个子层组成,包括Self-Attention、Encoder-Decoder Attention和FFNN。Decoder的Self-Attention如下图所示,和Encoder的Self-Attentoin非常像,只不过当要Decoder第二个词时,用黑框蒙住了第三、四个词的运算(设置值为-1e9)。因为对于机器翻译来说,Encoder时能看到源句子所有的词,但是翻译成目标句子的过程中,Decoder只能看到当前要翻译的词之前的所有词,看不到之后的所有词,所以要把之后的所有词都遮住。所以这个Attention也叫Masked Self-Attention。这也说明Transformer只是在Encoder阶段可以并行化,Decoder阶段依然要一个个词顺序翻译,依然是串行的。 不要忘了我们的任务是机器翻译,Decoder Self-Attention只用到了翻译出来的目标句子的前缀信息,还没用到源句子的信息,这部分就在Encoder-Decoder Attention中。前面说了对于源句子,通过Encoder的Self-Attention+FFNN,源句子的每个词都有一个输出向量,这些输出向量作为Encoder-Decoder Attention的Keys和Values,而从目标句子当前要翻译的词的Decoder Self-Attention出来的向量就是Encoder-Decoder Attention的Query。从下图可以看到,Encoder上面出来指向右边Multi-Head Attention的两个箭头就是Keys和Values,而从下面出来指向Multi-Head Attention的一个箭头就是Query。Encoder-Decoder Attention的作用就是看看当前要翻译的词在源句子中各个词上的注意力情况。 我们知道Attention机制是位置无关的,因为对于每个词,它都和句子中的所有词直连求Attention score,跟词在句子中的位置没有关系。但是句子作为一种线性结构,词在句子中的顺序对句子的含义至关重要。为了考虑词的位置信息,词在输入Attention前,把词向量和词在句子中的位置Positional Encoding加起来,得到一个新的向量,输入到Attention中,如上图所示。这个Positional Encoding可通过公式计算得到,这里不展开。 上图的Attention前面都有一个修饰词Multi-Head,也就是下图的Parallel attention heads。前面提到一个标准的Attention包括三个向量Q、K、V,它们分别由原始的查询向量和特征向量乘以矩阵\(matmul_Q\)、\(matmul_K\)、\(matmul_V\)得到。如果一个词在计算Attention时,选用多个不同的\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),得到的Attention输出向量也就不同了,这正好可以用来表示一个词在句子中的不同作用。比如句子“华为是一家中国的公司”中的“华为”,它的语义是一家公司,它在句子中的成分是主语,也就是说一个词至少有其语义信息和句法信息,如果只用一套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),则只能得到一种含义,如果设置多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),则能提取到这个词更多的信息。于是,在对每个词进行Attention时,都会设置多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\),提取多个Attention输出向量,然后拼接起来,这就是Multi-Head Attention,或者说Parallel attention heads。我个人理解多套\(matmul_Q\)、\(matmul_K\)、\(matmul_V\)相当于CNN中不同的kernal,相当于不同的特征提取器。 课上还介绍了利用Transformer生成图片和音乐的应用,感兴趣的同学可以搜索相关论文看一看。 有关Transformer的介绍,还可参考如下三个链接: http://jalammar.github.io/illustrated-transformer/ https://zhuanlan.zhihu.com/p/48508221 https://zhuanlan.zhihu.com/p/44121378

March 4, 2020 · 1 min

CS224N(2.14)Subword Models

今天介绍一下subword(子词)模型。之前介绍的NLP模型都是基于word的,对于英文来说是一个个单词,对于中文来说是一个个词语(需要分词)。不过,最近几年,subword模型多起来了,这就是我们今天要介绍的内容。 对于英文来说,文字的粒度从细到粗依次是character, subword, word,character和word都很好理解,subword相当于英文中的词根、前缀、后缀等,如unfortunately中的un、ly、fortun(e)等就是subword,它们都是有含义的。对于中文来说,只有两层,character和subword是同一层,表示单个的字,而word表示词语。 之前介绍的基于word的模型,存在out of vocabulary(OOV,未登录词)的问题。以英文为例,现存的英文单词数量太多了,随便加个前缀、后缀,变个时态什么的都变成新的单词了,所以英文单词的词典数量特别大,而且有很多低频稀疏词。很多模型在训练时都会去掉低频词,只保留高频词。那么这就存在一个问题,如果预测时遇到未登录词,则模型不认识,出现OOV的问题。 为了解决这个问题,一开始想到的是采用character级别的模型,即对26个字母训练word2vec,每个词由其字母的embedding拼接或者求平均得到。但是character级别的模型效果相比于word级别的模型效果差不多,并没有显著优势。而且如果用RNN来训练character级别的模型也有它的问题,就是训练起来非常慢。特别是对英文来说,原来的一个word,现在变成了七八个character,时间步长增加了很多,训练和预测都更久了,而且梯度消失(爆炸)的问题也会更严重。 后来,人们就想用subword模型作为character和word的折中模型。subword模型主要有两种,它们都能解决未登录词(OOV)的问题,如下图所示。第一种是模型结构和word模型完全一样,只不过把word换成了subword。第二种则是word和character模型的杂交模型。 对于第一种模型,关键问题是怎样得到subword。前面提到character的粒度太细,虽然能解决OOV问题,但效果并不是太好;word模型的word数量太多,存在大量稀疏word,删掉它们又会导致OOV问题,所以打算用subword模型。那么,怎样提取一个单词的subword呢?前面提到,unfortunately中的un、ly、fortun(e)等就是subword,但是对每个词都这样提取subword的话,费时费力不说,也不够智能。 有人就想出了用BPE算法来提取高频subword。BPE,全称是byte pair encoder,是上世纪提出的一种压缩算法,其核心思想是不断用字母表中不存在的char来代替最高频的char pair。举个例子,对于字符串aaabdaaabac,其字符串中最开始只出现了a/b/c/d这四个char;统计所有char pair,最高频的是aa,用不在字母表中的另一个字符Z代替aa,则原字符串变成了ZabdZabac,字母表变成了a/b/c/d/Z;如此不断进行下去,直到字母表大小达到一定的阈值,或者所有连续的char pair的频数都等于1了。关于BPE算法的进一步介绍请看这里:https://zhuanlan.zhihu.com/p/38130825。 第一种模型就是用BPE算法来得到高频subword的。比如下图的例子,语料集D中出现了5个low,2个lower等等。最开始,字母表V中是语料集中出现的所有单个字母的集合{l,o,…,d}。然后,发现e s这个char pair出现次数最多,将其作为一个subword加入到V中,同时将D中的es合并看作一个新的char。接着统计发现es t这个char pair(此时es已经是一个char了)出现次数最多,将其作为一个subword加入到V中,如此进行下去。发现没有,我们自动从D中提取了est这个subword,而est就是最高级的后缀。也就是说BPE算法自动提取到了英文的前缀、后缀等subword信息,完全避免了之前费时费力地从unfortunately中手工提取un、ly、fortun(e)的过程。这种方法也能解决未登录词问题,但是粒度又不至于太细。一方面是因为最开始的时候D中原始出现的单个字母都在V中,另一方面,由V中字母组成的未出现词也能由subword构成,比如下图中虽然less没有出现在D中,但可以由l es s这3个subword组成,而这3个subword是在最终的V中的。 得到高频subword作为V之后,后续在进行NLP任务时,encoder的时候查一下V,把char pair替换为新字符;decoder的时候查一下V,把新字符替换回原来的char pair。 最近比较流行的BERT,字典中既有相对比较常见的词,对于不太常见的词则用subwords/wordpieces来表示。 第二种被称为杂交模型的方法就相对简单了。这种方法是Manning老师提出来的,它就是在D中有这个word时就用word embedding,没有的时候就用char embedding来学习word embedding,非常简单。 fasttext就是skipgram+n-gram,一个词的embedding=组成这个词的n-gram的embedding的加权求和,所以fasttext也能解决OOV问题。

February 17, 2020 · 1 min

CS224N(2.12)Convolutional Networks for NLP

今天我们介绍如何使用CNN解决NLP问题。截止目前,我们学习了很多RNN模型来解决NLP问题,由于NLP是序列的问题,使用RNN这种循环神经网络是很符合直觉的,而且也取得了不错的效果。但是,由于RNN速度较慢,而且梯度消失问题比较严重,人们就想借用CV领域的CNN,看是否能解决NLP的问题。 我们在之前的博客中已经详细介绍过卷积神经网络CNN,这里不再详细介绍。下面我们以一篇paper中使用CNN对句子进行情感分类为例,简要介绍下怎样将CNN应用到NLP中。 上图是一个非常简单的CNN网络,用来对影评进行情感分类,输入是一个长度为7的句子,我们把每个词用长度为5的词向量来表示,则对于输入来说,得到了一个7×5的矩阵,这不就相当于一张图片了吗,后续操作就很像CV了。第二步,需要对输入“图片“进行卷积操作,请注意,虽然输入可以看做图片,但其本质上是“一维”的句子,所以我们设计卷积核大小时,卷积核的宽度要固定为5,保证卷积核能对完整的词向量进行操作。这里共设计了3个不同大小的卷积核,每种大小有2个卷积核,共6个卷积核。卷积操作完成之后得到了6个特征图,对每个特征图取max pooling再拼接起来,得到一个长为6的向量,这就是用CNN对句子抽取的特征向量。最后再接一个softmax进行二分类。 除了上图展示的CNN操作外,还有一些CNN操作有可能会用到: 卷积操作的stride=k,每k行一个group进行卷积,默认卷积操作是k=1 卷积操作的dilation=k,跨k行进行卷积,默认卷积操作是k=1 padding,上图卷积操作之后,feature map相比于输入维度变小了,如果要想保持维度不变,可对输入进行padding max/avg pooling over time,上图的max pooling即为max pooling over time,即对整个句子所有时间步的feature取max k-max pooling,对整个句子的所有时间步的feature取top-k的max值,同时保持feature的相对顺序不变,上述max pooling相当于1-max pooling local max pooling,stride=k,对每k个feature取max,这个和CV里默认的max pooling是一样的,CV里就是画一个框取max dropout=p,对于每个连接,随机以概率p丢弃,属于一种正则化技术,能有效增加模型的鲁棒性 skip connections,之前讲过很多次了,直连线路,没有中间商赚差价 batch normalization,对每次卷积操作的输出进行z-score标准化,使得均值为0,标准差为1,能有效增加模型的鲁棒性 卷积核大小为1×1的卷积,相当于卷积前后的feature map的全连接,但又比全连接的参数少,因为一个卷积核的参数是共享的 最后,给出我们目前所学的工具箱: 词袋模型:对于一个句子,简单的把所有词的词向量进行平均,也能取得不错的baseline效果 基于滑动窗口的模型:对于POS、NER等不需要很长的上下文信息的问题来说,效果不错 CNN:对分类问题效果很好,容易在GPU上并行,所以效率很高 RNN:对于NLP问题来说,符合认知,对分类问题效果不是很好(如果只用最后一个隐状态的话),加上Attention性能提升明显,特别适合序列标注、语言模型等序列问题

August 5, 2019 · 1 min

CS224N(2.7)Question Answering

这节课的内容比较简单,是问答系统(Question Answering, QA)的入门介绍。 QA简介 首先,为什么需要QA?目前各大搜索引擎对于一个查询,给出的都是一个结果列表。但是很多查询是一个问题,答案也往往比较确定,比如“现任美国总统是谁?”,此时,返回一堆结果列表就显得太过啰嗦了,尤其是在手机等移动设备上搜索时,简单的给出回答也许会更好一些。另一方面,智能手机上的助手如Siri、Google Now之类的,用户期望的也是简洁的答案,而不是一堆网页列表。 QA系统的组成主要有两个部分,一部分是根据问题检索到相关的文档,这部分是传统的信息检索的内容;另一部分是对检索到的文档进行阅读理解,抽取出能回答问题的答案,这部分就是本文要介绍的QA系统。 QA的历史可追溯到上世纪七十年代,但真正取得突破性进展也就是最近几年。2015/2016年,几个大规模QA标注数据集的发表,极大的推动了这个领域的发展。这其中比较有名的数据集是斯坦福大学发布的Stanford Question Answering Dataset (SQuAD)。 SQuAD数据集的每一个样例包含一段描述P,一个问题Q,以及对Q的人工标注答案A。为了使数据集更加鲁棒,对于每个问题,都给出了三个人工标注答案。每个答案都是描述P中的一小段文字,称为一个span。所以,问题相对来说比较简单,答案可以直接从描述中提取sub-sequence得到。 QA系统的评价指标有两个,一个是确定性匹配Exact match,即对于每个问题,模型给出的回答如果和3个答案中的任意一个完全匹配,则加1分,否则不加分。另一个是F1指标,使用词袋模型(不考虑词的顺序),对于每个问题,模型给出的回答和3个答案中的每一个计算F1,这个问题的F1是3个F1的最大值,最终得分是所有问题的F1打分的均值。Exact match和F1都不考虑标点符号和冠词。相对来说,F1比Exact match更可靠和鲁棒一些。 经过两年的两三年的刷榜,SQuAD数据集的最好性能已经超越了人类的性能,为了增加数据集的难度,斯坦福后续推出升级版本SQuAD 2.0。 由于1.1版本的问题都有答案,所以QA系统变成了一个排序系统,只需要把Answer列表中排名第一的结果输出就好了。2.0在1.1的基础上,增加了没有答案的问题,可以理解为假问题,对系统造成干扰。此时就要求系统判断这个问题能否从描述P中获得答案,如果没有答案的话,就不输出任何回答<No Answer>。对于没有答案的问题,如果系统没有输出答案,得1分,否则输出任何答案都得0分。 SQuAD数据集的局限性: 回答都是span-based类型的,没有yes/no、计数、why等的问答。 由于构造问题q的时候,已知了描述P,那么q和P的描述会很像,无论是用词还是语法。而搜索引擎面临的真实情况往往是,q根本不知道P是什么,有可能q和P的描述在行文及用词上有很大差别。 描述P比较简单,因为答案是一个span,所以模型把q和P匹配,找到可能的答案位置就行。实际的复杂场景有可能要综合好几个句子的信息,还要理解不同的指代关系等才能得出最终答案。 虽然SQuAD数据集还有不少局限性,但由于其是一个well-targeted、well-structured、clean dataset,在QA发展初期,还是为促进QA发展立下了汗马功劳。 Stanford Attentive Reader 下面介绍一下Chris Manning组针对SQuAD数据集开发的QA系统——Stanford Attentive Reader。该系统目前虽然不是最好性能,但它包含QA的基本模块,可以作为QA的一个baseline模型。 首先模型对问题q进行表征的方法如下,输入是q中每个词的词向量,然后使用一个Bi-LSTM提取句子特征,由于是双向的LSTM,所以模型把正向和反向的LSTM的最后一个隐状态拼接起来,作为对整个句子的表征。 由于SQuAD数据集的回答都是描述P中的一个span,那么,模型只需要预测出这个span在P中的起始位置和终止位置即可,具体方法如下图所示。其实也很简单,上一步我们得到的句子的表征向量q,下一步,我们对描述P也使用Bi-LSTM,得到描述P中每个词的表征向量\(\tilde p_i\)。然后,使用两次Attention,用q查询集合\(P=[\tilde p_1,…,\tilde p_n]\),得到答案span的起始位置\(\alpha_i\)和终止位置\(\alpha’_i\)。另外,由于\(q\)和\(\tilde p_i\)的维度可能不一样,又或者为了提升模型性能,在计算Attention score的时候,不是简单的向量点积,而是采用了线性变换的方法,增加了参数\(W\)。 后来,Chris Manning组又推出了升级版本Stanford Attentive Reader++,主要包括两个方面。首先,对表征问题的网络进行了改进,\(q\)不仅包含Bi-LSTM的两个尾结点的隐状态,而是包含整个问题所有隐状态的加权平均,而且网络层数增加到了3层。其次,对描述P的表征方面,原来的输入只包含词向量,现在还包含语言特征(如POS、NER的标签)、词频、以及近义词的相似度等。改进版模型性能提升了不少。 另一个比较流行的QA系统是BiDAF,如下图所示,这里不再详细介绍。它的特点一方面输入不仅包含词向量,还包含字符级别的特征。另一大创新是在Attention Flow Layer,相对于Stanford Attentive Reader,BiDAF的Attention是双向的,不但包含q对P的Attention,还包含P对q的Attention。

August 4, 2019 · 1 min

CS224N(1.31)Translation, Seq2Seq, Attention

今天介绍另一个NLP任务——机器翻译,以及神经网络机器翻译模型seq2seq和一个改进技巧attention。 机器翻译最早可追溯至1950s,由于冷战的需要,美国开始研制由俄语到英语的翻译机器。当时的机器翻译很简单,就是自动从词典中把对应的词逐个翻译出来。 后来在1990s~2010s,统计机器翻译(Statistical Machine Translation, SMT)大行其道。假设源语言是法语\(x\),目标语言是英语\(y\),机器翻译的目标就是寻找\(y\),使得\(P(y|x)\)最大,也就是下图的公式。进一步,通过贝叶斯公式可拆分成两个概率的乘积:其中\(P(y)\)就是之前介绍过的语言模型,最简单的可以用n-gram的方法;\(P(x|y)\)是由目标语言到源语言的翻译模型。为什么要把\(P(y|x)\)的求解变成\(P(x|y)*P(y)\)?逐个击破的意思,\(P(x|y)\)专注于翻译模型,翻译好局部的短语或者单词;而\(P(y)\)就是之前学习的语言模型,用来学习整个句子\(y\)的概率,专注于翻译出来的句子从整体上看起来更加通顺、符合语法与逻辑。所以问题就转化为怎样求解\(P(x|y)\)。 SMT进一步把\(P(x|y)\)分解成\(P(x,a|y)\),其中\(a\)表示一个对齐alignment,可以认为是两种语言之间单词和单词或短语和短语的一个对齐关系。如下图所示是一个英语和法语的alignment。对齐本身就很复杂,存在1对1,1对多,多对1,多对多等情况,所以\(P(x,a|y)\)的求解在给定\(y\)的情况下,不但要考虑对齐方案\(a\)的情况\(P(a|y)\),还需要考虑对齐之后词与词的翻译情况\(P(x|a,y)\),可能的情况非常多。 那么,SMT怎样找到\(\arg max_y\)呢?穷举所有情况是不可能的,启发式搜索是可行的。形象描述就是在搜索过程中,对概率较低的路径进行剪枝,只保留概率较大的翻译情况。如下图的搜索树,对于概率较低的路径就不往下搜索了。 总之,统计机器翻译非常复杂,有很多的子模块,需要很多的人工干预和特征工程。 2014年,seq2seq模型横空出世,神经网络机器翻译(Neural Machine Translation, NMT)方兴未艾。seq2seq顾名思义,就是从序列到序列的模型,因为机器翻译的源语言和目标语言都是seq。 seq2seq的NMT如下图所示,它由两个RNN组成,左边的红色部分称为Encoder RNN,它负责对源语言进行编码(Encode);右边的绿色部分称为Decoder RNN,它负责对目标语言进行解码(Decode)。首先,Encoder RNN可以是任意一个RNN,比如朴素RNN、LSTM或者GRU。Encoder RNN负责对源语言进行编码,学习源语言的隐含特征。Encoder RNN的最后一个神经元的隐状态作为Decoder RNN的初始隐状态。Decoder RNN是一个条件语言模型,一方面它是一个语言模型,即用来生成目标语言的;另一方面,它的初始隐状态是基于Encoder RNN的输出,所以称Decoder RNN是条件语言模型。Decoder RNN在预测的时候,需要把上一个神经元的输出作为下一个神经元的输入,不断的预测下一个词,直到预测输出了结束标志符<END>,预测结束。Encoder RNN的输入是源语言的word embeding,Decoder RNN的输入是目标语言的word embeding。 seq2seq是一个很强大的模型,不但可以用来做机器翻译,还可以用来做很多NLP任务,比如自动摘要、对话系统等。 seq2seq作为一个条件语言模型,形式化来说,它直接对\(P(y|x)\)进行建模,在生成\(y\)的过程中,始终有\(x\)作为条件,正如下图的条件概率所示。 上面介绍了seq2seq的预测过程,seq2seq的训练过程如下图所示。训练的时候,我们同时需要源语言和翻译好的目标语言,分别作为Encoder RNN和Deocder RNN的输入。对于Encoder RNN没什么好说的。Decoder RNN在训练阶段,每一个时间步的输入是提供的正确翻译词,输出是预测的下一个时间步的词的概率分布,比如在\(t=4\),预测输出是\(\hat y_4\),而正确答案是“with”,根据交叉熵损失函数,\(J_4=-\log P(“with”)\)。总的损失函数就是所有时间步的损失均值。 seq2seq的训练过程是end2end的,即把Encoder RNN和Decoder RNN作为一个整体进行训练,不会像SMT一样有很多的子模块单独训练。当然seq2seq也可以单独对encoder和deconder进行训练优化,再组合,但是这个效果不一定会比整体优化encoder和deconder更好。 上上张图介绍的seq2seq的预测过程,实际上是一个贪心的预测过程,即在Decoder RNN的每一步都贪心选择\(\hat y_t\)概率最大的那个词。但是贪心只能保证每一步是最优的,无法保证预测出来的句子整体是最优的。特别是如果在\(t\)时刻贪心选择的词不是全局最优,会导致\(t\)时刻往后的所有预测词都是错误的,没有回头路了。但是如果每个时间步都穷举所有可能的情况的话,时间复杂度\(O(V^T)\)又太高了。 Beam search搜索策略是贪心策略和穷举策略的一个折中方案,它在预测的每一步,都保留Top-k高概率的词,作为下一个时间步的输入。k称为beam size,k越大,得到更好结果的可能性更大,但计算消耗也越大。请注意,这里的Top-k高概率不仅仅指当前时刻的\(\hat y_t\)的最高概率,而是截止目前这条路径上的累计概率之和,如下图的公式所示。 举例如下,假设\(k=2\),第一个时间步保留Top-2的词为”he”和”I”,他们分别作为下一个时间步的输入。”he”输入预测输出前两名是”hit”和”struck”,则”hit”这条路的累加概率是”he”的概率加上”hit”的概率=-1.7,类似的可以算出其他几个词对应路径的概率打分。最后在这4条路上保留\(k=2\)条路,所以”hit”和”was”对应路径保留,作为下一个时间步的输入;”struck”和”got”对应路径被剪枝。 最终的搜索树如下图所示,可以看到在每个时间步都只保留了\(k=2\)个节点往下继续搜索。最后”pie”对应的路径打分最高,通过回溯法得到概率最高的翻译句子。请注意,beam search作为一种剪枝策略,并不能保证得到全局最优解,但它能以较大的概率得到全局最优解,同时相比于穷举搜索极大的提高了搜索效率。 在beam search的过程中,不同路径预测输出结束标志符<END>的时间点可能不一样,有些路径可能提前结束了,称为完全路径,暂时把这些完全路径放一边,其他路径接着beam search。beam search的停止条件有很多种,可以设置一个最大的搜索时间步数,也可以设置收集到的最多的完全路径数。 当beam search结束时,需要从n条完全路径中选一个打分最高的路径作为最终结果。由于不同路径的长度不一样,而beam search打分是累加项,累加越多打分越低,所以需要用长度对打分进行归一化,如下图所示。那么,为什么不在beam search的过程中就直接用下面的归一化打分来比较呢?因为在树搜索的过程中,每一时刻比较的两条路径的长度是一样的,即分母是一样的,所以归一化打分和非归一化打分的大小关系是一样的,即在beam search的过程中就没必要对打分进行归一化了。 ...

August 2, 2019 · 1 min

CS224N(1.29)Vanishing Gradients, Fancy RNNs

梯度消失 今天介绍RNN的梯度消失问题以及为了解决这个问题引出的RNN变种,如LSTM何GRU。 在上一篇博客中,通过公式推导,我们已经解释了RNN为什么容易产生梯度消失或梯度爆炸的问题,核心问题就是RNN在不同时间步使用共享参数\(W\),导致\(t+n\)时刻的损失对\(t\)时刻的参数的偏导数存在\(W\)的指数形式,一旦\(W\)很小或很大就会导致梯度消失或梯度爆炸的问题。下图形象的显示了梯度消失的问题,即梯度不断反传,梯度不断变小(箭头不断变小)。 梯度消失会带来哪些问题呢?一个很明显的问题就是参数更新更多的受到临近词的影响,那些和当前时刻\(t\)较远的词对当前的参数更新影响很小。如下图所示,\(h^{(1)}\)对\(J^{(2)}(\theta)\)的影响就比对\(J^{(4)}(\theta)\)的影响大。久而久之,因为梯度消失,我们就不知道\(t\)时刻是真的对\(t+n\)时刻没影响还是因为梯度消失导致我们没学习到这种影响。 下图是一个更形象的例子,假设我们需要预测句子The writer of the books下一个单词,由于梯度消失,books对下一个词的影响比writer对下一个词的影响更大,导致模型错误的预测成了are,但这显然是不对的。 类似的,如果梯度爆炸,则根据梯度下降的更新公式,参数会一瞬间更新非常大,导致网络震荡,甚至出现Inf或NaN的情况。 梯度爆炸一个比较好的解决方法是梯度裁剪,即如果发现梯度的范数大于某个阈值,则以一定的比例缩小梯度的范数,但不改变其方向。如下下图所示,左子图是没有梯度裁剪的情况,由于RNN的梯度爆炸问题,导致快接近局部极小值时,梯度很大,参数突然爬上悬崖,然后又飞到右边一个随机的区域,miss掉了中间的局部极小值。右子图是增加了梯度裁剪之后,更新步伐变小,参数稳定在局部极小值附近。 总的来说,梯度爆炸相对好解决,但梯度消失就没那么简单了。在RNN中,每个时刻\(t\),都改写了前一个时刻的隐状态,而由于梯度消失问题,长距离以前的状态对当前时刻的影响又很小,所以导致无法建模长距离依赖关系。那么,如果把每个时刻的状态单独保存起来,是否能解决长距离依赖问题呢? LSTM LSTM就是这样一个思路,请大家结合如下两幅图来理解: (下图)首先,从宏观上来说,LSTM的隐层神经元不仅包含隐状态\(h_t\),还专门开辟了一个cell来保存过去的“记忆”\(c_t\),LSTM希望用\(c_t\)来传递很久以前的信息,以达到长距离依赖的目的。所以LSTM隐层神经元的输入是上一时刻的隐状态\(h_{t-1}\)和记忆\(c_{t-1}\),输出是当前时刻的隐状态\(h_t\)和希望传递给下一个时刻的记忆\(c_t\)。 (上图)每个时刻\(t\),为了调控遗忘哪些记忆,写入哪些新记忆,LSTM设置了两个门,分别是遗忘门\(f^{(t)}\)和写入门\(i^{(t)}\)。它们都是上一时刻的隐状态\(h^{(t-1)}\)和当前时刻的输入\(x^{(t)}\)的函数。\(f^{(t)}\)控制遗忘哪些记忆,即\(f^{(t)}\circ c^{(t-1)}\);\(i^{(t)}\)控制写入哪些新记忆,即\(i^{(t)}\circ \tilde c^{(t)}\),其中\(\tilde c^{(t)}\)即为期望写入的新记忆,它也是\(h^{(t-1)}\)和\(x^{(t)}\)的函数。最终,新时刻\(t\)的记忆就是这两部分的组合,请看上图\(c^{(t)}\)表达式。 (上图)输出门\(o^{(t)}\)控制哪些记忆需要输出到下一个隐状态\(h^{(t)}\),\(o^{(t)}\)自己又是\(h^{(t-1)}\)和\(x^{(t)}\)的函数。 大家结合上图的公式和下图的示意图就不难理解了。 LSTM解决梯度消失最直接的方法就是,遗忘门选择不遗忘,每一时刻的\(f^{(t)}\)都选择记住前一时刻的记忆\(c^{(t-1)}\),然后直接传递给下一时刻。那么,所有前\(t-1\)时刻的记忆都会被完整的传递给第\(t\)时刻,从而对\(t\)时刻的输出产生影响。 而朴素RNN无法保存前期状态的原因就是因为朴素RNN把之前时间步的信息都一股脑存储在隐状态\(h^{(t)}\)中了,隐状态\(h^{(t)}\)成为了整个网络的瓶颈,一旦出现梯度消失,则很久以前的信息对当前时刻的影响就微乎其微了。LSTM的关键就是开辟了一个新的cell来存储记忆,这个新的cell相当于记忆的一条捷径,时刻\(t\)除了可以像常规RNN一样通过\(h^{(t-1)}\)来获取很久以前的信息,还可以通过cell存储的记忆\(c^{(t-1)}\)来便捷地获取到很久以前的信息,所以隐状态\(h^{(t)}\)不再成为整个网络的瓶颈,有新的cell来分担。 需要提醒的是,虽然LSTM开辟新的cell来存储记忆,但这个记忆也会受到连续梯度相乘的影响,所以依然存在梯度消失或梯度爆炸的问题,但从实际效果来看,LSTM性能很不错,也很鲁棒。 GRU 另一种能缓解RNN梯度消失的网络——GRU。为了简化LSTM,GRU又没有cell了,但依然保留了门来控制信息的传递。首先看下图最后一个公式,当前时刻的隐状态\(h^{(t)}\)等于上一时刻的隐状态\(h^{(t-1)}\)和新写入的隐状态\(\tilde h^{(t)}\)的加权平均,通过更新门\(u^{(t)}\)来控制它们之间的比例,\(u^{(t)}\)是上一时刻的隐状态\(h^{(t-1)}\)和当前时刻的输入\(x^{(t)}\)的函数。新写入的隐状态\(\tilde h^{(t)}\)又通过一个重置门\(r^{(t)}\)来控制,类似的,\(r^{(t)}\)也是\(h^{(t-1)}\)和\(x^{(t)}\)的函数。 个人觉得,GRU中的更新门\(u^{(t)}\)类似于LSTM中的输出门\(o^{(t)}\);GRU中的重置门\(r^{(t)}\)类似于LSTM中的遗忘门\(f^{(t)}\)和写入门\(i^{(t)}\)的组合;GRU中新写入的隐状态\(\tilde h^{(t)}\)类似于LSTM中的细胞记忆\(c^{(t)}\)。所以,可以把GRU看作LSTM的简化版本。 直观来说,GRU和LSTM类似,解决梯度消失的策略就是新增\(u^{(t)}\)来控制\(h^{(t-1)}\)和\(\tilde h^{(t)}\)的比例,如果\(u^{(t)}=0\),则\(h^{(t)}=h^{(t-1)}\),即\(t\)时刻的隐状态和上一时刻的隐状态相同,虽然这肯定效果不好,但至少说明GRU是有能力保留之前的隐状态的。 GRU和LSTM的性能差不多,但GRU参数更少,更简单,所以训练效率更高。但是,如果数据的依赖特别长且数据量很大的话,LSTM的效果可能会稍微好一点,毕竟参数量更多。所以默认推荐使用LSTM。 其他缓解梯度消失的策略 由于链式法则,或者所选非线性激活函数的原因,不仅仅RNN,所有神经网络都存在梯度消失或者梯度爆炸的问题,比如全连接网络和CNN。一些通用解决方法如下: ResNet。因为梯度是在传递的过程中逐渐减小并消失的,如果跨越好几层直接进行连接,天然能保持远距离信息。个人理解,这就相当于买家和卖家直接相连,没有中间商赚差价\(\mathcal F(x)\),买到的价格最接近卖出的价格\(x\)。能一定程度上减弱梯度消失的问题。 更激进的是DenseNet,把跨越多层之间的很多神经元都连起来,也就是说有更多的线路没有中间商赚差价,进一步减弱梯度消失问题。 HighwayNet。借鉴了LSTM和GRU的思路,不是像ResNet一样直接新增一条直连线路\(x\),而是搞一个平衡因子\(u\),卖家到买家的价格由\(u\)进行调和平均:\(u*\mathcal F(x)+(1-u)*x\),用\(u\)来控制多少走中间商,多少走直连线路。 虽然所有神经网络都存在梯度消失的问题,但RNN的这个问题更严重,因为它连乘的是相同的权重矩阵W,而且RNN针对的是序列问题,往往更深。 双向RNN 假设我们在对句子进行情感分类,如下图所示。对于terribly这个词,常规RNN,terribly的梯度只能看到左边的信息,看不到右边的信息,因为网络是从左到右的。单独看terribly或者从左往右看,在没有看到exciting时,可能认为terribly是贬义词,但是如果跟右边的exciting结合的话,则意思变为强烈的褒义词,所以有必要同时考虑左边和右边的信息。 双向RNN包含两个RNN,一个从左往右,一个从右往左,两个RNN的参数是独立的。最后把两个RNN的输出拼接起来作为整体输出。那么,对于terribly这个词,它的梯度能同时看到左边和右边的信息。 由于双向RNN对于某个时刻\(t\),既需要知道\(t\)时刻前的信息(Forward RNN),又需要知道\(t\)时刻之后的信息(Backward RNN),所以双向RNN无法用于学习语言模型,因为语言模型只知道时刻\(t\)之前的信息,下一时刻的词需要模型来预测。对于包含完整序列的NLP问题,双向RNN应该是默认选择,它通常比单向RNN效果更好。 多层RNN 前面展示的RNN从时间\(t\)的维度上来说可以认为是多层的,但是RNN还可以从另一个维度来增加层数。如下图所示,将上一层(RNN layer 1)的输出作为下一层(RNN layer 2)的输入,不断堆叠下去,变成一个多层RNN。通常来说,深度越大,性能越好,如果梯度下降能训练好的话。 RNN的层数通常不会很深,不会像CNN一样,达到上百层,RNN通常2层,最多也就8层。一方面是RNN的梯度消失问题比较严重,另一方面是RNN训练的时候是串行的,不易并行化,导致网络太深的话训练很花时间。 ...

August 1, 2019 · 1 min

CS224N(1.24)Language Models and RNNs

今天要介绍一个新的NLP任务——语言模型(Language Modeling, LM),以及用来训练语言模型的一类新的神经网络——循环神经网络(Recurrent Neural Networks, RNNs)。 语言模型就是预测一个句子中下一个词的概率分布。如下图所示,假设给定一个句子前缀是the students opened their,语言模型预测这个句子片段下一个词是books、laptops、exams、minds或者其他任意一个词的概率。形式化表示就是计算概率 $$\begin{eqnarray}P(x^{(t+1)}|x^{(t)},…,x^{(1)})\tag{1}\end{eqnarray}$$\(x^{(t+1)}\)表示第\(t+1\)个位置(时刻)的词是\(x\),\(x\)可以是词典\(V\)中的任意一个词。 既然语言模型在给定前t个词之后可以预测第t+1个词的概率,那么预测到第t+1个词之后,又可以递归的根据前t+1个词预测第t+2个词,如此不断的进行下去,就可以预测一整个句子的概率了。所以,也可以把语言模型看做一个可以计算一个句子出现的概率的系统,形式化表示就是如果一个句子是\(x^{(1)},…,x^{(T)}\) ,那么语言模型可以计算句子概率 $$\begin{eqnarray}P(x^{(1)},…,x^{(T)})& = & P(x^{(1)})\times P(x^{(2)}|x^{(1)})\times…\times P(x^{(T)}|x^{(T-1)},…,x^{(1)}) \tag{2}\\& = & \prod_{t=1}^T P(x^{(t)}|x^{(t-1)},…,x^{(1)})\tag{3}\end{eqnarray}$$可以看到(3)式连乘的项就是(1)式,所以这两个定义的内涵是一样的。 那么语言模型有什么用呢?它的用处可大了,比如现在的输入法会根据前一个输入的词预测下一个将要输入的词,此所谓智能输入法;比如在百度或谷歌搜索时,输入前几个关键词,搜索引擎会自动预测接下来可能的几个词;比如网上有很多智能AI会自动生成新闻、诗歌;还比如用在语音识别、机器翻译、问答系统等等。可以说语言模型是很多NLP任务的基础模块,具有非常重要的作用。 在前-深度学习时代,人们使用n-gram方法来学习语言模型。对于一个句子,n-gram表示句子中连续的n个词,比如还是上图的例子,n-gram对于n=1,2,3,4的结果是: 1-grams (unigrams): “the”, “students”, “opened”, “their” 2-grams (bigrams): “the students”, “students opened”, “opened their” 3-grams (trigrams): “the students opened”, “students opened their” 4-grams: “the students opened their” n-gram方法有一个前提假设,即假设每个词出现的概率只和前n-1个词有关,比如2-gram对于每个词出现的概率只和前面一个词有关,和更前面的词以及后面的词都没有关系,所以我们有如下图的assumption。又这是一个条件概率,展开之后得到如下除法的形式。n-gram的计算方法就是,统计语料库中出现\(x^{(t)},…,x^{(t-n+2)}\)的次数(分母),以及在这个基础上再接一个词\(x^{(t+1)}\)的次数\(x^{(t+1)},x^{(t)},…,x^{(t-n+2)}\)(分子),用后者除以前者来近视这个条件概率。 举个例子,假设完整的句子是as the proctor started the clock, the students opened their,需要预测下一个词的概率分布。对于4-gram方法,则只有students opened their对下一个词有影响,前面的词都没有影响。然后我们统计训练集语料库中发现,分母students opened their出现1000次,其后接books即students opened their books出现了400次,所以P(books|students opened their)=400/1000=0.4。类似的,可以算出下一个词为exams的概率是0.1。所以4-gram方法认为下一个词是books的概率更大。 ...

July 31, 2019 · 2 min