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(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