深入理解 Transformer 技术原理 | 得物技术
谷歌在 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)
向量是一个有序的数字列表,通常用来表示空间中的点或者方向。在数学中,向量可以表示为一个列向量或行向量,具体取决于上下文。例如,一个三维空间中的点可以用一个三维列向量表示,如 v=[x,y,z]T,其中 x,y,z 是实数。
矩阵(Matrix)
矩阵是一个二维数组,由行和列组成,可以被视为向量的一个特例。矩阵在数学中用于表示线性变换、系统方程的系数等。矩阵的维度通常表示为 m×n,其中 m 是行数,n 是列数。例如,一个 4×3 的矩阵有四行三列。
张量(Tensor)
张量是一个多维数组,可以看作是向量和矩阵的更底层的表示,向量和矩阵是张量的特例。例如向量是一维的张量,矩阵是二维的张量。张量可以有任意数量的维度,而不仅仅是一维(向量)或二维(矩阵)。张量在物理学中用来表示多维空间中的物理量,如应力、应变等。在深度学习中,张量用于表示数据和模型参数的多维结构。
将 token 转换成向量
在获取到 token 之后,再将每个 token 映射为一个数字,当然了,Transformer 能够处理的数据,并不是简单的 1/2/3 这样的数字,而是一种向量数据,如下图所示:
将向量转换成嵌入
得到向量之后,再将向量转换成词嵌入,也就是我们所熟知的 embeddings。
在 Transformer 模型中,编码器接收的词嵌入(embeddings)可以被视为矩阵。这些词嵌入是将输入序列中的每个 token 映射到一个固定维度的向量空间中的结果。每个词嵌入都是一个向量,而这些向量按顺序排列形成一个矩阵。
具体来说,如果你有一个句子或序列,其中包含了 N 个 token,每个 token 都被映射到一个 d 维的向量空间中,那么你将得到一个 N×d 的矩阵,其中 N 是序列的长度,d 是嵌入向量的维度。这个矩阵就是词嵌入矩阵,它是一个二维张量,因为它具有两个维度:序列长度(时间步长)和嵌入维度。
在 Transformer 模型的编码器中,这个嵌入矩阵首先会通过一个线性层(可选)进行处理,然后添加位置编码(positional encoding),最后输入到自注意力(self-attention)和前馈网络(feed-forward network)等组件中进行进一步的处理,具体细节我接下来会进行详细解释。
总结来说,编码器接收的词嵌入是一个矩阵,这个矩阵可以被视为一个二维张量,其中包含了序列中每个词的 d 维向量表示。
四、理解 Transformer 的编解码器
下面让我们以文本翻译来深入理解 Transformer 中的 Encoder 和 Decoder 是怎样工作的,假设我们有以下这个翻译任务,输入是一段法文,输出是英文。
整个流程是 Transformer 将输入的 input,经过 Encoders 处理后,将结果投递到 Decoders 中处理,最后输出翻译后的结果。
但是实际在 Transformer 的内部,是由多个独立的 Encoder 和 Decoder 组成的,这里我们使用 6 个做验证,当然我们也可以使用其他数量的 Encoder 和 Decoder,笔者怀疑 6 个是经过验证后相对折中的一个值。
这 6 个 Encoder 和 Decoder 在结构上都是相同的,但是 Encoder 和 Decoder 的内部还有更细分的组件:
每一层的 Encoder 由 2 个子组件组成:自注意力层和前馈网络层,其中文本的输入会先流入自注意力层,正是由于自注意力层的存在,帮助 Encoder 在对特定文本进行遍历时,能够让 Encoder 观察到文本中的其他单词。然后自注意力层的结果被输出到前馈网络层。
每一层的 Decoder 由 3 个子组件组成:除了自注意力层、前馈网络层,在两者之间还有一个编解码注意力层,这个组件主要是帮助 Decoder 专注于输入句子的相关部分。
五、理解 Token 在编码器中的流转
现在我们已经知道了 Transformer 模型中的核心组件 Encoder 和 Decoder,接下来我们来看 Token 在 Transformer 中是怎么流转的,换句话说 Encoder 和 Decoder 是怎么处理 Token 的。
拿最开始的法文翻译的例子,模型将文本转换 token 后,紧接着就是将每个 token 转换成向量表达,在下图中,我们用 x1、x2、x3 这样的 box 来表示每个 token 的向量:
得到每个 token 的向量之后,从最底层的 Encoder 开始,每个 token 会沿着自己的路径从下往上流转,经过每一个 Encoder,对每个 Encoder 来说,共同点是他们接收的向量的维度都是相同的,为了保证这一点,所有的 token 都需要被 embedding 成相同大小的向量。
对 Token 进行位置编码
在流经每个 Encoder 时,向量都会从自注意力层流向前馈层,如下图所示:
这里需要注意的是,不同位置的向量在进入自注意力层的时候,相互之间是有依赖关系的,这就是注意力层需要关注的上下文的信息。
而当自注意力层处理后的向量进入前馈层后,前馈层是可以并行计算以提升性能的,因为在前馈层中向量之间不存在依赖关系。每个向量在经过 Encoder 处理后,得到的仍然是一个相同大小的向量,然后再提交给下一个 Encoder 进行处理。
为什么说不同位置的向量相互之间是有依赖关系的呢?我们可以想象一下,如果不关注一整个句子中 token 的位置信息,那么翻译出来的结果是不准确的,比如:
Sam is always looking for trouble
Trouble is always looking for Sam
为了让模型知道每个 token 的位置信息,传统的 RNN 网络的做法是,顺序处理每个 token,这样在处理当前 token 时,可以往前查看已经处理过的 token 的信息,但是缺点是所有的 token 节点都共用一套相同的参数,即下图中的:
U:输入参数
W:隐藏参数
V:输出参数
由于 RNN 的窗口较小,这种方案带来的问题是,当 token 数变大时,模型无法参考更早之前已经参考过的 token,这样就会造成上下文记忆丢失。
那么 Transformer 是怎么对 token 进行位置编码的呢?
首先我们知道每个 token 会被转换成 512 维(或更高的维度)的向量,比如:[0.12142,0.34181,....,-0.21231] 可以将这个向量分为两个部分,奇数和偶数部分。
奇数部分使用 cos 函数,加上当前 token 的位置信息 pos,通过 cos 编码得到一个奇数编码值;
偶数部分使用 sin 函数,加上当前 token 的位置信息 pos,通过 sin 编码得到一个偶数编码值;
最后拿 token 的 embeddings 和 pos 的 embeddings 相加,得到位置编码后的 positional input embeddings。
自注意力机制
有了位置编码的信息后,模型将接收经过位置编码的 embeddings 输入,为了方便描述,我们把 token 换成更简单的文本,如下图所示,Encoder 在接收到两个向量之后,通过自注意力层,将原始向量转换成携带了自注意力值的向量,也就是图中的 z1 和 z2。
计算注意力值
那 z1 和 z2 这两个向量是怎么得到的呢?原论文中给出了计算公式:
这个公式是用来计算注意力值的,借助了 Q、K、V 这三个矩阵:
首先通过 Q 矩阵和转置后的 K 矩阵转置相乘,得到结果后再除以 dk 的开平方,再通过 softmax 函数得到一个归一化的结果,最后将归一化的结果和矩阵 V 相乘就得到了表示注意力值的矩阵。
这里的 Q、K、V 三个矩阵(查询矩阵、键矩阵、值矩阵)是通过原始 token 的 embedding 矩阵计算得到的,具体的方法是,先训练出三个矩阵:Wq,Wk,Wv, 然后使用 embedding 处理后的 X 矩阵和这三个矩阵相乘得到:
Q=Wq * X
K=Wk * X
V=Wv * X
需要注意的是我们 embedding 输入原本是向量,并不是矩阵,这里是将所有的向量打包之后,形成了一个矩阵,方便矩阵之间的计算。
下面我们一步步了解下注意力值是怎么计算的,使用原始的 embedding,而不是打包后的矩阵,首先模型将会为句子中的每个 token 都计算出一个 score 分数,这个分数表示了该 token 对句子中其他 token 的关注程度,分数越高关注度越高。
但是需要注意的是这里计算得到的中间向量 q、k、v 的维度是 64 维,小于 Encoder 接收的输入向量的维度,这是一个经过计算后得到的相对稳定的数值。
如下图所示,当我们在计算 Tinking 这个 token 的注意力值时,会依次计算出 Thinking 对其他 token(在这里也就是 Thinking 和 Machines)的注意力值,计算 token1 对其他各个 token 的 score 的方式是:q1 与 k1 做点积,q1 与 k2 做点积。
得到每个 score 后,再把 score 除以 K 向量维度的平方根也就是√64=8,然后将结果通过 Softmax 进行归一化,得到一个 0~1 之间的概率值,所有的归一化的加和值等于 1。
最后将 Softmax 的值,与 V 向量相乘,得到自注意力层的输出结果向量:z1 和 z2,需要注意的是相乘的过程中会将不相关的 token 的关注度降低。
到这里其实已经把 Encoder 是怎么计算每个 token 对句子中其他 token 的注意力值的方法解释清楚了,下面我们用一张图从更高的层面来观察这个过程,假设我们想要翻译下面这个句子:
The animal didn't cross the street because it was too tired.
句子中的 it 是表示什么呢,是 animal 还是 street?模型就是通过自注意力值来确定的,当对 it 进行编码时,Encoder 会对句子中的每个 token 都计算一遍注意力值,模型会将 it 更关注的“The animal”编码到 it 中。这样模型在后续的处理过程中就能知道 it 指代的是“The animal”。
多头注意力机制
论文中通过引入多个 Encoder 形成了一种“多头注意力”的机制,对自注意力层进行了能力的提升,主要包括:
多头注意力扩展了模型关注不同位置的能力
多头注意力为自注意力层提供了多个子空间
Transformer 模型使用了 8 个注意力头,所以在计算每个 Encoder 的输出时,我们会得到 8 个 z 向量。
但是前馈层只能接收 1 个 z 向量,所以我们还需要将这 8 个 z 向量做压缩得到 1 个向量,具体的做法是将这 8 个 z 向量链接起来,然后乘以一个附加的权重矩阵 Wo,最后得到 z 向量,如下图所示:
最后我们用一张完整的大图来描述下在多个 Encoder 下,注意力值的计算过程,也就是多头注意力机制:
下面我们可以看下,在多头注意力机制下,在编码 it 这个 token 时,模型在注意哪些其他的 token:
可以看到其中一个头(橙色)更关注“The Animal”,因为这两个 token 对应的橙色更深,另外一个头(绿色)则更关注“tired”,因为这两个 token 对应的绿色更深。
残差网络
首先我们了解下什么是残差网络,残差网络(Residual Network,简称 ResNet)是一种深度卷积神经网络(CNN)架构,由 Microsoft Research Asia 的 Kaiming He 等人在 2015 年提出。ResNet 的核心思想是通过引入“残差学习”(residual learning)来解决深度神经网络训练中的退化问题(degradation problem)。
在传统的深度神经网络中,随着网络层数的增加,理论上网络的表示能力应该更强,但实际上,过深的网络往往难以训练,性能反而不如层数较少的网络。这种现象被称为“退化问题”,即随着网络深度的增加,网络的准确率不再提升,甚至下降。
ResNet 通过引入“跳跃连接”(skip connections)或“捷径连接”(shortcut connections)来解决这个问题。在 ResNet 中,输入不仅传递给当前层,还直接传递到后面的层,跳过一些中间层。这样,后面的层可以直接学习到输入与输出之间的残差(即差异),而不是学习到未处理的输入。这种设计允许网络学习到恒等映射(identity mapping),即输出与输入相同,从而使得网络可以通过更简单的路径来学习到正确的映射关系。
在 Transformer 模型中,残差网络的使用主要是为了解决自注意力机制(self-attention)带来的问题。Transformer 模型完全基于注意力机制,没有卷积层,但其结构本质上也是深度网络。在 Transformer 中,每个编码器(encoder)和解码器(decoder)层都包含自注意力和前馈网络,这些层的参数量非常大,网络深度也很容易变得很深。
使用残差连接可以帮助 Transformer 模型更有效地训练深层网络。在 Transformer 的自注意力层中,输入通过自注意力和前馈网络后,与原始输入相加,形成残差连接。这种设计使得网络即使在增加更多层数时,也能保持较好的性能,避免了退化问题。
总结来说,残差网络在 Transformer 模型中的应用解决了以下几个问题:
缓解退化问题:通过残差学习,使得网络即使在增加层数时也能保持或提升性能。
加速收敛:残差连接提供了梯度的直接路径,有助于梯度在深层网络中的传播,加速训练过程。
提高表示能力:允许网络学习更复杂的函数,同时保持对简单函数的学习能力。
Transformer 模型的成功部分归功于残差连接的设计,这使得它能够构建更深、更强大的模型,从而在自然语言处理(NLP)和计算机视觉等领域取得了显著的成果。
可以使用下面这张图来解释残差网络,原始向量 x 在经过自注意力层之后得到 z 向量,为了防止网络过深带来的退化问题,Transformer 模型使用了残差网络,具体做法是使用计算得到的 z 矩阵,在和原始输入的 x 矩阵做残差链接,即图中的 X+Z,然后使用 LayerNorm 函数进行层归一化,计算得到新的 z 向量,然后输入到前馈层。
将 Add & Normalize 简化之后表示为如下:
前馈网络
归一化后的残差输出,被送入点对点前馈网络进行进一步处理,点对点前馈网络是几个线性层,中间有 ReLU 激活函数,将点对点输出的结果与前馈网络的输入进行相加做残差链接,然后再做进一步的归一化。
点对点前馈网络主要用于进一步处理注意力的输出,让结果具有更丰富的表达。
到这里编码器就已经介绍完了,编码器输出的结果中携带了每个 token 的相关注意力信息,将用以帮助解码器在解码过程中关注输入中的特定的 token 信息。
六、理解 Token 在解码器中的流转
每个解码器拥有与编码器相似的结构但也有不同的地方,它有两个多头注意力层,一个点对点前馈网络层,并且在每个子层之后都有残差链接和层归一化。
解码器是自回归的,它将前一个 Decoder 输出的结果和来自编码器输出的注意力信息当做解码器的输入。
这里我们需要了解清楚,解码器的先前的输出结果是怎么得到的,即解码器的第一个输出结果从哪得到的。
在 Transformer 模型的训练过程中,解码器的第一个输出序列通常是根据特定的起始标记(start token)或者一个预先定义的初始状态得到的。这个起始标记是一个特殊的符号,它标志着输出序列的开始。以下是解码器如何获得第一个输出序列的详细过程:
1. 起始标记:
在训练阶段,解码器的输入序列通常以一个起始标记(例如<START>)开始。这个标记是一个预定义的词汇表中的词,它告诉解码器输出序列的生成即将开始。
2. 初始化状态:
解码器在开始生成输出之前,会接收到编码器的输出,即编码器的上下文向量。这些上下文向量包含了输入序列(如源语言文本)的信息。在某些情况下,解码器的初始状态也可以通过一个额外的向量(如位置编码)来初始化,以提供序列中每个位置的信息。
3. 自注意力机制:
在第一个时间步,解码器使用自注意力机制处理起始标记。由于此时没有之前的输出,自注意力机制会关注起始标记本身,以及可能的位置编码。
4. 编码器-解码器注意力:
解码器接着使用编码器-解码器注意力机制来关注编码器的输出。这允许解码器在生成第一个输出时就利用到输入序列的信息。
5. 输出层:
解码器的输出层将上述步骤得到的向量转换为概率分布,这个分布表示了词汇表中每个词作为第一个输出的概率。
6. 选择第一个输出:
在训练阶段,解码器可能会使用强制策略,这意味着解码器的第一个输出会直接使用目标序列中的第一个词。在推理阶段,解码器会根据概率分布选择概率最高的词作为第一个输出。
7. 迭代生成:
一旦获得了第一个输出,解码器就会将其作为下一个时间步的输入,并重复上述过程来生成后续的输出序列。
在实际应用中,解码器的第一个输出序列的生成方式可能会根据具体的任务和模型配置有所不同。例如,在某些情况下,解码器可能会接收到一个完整的前缀序列,而不是单一的起始标记,这在文本摘要任务中较为常见。在这种情况下,解码器会基于这个前缀序列来生成剩余的输出序列。
Masked 多头注意力机制
需要注意的是解码器中的第一层是一个特殊的多头注意力层,是一个执行了 mask 的注意力层,因为解码器是自回归的,并且依次生成结果 token,我们需要防止它在处理某个 token 时,对未来的 token 进行处理,原因是模型在训练的时候,是不知道未来输出的 token 是什么的,为了保证训练过程和解码的过程的一致性,我们需要让解码器在计算某个 token 的注意力值的时候,只关注这个句子中已经出现过的 token,而屏蔽掉句子中当前 token 之后的其他 token 的信息。
可以通过以下这张图来描述 Mask 的过程,当解码器在处理 it 时,应该把 it 之后的所有 token 屏蔽掉。
计算注意力值
在解码器中计算注意力值时,是用 Encoder 最后的输出,和每一个 Decoder 进行交互,这就需要 Decoder 中的第二层 Encoder-Decoder Attention。每个 Decoder 计算出结果之后,再作为输入传递给下一个 Decoder。
线性分类器 &Softmax
当最后一个 Decoder 计算完毕后,解码器得到了一个输出结果的向量数据。我们如何把它变成一个词呢?这就是最后一个 Linear 层的工作,后面是 Softmax 层。
线性层是一个简单的全连接神经网络,它将解码器产生的向量投影到一个更大的向量中,称为 logits 向量。
假设我们的模型知道从训练数据集中学习的 10,000 个独特的英语单词。这将使 logits 向量有 10,000 个单元格宽——每个单元格对应一个唯一单词的分数,这就是我们解释线性层模型输出的方式。
然后,softmax 层将这些分数转换为概率(全部为正,全部加起来为 1.0)。选择概率最高的单元格,并生成与其关联的单词作为该时间步的输出。
编解码器的协同工作
现在让我们看看编码器和解码器之间是如何协同工作的。
编码器首先处理输入的文本 token,然后输出一组注意力向量 K 和 V。这些向量将由每个解码器在其“编码器-解码器注意力”层中使用,这有助于解码器关注输入序列中的特定 token 的位置信息,具体计算注意力值的方法跟编码器中是一样的,需要注意的是,这里的 K、V 矩阵来自于编码器的输出,而 Q 矩阵来自于解码器的输入。
重复回归以上的步骤,直到出现结束符号的标识,表示解码器已完成其输出。每个步骤的输出在下一个时间步骤中被反馈到底部解码器,并且解码器像编码器一样向上反馈其解码结果。就像我们对编码器输入所做的那样,我们将位置编码嵌入并添加到这些解码器输入中以指示每个单词的位置。
至此,已经分析完 Transformer 的编码器和解码器的全流程了。
七、Transformer-XL 怎样提升上下文长度
传统的 Transformer 模型中,上下文长度是固定的,主要有以下几个原因:
计算效率:在最初的设计中,Transformer 模型是为了处理序列到序列的任务,如机器翻译。对于这类任务,输入序列(如源语言句子)和输出序列(如目标语言句子)通常具有相似的长度,因此固定上下文长度可以简化模型设计,提高计算效率。
模型复杂度:Transformer 模型的核心是自注意力机制,该机制在计算时需要对序列中的每个元素进行成对的比较,以计算注意力权重。如果上下文长度不固定,那么每次添加或删除元素时,都需要重新计算整个序列的注意力权重,这会导致计算复杂度和内存需求急剧增加。
训练稳定性:固定长度的上下文可以提供稳定的训练环境,有助于模型学习到更加一致和可靠的表示。如果上下文长度不固定,模型可能需要在每次迭代中适应新的序列长度,这可能会影响训练的稳定性和模型的收敛速度。
硬件限制:在实际应用中,硬件资源(如 GPU 内存)是有限的。固定长度的上下文可以确保模型在任何时候都不会超出硬件资源的限制,从而避免因资源不足而导致的训练中断。
模型泛化:固定长度的上下文允许模型在训练时学习到特定长度范围内的依赖关系,这有助于模型在实际应用中泛化到类似的序列长度上。
然而,固定上下文长度也带来了一些限制,特别是在处理长序列时,模型无法捕获超过固定长度的依赖关系,这限制了模型在某些任务(如长文本生成和理解)上的性能。为了解决这个问题,Transformer-XL 等模型通过引入新的机制来允许处理更长的上下文,从而在不牺牲计算效率的情况下捕获更长期的依赖关系。
国产开源公司月之暗面的大模型产品 kimi,能够处理长达 20 万字的超长上下文,那么他是怎么做到的呢,核心是他的模型在 Transformer 的基础上做了扩展,实现了自己的 Transformer-XL 架构。
Transformer-XL 通过引入两个关键的技术改进来提升 token 上下文长度的处理能力:片段递归机制(segment-level recurrence)和相对位置编码机制(relative positional encoding)。
片段递归机制:在传统的 Transformer 模型中,由于上下文长度是固定的,模型无法捕获超过预定义上下文长度的长期依赖性。Transformer-XL 通过引入循环机制来解决这个问题。具体来说,它不再从头开始计算每个新片段的隐藏状态,而是重复使用之前片段中获得的隐藏状态,并将这些状态作为当前片段的“记忆”。这样,信息就可以在不同片段之间传递,从而捕获更长的依赖关系。这种机制允许模型在不引起时间混乱的前提下,超越固定长度去学习依赖性,同时解决了上下文碎片化问题。
相对位置编码机制:在 Transformer-XL 中,为了能够在不造成时间混乱的情况下重复使用状态,引入了相对位置编码的概念。相对位置编码与传统的绝对位置编码不同,它只编码 token 之间的相对位置关系,而不是 token 与固定起始点的绝对位置。这种编码方式使得模型能够在处理长序列时更有效地利用位置信息,并且可以泛化至比在训练过程中观察到的长度更长的注意力长度。
通过这两种机制,Transformer-XL 显著提升了模型在处理长序列时的性能。
八、Transformer 相关应用分享
使用 BERT 做掩词填充
BERT(Bidirectional Encoder Representations from Transformers)是一种预训练语言表示模型,由 Google AI 在 2018 年提出。BERT 的核心创新在于利用 Transformer 架构的编码器部分来学习文本数据的深层次双向表示。这种表示能够捕捉到文本中词汇的上下文关系,从而在多种自然语言处理(NLP)任务中取得了显著的性能提升。
以下是 BERT 模型的一些关键特点:
双向上下文理解:与之前的单向语言模型不同,BERT 通过在预训练阶段使用掩码语言模型(Masked Language Model, MLM)任务,学习了词汇在句子中的双向上下文信息。这意味着模型能够同时考虑一个词前后的词汇来理解其含义。
预训练和微调:BERT 采用了两阶段的训练策略。在预训练阶段,BERT 在大量文本数据上进行无监督学习,学习语言的通用模式。在微调阶段,BERT 可以通过少量标注数据针对特定任务进行有监督学习,以适应各种 NLP 任务,如情感分析、问答系统、命名实体识别等。
Transformer 架构:BERT 基于 Transformer 的编码器部分,这是一种注意力机制(Attention Mechanism)的架构,它允许模型在处理序列数据时考虑序列中所有位置的信息。
大规模预训练:BERT 在非常大的文本语料库上进行预训练,这使得模型能够学习到丰富的语言知识。预训练的规模和质量对模型性能有重要影响。
多样化的任务适应性:通过微调,BERT 可以适应多种不同的 NLP 任务,而不需要对模型架构进行大的修改。这使得 BERT 成为了一个灵活且强大的工具。
BERT 的推出标志着 NLP 领域的一个重大进展,它在多项 NLP 任务上刷新了记录,并催生了一系列基于 BERT 的改进模型,如 RoBERTa、ALBERT、DistilBERT 等。这些模型在不同的方面对 BERT 进行了优化,以提高性能、减少计算资源消耗或改善特定任务的表现。
以下是使用 BERT 做掩词填充的示例,输入一段文本,让模型预测出下一个被掩盖的词:
https://huggingface.co/google-bert/bert-base-uncased
使用 BART 做文本摘要
BART(Bidirectional and Auto-Regressive Transformers)是一种先进的自然语言处理(NLP)模型,它结合了 BERT(Bidirectional Encoder Representations from Transformers)和 GPT(Generative Pre-trained Transformer)的特点,用于文本理解和生成任务。BART 模型特别擅长处理不同类型的文本噪声和序列变换,使其在多种 NLP 任务中表现出色。
设计原理和结构
BART 是基于 Transformer 架构的自编码自回归模型。它通过两个主要步骤进行预训练:
使用任意噪声函数破坏文本(例如,随机打乱句子顺序、删除或遮蔽 token)。
模型学习重建原始文本。
这种预训练方式使得 BART 能够有效地处理文本生成、翻译和理解等任务。BART 的编码器是双向的,能够捕捉文本的前后文信息,而解码器是自回归的,能够基于前面的输出生成后续的内容。
应用
BART 在多种 NLP 任务上取得了显著的成绩,包括但不限于:
文本摘要
机器翻译
对话生成
问答系统
文本分类
与其他模型的对比
与其他预训练模型相比,BART 在处理文本生成任务时尤其出色。它在自然语言理解任务中也有很好的表现,与 BERT 和 GPT 等模型相比,BART 在多个基准数据集上取得了竞争性或更好的结果。
预训练和微调
BART 模型通过大量的文本数据进行预训练,然后在特定任务上进行微调。预训练阶段,模型学习如何从噪声文本中恢复原始文本,而微调阶段则是针对特定任务调整模型参数,以优化任务性能。
以下是使用 BART 做文本摘要的应用示例:
https://huggingface.co/facebook/bart-large-cnn
使用 DistilBERT 做问答
DistilBERT 是一种轻量级的 BERT 模型,它通过知识蒸馏(knowledge distillation)技术从预训练的 BERT 模型中学习知识。这种方法的核心思想是使用一个较小的 BERT 模型作为“学生”模型,而原始的、较大的 BERT 模型则充当“教师”模型。在训练过程中,学生模型尝试复制教师模型的输出,以此来学习教师模型的知识。
主要特点和优势
模型大小和效率:DistilBERT 的模型大小和参数量都比原始的 BERT 模型小,这使得它在资源受限的环境中(如移动设备)更加实用。它的推理速度也比 BERT 快,因为它需要处理的参数更少。
知识蒸馏:DistilBERT 使用了一种称为“软目标”的知识蒸馏方法。在这种方法中,学生模型不仅学习来自训练数据的标签,还学习教师模型的输出,这些输出被视为附加的、软性的标签。
保持性能:尽管 DistilBERT 的模型大小减小了,但它仍然保持了与原始 BERT 模型相当的性能,特别是在自然语言理解任务上。
灵活性:DistilBERT 保留了 BERT 模型的基本架构,包括 Transformer 的串联层,这使得它可以很容易地适应各种下游任务。
结构和训练
DistilBERT 的结构相对简单,它仅保留了 BERT 的 6 层 Transformer,删除了 token type embedding 和 pooler 层。在训练过程中,它使用了一种称为“模型压缩”的技术,通过这种方法,模型的层数被减半,同时从教师模型的层初始化学生模型的层。
应用场景
由于其较小的模型大小和较快的推理速度,DistilBERT 适用于需要快速处理和低资源消耗的 NLP 任务,例如文本分类、情感分析、问答系统和语言模型等。
总的来说,DistilBERT 是一个高效的 BERT 变体,它通过知识蒸馏技术实现了模型的压缩,同时保持了良好的性能,特别适合在资源受限的环境中使用。
以下是是使用 DistilBERT 做问答的实例:
https://huggingface.co/distilbert/distilbert-base-uncased-distilled-squad
使用 T5 做文本翻译
T5 模型,全称为“Text-to-Text Transfer Transformer”,是由 Google Research 团队开发的一种自然语言处理(NLP)模型。T5 模型的核心思想是将所有 NLP 任务统一转换为文本到文本(Text-to-Text)的格式,从而可以使用同一个模型和训练过程来处理多种不同的任务,如翻译、摘要、问答等。
主要特点和优势
统一的框架:T5 模型通过将任务转换为文本到文本的格式,简化了不同 NLP 任务的处理方式。例如,对于翻译任务,输入可以是“translate English to German: [English text]”,输出则是翻译后的文本。
基于 Transformer 架构:T5 模型采用了 Transformer 的 encoder-decoder 架构,这是一种高效的网络结构,特别适合处理序列数据。
预训练和微调:T5 模型首先在大规模的数据集上进行预训练,学习语言的通用表示,然后可以针对特定任务进行微调,以优化任务性能。
广泛的应用场景:T5 模型可以应用于多种 NLP 任务,包括但不限于文本分类、命名实体识别、情感分析、机器翻译和对话生成等。
高效的计算能力:T5 模型的设计允许它高效地处理大规模数据集,并且具有强大的并行处理能力。
训练和应用
T5 模型在训练时使用了一种称为“C4”的大规模数据集,这个数据集由经过清洗的 Common Crawl 数据组成。模型通过不同的预训练目标和策略进行训练,包括自回归、自编码和文本重排等。
在应用方面,T5 模型的强大语言表示能力和广泛的应用场景使其成为 NLP 领域的一个重要工具。它可以通过微调来适应不同的领域和任务,从而在多个 NLP 任务上取得优异的性能。
T5 模型通过其创新的 Text-to-Text 框架和基于 Transformer 的架构,在自然语言处理领域提供了一种新的解决方案,能够处理多种复杂的语言任务,并且具有很好的扩展性和适应性。
以下是使用 T5 做文本翻译的示例:
https://huggingface.co/google-t5/t5-base
使用 GPT-2 写小说
GPT-2(Generative Pre-trained Transformer 2)是由 OpenAI 开发的自然语言处理(NLP)模型,它是 GPT 系列模型的第二代。GPT-2 在自然语言理解和生成方面表现出色,能够生成连贯、相关且多样化的文本。这个模型在发布时因其生成文本的质量和多样性而受到广泛关注。
主要特点和优势
大规模预训练:GPT-2 通过在大规模的互联网文本数据集上进行预训练,学习到了丰富的语言模式和知识。这种预训练使得模型能够理解和生成自然语言文本。
Transformer 架构:GPT-2 基于 Transformer 模型架构,这是一种依赖于自注意力(self-attention)机制的深度学习架构,非常适合处理序列数据,如文本。
无监督学习:GPT-2 采用无监督学习方法,通过预测下一个词的任务来预训练模型。这种训练方式不依赖于标注数据,使得模型能够学习到更广泛的语言知识。
生成能力:GPT-2 特别擅长文本生成任务,能够生成连贯、有逻辑的段落和文章,甚至能够模仿特定的写作风格。
多样性:GPT-2 能够处理多种语言任务,包括文本生成、翻译、问答、摘要等。
版本和规模
GPT-2 有多个版本,不同版本之间主要区别在于模型的大小和参数数量。例如,最小的版本有 1.17 亿个参数,而最大的版本(GPT-2 1.5 Billion)有 15 亿个参数。随着模型规模的增加,性能和生成文本的质量也相应提高。
应用场景
GPT-2 可以应用于多种场景,如聊天机器人、文本摘要、内容创作辅助、语言翻译等。它的生成能力使得在创意写作、新闻生成和其他需要自然语言生成的领域中具有潜在的应用价值。
挑战和限制
尽管 GPT-2 在生成文本方面表现出色,但它也面临一些挑战和限制,包括生成文本的偏见问题、事实准确性问题以及潜在的滥用风险。因此,OpenAI 在发布 GPT-2 时采取了谨慎的态度,逐步放开对模型的访问权限。
总的来说,GPT-2 是一个强大的 NLP 模型,它在文本生成和理解方面的能力使其成为自然语言处理领域的一个重要里程碑。
以下是使用 GPT-2 做文本生成的示例:
https://huggingface.co/openai-community/gpt2
完整的体验地址:https://transformer.huggingface.co/doc/gpt2-large
我们可以输入一段小说的开头,比如:As aliens entered our planet,然后 Transformer 就会依据我们输入的文本,自动脑补剩下的小说情节。
那 Transformer 是怎么做到的呢?如下图所示,Transformer 在生成每一个 token 时,会参考前面所有的 token,并生成与之相符的 token,这样循环往复就能生成完整的一段内容。
九、参考文档
https://arxiv.org/pdf/1706.03762.pdf
https://jalammar.github.io/illustrated-transformer/
https://www.bilibili.com/video/BV1ih4y1J7rx
*文/逅弈
本文属得物技术原创,更多精彩文章请看:得物技术官网
未经得物技术许可严禁转载,否则依法追究法律责任!
版权声明: 本文为 InfoQ 作者【得物技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/dc04ac177e0cb353129ef4949】。文章转载请联系作者。
评论