Transformer

【1】https://blog.csdn.net/weixin_45875127/article/details/108572928  

【2】https://zhuanlan.zhihu.com/p/253395821

【3】https://www.zhihu.com/question/347366108/answer/835047041

【4】https://blog.csdn.net/longxinchen_ml/article/details/86533005

此篇文章是基于以上文章的理论理解,仅供自己学习参考和笔记

一、根据【1】的总结:语言翻译模型主要分为3类:

(1)基于RNN的模型,(2)基于RNN模型+attention (3)基于transformer模型。下面分别将这三个模型的基本思想。

首先这些模型都是基于编码和解码的语言翻译。都包括编码器和解码器。

语言翻译过程流程可大致为:编码器->中间信息->解码器。

(1)基于RNN模型(seq2seq)

Seq2Seq模型分为encoder层与decoder层,并均由RNN或RNN的变体构成


在encode阶段,第一个节点输入一个词,之后的节点输入的是下一个词与前一个节点的hidden state,最终encoder会输出一个context,这个context又作为decoder的输入,每经过一个decoder的节点就输出一个翻译后的词,并把decoder的hidden state作为下一层的输入。该模型对于短文本的翻译来说效果很好,但是其也存在一定的缺点,如果文本稍长一些,就很容易丢失文本的一些信息,为了解决这个问题,Attention应运而生。

(2)基于RNN模型和attention模型

Attention模型在decode阶段,会选择最适合当前节点的context作为输入。
Attention与传统的Seq2Seq模型主要有以下两点不同:

  •  encoder提供了更多的数据给到decoder,encoder会把所有的节点的hidden state提供给decoder,而不仅仅只是encoder最后一个节点的hidden state。

  •  decoder并不是直接把所有encoder提供的hidden state作为输入,而是采取一种选择机制,把最符合当前位置的hidden state选出来,步骤如下
  1. 确定哪一个hidden state与当前节点关系最为密切
  2. 计算每一个hidden state的分数值(具体怎么计算我们下文讲解)
  3. 对每个分数值做一个softmax的计算,这能让相关性高的hidden state的分数值更大,相关性低的hidden state的分数值更低

下面以一个具体的例子解释详细计算步骤:

 

  1.  把每一个encoder节点的hidden states的值与decoder当前节点的上一个节点的hidden state相乘(如上图,h1、h2、h3分别与当前节点的上一节点的hidden state进行相乘,如果是第一个decoder节点,需要随机初始化一个hidden state);
  2.  最后会获得三个值,这三个值就是上文提到的hidden state的分数(这个数值对于每一个encoder的节点来说是不一样的);
  3.  把该分数值进行softmax计算,计算之后的值就是每一个encoder节点的hidden states对于当前节点的权重;
  4.  把权重与原hidden states相乘并相加,得到的结果即是当前节点的hidden state(Atttention的关键就是计算这个分值)。

明白每一个节点是怎么获取hidden state之后,接下来就是decoder层的工作原理了,其具体过程如下:

  1.  第一个decoder的节点初始化一个向量,并计算当前节点的hidden state;
  2.  把该hidden state作为第一个节点的输入,经过RNN节点后得到一个新的hidden state与输出值。

注意,这里和Seq2Seq有一个很大的区别,Seq2Seq是直接把输出值作为当前节点的输出,但是Attention会把该值与hidden state做一个连接,并把连接好的值作为context,并送入一个前馈神经网络,最终当前节点的输出内容由该网络决定.
重复以上步骤,直到所有decoder的节点都输出相应内容。

 

(3)Transformer模型

 

以上三种模型的总结:

(1)基于RNN模型的seq2seq和seq2seq2+attention的区别主要在编码器的输入阶段。两个模型在解码的时候,每一时刻t输入都为《编码向量,当前输入,上一时刻隐藏输入》,区别在于无注意力的t时刻输入编码向量是固定的,而注意力t时刻输入的编码向量是时刻变化的。

(2)RNN模型和Transformer模型在解码器方面没有区别,都是给一个开始标记然后不断循环直到遇到结束标记停止。最大的区别在于RNN的编码器输入是按时刻输入的,而Transformer编码器是一次性输入整句话然后通过多层注意力堆叠得到编码向量信息供解码器使用。

(3)RNN模型和Transformer模型的另一区别是RNN输出的是编码向量供解码器使用,而Transformer输出的是编码向量转化后的K和V矩阵供Transformer解码器使用。

 

二、:知道上面区别后就可以把任务分解为三个部分:

2.1 如何编码器如何处理输入的一句话,从而得到输出向量

2.2 输出向量如何转化为两个K,V编码矩阵供解码器使用

2.3 编码器如和根据两个K,V矩阵得到当前时刻输出

 

2.1 如何编码器如何处理输入的一句话,从而得到输出向量

由于编码器是堆叠的,所有处理流程都是一样,所以只需要将明白一个编码器就能行了

下面就讲如何根据编码器得到输出向量供下一个编码器处理的:下面是编码器的示意图,包括三个部分

1表示的多个自注意力模块;2表示的是残差和正则化模型;3表示的是前向神经网络。其中最重要

 

最重要的就是1部分了,下面是1部分对应的分解图:看不懂Q,K,V没关系,下面就讲一下自注意力:

 

2.1.1.自注意力机制的细节   :参考自:【2】https://zhuanlan.zhihu.com/p/253395821

让我们先来看看如何用向量来计算自我注意力,然后再来看看它是如何使用矩阵来实现的。

1) 自我注意力计算 Step 1

计算自我注意力的第一步是从编码器的每个输入向量(每个单词的嵌入)中创建三个向量。

因此,对于每个单词,我们创建了一个查询向量(Query Vector)、一个键向量(Key Vector)和一个值向量(Value Vector)。这些向量是通过将嵌入乘以我们在训练过程中训练的三个矩阵来创建的。

注意,这些新向量的维数比嵌入向量小。它们的维数为64,而嵌入和编码器输入/输出向量的维数为512。他们并不一定要更小,这是一个架构选择,使计算的多头注意力(大部分)不变。

将x1乘以wq权重矩阵产生q1,即与该单词相关联的“查询”向量。最后,我们对输入句子中的每个单词创建“查询”、“键”和“值”映射。

2)自我注意力计算 Step 2

计算自我注意力的第二步是计算分数。假设我们在为这个例子中的第一个词“Thinking”计算自我注意力。我们需要对输入句子中的每个单词打分(和我们正在编码的Thinking有关)。当我们在一个特定的位置编码一个单词时,这个分数决定了我们在输入句子的其他部分上要集中多少注意力。

这个分数是通过查询向量和我们要计算得分的键向量的点积进行计算的。所以,如果我们在计算位置#1的自我注意力机制,第一个分数就是q1k1的点积,第二个分数就是q1k2的点积。

 

3)自我注意力计算 Step 3 & Step 4

第三步和第四步是将分数除以8(键向量维度的平方根,论文中该值为64。这能得到更稳定的梯度。这里可能还有其他可能的值,但这是默认的)。然后结果经过Softmax操作处理。Softmax将规范化分数,所以它们都是正数,且加和为1。

这个Softmax分数决定了每个单词在这个位置上的贡献量。显然,这个位置上的单词将获得最高的Softmax分数,但有时注意到另一个与当前单词相关的单词是有用的。

4)自我注意力计算 Step 5

第五步是将每个值向量乘以Softmax分数(准备将它们加起来)。这里的直觉是保持我们想要关注的单词的价值不变,并降低不相关的单词的贡献(例如,将它们乘以0.001这样的微小数字)。

5)自我注意力计算 Step 6

第六步是对加权后的值向量进行加和。这将在这个位置产生自我注意层的输出(对于第一个单词)。

这就完成了自我注意力机制的计算。得到的向量是一个我们可以发送到前馈神经网络的向量。然而,在实际的实现中,这种计算是以矩阵形式完成的,以便更快地处理。让我们来看看,现在我们已经看到了在单词级别上计算的直觉。

2.1.2. 矩阵形式的自注意力计算

第一步是计算查询、键和值矩阵。为此,我们将嵌入打包成一个矩阵X,并将其乘以我们训练的权重矩阵(WQ,WK,WV)。

X矩阵中的每一行对应于输入句子中的一个单词。我们再次看到嵌入向量大小的差异(图中为512或4方框),以及q/k/v矢量(图中为64或3方框))。

最后,由于我们处理的是矩阵,我们可以将步骤2到步骤6浓缩为一个公式来计算自我关注层的输出。

矩阵形式自注意力的计算

2.1.3. 有许多头的加强版本(The Beast With Many Heads)

该论文通过增加一种叫做“多头”注意的机制,进一步完善了自我注意层。这在两方面提高了注意层的性能:

  1. 它扩展了模型专注于不同位置的能力。是的,在上面的例子中,Z1包含了其他编码的一点点,但是它可能被实际的单词本身所主导。如果我们翻译一个句子,比如“The animal didn’t cross the street because it was too tired”,我们会想知道“it”指的是哪个词,这将是有用的。
  2. 它给出了注意层多个“表示子空间”。接下来我们将看到,在多头关注下,我们不仅有一组查询/键/值权重矩阵,而且还有多组查询/键/值权重矩阵(Transformer使用八个注意头,因此每个编码器/解码器最终有八组)。这些集合中的每一个都是随机初始化的。然后,在训练之后,每个集合被用来将输入嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。

在多头关注下,我们为每个头保持独立的q/k/v权矩阵,从而产生不同的q/k/v矩阵。和以前一样,我们把X乘以WQ/WK/WV矩阵来产生Q/K/V矩阵

如果做与我们上面提到过的相同的自注意力计算,我们只需要八次不同的权重矩阵,我们就得到了八个不同的Z矩阵。

这给我们带来了一点挑战。前馈层不想要接受到8个矩阵-它更期待输入一个矩阵(每个单词的向量)。所以我们需要一种方法把这八块压缩成一个矩阵。

我们该怎么做?我们把这些矩阵连在一起,然后用一个附加的加权矩阵WO将它们相乘。

这几乎就是多头自我关注的全部。我意识到这里的矩阵比较少,让我试着把它们放在一个视觉图中,这样我们就可以在一个地方看到它们。

既然我们已经接触到了注意力头,那么让我们重温我们以前的例子,看看我们在示例句中编码“it”一词时,不同的注意力头集中在哪里:

当我们编码“it”一词时,一个注意力头集中在“animal”上,而另一个则集中在“tired”上--从某种意义上说,模型对“it”一词的表达在某种程度上融合了“animal”和“tired”的表达。

然而,如果我们把所有的注意力都加到画面上,事情就更难解释了:

2.1.4. 使用位置编码表示序列的顺序(Representing The Order of The Sequence Using Positional Encoding

正如我们到目前为止所描述的,在模型中缺少的是一种解释输入序列中单词顺序的方法

(我理解的意思是,计算注意力机制的时候,只关注到了单词的表达本身,而没有引入位置信息来表达单词的前后顺序,比如上图中的两个the位置不一样)

为了解决这个问题,Transformer为每个输入嵌入添加了一个向量。这些向量遵循模型学习的特定模式,这有助于确定每个单词的位置,或序列中不同单词之间的距离。这里的直觉是,将这些值添加到嵌入中,在嵌入向量被投影到Q/K/V向量时,以及在点积注意期间,它们之间提供了有意义的距离。

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

如果我们假设嵌入的维数为4,则实际的位置编码如下所示:

嵌入尺寸为4的位置编码简单实例

这个模式会是什么样子?

在下图中,每一行对应一个向量的位置编码。所以第一行是我们要在输入序列中嵌入第一个单词的向量。每行包含512个值,每个值介于1和-1之间。我们已经对它们进行了颜色编码,所以图案是可见的。

20单词(行)的位置编码实例,嵌入大小为512(列)。你可以看到它从中间分裂成两半。这是因为左半部分的值由一个函数(使用正弦)生成,而右半部分由另一个函数(使用余弦)生成。然后将它们连在一起,形成每个位置编码向量。

位置编码的公式在原论文的 Section 3.5 中描述。您可以看到get_timing_signal_1d()中生成位置编码的代码。这不是唯一可能的位置编码方法。然而,它的优点是能够扩展到未知的序列长度(例如,如果我们的训练模型被要求翻译的句子比我们训练集中的任何一个都长)。

2020年7月最新情况:上面显示的位置编码来自Transformer的Transformer2Transformer实现。论文中给出的方法略有不同,因为它不是直接级联,而是将两个信号交织在一起。下图显示了它的样子。

Here’s the code to generate it :

2.1.5. 残差连接

在我们继续前进之前,我们需要提到编码器结构中的一个细节,就是每个编码器中的每个子层(自我注意,ffnn)周围都有一个残差连接(residual connection),然后是一个层级归一化(layer-normalization)步骤。

如果我们要可视化与自注意力相关的向量和层规范操作,它将如下所示:

 

 

 

 

2.2)输出向量如何转化为两个K,V编码矩阵供解码器使用?

关于这个问题的解答,可见:【3】https://www.zhihu.com/question/347366108/answer/835047041

 

2.3)编码器如和根据两个K,V矩阵得到当前时刻输出 ?

参考自:【2】https://zhuanlan.zhihu.com/p/253395821

2.3.1 解码器部分(The Decoder Side

既然我们已经讨论了编码器方面的大部分概念,基本上也知道了解码器的组件是如何工作的。但让我们来看看他们是如何一起工作的。

编码器从处理输入序列开始。然后将顶级编码器的输出转换为一组注意向量K和V。这些注意向量将由每个解码器在其“编解码器注意”层中使用,从而帮助解码器集中注意输入序列中的适当位置:

gif:https://vdn1.vzuu.com/SD/dce8fcc8-23c8-11eb-abfb-e6c8942ef9e9.mp4?disable_local_cache=1&bu=pico&expiration=1611996790&auth_key=1611996790-0-0-c78a3c057551ec3df8453a2d278f620a&f=mp4&v=hw

在完成编码阶段后,我们开始解码阶段。解码阶段的每一步都从输出序列中输出一个元素(在本例中为英文翻译句)。

以下步骤重复该过程,直到达到指示Transformer解码器已完成其输出的特殊符号(end of sentence)为止。每个步骤的输出在下一时间步骤中被输入到底层解码器,解码器就像编码器一样将它们的解码结果加起来。就像我们对编码器输入做的那样,我们将位置编码嵌入到这些解码器输入中,以指示每个单词的位置。

gif:https://vdn1.vzuu.com/SD/cf255d34-ec82-11ea-acfd-5ab503a75443.mp4?disable_local_cache=1&bu=pico&expiration=1611990146&auth_key=1611990146-0-0-b8bb88fc63a1c3f75187a3f2ce128f1a&f=mp4&v=hw

解码器中的自注意力层的操作方式与编码器中的层略有不同:

在解码器中,只允许自注意层处理输出序列中的早期位置。这是通过掩蔽未来的位置(将其设置为-inf)来完成的,然后再进行自注意力机制计算中的Softmax步骤。

“编码器-解码器”注意力层的工作方式类似于多头自注意力,只不过它从下面的层创建查询矩阵,并从编码组件的输出中获取键和值矩阵。

最终的线性层和Softmax层(The Final Linear and Softmax Layer)

解码器堆栈输出浮点型的向量。我们怎么把它变成一个词?这是最后一个线性层+Softmax层的工作。

线性层是一个简单的完全连接的神经网络,它将解码器堆栈产生的向量投影到一个更大的向量中,称为Logits向量

让我们假设我们的模型知道10,000个独特的英语单词(我们的模型的“输出词汇表”),这是从它的训练数据集中学到的。这将使logits向量为10,000个单元格宽--每个单元格对应一个唯一单词的分数。这就是我们如何解释模型的输出要接一个线性层。

然后,Softmax层将这些分数转化为概率(全部为正数,加起来为1.0)。选择概率最高的单元格,并生成与其相关的单词作为此时间步骤的输出。

这个数字从底部开始,向量作为解码器堆栈的输出。然后,它被转换成一个输出字。

2.3.2训练总结(Recap Of Training

既然我们已经清楚了Transformer的前向传播过程,那么了解训练模型的直觉是有用的。

在训练期间,一个未经训练的模型会经过相同的前向传播过程。但是,由于我们是在一个标记的训练数据集上训练它,所以我们可以将它的输出与实际的正确输出进行比较。

为了可视化这一点,让我们假设输出词汇表只包含六个单词(“a”, “am”, “i”, “thanks”, “student”, and “<eos>” 。

我们的模型的输出词汇表是在我们开始训练之前在预处理阶段创建的。

一旦定义了输出词汇表,就可以使用相同宽度的向量来表示词汇表中的每个单词。这也被称为One-Hot编码。例如,我们可以使用以下向量表示单词“am”:

示例:输出词汇表的一种热编码。

接下来,让我们来讨论模型的损失函数--我们在训练阶段正在优化的度量,以达到一个经过训练的,希望是非常精确的模型。

2.3.3 损失函数

翻译预测一个单词可以用交叉熵cross-entropy或者KL散度Kullback–Leibler divergence.

在翻译整个句子的时候,我们可以每次选择一个概率最高的单词位置,然后丢掉剩下的(称之为贪心解码)。另一种方法是保留概率分布最高的两个单词(例如,‘i’和‘a’),然后在下一步运行模型两次:一次假设第一个输出位置是‘i’,另一次假设第一个输出位置是‘a’,而考虑到#1和#2这两个位置时产生的错误较少的版本就保留了。在我们的示例中,BEAM_SIZE为2(意味着在任何时候,两个部分假设(未完成的翻译)都保存在内存中),TOP_BEAM也是2(意味着我们将返回两个翻译)。这些都是你可以实验的超参数。

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Transformer发轫于NLP(自然语言处理),并跨界应用到CV(计算机视觉)领域。目前已成为深度学习的新范式,影响力和应用前景巨大。 本课程对Transformer的原理和TensorFlow 2代码进行精讲,来帮助大家掌握其详细原理和具体实现。 原理精讲部分包括:注意力机制和自注意力机制、Transformer的架构概述、Encoder的多头注意力(Multi-Head Attention)、Encoder的位置编码(Positional Encoding)、残差链接(Residual Connection)、层规范化(Layer Normalization)、FFN(Feed Forward Network)、Transformer的训练及性能、Transformer的机器翻译工作流程。  代码精讲部分使用Jupyter Notebook对Transformer的TensorFlow 2实现代码进行逐行解读,包括:安装TensorFlow、Transformer的数据集加载与预处理代码解读、Transformer的位置编码与多头注意力代码解读、TransformerTransformer类代码解读、Transformer的优化器与损失函数代码解读、Transformer的训练代码解读、Transformer的推理与权重保存代码解读。相关课程: 《Transformer原理与代码精讲(PyTorch)》https://edu.csdn.net/course/detail/36697《Transformer原理与代码精讲(TensorFlow)》https://edu.csdn.net/course/detail/36699《ViT(Vision Transformer)原理与代码精讲》https://edu.csdn.net/course/detail/36719《DETR原理与代码精讲》https://edu.csdn.net/course/detail/36768《Swin Transformer实战目标检测:训练自己的数据集》https://edu.csdn.net/course/detail/36585《Swin Transformer实战实例分割:训练自己的数据集》https://edu.csdn.net/course/detail/36586《Swin Transformer原理与代码精讲》 https://download.csdn.net/course/detail/37045

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值