<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Transformer on bitJoy</title><link>https://bitjoy.net/tags/transformer/</link><description>Recent content in Transformer on bitJoy</description><generator>Hugo -- 0.148.2</generator><language>en</language><lastBuildDate>Wed, 04 Mar 2020 22:19:50 +0800</lastBuildDate><atom:link href="https://bitjoy.net/tags/transformer/index.xml" rel="self" type="application/rss+xml"/><item><title>CS224N（2.21）Transformers and Self-Attention For Generative Models</title><link>https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/</link><pubDate>Wed, 04 Mar 2020 22:19:50 +0800</pubDate><guid>https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/</guid><description>&lt;p>今天介绍大名鼎鼎的Transformer，它于2017年出自谷歌的论文《Attention Is All You Need》（&lt;a href="https://arxiv.org/pdf/1706.03762.pdf">https://arxiv.org/pdf/1706.03762.pdf&lt;/a>），用Attention实现机器翻译模型，并取得了新的SOTA性能。&lt;/p>
&lt;p>传统的机器翻译模型一般是结合RNN和Attention，可以看我之前的博客介绍：&lt;a href="https://bitjoy.net/posts/2019-08-02-cs224n-0131-translation-seq2seq-attention/">CS224N（1.31）Translation, Seq2Seq, Attention&lt;/a>。虽然RNN+Attention的组合取得了不错的效果，但依然存在一些问题。由于RNN是序列依赖的模型，难以并行化，训练时间较长；且当句子很长时由于梯度消失难以捕捉长距离依赖关系。虽然相继推出的LSTM和GRU能一定程度上缓解梯度消失的问题，但这个问题依然存在。而且LSTM和GRU难以解释，我们根本不知道当前timestep依赖远的词多一点还是近的词多一点。&lt;/p>
&lt;p>Transformer的思想很激进，它完全抛弃了RNN，只保留Attention，从其论文标题可见一斑。RNN无法并行化的根本原因是它的正向和反向传播是沿着句子方向（即水平方向），要想实现并行化，肯定不能再走水平方向了。于是，Transformer完全抛弃水平方向的RNN，而是在垂直方向上不断叠加Attention。由于每一层的Attention计算只和其前一层的Attention输出有关，所以当前层的所有词的Attention可以并行计算，互不干扰，这就使得Transformer可以利用GPU进行并行训练。&lt;/p>
&lt;p>具体来说，Transformer的结构如下图所示。我们知道end2end的机器翻译模型一般都是Encoder+Decoder的组合，Encoder对源句子进行编码，将编码信息传给Decoder，Decoder翻译出目标句子。Transformer也不例外，下图左边即为Encoder，右边即为Decoder。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/p13.png">&lt;/p>
&lt;p>Encoder的每一层有两个子层组成，包括Self-Attention和Feed-forward neural network (FFNN)。FFNN就是常规的全连接网络，没什么可说的，下面重点介绍Self-Attention。&lt;/p>
&lt;p>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。图中右下角的公式中的分母只是个缩放因子。&lt;/p>
&lt;p>回顾一下，一个标准的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\)。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/p14.png">&lt;/p>
&lt;p>Self-Attention的优点。因为每个词都和周围所有词做attention，所以任意两个位置都相当于有直连线路，可捕获长距离依赖。而且Attention的可解释性更好，根据Attention score可以知道一个词和哪些词的关系比较大。易于并行化，当前层的Attention计算只和前一层的值有关，所以一层的所有节点可并行执行self-attention操作。计算效率高，一次Self-Attention只需要两次矩阵运算，速度很快。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/p11.png">&lt;/p>
&lt;p>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阶段依然要一个个词顺序翻译，依然是串行的。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/p15.png">&lt;/p>
&lt;p>不要忘了我们的任务是机器翻译，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的作用就是看看当前要翻译的词在源句子中各个词上的注意力情况。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/TheTransformer.png">&lt;/p>
&lt;p>我们知道Attention机制是位置无关的，因为对于每个词，它都和句子中的所有词直连求Attention score，跟词在句子中的位置没有关系。但是句子作为一种线性结构，词在句子中的顺序对句子的含义至关重要。为了考虑词的位置信息，词在输入Attention前，把词向量和词在句子中的位置Positional Encoding加起来，得到一个新的向量，输入到Attention中，如上图所示。这个Positional Encoding可通过公式计算得到，这里不展开。&lt;/p>
&lt;p>上图的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，相当于不同的特征提取器。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-03-04-cs224n-0221-transformers-and-self-attention-for-generative-models/p25.png">&lt;/p>
&lt;p>课上还介绍了利用Transformer生成图片和音乐的应用，感兴趣的同学可以搜索相关论文看一看。&lt;/p>
&lt;p>有关Transformer的介绍，还可参考如下三个链接：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://jalammar.github.io/illustrated-transformer/">http://jalammar.github.io/illustrated-transformer/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/48508221">https://zhuanlan.zhihu.com/p/48508221&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/44121378">https://zhuanlan.zhihu.com/p/44121378&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>CS224N（2.19）Contextual Word Embeddings</title><link>https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/</link><pubDate>Mon, 24 Feb 2020 20:21:45 +0800</pubDate><guid>https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/</guid><description>&lt;p>今天介绍几种新的词向量学习方法，在此之前，建议大家看看我关于word2vec或GloVe等传统词向量的介绍：&lt;a href="https://bitjoy.net/posts/2019-06-22-cs224n-1-8-introduction-and-word-vectors/">CS224N（1.8）Introduction and Word Vectors&lt;/a>。&lt;/p>
&lt;p>传统词向量，比如word2vec，它在训练阶段学习到一个词的向量表示之后，在下游的各种NLP任务中，这个词向量不再变动了。也就是说传统词向量的特点是，对一个词只学习一个词向量，且在具体任务中固定不变。传统词向量有两个主要的不足：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>难以表达一词多义。一个词在不同的上下文语境中可能表示不同的含义，比如“苹果”在“苹果真好吃”和“苹果手机很好用”这两个句子中表示不同的含义，但word2vec学习到的“苹果”词向量只有一个，也就是说下游任务对于这两个句子用的是同一个词向量。虽然word2vec的词向量可能同时包含了这两个含义，但它把这两个含义糅合到一个向量中了，导致在“苹果真好吃”中可能引入了“苹果手机”的干扰因素，在“苹果手机很好用”中引入了“吃的苹果”的干扰因素。总之就是，word2vec学习到的词向量粒度较粗，向量固定不变，无法根据具体的上下文语境进行改变。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>难以表达不同的语法或语义信息。一个词，即使是同一个意思，在语法或语义上也可能充当不同的角色，比如“活动”这个词，既可以做名词、也可以做动词，既可以做主语、也可以做谓语等。但word2vec对一个词只给出一个词向量，无论这个词在句子中充当什么角色，词向量都是一样的。虽然word2vec训练时可能已经学到了一个词的不同语法或语义特征，但它把这些信息糅合到一个向量中了，也就是粒度较粗的问题。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>其实上述两点暴露出来的word2vec的不足，本质上是同样的两个原因：1. 词向量是静态的，无法根据上下文进行调整；2. 词向量表示只有一个向量，糅合了太多信息，粒度较粗。&lt;/p>
&lt;p>下图中的word type表示一个相同的词，比如“苹果”；word token表示同一个word type在不同上下文的具体实例，比如“苹果真好吃”和“苹果手机很好用”中的“苹果”就是两个不同的word token。word type和word token有点类似于类和实例的关系。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/p11.png">&lt;/p>
&lt;hr>
&lt;p>2018年艾伦人工智能研究所提出了ELMo: Embeddings from Language Models，即从语言模型中学习词向量的方法，它的原文标题为Deep contextualized word representations（&lt;a href="https://www.aclweb.org/anthology/N18-1202.pdf">https://www.aclweb.org/anthology/N18-1202.pdf&lt;/a>），即深度的上下文词向量表示。ELMo很好地解决了上述传统词向量的两个不足，ELMo对一个词的表示由多个向量组成，并且每个向量的权重在具体的上下文中动态更新，由此不但粒度更细，而且能根据上下文动态调整一个词的最终词向量。使用ELMo词向量，作者在很多NLP任务上刷新了SOTA，在当年引起了很大的轰动。&lt;/p>
&lt;p>ELMo有三个特点：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>学习的是word token的词向量，根据上面的定义，word token与具体的上下文有关，不再是静态的word type的词向量；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用很长的上下文进行学习，而不像word2vec一样使用较小的滑动窗口，所以ELMo能学到长距离词的依赖关系；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用双向的语言模型进行学习，并使用网络的所有隐藏层作为这个词的特征表示。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/p20.png">&lt;/p>
&lt;p>下面介绍ELMo具体的训练过程，以下截图来自&lt;a href="https://www.youtube.com/watch?v=UYPa347-DdE&amp;amp;list=PLJV_el3uVTsOK_ZK5L0Iv_EQoL1JefRL4&amp;amp;index=61">台湾大学李宏毅老师的教学视频&lt;/a>。&lt;/p>
&lt;p>首先，既然word2vec使用滑动窗口只能学习到局部特征，那么ELMo就用RNN来建模长距离的依赖关系。如下图所示，ELMo使用双向语言模型bi-LM学习每个词的特征。我们知道，语言模型是给定一个句子前缀，预测下一个可能出现的词，也就是说常规的语言模型是只知道t时刻前的信息，不知道t时刻之后的信息，即通常都是单向的。而ELMo则使用了bi-LM，比如下图我们要学习“退了”这个词的特征，在前向网络中，模型学到了在给定上文的情况下，“退了”的隐藏层特征；在反向网络中，模型学到了在给定下文的情况下，“退了”的隐藏层特征。最后，ELMo把两个方向的隐藏层特征拼起来，作为bi-LM学到的“退了”这个词的特征表示。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/elmo-1.png">&lt;/p>
&lt;p>为了学到更多的特征，ELMo对双向RNN进行堆叠，每增加一层就能多学习到2个特征表示（一正一反，下图把这两个向量拼接起来作为一个整体向量h）。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/elmo-2.png">&lt;/p>
&lt;p>ELMo文中只使用了两层的双向LSTM抽取特征，所以对一个词能抽取到4个特征表示，即下图中的h1和h2（每个h包含一正一反特征向量组合）。&lt;/p>
&lt;p>在使用ELMo词向量时，每个词的最终词向量是所有隐藏层特征向量h的加权求和，系数是α。这个系数是根据词在不同的上下文中学习得来的。ELMo文章分析发现，不同的NLP任务学到的系数不尽相同，比如在Coref和SQuAD任务中，第一层的系数更大。有可能ELMo在第一层学到的是词的句法特征，第二次学到的是更高级的语义特征。有点类似于CNN中在浅层学到点、线、转角，在高层学到轮廓等高级特征。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/elmo-3.png">&lt;/p>
&lt;p>ELMo的形式化表示如下图所示。假设堆叠的bi-LSTM有L层，则每一层都能学到前向特征\(\underset{h_{k,j}^{LM}}{\rightarrow}\)和反向特征\(\underset{h_{k,j}^{LM}}{\leftarrow}\)，这两个组合起来就是\(h_{k,j}^{LM}\)（也就是上图的\(h_i\)）。下图的ELMo还有个\(x_k^{LM}\)，它是bi-LM的词向量输入，这相当于词最原始的输入向量，ELMo使用char-CNN抽取一个词的原始输入向量，和上一课提到的Subword model类似，这里不展开。下图j=0时的特征就是\(x_k^{LM}\) 。&lt;/p>
&lt;p>所以，对于词\(k\)，ELMo得到的完整词向量是\(R_k\)，它其实是这个词的一系列特征向量的组合，它还不是这个词最终的词向量，因为最终的词向量要在具体的NLP任务中根据上下文来定。词\(k\)在某个任务task中的词向量\(ELMo_k^{task}\)如下图所示，它等于对所有层的特征向量的加权求和，权重\(s_j^{task}\)（即上图的α）根据当前任务动态学习得到。最终还会有一个参数\(\gamma^{task}\)，控制ELMo词向量对任务的贡献程度。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/p22.png">&lt;/p>
&lt;p>不同层抽取的特征不一样，ELMo只使用了两层bi-LSTM，如果堆叠更多层的话，估计能学到更多有意思的特征。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/p27.png">&lt;/p>
&lt;p>ELMo词向量的使用方法。在下游的NLP任务中，既可以把ELMo词向量作为输入层特征，也可以将其拼接到隐藏层，反正都需要学习参数\(s_j^{task}\)和\(\gamma^{task}\)。&lt;/p>
&lt;p>性能方面，ELMo刷新了很多NLP任务的SOTA，大概能提高3个百分点。&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://bitjoy.net/posts/2020-02-24-cs224n-0219-contextual-word-embeddings/p26.png">&lt;/p>
&lt;p>个人理解word2vec和ELMo的本质区别。其实两者在训练阶段都考虑了上下文，只不过word2vec使用的是滑动窗口，只能学习局部特征，而ELMo使用RNN，能学到长距离特征。word2vec学到embedding之后固定成一个向量了，在下游NLP任务中不再变动；而ELMo训练完之后得到这个词的多个特征向量（词的隐含特征表示，粒度更细），且在下游NLP任务中会根据上下文动态组合这些特征向量，得到在这一上下文中的词向量表示（针对性更强）。&lt;/p>
&lt;p>其实，仔细想想的话，这个本质区别也没有那么大，虽然word2vec训练到的词向量只有一个，且固定了，但词向量的维度如果够大的话，可以用不同维度来表示一词多义呀。而且用到下游NLP任务时，词向量不同维度的权重也可以不一样啊，不就相当于ELMo的不同\(s_j^{task}\)吗？虽然如此，可能还是不如ELMo的多个隐藏层向量，一方面是经过隐藏层的特征抽取，另一方面是毕竟ELMo出来的特征向量有2L+1个，仅维度上就比word2vec多很多，效果好是理所应当的。&lt;/p>
&lt;p>ELMo的作者在2017年发过TagLM的工作，和ELMo类似，被称为Pre-ELMo，感兴趣的可以阅读原文（&lt;a href="https://arxiv.org/abs/1705.00108">https://arxiv.org/abs/1705.00108&lt;/a>）。ELMo是把语言模型的隐藏层抽取出来作为词的特征向量，2017年的CoVe词向量是把机器翻译过程中的隐藏层提取出来作为词的特征向量，但效果没ELMo好，感兴趣的可以阅读原文（&lt;a href="https://arxiv.org/abs/1708.00107">https://arxiv.org/abs/1708.00107&lt;/a>）。&lt;/p>
&lt;p>同样在2018年，OpenAI和GoogleAI借助Transformer模型，提出了更加强大的上下文词向量学习方法GPT和BERT。Transformer是为了解决RNN无法并行化的问题，提出Attention is all you need（&lt;a href="https://arxiv.org/abs/1706.03762">https://arxiv.org/abs/1706.03762&lt;/a>）。以前我们提到&lt;a href="https://bitjoy.net/posts/2019-08-02-cs224n-0131-translation-seq2seq-attention/">Attention用来提升RNN的性能&lt;/a>，Transformer更加激进，直接不要RNN，只留下Attention，然后堆叠网络深度，使得其可以和CNN一样并行化，使用GPU之后可以大大加速训练过程。下节课将详细介绍Transformer的内容，这里我们只需要知道Transformer是一个强大的seq2seq（Encoder-Decoder）模型。&lt;/p>
&lt;p>BERT就是借助Transformer学习上下文词向量的方法，它的全称是BERT (Bidirectional Encoder Representations from Transformers): Pre-training of Deep Bidirectional Transformers for Language Understanding。&lt;/p></description></item></channel></rss>