This commit is contained in:
erenup 2021-09-01 00:21:46 +08:00
parent e1bfc0db47
commit 0f039659f5
1 changed files with 152 additions and 151 deletions

View File

@ -1,217 +1,220 @@
## 图解GPT # 图解GPT
除了BERT以外另一个预训练模型GPT也给NLP领域带来了不少轰动本节也对GPT做一个详细的讲解感兴趣的读者可以选择性阅读。 内容组织:
- 图解GPT
- 什么是语言模型
- 自编码auto-encoder语言模型
- 自回归auto-regressive语言模型
- 基于Transformer的语言模型
- Transformer进化
- GPT2概述
- GPT2详解
- 输入编码
- 多层Decoder
- Decoder中的Self-Attention
- 详解Self-Attention
- 可视化Self-Attention
- 图解Masked Self-attention
- GPT2中的Self-Attention
- GPT2全连接神经网络
- 语言模型应用
- 机器翻译
- 生成摘要
- 迁移学习
- 音乐生成
- 总结
- 致谢
![结构总览](./pictures/4-stru.webp)图: 结构总览 除了BERT以外另一个预训练模型GPT也给NLP领域带来了不少轰动本节也对GPT做一个详细的讲解。
## 前言 OpenAI提出的GPT-2模型(https://openai.com/blog/better-language-models/)能够写出连贯并且高质量的文章比之前语言模型效果好很多。GPT-2是基于Transformer搭建的相比于之前的NLP语言模型的区别是基于Transformer大模型、在巨大的数据集上进行了预训练。在本章节中我们将对GPT-2的结构进行分析对GPT-2的应用进行学习同时还会深入解析所涉及的self-attention结构。本文可以看作是篇章2.2图解Transformer、2.3图解BERT的一个补充。
这篇文章翻译自[GPT2](http://jalammar.github.io/illustrated-gpt2)。多图详细解释当今最为强大的人工智能 GPT-2(截至 2019 年 8 月 12 日)。 这篇文章翻译自[GPT2](http://jalammar.github.io/illustrated-gpt2)。
今年我们看到了机器学习在许多场景的广泛应用。OpenAI GPT-2(https://openai.com/blog/better-language-models/)表现出了令人印象深刻的能力它能够写出连贯而充满激情的文章这超出了我们当前对语言模型的预期效果。GPT-2 不是一个特别新颖的架构,而是一种与 Transformer 解码器非常类似的架构。不过 GPT-2 是一个巨大的、基于 Transformer 的语言模型,它是在一个巨大的数据集上训练的。在这篇文章,我们会分析它的结构,以及这种结构产生的作用。我们会深入了解 Self Attention 层的细节。然后我们会再了解一下这种只有 Decoder 的 Transformer 在语言建模之外的应用。 ## 语言模型和GPT-2
这篇文章可以看作是之前文章[《图解Transformer完整版》](https://github.com/datawhalechina/transformers-quick-start-zh/blob/main/transformer%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/2-%E5%9B%BE%E8%A7%A3transformer.md)的补充。图解 Transformer 的文章使用了更多的图来解释 Transformer 的内部工作原理,以及它们是如何从原始论文一步一步进化的。我希望这种可视化的方式能够更加容易解释基于 Transformer 的模型内部原理和进化。
## GPT2 和语言模型
首先,我们来看看什么是语言模型。
### 什么是语言模型 ### 什么是语言模型
本文主要描述和对比2种语言模型
在 图解 Word2Vec(https://jalammar.github.io/illustrated-word2vec/) 中,我们了解到语言模型基本上是一个机器学习模型,它可以根据句子的一部分预测下一个词。最著名的语言模型就是手机键盘,它可以根据你输入的内容,提示下一个单词。 - 自编码auto-encoder语言模型
- 自回归auto-regressive语言模型
![词之间的关系](./pictures/4-word2vec.webp)图:词之间的关系
从这个意义上讲GPT-2 基本上就是键盘应用程序中预测下一个词的功能,但 GPT-2 比你手机上的键盘 app 更大更复杂。GPT-2 是在一个 40 GB 的名为 WebText 的数据集上训练的OpenAI 的研究人员从互联网上爬取了这个数据集,作为研究工作的一部分。从存储空间大小方面来比较,我使用的键盘应用程序 SwiftKey占用了 78 MB 的空间。而最小的 GPT-2 变种,需要 500 MB 的空间来存储它的所有参数。最大的 GPT-2 模型变种是其大小的 13 倍,因此占用的空间可能超过 6.5 GB。
![GPT发展](./pictures/4-gpt-his.webp)图GPT发展
对 GPT-2 进行实验的一个很好的方法是使用 AllenAI GPT-2 Explorer(https://gpt2.apps.allenai.org/?text=Joel is)。它使用 GPT-2 来显示下一个单词的 10 种预测(包括每种预测的分数)。你可以选择一个单词,然后就能看到下一个单词的预测列表,从而生成一篇文章。
### 语言模型的 Transformer
正如我们在图解 Transformer中看到的原始的 Transformer 模型是由 Encoder 和 Decoder 组成的,它们都是由 Transformer 堆叠而成的。这种架构是合适的因为这个模型是用于处理机器翻译的。在机器翻译问题中Encoder-Decoder 的架构已经在过去成功应用了。
![transformer](./pictures/4-transformer.webp)图transformer
在随后的许多研究工作中,只使用 Transformer 中的一部分,要么去掉 Encoder要么去掉 Decoder并且将它们堆得尽可能高。使用大量的训练文本并投入大量的计算数十万美元用于训练这些模型在 AlphaStar 的例子中可能是数百万美元)。
![gpt-bert](./pictures/4-gpt-bert.webp)图gpt-bert
我们可以将这些模块堆得多高呢?事实证明,这是区分不同的 GPT-2 的主要因素之一。 先看自编码语言模型。
自编码语言模型典型代表就是篇章2.3所描述的BERT。如下图所示自编码语言模型通过随机Mask输入的部分单词然后预训练的目标是预测被Mask的单词不仅可以融入上文信息还可以自然的融入下文信息。
![gpt区分](./pictures/4-gpt-his2.webp)图gpt区分 ![BERT mask](./pictures/3-bert-mask.webp)图: BERT mask
### 与 BERT 的一个不同之处 自编码语言模型的优缺点:
- 优点:自然地融入双向语言模型,同时看到被预测单词的上文和下文
- 缺点:训练和预测不一致。训练的时候输入引入了[Mask]标记,但是在预测阶段往往没有这个[Mask]标记导致预训练阶段和Fine-tuning阶段不一致。
``` 接着我们来看看什么是常用的自回归auto-regressive语言模型语言模型根据输入句子的一部分文本来预测下一个词。日常生活中最常见的语言模型就是输入法提示它可以根据你输入的内容提示下一个单词。
机器人第一定律:
机器人不得伤害人类,也不能因不作为而使人类受到伤害。 ![词之间的关系](./pictures/4-word2vec.webp)
```
GPT-2 是使用 Transformer 的 Decoder 模块构建的。另一方面BERT 是使用 Transformer 的 Encoder 模块构建的。我们将在下一节中研究这种差异。但它们之间的一个重要差异是GPT-2 和传统的语言模型一样,一次输出一个 token。例如让一个训练好的 GPT-2 背诵机器人第一定律: 图:输入提示
![gpt2 output](./pictures/4-gpt2-output.webp)图: gpt2 output 自回归语言模型的优点和缺点:
- 优点对于生成类的NLP任务比如文本摘要机器翻译等从左向右的生成内容天然和自回归语言模型契合。
- 缺点:由于一般是从左到右(当然也可能从右到左),所以只能利用上文或者下文的信息,不能同时利用上文和下文的信息。
这些模型的实际工作方式是,在产生每个 token 之后,将这个 token 添加到输入的序列中形成一个新序列。然后这个新序列成为模型在下一个时间步的输入。这是一种叫“自回归auto-regression”的思想。这种做法可以使得 RNN 非常有效。 GPT-2属于自回归语言模型相比于手机app上的输入提示GPT-2更加复杂功能也更加强大。因为OpenAI的研究人员从互联网上爬取了40GB的WebText数据集并用该数据集训练了GPT-2模型。我们可以直接在[AllenAI GPT-2 Explorer网站](https://gpt2.apps.allenai.org/?text=Joel)上试用GPT-2模型。
![gpt2 output](./pictrues/../pictures/2-4-gpt-2-autoregression-2.gif)图自回归GPT-2
![gpt2 output](./pictures/4-gpt2-output2.webp)图: gpt2 output ![GPT发展](./pictures/4-gpt-his.webp)图多种GPT模型
### 基于Transformer的语言模型
GPT-2和后来的一些模型如 TransformerXL 和 XLNet本质上都是自回归的模型。但 BERT 不是自回归模型。这是一种权衡。去掉了自回归后BERT 能够整合左右两边的上下文从而获得更好的结果。XLNet 重新使用了 自回归,同时也找到一种方法能够结合两边的上下文。 正如我们在图解Transformer所学习的原始的Transformer模型是由 Encoder部分和Decoder部分组成的它们都是由多层transformer堆叠而成的。原始Transformer的seq2seq结构很适合机器翻译因为机器翻译正是将一个文本序列翻译为另一种语言的文本序列。
![transformer](./pictures/4-transformer.webp)图原始Transformer结构
但如果要使用Transformer来解决语言模型任务并不需要完整的Encoder部分和Decoder部分于是在原始Transformer之后的许多研究工作中人们尝试只使用Transformer Encoder或者Decoder并且将它们堆得层数尽可能高然后使用大量的训练语料和大量的计算资源数十万美元用于训练这些模型进行预训练。比如BERT只使用了Encoder部分进行masked language model自编码训练GPT-2便是只使用了Decoder部分进行自回归auto regressive语言模型训练。
![gpt-bert](./pictures/4-gpt-bert.webp)图GPT、BERT、Transformer-XL
![gpt区分](./pictures/4-gpt-his2.webp)图层数越来越多的GPT2模型
### Transformer 模块的进化 ### Transformer进化
Transformer的Encoder进化成了BERTDecoder进化成了GPT2。
Transformer 原始论文(https://arxiv.org/abs/1706.03762) 介绍了两种模块: 首先看Encoder部分。
Encoder 模块 ![encoder](./pictures/4-encoder.webp)
首先是 Encoder 模块。 encoder
原始的Transformer论文中的Encoder部分接受特定长度的输入如 512 个 token。如果一个输入序列比这个限制短我们可以使用pad填充序列的其余部分。如篇章2.3所讲BERT直接使用了Encoder部分。
![encoder](./pictures/4-encoder.webp)图: encoder 再回顾下Decoder部分
与Encoder相比Decoder部分多了一个Encoder-Decoder self-attention层使Decoder可以attention到Encoder编码的特定的信息。
原始的 Transformer 论文中的 Encoder 模块接受特定长度的输入(如 512 个 token。如果一个输入序列比这个限制短我们可以填充序列的其余部分。
Decoder 模块
其次是 Decoder。与 Encoder 相比,它在结构上有一个很小的差异:它有一个层,使得它可以关注来自 Encoder 特定的段。
![decoder](./pictures/4-decoder.webp)图: decoder ![decoder](./pictures/4-decoder.webp)图: decoder
这里的 Self Attention 层的一个关注差异是,它会屏蔽未来的 token。具体来说它不像 BERT 那样将单词改为mask而是通过改变 Self Attention 的计算,阻止来自被计算位置右边的 token Decoder中的的 Masked Self-Attention会屏蔽未来的token。具体来说它不像 BERT那样直接将输入的单词随机改为mask而是通过改变Self-Attention的计算来屏蔽未来的单词信息。
例如,我们想要计算位置 4我们可以看到只允许处理以前和现在的 token。 例如,我们想要计算位置4的attention我们只允许看到位置4以前和位置4的token。
![decoder只能看到以前和现在的token](./pictures/4-decoder1.webp)图: decoder只能看到以前和现在的token ![decoder只能看到以前和现在的token](./pictures/4-decoder1.webp)图: decoder只能看到以前和现在的token
很重要的一点是BERT 使用的Self Attention 和 GPT-2 使用的masked Self Attention 有明确的区别。一个正常的 Self Attention 模块允许一个位置关注到它右边的部分。而 masked Self Attention 阻止了这种情况的发生 由于GPT2基于Decoder构建所以BERT和GPT的一个重要区别来了由于BERT是基于Encoder构建的BERT使用是Self Attention层而GPT2基于Decoder构建GPT-2 使用masked Self Attention。一个正常的 Self Attention允许一个位置关注到它两边的信息而masked Self Attention只让模型看到左边的信息
![mask attention](./pictures/4-mask.png)图: mask attention ![mask attention](./pictures/4-mask.png)图: self attention vs mask self attention
只有 Decoder 的模块 那么GPT2中的Decoder长什么样子呢先要说一下[Generating Wikipedia by Summarizing Long Sequences](https://arxiv.org/pdf/1801.10198.pdf)这篇文章它首先提出基于Transformer-Decoder部分进行语言模型训练。由于去掉了Encoder部分于是Encoder-Decoder self attention也不再需要新的Transformer-Decoder模型如下图所示
在 Transformer 原始论文发布之后Generating Wikipedia by Summarizing Long Sequences(https://arxiv.org/pdf/1801.10198.pdf) 提出了另一种能够进行语言建模的 Transformer 模块的布局。这个模型丢弃了 Transformer 的 Encoder。因此我们可以把这个模型称为 Transformer-Decoder。这种早期的基于 Transformer 的语言模型由 6 个 Decoder 模块组成。
![transformer-decoder](./pictures/4-trans-decoder.webp)图: transformer-decoder ![transformer-decoder](./pictures/4-trans-decoder.webp)图: transformer-decoder
这些 Decoder 模块都是相同的。我已经展开了第一个 Decoder因此你可以看到它的 Self Attention 层是 masked 的。注意,现在这个模型可以处理多达 4000 个 token--是对原始论文中 512 个 token 的一个大升级 随后OpenAI的GPT2也使用的是上图的Transformer-Decoder结构
这些模块和原始的 Decoder 模块非常类似,只是它们去掉了第二个 Self Attention 层。在 Character-Level Language Modeling with Deeper Self-Attention(https://arxiv.org/pdf/1808.04444.pdf) 中使用了类似的结构,来创建一次一个字母/字符的语言模型。 ### GPT2概述
OpenAI 的 GPT-2 使用了这些 Decoder 模块。 现在来拆解一个训练好的GPT-2看看它是如何工作的。
### 语言模型入门:了解 GPT2
让我们拆解一个训练好的 GPT-2看看它是如何工作的。
![拆解GPT2](./pictures/4-gpt2-1.png)图拆解GPT2 ![拆解GPT2](./pictures/4-gpt2-1.png)图拆解GPT2
GPT-2 能够处理 1024 个 token。每个 token 沿着自己的路径经过所有的 Decoder 模块 GPT-2能够处理1024 个token。每个token沿着自己的路径经过所有的Decoder层。试用一个训练好的GPT-2模型的最简单方法是让它自己生成文本这在技术上称为生成无条件文本。或者我们可以给它一个提示让它谈论某个主题即生成交互式条件样本
运行一个训练好的 GPT-2 模型的最简单的方法是让它自己生成文本(这在技术上称为 生成无条件样本)。或者,我们可以给它一个提示,让它谈论某个主题(即生成交互式条件样本)。在漫无目的情况下,我们可以简单地给它输入初始 token并让它开始生成单词训练好的模型使用 <|endoftext|> 作为初始的 token。我们称之为 \<s>)。 在漫无目的情况下,我们可以简单地给它输入一个特殊的\<s>初始token让它开始生成单词。如下图所示
![拆解GPT2初始token](./pictures/4-gpt2-start.webp)图:拆解GPT2初始token ![拆解GPT2初始token](./pictures/4-gpt2-start.webp)图GPT2初始token
模型只有一个输入的 token因此只有一条活跃路径。token 在所有层中依次被处理,然后沿着该路径生成一个向量。这个向量可以根据模型的词汇表计算出一个分数(模型知道所有的 单词,在 GPT-2 中是 5000 个词)。在这个例子中,我们选择了概率最高的 the。但我们可以把事情搞混--你知道如果一直在键盘 app 中选择建议的单词它有时候会陷入重复的循环中唯一的出路就是点击第二个或者第三个建议的单词。同样的事情也会发生在这里GPT-2 有一个 top-k 参数我们可以使用这个参数让模型考虑第一个词top-k =1之外的其他词。 由于模型只有一个输入,因此只有一条活跃路径。\<s> token在所有Decoder层中依次被处理然后沿着该路径生成一个向量。根据这个向量和模型的词汇表给所有可能的词计算出一个分数。在下图的例子中我们选择了概率最高的 the。下一步我们把第一步的输出添加到我们的输入序列然后让模型做下一个预测。
下一步,我们把第一步的输出添加到我们的输入序列,然后让模型做下一个预测。
![拆解GPT2](./pictures/4-gpt2-the.gif)动态图拆解GPT2 ![拆解GPT2](./pictures/4-gpt2-the.gif)动态图拆解GPT2
请注意第二条路径是此计算中唯一活动的路径。GPT-2 的每一层都保留了它自己对第一个 token 的解释,而且会在处理第二个 token 时使用它(我们会在接下来关于 Self Attention 的章节中对此进行更详细的介绍。GPT-2 不会根据第二个 token 重新计算第一个 token。 请注意第二条路径是此计算中唯一活动的路径。GPT-2 的每一层都保留了它对第一个 token所编码的信息而且会在处理第二个 token 时直接使用它GPT-2 不会根据第2个 token 重新计算第一个 token。
不断重复上述步骤,就可以生成更多的单词了。
### 深入理解 GPT2 的更多细节 ### GPT2详解
**输入编码** #### 输入编码
让我们更深入地了解模型。首先从输入开始。与之前我们讨论的其他 NLP 模型一样GPT-2 在嵌入矩阵中查找输入的单词的对应的 embedding 向量--这是我们从训练好的模型中得到的组件之一 现在我们更深入了解和学习GPT先看从输入开始。与之前我们讨论的其他 NLP 模型一样GPT-2 在嵌入矩阵中查找输入的单词的对应的 embedding 向量。如下图所示:每一行都是词的 embedding这是一个数值向量可以表示一个词并捕获一些含义。这个向量的大小在不同的 GPT-2 模型中是不同的。最小的模型使用的 embedding 大小是 768
![token embedding](./pictures/4-gpt-token.png)图token embedding ![token embedding](./pictures/4-gpt-token.png)图token embedding
每一行都是词的 embedding这是一个数字列表可以表示一个词并捕获一些含义。这个列表的大小在不同的 GPT-2 模型中是不同的。最小的模型使用的 embedding 大小是 768 于是在开始时,我们会在嵌入矩阵查找第一个 token \<s> 的 embedding。在把这个 embedding 传给模型的第一个模块之前我们还需要融入位置编码参考篇章2.2详解Transformer这个位置编码能够指示单词在序列中的顺序。
因此在开始时,我们会在嵌入矩阵查找第一个 token \<s> 的 embedding。在把这个 embedding 传给模型的第一个模块之前,我们需要融入位置编码,这个位置编码能够指示单词在序列中的顺序。训练好的模型中,有一部分是一个矩阵,这个矩阵包括了 1024 个位置中每个位置的位置编码向量。
![位置编码](./pictures/4-gpt-pos.webp)图:位置编码 ![位置编码](./pictures/4-gpt-pos.webp)图:位置编码
在这里,我们讨论了输入单词在传递到第一个 Transformer 模块之前,是如何被处理的。我们还知道,训练好的 GPT-2 包括两个权重矩阵。
![token+position](./pictures/4-gpt-token-pos.png)图: token+position ![token+position](./pictures/4-gpt-token-pos.png)图: token+position
把一个单词输入到 Transformer 的第一个模块,意味着寻找这个单词的 embedding并且添加第一个位置的位置编码向量 于是输入的处理:得到词向量+位置编码
**在这些层中向上流动** #### 多层Decoder
第一个模块现在可以处理 token首先通过 Self Attention 层,然后通过神经网络层。一旦 Transformer 的第一个模块处理了 token会得到一个结果向量这个结果向量会被发送到堆栈的下一个模块处理。每个模块的处理过程都是相同的不过每个模块都有自己的 Self Attention 和神经网络层 第一层Decoder现在可以处理 \<s> token所对应的向量了首先通过 Self Attention 层然后通过全连接神经网络。一旦Transformer 的第1个Decoder处理了\<s> token依旧可以得到一个向量这个结果向量会再次被发送到下一层Decoder
![向上流动](./pictures/4-gpt-fllow.webp)图:向上流动 ![向上流动](./pictures/4-gpt-fllow.webp)图:多层编码
**回顾 Self-Attention** #### Decoder中的Self-Attention
语言严重依赖于上下文。例如,看看下面的第二定律 Decoder中包含了Masked Self-Attention由于Mask的操作可以独立进行于是我们先独立回顾一下self-attention操作。语言严重依赖于上下文。给个例子
``` ```
机器人第二定律 机器人第2定律机器人必须服从人给予 它 的命令,当 该命令 与 第一定律 冲突时例外。
机器人必须服从人给予 它 的命令,当 该命令 与 第一定律 冲突时例外。
``` ```
我在句子中高亮了 3 个部分,这些部分的词是用于指代其他的词。如果不结合它们所指的上下文,就无法理解或者处理这些词。当一个模型处理这个句子,它必须能够知道: 例句中包含了多个代词。如果不结合它们所指的上下文,就无法理解或者处理这些词。当一个模型处理这个句子,它必须能够知道:
- 它 指的是机器人 - 它 指的是机器人
- 该命令 指的是这个定律的前面部分,也就是 人给予 它 的命令 - 该命令 指的是这个定律的前面部分,也就是 人给予 它 的命令
- 第一定律 指的是机器人第一定律 - 第一定律 指的是机器人第一定律
这就是 Self Attention 所做的事。它在处理某个词之前,将模型对这个词的相关词和关联词的理解融合起来(并输入到一个神经网络)。它通过对句子片段中每个词的相关性打分,并将这些词的表示向量加权求和。 self-attention所做的事情是它通过对句子片段中每个词的相关性打分,并将这些词的表示向量根据相关性加权求和,从而让模型能够将词和其他相关词向量的信息融合起来
举个例子,下图顶部模块中的 Self Attention 层在处理单词 `it` 的时候关注到` a robot`。它传递给神经网络的向量,是 3 个单词和它们各自分数相乘再相加的和。 举个例子,如下图所示最顶层的Decoder中的 Self Attention 层在处理单词 `it` 的时候关注到` a robot`。于是self-attention传递给后续神经网络的`it` 向量是3个单词对应的向量和它们各自分数的加权和。
![it的attention](./pictures/4-gpt-it.webp)图it的attention ![it的attention](./pictures/4-gpt-it.webp)图it的attention
**Self-Attention 过程** **Self-Attention 过程**
Self-Attention 沿着句子中每个 token 的路径进行处理,主要组成部分包括 3 个向量。 Self-Attention 沿着句子中每个 token 进行处理,主要组成部分包括 3 个向量。
- QueryQuery 向量是当前词的表示,用于对其他所有单词(使用这些单词的 key 向量)进行评分。我们只关注当前正在处理的 token 的 query 向量。 - QueryQuery 向量是当前词的向量表示获得,用于对其他所有单词(使用这些单词的 key 向量)进行评分。
- KeyKey 向量就像句子中所有单词的标签。它们就是我们在搜索单词时所要匹配的 - KeyKey 向量由句子中的所有单词的向量表示获得,可以看作一个标识向量
- ValueValue 向量是实际的单词表示,一旦我们对每个词的相关性进行了评分,我们需要对这些向量进行加权求和,从而表示当前的词 - ValueValue 向量在self-attention中与Key向量其实是相同的
![query](./pictures/4-gpt-query.webp)图: query ![query](./pictures/4-gpt-query.webp)图: query
一个粗略的类比是把它看作是在一个文件柜里面搜索Query 向量是一个便签,上面写着你正在研究的主题,而 Key 向量就像是柜子里的文件夹的标签。当你将便签与标签匹配时,我们取出匹配的那些文件夹的内容,这些内容就是 Value 向量。但是你不仅仅是寻找一个 Value 向量,而是在一系列文件夹里寻找一系列 Value 向量。
将 Query 向量与每个文件夹的 Key 向量相乘,会为每个文件夹产生一个分数(从技术上来讲:就是点积后面跟着 softmax 一个粗略的类比是把它看作是在一个文件柜里面搜索Query 向量是一个便签,上面写着你正在研究的主题,而 Key 向量就像是柜子里的文件夹的标签。当你将便签与标签匹配时,我们取出匹配的那些文件夹的内容,这些内容就是 Value 向量。但是你不仅仅是寻找一个 Value 向量而是找到一系列Value 向量。
将 Query 向量与每个文件夹的 Key 向量相乘,会为每个文件夹产生一个分数(从技术上来讲:点积后面跟着 softmax
![score](./pictures/4-gpt-score.webp)图: score ![score](./pictures/4-gpt-score.webp)图: score
我们将每个 Value 向量乘以对应的分数,然后求和,得到 Self Attention 的输出。 我们将每个 Value 向量乘以对应的分数,然后求和,就得到了得到 Self Attention 的输出。
![Self Attention 的输出](./pictures/4-gpt-out.webp)图Self Attention 的输出 ![Self Attention 的输出](./pictures/4-gpt-out.webp)图Self Attention 的输出
这些加权的 Value 向量会得到一个向量,它将 50% 的注意力放到单词 robot 上,将 30% 的注意力放到单词 a将 19% 的注意力放到单词 it。在下文中我们会更加深入 Self Attention但现在首先让我们继续在模型中往上走直到模型的输出。 这些加权的 Value 向量会得到一个向量,比如上图,它将 50% 的注意力放到单词 robot 上,将 30% 的注意力放到单词 a将 19% 的注意力放到单词 it。
而所谓的Masked self attention指的的是将mask位置对应的的attention score变成一个非常小的数字或者0让其他单词再self attention的时候加权求和的时候不考虑这些单词。
**模型输出** **模型输出**
当模型顶部的模块产生输出向量时(这个向量是经过 Self Attention 层和神经网络层得到的),模型会将这个向量乘以嵌入矩阵。 当模型顶部的Decoder层产生输出向量时(这个向量是经过 Self Attention 层和神经网络层得到的),模型会将这个向量乘以一个巨大的嵌入矩阵vocab size x embedding size来计算该向量和所有单词embedding向量的相关得分
![顶部的模块产生输出](./pictures/4-gpt-out1.webp)图:顶部的模块产生输出 ![顶部的模块产生输出](./pictures/4-gpt-out1.webp)图:顶部的模块产生输出
回忆一下,嵌入矩阵中的每一行都对应于模型词汇表中的一个词。这个相乘的结果,被解释为模型词汇表中每个词的分数。 回忆一下,嵌入矩阵中的每一行都对应于模型词汇表中的一个词。这个相乘的结果,被解释为模型词汇表中每个词的分数经过softmax之后被转换成概率
![token概率](./pictures/4-gpt-out3.webp)图token概率 ![token概率](./pictures/4-gpt-out3.webp)图token概率
我们可以选择最高分数的 tokentop_k=1。但如果模型可以同时考虑其他词,那么可以得到更好的结果。所以一个更好的策略是把分数作为单词的概率,从整个列表中选择一个单词(这样分数越高的单词,被选中的几率就越高)。一个折中的选择是把 top_k 设置为 40让模型考虑得分最高的 40 个词 我们可以选择最高分数的 tokentop_k=1也可以同时考虑其他词top k。假设每个位置输出k个token假设总共输出n个token那么基于n个单词的联合概率选择的输出序列会更好
![top k选择输出](./pictures/4-gpt-out4.webp)图top k选择输出 ![top k选择输出](./pictures/4-gpt-out4.webp)图top 1选择输出
这样,模型就完成了一次迭代,输出一个单词。模型会继续迭代,直到所有的上下文都已经生成1024 个 token,或者直到输出了表示句子末尾的 token。 这样,模型就完成了一次迭代,输出一个单词。模型会继续迭代,直到所有的单词都已经生成,或者直到输出了表示句子末尾的 token。
### GPT2 总结 ## 详解Self-Attention
现在我们基本知道了 GPT-2 是如何工作的。如果你想知道 Self Attention 层里面到底发生了什么,那么文章接下来的额外部分就是为你准备的,我添加这个额外的部分,来使用更多可视化解释 Self Attention以便更加容易讲解后面的 Transformer 模型TransformerXL 和 XLNet 现在我们基本知道了 GPT-2 是如何工作的。如果你想知道 Self Attention 层里面到底发生了什么,那么文章接下来的额外部分就是为你准备的,我添加这个额外的部分,来使用更多可视化解释 Self Attention
我想在这里指出文中一些过于简化的说法: 在这里指出文中一些过于简化的说法:
- 我在文中交替使用 token 和 词。但实际上GPT-2 使用 Byte Pair Encoding 在词汇表中创建 token。这意味着 token 通常是词的一部分。 - 我在文中交替使用 token 和 词。但实际上GPT-2 使用 Byte Pair Encoding 在词汇表中创建 token。这意味着 token 通常是词的一部分。
- 我们展示的例子是在推理模式下运行。这就是为什么它一次只处理一个 token。在训练时模型将会针对更长的文本序列进行训练并且同时处理多个 token。同样在训练时模型会处理更大的 batch size而不是推理时使用的大小为 1 的 batch size。 - 我们展示的例子是在推理模式下运行。这就是为什么它一次只处理一个 token。在训练时模型将会针对更长的文本序列进行训练并且同时处理多个 token。同样在训练时模型会处理更大的 batch size而不是推理时使用的大小为 1 的 batch size。
@ -221,16 +224,15 @@ Self-Attention 沿着句子中每个 token 的路径进行处理,主要组成
![输入与输出维度](./pictures/4-gpt-sum.webp)图:输入与输出维度 ![输入与输出维度](./pictures/4-gpt-sum.webp)图:输入与输出维度
## 可视化 Self-Attention ### 可视化Self-Attention
在这篇文章的前面,我们使用了这张图片来展示,如何在一个层中使用 Self Attention这个层正在处理单词 `it` 在这篇文章的前面,我们使用了这张图片来展示Self Attention如何处理单词 `it`
![it的attention](./pictures/4-att-it.png)图it的attention ![it的attention](./pictures/4-att-it.png)图it的attention
在这一节,我们会详细介绍如何实现这一点。请注意,我们会讲解清楚每个单词都发生了什么。这就是为什么我们会展示大量的单个向量而实际的代码实现,是通过巨大的矩阵相乘来完成的。但我想把重点放在词汇层面上。 在这一节,我们会详细介绍如何实现这一点。请注意,我们会讲解清楚每个单词都发生了什么。这就是为什么我们会展示大量的单个向量而实际的代码实现,是通过巨大的矩阵相乘来完成的。
### Self-Attention 让我们看看一个简答的Transformer假设它一次只能处理 4 个 token。
让我们先看看原始的 Self Attention它被用在 Encoder 模块中进行计算。让我们看看一个玩具 Transformer它一次只能处理 4 个 token。
Self-Attention 主要通过 3 个步骤来实现: Self-Attention 主要通过 3 个步骤来实现:
@ -265,19 +267,21 @@ Self-Attention 主要通过 3 个步骤来实现:
![汇总](./pictures/4-att-34.webp)图:汇总 ![汇总](./pictures/4-att-34.webp)图:汇总
### 图解 Masked Self_attention ### 图解Masked Self-attention
现在,我们已经了解了 Transformer 的 Self Attention 步骤,现在让我们继续研究 masked Self Attention。Masked Self Attention 和 Self Attention 是相同的,除了第 2 个步骤。假设模型只有 2 个 token 作为输入,我们正在观察(处理)第二个 token。在这种情况下最后 2 个 token 是被屏蔽masked的。所以模型会干扰评分的步骤。它基本上总是把未来的 token 评分为 0因此模型不能看到未来的词 现在,我们已经了解了 Transformer 的 Self Attention 步骤,现在让我们继续研究 masked Self Attention。Masked Self Attention 和 Self Attention 是相同的,除了第 2 个步骤。
现在假设模型有2个 token 作为输入,我们正在观察(处理)第二个 token。在这种情况下最后 2 个 token 是被屏蔽masked的。所以模型会干扰评分的步骤。它总是把未来的 token 评分设置为0因此模型不能看到未来的词如下图所示
![masked self attention](./pictures/4-mask.webp)图masked self attention ![masked self attention](./pictures/4-mask.webp)图masked self attention
这个屏蔽masking经常用一个矩阵来实现称为 attention mask。想象一下有 4 个单词的序列(例如,机器人必须遵守命令)。在一个语言建模场景中,这个序列会分为 4 个步骤处理--每个步骤处理一个词(假设现在每个词是一个 token。由于这些模型是以 batch size 的形式工作的,我们可以假设这个玩具模型的 batch size 为 4它会将整个序列作包括 4 个步骤)为一个 batch 处理 这个屏蔽masking经常用一个矩阵来实现称为 attention mask矩阵。依旧以4个单词的序列为例例如robot must obay orders。在一个语言建模场景中这个序列会分为 4 个步骤处理:每个步骤处理一个词(假设现在每个词就是是一个token另外,由于模型是以 batch size 的形式工作的,我们可以假设这个简单模型的 batch size 为4它会将4个序列生成任务作为一个 batch 处理如下图所示左边是输入右边是label
![masked 矩阵](./pictures/4-mask-matrix.webp)图:masked 矩阵 ![masked 矩阵](./pictures/4-mask-matrix.webp)图:batch形式的输入和输出
在矩阵的形式中,我们把 Query 矩阵和 Key 矩阵相乘来计算分数。让我们将其可视化如下,不同的是,我们不使用单词,而是使用与格子中单词对应的 Query 矩阵(或者 Key 矩阵) 在矩阵的形式中,我们使用Query 矩阵和 Key 矩阵相乘来计算分数。将其可视化如下。但注意,单词无法直接进行矩阵运算,所以下图的单词还需要对应成一个向量
![Query矩阵](./pictures/4-mask-q.webp)图Query矩阵 ![Query矩阵](./pictures/4-mask-q.webp)图Query和Keys的相关矩阵
在做完乘法之后,我们加上三角形的 attention mask。它将我们想要屏蔽的单元格设置为负无穷大或者一个非常大的负数例如 GPT-2 中的 负十亿): 在做完乘法之后,我们加上三角形的 attention mask。它将我们想要屏蔽的单元格设置为负无穷大或者一个非常大的负数例如 GPT-2 中的 负十亿):
@ -293,16 +297,17 @@ Self-Attention 主要通过 3 个步骤来实现:
- 当模型处理数据集中的第 2 个数据(第 2 行其中包含着单词robot must。当模型处理单词 must它将 48% 的注意力集中在 robot将 52% 的注意力集中在 must。 - 当模型处理数据集中的第 2 个数据(第 2 行其中包含着单词robot must。当模型处理单词 must它将 48% 的注意力集中在 robot将 52% 的注意力集中在 must。
- 诸如此类,继续处理后面的单词。 - 诸如此类,继续处理后面的单词。
到目前为止我们就搞明白了mask self attention啦。
### GPT2Self-Attention ### GPT2中的Self-Attention
让我们更详细地了解 GPT-2 masked attention。 让我们更详细地了解 GPT-2的masked self attention。
*评价模型:每次处理一个 token* *模型预测的时候:每次处理一个 token*
我们可以让 GPT-2 像 mask Self Attention 一样工作。但是在评价模型时,当我们的模型在每次迭代后只添加一个新词,那么对于已经处理过的 token 来说,沿着之前的路径重新计算 Self Attention 是低效的。 但我们用模型进行预测的时候,模型在每次迭代后只添加一个新词那么对于已经处理过的token来说沿着之前的路径重新计算 Self Attention 是低效的。那么GPT-2是如何实现高效处理的呢
在这种情况下,我们处理第一个 token(现在暂时忽略 \<s>)。 先处理第一个token a如下图所示(现在暂时忽略 \<s>)。
![gpt2第一个token](./pictures/4-gpt2-self.png)图gpt2第一个token ![gpt2第一个token](./pictures/4-gpt2-self.png)图gpt2第一个token
@ -316,40 +321,38 @@ GPT-2 保存 token `a` 的 Key 向量和 Value 向量。每个 Self Attention
`(1) 创建 Query、Key 和 Value 矩阵` `(1) 创建 Query、Key 和 Value 矩阵`
让我们假设模型正在处理单词 `it`如果我们讨论最下面的模块(对于最下面的模块来说),这个 token 对应的输入就是 `it` 的 embedding 加上第 9 个位置的位置编码: 让我们假设模型正在处理单词 `it`进入Decoder之前,这个 token 对应的输入就是 `it` 的 embedding 加上第 9 个位置的位置编码:
![处理it](./pictures/4-gpt2-it.webp)图处理it ![处理it](./pictures/4-gpt2-it.webp)图处理it
Transformer 中每个模块都有它自己的权重(在后文中会拆解展示)。我们首先遇到的权重矩阵是用于创建 Query、Key、和 Value 向量的。 Transformer 中每个层都有它自己的参数矩阵在后文中会拆解展示。embedding向量我们首先遇到的权重矩阵是用于创建 Query、Key、和 Value 向量的。
![处理it](./pictures/4-gpt2-it1.webp)图处理it ![处理it](./pictures/4-gpt2-it1.webp)图处理it
Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量,此处没有画出) Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量,此处没有画出)
这个相乘会得到一个向量,这个向量基本上是 Query、Key 和 Value 向量的拼接。 这个相乘会得到一个向量,这个向量是 Query、Key 和 Value 向量的拼接。
![处理it](./pictures/4-gpt2-it2.webp)图:处理it ![处理it](./pictures/4-gpt2-it2.webp)图:Query、Key 和 Value
将输入向量与 attention 权重向量相乘(并加上一个 bias 向量)得到这个 token 的 Key、Value 和 Query 向量拆分为 attention heads 得到Query、Key和Value向量之后我们将其拆分multi-head如下图所示。其实本质上就是将一个大向量拆分为多个小向量
在之前的例子中,我们只关注了 Self Attention忽略了 multi-head 的部分。现在对这个概念做一些讲解是非常有帮助的。Self-attention 在 Q、K、V 向量的不同部分进行了多次计算。拆分 attention heads 只是把一个长向量变为矩阵。小的 GPT-2 有 12 个 attention heads因此这将是变换后的矩阵的第一个维度 ![处理it](./pictures/4-gpt2-it3.png)图multi head
![处理it](./pictures/4-gpt2-it3.png)图处理it 为了更好的理解multi head我们将其进行如下展示
在之前的例子中,我们研究了一个 attention head 的内部发生了什么。理解多个 attention-heads 的一种方法,是像下面这样(如果我们只可视化 12 个 attention heads 中的 3 个):
![处理it](./pictures/4-gpt2-it4.webp)图:处理it ![处理it](./pictures/4-gpt2-it4.webp)图:multi head
`(2) 评分` `(2) 评分`
我们现在可以继续进行评分,这里我们只关注一个 attention head其他的 attention head 也是在进行类似的操作)。 我们现在可以继续进行评分,假设我们只关注一个 attention head其他的 attention head 也是在进行类似的操作)。
![](./pictures/4-gpt2-it5.webp)图:处理it ![](./pictures/4-gpt2-it5.webp)图:打分
现在,这个 token 可以根据其他所有 token 的 Key 向量进行评分(这些 Key 向量是在前面一个迭代中的第一个 attention head 计算得到的): 现在,这个 token 可以根据其他所有 token 的 Key 向量进行评分(这些 Key 向量是在前面一个迭代中的第一个 attention head 计算得到的):
![](./pictures/4-gpt2-it6.webp)图: ![](./pictures/4-gpt2-it6.webp)图: 加权和
`(3) 求和` `(3) 求和`
@ -359,19 +362,19 @@ Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量
`合并 attention heads` `合并 attention heads`
我们处理各种注意力的方法是首先把它们连接成一个向量 multi head对应得到多个加权和向量我们将他们都再次拼接起来
![处理it](./pictures/4-gpt2-it8.webp)图:处理it ![处理it](./pictures/4-gpt2-it8.webp)图:拼接multi head多个加权和向量
但这个向量还没有准备好发送到下一个子层(向量的长度不对)。我们首先需要把这个隐层状态的巨大向量转换为同质的表示 再将得到的向量经过一个线性映射得到想要的维度,随后输入全连接网络
`(4) 映射(投影)` `(4) 映射(投影)`
我们将让模型学习如何将拼接好的 Self Attention 结果转换为前馈神经网络能够处理的形状。在这里,我们使用第二个巨大的权重矩阵,将 attention heads 的结果映射到 Self Attention 子层的输出向量: 我们将让模型学习如何将拼接好的 Self Attention 结果转换为前馈神经网络能够处理的输入。在这里,我们使用第二个巨大的权重矩阵,将 attention heads 的结果映射到 Self Attention 子层的输出向量:
![映射](./pictures/4-project.png)图:映射 ![映射](./pictures/4-project.png)图:映射
通过这个,我们产生了一个向量,我们可以把这个向量传给下一层: 通过以上步骤,我们产生了一个向量,我们可以把这个向量传给下一层:
![传给下一层](./pictures/4-vector.webp)图:传给下一层 ![传给下一层](./pictures/4-vector.webp)图:传给下一层
@ -379,7 +382,7 @@ Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量
`第 1 层` `第 1 层`
全连接神经网络是用于处理 Self Attention 层的输出,这个输出的表示包含了合适的上下文。全连接神经网络由两层组成。第一层是模型大小的 4 倍(由于 GPT-2 small 是 768因此这个网络会有3072个神经元为什么是四倍?这只是因为这是原始 Transformer 的大小(如果模型的维度是 512那么全连接神经网络中第一个层的维度是 2048。这似乎给了 Transformer 足够的表达能力,来处理目前的任务。 全连接神经网络是用于处理 Self Attention 层的输出,这个输出的表示包含了合适的上下文。全连接神经网络由两层组成。第一层是模型大小的 4 倍(由于 GPT-2 small 是 768因此这个网络会有3072个神经元
![全连接层](./pictures/4-full.gif)动态图:全连接层 ![全连接层](./pictures/4-full.gif)动态图:全连接层
@ -394,11 +397,9 @@ Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量
没有展示 bias 向量 没有展示 bias 向量
你完成了! 总结一下,我们的输入会遇到下面这些权重矩阵:
这就是我们讨论的 Transformer 的最详细的版本!现在,你几乎已经了解了 Transformer 语言模型内部发生了什么。总结一下,我们的输入会遇到下面这些权重矩阵: ![总结](./pictures/4-sum.png)图:汇总
![总结](./pictures/4-sum.png)图:
每个模块都有它自己的权重。另一方面,模型只有一个 token embedding 矩阵和一个位置编码矩阵。 每个模块都有它自己的权重。另一方面,模型只有一个 token embedding 矩阵和一个位置编码矩阵。
@ -410,9 +411,9 @@ Self-Attention 将它的输入乘以权重矩阵(并添加一个 bias 向量
![总结](./pictures/4-sum2.png)图:总结 ![总结](./pictures/4-sum2.png)图:总结
由于某些原因,它们加起来是 124 M而不是 117 M。我不确定这是为什么但这个就是在发布的代码中展示的大小如果我错了请纠正我 由于某些原因,它们加起来是 124 M而不是 117 M。我不确定这是为什么但这个就是在发布的代码中展示的大小如果我错了请纠正我
## 语言模型之外 ## 语言模型应用
只有 Decoder 的 Transformer 在语言模型之外一直展现出不错的应用。它已经被成功应用在了许多应用中,我们可以用类似上面的可视化来描述这些成功应用。让我们看看这些应用,作为这篇文章的结尾。 只有 Decoder 的 Transformer 在语言模型之外一直展现出不错的效果。它已经被成功应用在了许多应用中,我们可以用类似上面的可视化来描述这些成功应用。让我们看看这些应用,作为这篇文章的结尾。
### 机器翻译 ### 机器翻译
@ -464,5 +465,5 @@ Music Transformer(https://magenta.tensorflow.org/music-transformer) 论文使用
## 致谢 ## 致谢
主要由哈尔滨工业大学张贤同学翻译(经过原作者授权)撰写,由本项目同学组织和整理。 主要由哈尔滨工业大学张贤同学翻译(经过原作者授权)撰写,由多多同学组织和整理。