目录
一、什么是注意力机制
二、Transformer架构设计
三、 理解Transformer中的Token
1. 将文本拆分成token
1.1 基于单词的分词器
1.2 基于字符的标记器
1.3 基于子词的标记器
2. 向量、矩阵、张量
2.1 向量(Vector)
2.2 矩阵(Matrix)
2.3 张量(Tensor)
3. 将token转换成向量
4. 将向量转换成嵌入
四、理解Transformer的编解码器
五、理解Token在编码器中的流转
1. 对Token进行位置编码
2. 自注意力机制
2.1 计算注意力值
2.2 多头注意力机制
3. 残差网络
4. 前匮网络
六、理解Token在解码器中的流转
1. Masked多头注意力机制
2. 计算注意力值
3. 线性分类器&Softmax
4. 编解码器的协同工作
七、Transformer-XL怎样提升上下文长度
八、Transformer相关应用分享
1. 使用BERT做掩词填充
2. 使用BART做文本摘要
3. 使用DistilBERT做问答
4. 使用T5做文本翻译
5. 使用GPT-2写小说
九、参考文献
谷歌在2017年发布Transformer架构的论文时,论文的标题是:Attention Is All You Need。重点说明了这个架构是基于注意力机制的。
一
什么是注意力机制
在深入了解Transformer的架构原理之前,我们首先要了解下,什么是注意力机制。
人类的大脑对于信息的获取也存在注意力机制,下面我举几个简单的例子:
从上面的图片中,我们可能更容易关注,颜色更深的字、字号更大的字,另外像“震惊”这种吸引人眼球的文案也非常容易吸引人的关注。
我们知道在海量的互联网信息中,往往那些起着“标题党”的文章更能吸引人的注意,从而达到吸引流量的目的,这是一种简单粗暴的方式。另外在大量的同质化图片中,如果有一张图片它的色彩、构图等都别出一格,那你也会一眼就能注意到它,这也是一种简单的注意力机制。
假设有以下这两段文字,需要翻译成英文:
1、我在得物上买了最新款的苹果,体验非常好。
2、我在得物上买了阿克苏的苹果,口感非常好。
我们人类能很快注意到第一段文字中的苹果是指苹果手机,那么模型在翻译时就需要把他翻译成iPhone,而第二段文字中的苹果就是指的苹果这种水果,模型翻译时就需要将他翻译成apple。
人类的大脑为什么能分辨出这两个苹果是指代的不同的意思呢?原因就是人类的大脑能从上下文中获取到关键信息,从而帮助我们理解每种苹果是什么意思。
其实说到这里,我们就已经揭开了Transformer架构的核心,即注意力机制的原理:从文本的上下文中找到需要注意的关键信息,帮助模型理解每个字的正确含义。但是实际的实现方式又是非常复杂的。
接下来让我们一起深入理解下Transformer的架构原理。
二
Transformer架构设计
Transformer的架构设计如下图所示:
Transformer架构中有两个核心的组件Encoder和Decoder,左边的这张图是Transformer架构的一个简单表示形式,右边的这张图是Transformer架构的一个完整表示形式,其中有一个重要的Multi-Head Attention组件,称为注意力层。
Transformer架构中的两个核心的组件Encoder和Decoder,每个组件都可以单独使用,具体取决于任务的类型:
Encoder-only models: 适用于需要理解输入的任务,如句子分类和命名实体识别。
Decoder-only models: 适用于生成任务,如文本生成。
Encoder-decoder models 或者 sequence-to-sequence models: 适用于需要根据输入进行生成的任务,如翻译或摘要。
三
理解Transformer中的Token
因为模型是无法直接处理文本的,只能处理数字,就跟ASCII码表、Unicode码表一样,计算机在处理文字时也是先将文字转成对应的字码,然后为每个字码编写一个对应的数字记录在表中,最后再处理。
将文本拆分成token
所以模型在处理文本时,第一步就是先将文本转换成对应的字码,也就是大模型中的token,但是怎么将文本转换成对应的token却是一个复杂的问题,在Transformers(HuggingFace提供的一个对Transformer架构的具体实现的组件库)中提供了专门的Tokenizer分词器来实现这个任务,一般来说有以下几种方式:
基于单词的分词器
第一种标记器是基于单词的(word-based)。它通常很容易设置和使用,只需几条规则,并且通常会产生不错的结果。例如,在下图中,目标是将原始文本拆分为单词,并为每个单词找到一个映射的数字表达:
将文本拆分成单词,也有很多不同的方式,比如通过空格来拆分、通过标点符号来拆分。
如果我们想使用基于单词的标记器(tokenizer)完全覆盖一种语言,我们需要为语言中的每个单词都创建一个数字标记,这将生成大量的标记。除此之外,还可能存在一些无法覆盖的单词,因为单词可能存在很多的变种情况,比如:dogs是dog的变种,running是run的变种。如果我们的标识符中没有覆盖所有的单词,那么当出现一个未覆盖的单词时,标记器将无法准确知道该单词的数字标记是多少,进而只能标记为未知:UNK。如果在文本转换的过程中有大量的文本被标记为UNK,那么也将影响后续模型推理。
基于字符的标记器
为了减少未知标记数量的一种方法是使用更深一层的标记器(tokenizer),即基于字符的(character-based)标记器(tokenizer)。
基于字符的标记器(tokenizer)将文本拆分为字符,而不是单词。这有两个主要好处:
词汇量要小得多。
未知的标记(token)要少得多,因为每个单词都可以从字符构建。
但是这里也出现了一些关于空格和标点符号的问题:
这种方法也不是完美的。由于现在表示是基于字符而不是单词,因此人们可能会争辩说,从直觉上讲,它的意义不大:每个字符本身并没有多大意义,而单词就是这种情况。然而,这又因语言而异;例如,在中文中,每个字符比拉丁语言中的字符包含更多的信息。
另一件要考虑的事情是,我们的模型最终会处理大量的词符(token):虽然使用基于单词的标记器(tokenizer),单词只会是单个标记,但当转换为字符时,它很容易变成 10 个或更多的词符(token)。
为了两全其美,我们可以使用结合这两种方法的第三种技术: 子词标记化(subword tokenization)。
基于子词的标记器
子词分词算法依赖于这样一个原则, 即不应将常用词拆分为更小的子词,而应将稀有词分解为有意义的子词。
例如,“annoyingly”可能被认为是一个罕见的词,可以分解为“annoying”和“ly”。这两者都可能作为独立的子词出现得更频繁,同时“annoyingly”的含义由“annoying”和“ly”的复合含义保持。
下面这张图,展示了基于子词标记化算法,如何标记序列“Let’s do tokenization!”:
这些子词最终提供了很多语义含义:例如,在上面的示例中,“tokenization”被拆分为“token”和“ization”,这两个具有语义意义同时节省空间的词符(token)(只需要两个标记(token)代表一个长词)。这使我们能够对较小的词汇表进行相对较好的覆盖,并且几乎没有未知的标记。
向量、矩阵、张量
了解完token之后,我们还要了解下向量、矩阵和张量的概念,因为他们是大模型计算中基础的数据结构。
向量(Vector)