跳转至

95-一文读懂Transformer【超详细】【原理篇&实战篇】-飞书云文档

本文讨论了Transformer模型的原理和自注意力机制的实现,涵盖了Transformer的结构、特点、各组件功能及自注意力机制的具体计算步骤等内容。关键要点包括:

Transformer简介:2017年由Vaswani等人提出的深度学习模型架构,用于自然语言处理和序列到序列任务,引入自注意力机制,处理序列数据表现出色。

结构组成:编码和解码组件均由6个编码器/解码器叠加,编码器由多头注意力和全连接神经网络构成,解码器包含两个多头注意力层。

自注意力机制:核心概念,可并行计算,能捕捉长距离依赖,有“多头”扩展形式,可让模型关注不同位置。

位置编码:为让模型理解单词顺序,给输入词嵌入添加向量,词嵌入与位置编码相加。

Add&Normalize:多头注意力输出经此步骤,Add加残差块防网络退化,Normalize用Layer Normalization加快训练速度、提高稳定性。

全连接层:两层神经网络,先线性变换,ReLU非线性,再线性变换,将输入映射到高维空间筛选后变回原维度。

自注意力实现步骤:准备输入(词嵌入向量)、初始化参数(Q、K、V矩阵)、获取key,query和value、计算注意力分数、计算softmax、给value乘上score、给value加权求和获取output。

【原理篇】一文读懂Transformer

前言

一、Transformer是什么?

二、Self-Attention的实现

前言

Transformer是一种用于自然语言处理(NLP)和其他序列到序列(sequence-to-sequence)任务的深度学习模型架构,它在2017年由Vaswani等人首次提出。Transformer架构引入了自注意力机制(self-attention mechanism),这是一个关键的创新,使其在处理序列数据时表现出色。

一、 Transformer是什么?



Transformer是一种用于自然语言处理(NLP)和其他序列到序列(sequence-to-sequence)任务的深度学习模型架构,它在2017年由Vaswani等人首次提出。Transformer架构引入了自注意力机制(self-attention mechanism),这是一个关键的创新,使其在处理序列数据时表现出色。

自注意力机制(Self-Attention):这是Transformer的核心概念之一,它使模型能够同时考虑输入序列中的所有位置,而不是像循环神经网络(RNN)或卷积神经网络(CNN)一样逐步处理。自注意力机制允许模型根据输入序列中的不同部分来赋予不同的注意权重,从而更好地捕捉语义关系。

多头注意力(Multi-Head Attention):Transformer中的自注意力机制被扩展为多个注意力头,每个头可以学习不同的注意权重,以更好地捕捉不同类型的关系。多头注意力允许模型并行处理不同的信息子空间。

堆叠层(Stacked Layers):Transformer通常由多个相同的编码器和解码器层堆叠而成。这些堆叠的层有助于模型学习复杂的特征表示和语义。

位置编码(Positional Encoding):由于Transformer没有内置的序列位置信息,它需要额外的位置编码来表达输入序列中单词的位置顺序。

残差连接和层归一化(Residual Connections and Layer Normalization):这些技术有助于减轻训练过程中的梯度消失和爆炸问题,使模型更容易训练。

编码器和解码器:Transformer通常包括一个编码器用于处理输入序列和一个解码器用于生成输出序列,这使其适用于序列到序列的任务,如机器翻译。

Image

English | 简体中文 | 繁體中文 | 한국어 | Español | 日本語

State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow

Image

Part of the Hugging Face course!

1 -1、Transformer的结构:

Nx = 6,Encoder block由6个encoder堆叠而成,图中的一个框代表的是一个encoder的内部结构,一个Encoder是由Multi-Head Attention和全连接神经网络Feed Forward Network构成。如下图所示:


Image

输入


Image

1 -2、自注意力机制

自注意力的作用:随着模型处理输入序列的每个单词,自注意力会关注整个输入序列的所有单词,帮助模型对本单词更好地进行编码。在处理过程中,自注意力机制会将对所有相关单词的理解融入到我们正在处理的单词中。更具体的功能如下:

序列建模:自注意力可以用于序列数据(例如文本、时间序列、音频等)的建模。它可以捕捉序列中不同位置的依赖关系,从而更好地理解上下文。这对于机器翻译、文本生成、情感分析等任务非常有用。

并行计算:自注意力可以并行计算,这意味着可以有效地在现代硬件上进行加速。相比于RNN和CNN等序列模型,它更容易在GPU和TPU等硬件上进行高效的训练和推理。(因为在自注意力中可以并行的计算得分)

长距离依赖捕捉:传统的循环神经网络(RNN)在处理长序列时可能面临梯度消失或梯度爆炸的问题。自注意力可以更好地处理长距离依赖关系,因为它不需要按顺序处理输入序列。

自注意力的结构如下所示:


Image
Self-Attention 结构

CSDN @与知冷煖★

自注意力的计算:从每个编码器的输入向量(每个单词的词向量,即Embedding,可以是任意形式的词向量,比如说word2vec,GloVe,one-hot编码)

Image

输入ThinkingMachines
词嵌入ImageImage
查询向量ImageImageImageWQ
键向量Image$ k_2 $ImageImageWK
值向量ImageImageImageImage

更一般的,将以上所得到的查询向量、键向量、值向量组合起来就可以得到三个向量矩阵Query、Keys、Values。

查询向量、键向量和值向量

计算自注意力的第二步是计算得分,假设我们在为这个例子中的第一个词“Thinking”计算自注意力向量,我们需要拿输入句子中的每个单词对“Thinking”打分。这些分数是通过所有输入句子的单词的键向量与“Thinking”的查询向量相点积来计算的。

Thinking

Machines

Image
Image
Image
Image
Image
Image
Image

$ q_1 \cdot k_1 = 112 $

Image

$ q_1 \cdot k_2 = 96 $


Image

Image

整体的计算图如图所示:

Image

最终得到了自注意力,并将得到的向量传递给前馈神经网络。以上二到六步合为一个公式计算自注意力层的输出。

自注意力层的完善——“多头”注意力机制:

对应整体结构图中的 Multi——Head Attention

Image

注意力头0

注意力头1

注意力头7

1)将所有注意力头拼接起来

2)乘以矩阵 $ W^{0} $,它在模型中是联合训练的

3)结果是一个融合所有注意力头信息的矩阵Z,我们可以将其送到前馈神经网络

CSDN @告白气球~


1)这是我们的输入句子*

2)编码每一个单词

3) 将其分为8个头,

将矩阵X或R乘以各

个权重矩阵

4) 通过输出的查询

/键/值(Q/K/V)矩

阵计算注意力

5) 将所有注意力头拼接起来,乘以

权重矩阵W^{0}

Image

$ ^{*} $注:除了第0个编码器,其他编码器都不需要进行词嵌入。它可以直接讲前面一层编码器的输出作为输入(矩阵R)。

其中一个注意力头集中在The animal

另一个注意力头集中在tire上。即形象解释it代指的是animal和tire。

Image

1 -3、使用位置编码表示序列的顺序

为什么要用位置编码?

如果不添加位置编码,那么无论单词在什么位置,它的注意力分数都是确定的。这不是我们想要的。

为了理解单词顺序,Transformer为每个输入的词嵌入添加了一个向量,这样能够更好的表达词与词之间的关系。词嵌入与位置编码相加,而不是拼接,他们的效率差不多,但是拼接的话维度会变大,所


以不考虑。(这里位置向量如何得到,以哪种计算方法得到,以及词嵌入与位置编码如何结合是可以尝试实验的点,可以看以下链接思考这个问题)。如何理解Transformer论文中的positional encoding,和三角函数有什么关系?

Image
Image

为了让模型理解单词的顺序,我们添加了位置编码向量,这些向量的值遵循特定的模式。

Image

1 -4、Add&Normalize

在经过多头注意力机制得到矩阵Z之后,并没有直接传入全连接神经网络,而是经过了一步Add&Normalize。


Image

Add & Norm 层由 Add 和 Norm 两部分组成,其计算公式如下:

$$ LayerNorm(X+ $$

$$ MultiHeadAttention(X) $$

$$ LayerNorm(X+FeedForward(X)) $$


其中 X 表示 Multi-Head Attention 或者 Feed Forward 的输入,MultiHeadAttention(X) 和 FeedForward(X) 表示输出 (输出与输入 X 维度是一样的,所以可以相加)。

AddAdd,就是在z的基础上加了一个残差块X,加入残差块的目的是为了防止在深度神经网络的训练过程中发生退化的问题,退化的意思就是深度神经网络通过增加网络的层数,Loss逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss反而增大。

Add指 X+MultiHeadAttention(X),是一种残差连接,通常用于解决多层网络训练的问题,可以让网络只关注当前差异的部分,在 ResNet 中经常用到:

Image
残差连接

ResNet残差神经网络为了了解残差块,我们引入ResNet残差神经网络,神经网络退化指的是在达到最优网络层数之后,神经网络还在继续训练导致Loss增大,对于多余的层,我们需要保证多出来的网络进行恒等映射。只有进行了恒等映射之后才能保证这多出来的神经网络不会影响到模型的效果。残差连接主要是为了防止网络退化。

Image
Figure 2. Residual learning: a building block.

上图就是构造的一个残差块,X是输入值,F(X)是经过第一层线性变换后并且激活的输出,在第二层线性变化之后,激活之前,F(X)加入了这一层输入值X,然后再进行激活后输出。

要恒等映射,我们只需要让F(X)=0就可以了。x经过线性变换(随机初始化权重一般偏向于0),输出值明显会偏向于0,而且经过激活函数Relu会将负数变为0,过滤了负数的影响。

这样当网络自己决定哪些网络层为冗余层时,使用ResNet的网络很大程度上解决了学习恒等映射的问题,用学习残差F(x)=0更新该冗余层的参数来代替学习h(x)=x更新冗余层的参数。

Normalize归一化目的:1、加快训练速度2、提高训练的稳定性使用到的归一化方法是Layer Normalization。


Image

LN是在同一个样本中不同神经元之间进行归一化,而BN是在同一个batch中不同样本之间的同一位置的神经元之间进行归一化。

BN是对于相同的维度进行归一化,但是咱们NLP中输入的都是词向量,一个300维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是适合地,因此这里选用的是LN。

Image

1 -5、全连接层Feed Forward

全连接层公式如下:

$$ F F N(x)=\max(0,x W_{1}+b_{1})W_{2}+b_{2} $$


Image

全连接层是一个两层的神经网络,先线性变换,然后ReLU非线性,再线性变换。

这两层网络就是为了将输入的Z映射到更加高维的空间中然后通过非线性函数ReLU进行筛选,筛选完后再变回原来的维度。

经过6个encoder后输入到decoder中。

1 -6、Decoder整体结构

Masked Multi-Head Attention

padding mask 什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的 attention 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax,这些位置的概率就会接近 0!

sequence masksequence mask 是为了使得 decoder 不能看见未来的信息。对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。这在训练的时候有效,因为训练的时候每次我们是将 target 数据完整输入进 decoder 中地,预测时不需要,预测的时候我们只能得到前一时刻预测出的输出。那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0。把这个矩阵作用在每一个序列上,就可以达到我们的目的。


注意:

1 -7、输出

Output如图中所示,首先经过一次线性变换(线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的,被称为对数几率的向量里),然后Softmax得到输出的概率分布(softmax层会把向量变成概率),然后通过词典,输出概率最大的对应的单词作为我们的预测输出。

Image

1 -8、transformer的优缺点:

优点:

1、效果好

2、可以并行训练,速度快

3、很好的解决了长距离依赖的问题

缺点:

完全基于self-attention,对于词语位置之间的信息有一定的丢失,虽然加入了positional encoding来解决这个问题,但也还存在着可以优化的地方。

二、 Self-Attention的实现

2 -0、过程

准备输入

初始化参数

获取key,query和value

给input1计算attention score

计算softmax

给value乘上score

给value加权求和获取output1

重复步骤4-7,获取output2,output3

2 -1、准备输入(词嵌入向量)


代码块

1 import torch 2 x = [ 3 [1, 0, 1, 0], # Input 1 4 [0, 2, 0, 2], # Input 2

输出:

2 -2、初始化参数(Q、K、V矩阵)

Note:Q、K、V矩阵在神经网络初始化的过程中,一般都是随机采样完成并且比较小,可以根据想要输出的维度来确定Q、K、V矩阵的维度。

代码块 1 w_key = [ 2 [0, 0, 1], 3 [1, 1, 0], 4 [0, 1, 0], 5 [1, 1, 0] 6 ] 7 w_query = [ 8 [1, 0, 1], 9 [1, 0, 0], 10 [0, 0, 1], 11 [0, 1, 1] 12 ]

输出:

2 -3、获取key,query和value


Image

下图为得到的key,query和value:

2 -4、计算注意力分数

代码块 1 attn_scores = queries @ keys.T 2 print(attn_scores)

输出:

2 -5、计算softmax

Image

输出:


2 -6、给value乘上score

输出:

2 -7、给value加权求和获取output(得到input1的结果向量)

Image

把所有的weighted values(黄色)进行element-wise的相加。

[0.0, 0.0, 0.0]

  • [1.0, 4.0, 0.0]

  • [1.0, 3.0, 1.5]

= [2.0, 7.0, 1.5]

得到结果向量 $ [2.0, 7.0, 1.5] $(深绿色)就是output1的和其他key交互的query representation。