以注意力机制为核心的Transformer革命

绪论

为什么Transformer大扫深度学习领域?

五年前Transformer最早提出时,面向的是机器翻译(machine translation)等自然语言处理任务,但到了五年后的今天,它的应用已经遍布自然语言处理、计算机视觉、视频音频处理以及多模态学习,可以毫不夸张地说,Transformer不但一手造就了RNN在自然语言处理领域跌落神坛,如今更是大有取代卷积神经网络在视觉领域霸主地位的势头。Transformer强大的表达能力和通用性来自于其核心结构注意力机制,而随着Transformer的流行,注意力机制本身(包括除了Transformer所用之外的其他注意力类型)也逐渐被大家熟悉,越来越广泛地应用到各类任务中。注意力是一个很大而且很深刻的概念,正是这个概念支撑起了最终席卷整个深度学习领域的Transformer革命,所以吃透这个概念对我们在工作中活学活用Transformer至关重要。本系列对Transformer的解剖,也是以注意力机制为核心内容,力求不局限于表面形式,而是探索表面之下的工作原理,把复杂的结构、灵活多变的应用形式蒸馏成一个清晰的流程,让我们了然于胸。

Transformer的提出是在2017年,尽管它以基于点积的多头注意力机制为核心,但并不是注意力机制的创造者。当时,在对序列的编码和解码中,RNN都是很自然的选择。注意力机制(attention)在当时并不是什么新鲜事,也已经作为源序列和目标序列之间进行对齐(alignment)的方法,在RNN的框架之内广泛使用。

然而RNN有一个重要的缺点,就是它是一个串行计算的结构,在长序列中,经过多个步骤的串行计算,很容易产生梯队消失问题。从这个角度出发,上述论文的作者之一Uszkoreit构想,RNN对序列的编解码过程可能可以换成用注意力机制来实现。由此得到的是一种破天荒不依赖RNN结构、完全由注意力机制组成的架构,也就是“Transformer”,因为注意力计算的并行性提高了训练效率,在机器翻译等任务上的表现超越了以往的RNN方法,更重要的是大大提高了模型能力的天花板,打开了通向后来大规模预训练模型(像BERT,GPT-3)的大门。

Transformer的核心设计理念,即用注意力进行信息交换基于注意力机制的序列编解码器进行序列处理。因此,从适用范围层面来看,虽然Transformer设计之初是为了用于机器翻译,但是只要是符合序列转换形式的任务,不论是什么东西的序列(文字,图片,语音,视频,交通场景,车辆轨迹等等,只要配套相应类型的特征提取或者embedding即可),都可以套用Transformer架构。所以下文中,我们在组装Transformer的时候,重点关注序列这个形式,不考虑太多具体任务类型的要求。

在Transformer架构中,源到目标序列之间的对齐仍然使用以前的注意力机制,Transformer的创新是在对源序列的编码和目标序列的解码中用自注意力(self-attention),替换掉了RNN。至此,在Seq2Seq中,全部由attention机制来实现对齐和解码,因此论文也起名为《all you need is attention》,其含义就是用attention机制(包含mutil-head attention)实现了整个对齐、编码和解码过程。

对CV的冲击

卷积神经网络(convolutional neural networks)是受人类视觉中枢的神经元连接结构启发而设计,可以说为CV量身定做的网络结构,而且从2012年AlexNet时代开始,也几乎一直是CV领域的主流网络架构,直到最近Transformer开始动摇CNN的首发地位。Transformer依赖的注意力机制和CNN的稠密的局部信息解析是很互补的两种思路,在视觉领域也早就有人不断在尝试注意力机制,不论是通过在CNN上添加注意力还是用纯注意力结构替代卷积层。但取得最大突破的还是2021年的ViT,这是一个几乎照搬NLP的Transformer架构(不只是借用其注意力结构或者设计理念)的视觉模型,在大规模预训练的加持下效果极其优异,体现出Transformer模型的强大的表达能力。在此基础上,像PiTSwin Transformer等在Transformer基础上针对CV的特点进行改进的工作也如雨后春般涌现。不过现在断言CNN会被Transformer取代可能还为时尚早,毕竟Transformer在表达能力上的优势需要模型规模、训练数据规模达到一定程度才能显著体现,CNN仍有很大的用武之地,学术界也不断有让Transformer和CNN互相取长补短的研究尝试。

为什么许多新学者只通过学习原文会容易产生疑惑?

许多新学者容易对attention机制产生混淆,主要原因是没有分清attention在不同问题中的作用,也没有通过问题导向来理解attention机制的本质原理与需要解决的实际问题。因此,从学习的角度出发,我们需要学习两部分内容,一个是基于attention的对齐机制,一个是基于attention的编解码机制。而且需要结合实际案例,通过在案例中遇到问题,来理解attention的绝妙之处。

attention机制的发现

Steven Hillyard和他的同事在1973年开展人类神经元对刺激的反应实验。他们发现,当人类主动关注一个刺激时,大脑的神经元会产生比没有关注的刺激更强烈的信号。

被注意的刺激与被忽视的刺激相比激发了更大的神经反应,随后,Hillyard在多个视觉皮层区域都观察到了这一现象。

问题是在神经加工过程中这一现象是如何产生的呢?John Duncan提出了一个选择注意的有偏竞争模型[3]:不同的刺激落入同一个视觉神经元的感受野内会造成两个刺激对神经元控制的竞争,注意可以有利于被注意的刺激夺得优先权(但究竟是如何帮助的仍然不清楚,可能是由于被注意的目标会受到自上而下注意信号的影响,因此可能会在视觉刺激呈现之前相关表征便得到了激活,叠加上刺激呈现后的神经元反应,使得最终的神经反应增强)。随后Sabine Kastner等人的实验对这一模型进行了验证,他们发现与每次只有一个刺激单独呈现相比,在空间集中注意缺失的情况下,同时呈现的临近刺激会相互干扰,而当引入注意后,同时呈现竞争刺激不再产生干扰。这种干扰减弱的现象被认为是注意削弱了分心刺激的影响。

Attention机制在人工智能领域的应用

注意力机制通俗的讲就是把注意力集中放在重要的信息上,而忽略其他不重要的信息。其中重要程度的判断取决于应用场景和任务要求,使用注意力机制的神经网络可以更好的进行“自主学习”。

深度学习中的注意力机制从形式上讲和人类的选择性视觉注意力机制类似,核心目标也是从众多信息中选择出对当前任务目标更关键的信息;从结果上看,人类的注意力机制的影响表现为被注意的特征引起的神经元反应幅度更大;而在深度学习中,注意力机制表现为给不同位置的信息赋予不同的权重,权重的大小代表了不同位置的注意程度,被注意信息的表征强度更大;从这两方面看深度学习中的注意力机制很好的模仿了人类的注意力机制

注意力机制是一种从一系列输入中选择性地提取信息的结构。只要输入的形式是一个某种单位元素的集合或者序列,就可以使用注意力机制来让元素之间进行信息交换。简单来说,深度学习中的注意力机制就是通过一定的计算得到一组权重,而这组权重代表了各个信息的重要程度,那么使用这种权重与原始信息相乘就得到了注意力处理后的信息。

想通这一核心理念,那么所有的注意力机制都万变不离其宗。根据注意力的关注域的不同,我们可以将深度学习中的注意力机制分为空间域、通道域、层域、时间域和混合域等。空间域很容易理解,就是在一张图片中不同的位置赋予不同的关注程度;而通道域就是对图片或特征图的不同通道赋予不同的权重;层域、时间域也是类似的道理,仅仅是选择注意的信息不同。总结来说,核心思想就是帮助模型为输入图像或自然语言的各个部分分配不同的权重,提取更关键、更重要的信息,使得模型可以做出更准确的判断,同时不会给模型的计算和存储带来过多的负担。

基于attention的对齐机制

为什么我们需要对齐(alignment)?

首先理解seq2seq对范围问题的处理范式

在以encoder-decoder处理时间序列的相关的问题时,我们根据“时序对齐(Alignment)”与“时序同步性(Synchronous)”两个维度将所有问题分为四个不同的类型:

对齐不对齐
同步输入时同时输出,一帧输入与一帧输出对应(视频标注)-LSTM,RNN
不同步输入与输出的顺序相同,但是没有一一对应的关系(语音识别):Connectionist Temporal Classification (CTC)输入一个序列,在整个输入完成后输出一个顺序不一定对应的序列 (机器翻译)模型:Seq2Seq

假如我们要让模型将英语 "I ate an apple" 翻译为德语 "Ich habe einen apfel gegessen",我们会发现翻译结果中的语序和原来并不相同,同时,一些翻译结果并不能与英语中的词汇一一对应到。

在不对齐不同步问题中,我们不能直接使用LSTM这样的模型,因为在时间序列的 � 时刻,我们并不能确定此时的模型是否已经获得了所有输出正确结果所需要的信息。因此,我们的解决方案是:先处理完所有的输入,将所有输入编码(encode)到一个隐藏状态(Hidden State),然后再逐步对这个隐藏状态进行解码(decode)。通过这样的结构,我们能确定在解码过程中模型一定收到了所有所需的信息。

上面的模型在解决了输入信息完整性的同时还有一个明显的缺陷:在解码阶段中,每一个输出只和当前隐藏状态有关,与之前的输出没有联系。由于一个隐状态需要对应多个字段,这就造就了seq2seq模型存在着难以对齐的问题。比如中译音“我爱你”“I love you”,输入序列中的“我”应该与“I”对齐(贡献最大),然而在seq2seq模型中,“我”对"I","love","you"的贡献都是一致的。

在图三这种模型中,接受输入的部分被称为编码器(Encoder)。编码器会“总结”输入内容并将结果总结到自己的隐藏状态中。当编码器接受完整个输入后,其隐藏状态被称为 “Summary Vector”,因为这个张量包含了整个输入的语义。接着,Summary Vector 被传递到了解码器(Decoder)的隐藏状态。解码器会解析传入的隐藏状态并(在这个例子中)给出翻译结果。

在现实问题中,模型输出的序列一般都会有上下文相关性 - 也就是说,知道模型在 �−1 时刻的输出会改变模型在 � 时刻输出的概率分布 - �(��∣��−1)≠�(��)。比如在机器翻译的任务中,如果第 �−1 个词是 “an”,那么第 � 个词是 ���� 的概率就应该很很低。

为了解决这个问题,我们可以将模型在 �−1 时刻的输出作为模型的输入传递到 � 时刻。这样带来的好处非常明显:我们可以直接在 � 时刻与 �−1 时刻建立联系在解码过程中,模型在过去时刻的输出也会被编码到模型的hidden state中,所以模型可以“知道”自己在整个解码阶段中曾经输出过什么内容。

到这里的时候,你是不是跟我一样,首先想到的是用贪心算法来解决编码和解码问题:

一种简单的思路是使用贪心算法 - 在每一时刻 �,我们都选择当前输出向量 �� 所代表的,(相似度)概率最高的词向量 � 作为 �� 的解读结果。

��=argmax��(����)

然而,这样的方法有一个弊端:如果在时刻 � 模型的最优输出并不是正确的结果,从 �+1 开始的每个时刻,模型都会受到这样一个错误输出的影响,最后导致模型“越走越偏”。

我们来看看人类是怎么解决这个问题的?

实际上,我们的大脑每时每刻都在处理同样的问题 - 自然语言中充斥着模糊性,这种模糊性来自于词法(Lexical Ambiguity) ,结构(Structural Ambiguity) 和 语音(Acoustic Ambiguity) 。而大脑在日常交流中在无时无刻的进行去模糊化的动作。用于去模糊化的信息主要来自于对话的上下文和场景。
也就是说 - 在日常交流的过程中我们的大脑会先保留时刻�的语言模糊性,等到积累了足够的上下文后再对�时刻的结果进行去模糊化操作。

既然我们在时刻�无法得知足够的信息来做出选择,那么我们就不做出选择- 直到我们积累了足够多的信息来做出选择我们再返回来�时刻进行决择。在每个时刻,我们选择概率最高的�个词向量作为对解码器在�时刻输出的解读。我们将解码器 “分叉到不同的时间线”上 - 在每个分叉中,我们使用不同的词向量作为�+1时刻的解码器输入。

每一个时刻,如果概率最高的两个解读结果概率差距不大,我们可以将解码器“分叉”,在不同的assumption 上继续推理并在未来回溯至该时刻进行选择

当解码器所有的分支都结束输出以后,我们可以对每一个分支输出的序列进行评估并在其中选择最优的序列。

解码器每个时刻因为对解码结果的不同解读产生不同的分叉

因为分支的数量随序列长度成指数上升,这里我们一般会在评估时使用一种叫 Beam Search的启发式搜索算法对需要评估的分支进行剪枝。

Seq2Seq 模型的训练

在训练 Seq2Seq 模型时,我们不用对模型在编码阶段的输出做任何评估。我们只需要对比模型在解码阶段给出的输出即可。

在训练阶段,我们并不会将解码器在 �−1 时刻的输出作为输入传输给 � 时刻(我们在模型推理阶段会这么做来建立模型输出的上下文联系)。我们会直接将正确的 ground truth 中的第 �−1 时刻的内容作为输入传输给 � 时刻。

训练阶段的 Seq2Seq 模型,梯度沿紫色箭头路线反向传播

训练阶段的 Seq2Seq 模型,梯度沿紫色箭头路线反向传播,在训练阶段中,如果我们选择 SGD 作为优化方法,我们可以这样训练模型:

  1. 随机在训练集中选择一个 (input, output) 数据
  2. 使用这个数据让模型进行前向计算
  3. 随机选择模型在解码阶段的一个输出进行 Loss 计算并进行反向传播
  4. 更新模型权重参数

Seq2Seq 模型到现在依然是解决非同步非对齐序列预测问题的经典模型之一。然而,它的结构存在一个缺陷 - 这个缺陷会在模型接受长输入序列时被暴露无遗。注意到图3中的编码器-解码器模型。在这个模型中,编码器与解码器只通过隐藏状态(总结向量)连接起来。这直接导致了两个问题:

  1. 当输入序列较长时,这个隐藏状态可能无法完全容纳输入中蕴含的所有信息量,从而导致模型接受信息的损失。【编码器与解码器之间的连接太狭窄,无法传递所有信息】
  2. 如果输入序列较长,一些信息可能会在编码器接受新信息的过程中被“稀释”,这可能会导致解码器遗漏输入中的关键信息。【编码器中所有的隐藏状态都含有独特的信息,但是只有最终状态被传递给了解码器】

基于attention的编解码机制

为什么我们需要编解码?

注意力机制最早被应用到机器翻译的任务中,机器翻译是一个seq2seq的任务,也就是说由一个序列生成另一个序列的过程,在最开始的时候,该任务通常使用Autoencoder的方式:输入一个源语言的句子,经过编码器获得一个包含全部输入信息的中间语义向量,随后将该向量送入后续的解码器逐渐获得目标输出。

这个多维的中间语义向量实际上是将输入序列都压缩成一个固定大小的隐变量,就像我们的压缩文件一样,这个过程是有损压缩的,会迫使丢失许多输入序列中的信息。

在seq2seq中,我们的想法是让两个具有编码器——解码器架构的递归神经网络(RNN):逐个读取输入字以获得固定维度(编码器)的向量表示,并且以这些输入为条件,使用另一个RNN(解码器)一个接一个地提取输出字。

seq2seq的问题在于,解码器从编码器接收的唯一信息是最后一个编码器的隐藏状态(参见图0.2中的2个微小节点),这是一种向量表示,类似于输入序列的数字汇总。因此,对于长文本,我们不合理地期望着解码器仅使用这一个向量表示来输出翻译。

attention机制解析:

基于以上的通用理解,在语言的Transformer架构中,既用到了在序列转换的源序列和目标序列之间进行对齐的过程中,也用到了编码和解码的过程中。


soft attention模型的图解

第一步:

第二步:

Attention机制本质思想的解读-KQV表达

如果把attention剥离出来去看的话,其实是以下的机制:

输入是query(Q), key(K), value(V),输出是attention value。

如果与之前的模型对应起来的话,query就是decoder的隐层(如 zi或者si) ,key就是encoder的隐层(如 hi ),value也是encoder的隐层。模型通过Q和K的匹配计算出权重,再结合V得到输出。因此attention可由以下方式表示:

接下来我们把Attention机制从上文讲述例子中的Encoder-Decoder框架中剥离,并进一步做抽象,可以更容易看懂Attention机制的本质思想。

我们可以这样来看待Attention机制(上上图):将Source中的构成元素想象成是由一系列的<Key,Value>数据对构成,此时给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。所以本质上Attention机制是对Source中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。从概念上理解,把Attention仍然理解为从大量信息中有选择地筛选出少量重要信息并聚焦到这些重要信息上,忽略大多不重要的信息,这种思路仍然成立。聚焦的过程体现在权重系数的计算上,权重越大越聚焦于其对应的Value值上,即权重代表了信息的重要性,而Value是其对应的信息。

另外一种理解,也可以将Attention机制看作一种软寻址(Soft Addressing):Source可以看作存储器内存储的内容,元素由地址Key和值Value组成,当前有个Key=Query的查询,目的是取出存储器中对应的Value值,即Attention数值。通过Query和存储器内元素Key的地址进行相似性比较来寻址,之所以说是软寻址,指的不像一般寻址只从存储内容里面找出一条内容,而是可能从每个Key地址都会取出内容,取出内容的重要性根据Query和Key的相似性来决定,之后对Value进行加权求和,这样就可以取出最终的Value值,也即Attention值。所以不少研究人员将Attention机制看作软寻址的一种特例,这也是非常有道理的。

Attention机制的具体计算过程的通俗解读(一),如果对目前大多数方法进行抽象的话,可以将其归纳为两个过程:第一个过程是根据Query和Key计算权重系数,第二个过程根据权重系数对Value进行加权求和。而第一个过程又可以细分为两个阶段:第一个阶段根据Query和Key计算两者的相似性或者相关性;第二个阶段对第一阶段的原始分值进行归一化处理;这样,可以将Attention的计算过程抽象为如图10展示的三个阶段。

第一个阶段,可以引入不同的函数和计算机制,根据Query和某个Key_i,计算两者的相似性或者相关性,最常见的方法包括:求两者的向量点积、求两者的向量Cosine相似性或者通过再引入额外的神经网络来求值,即如下方式:

第一阶段产生的分值根据具体产生的方法不同其数值取值范围也不一样,第二阶段引入类似SoftMax的计算方式对第一阶段的得分进行数值转换,一方面可以进行归一化,将原始计算分值整理成所有元素权重之和为1的概率分布;另一方面也可以通过SoftMax的内在机制更加突出重要元素的权重。在点积之外在所有源元素间进行的softmax操作,使得所有value的权重总和为1,这是为了保证所有源元素贡献的特征总量保持一定;如果有多个key都与query高度相似,那么它们各自的通道都会只打开一部分(好像“注意力分散在这几个源元素上”)。从这个角度来看,可以理解为输出y是在value之间根据key-query的相似度进行内插值,或者加权平均。即一般采用如下公式计算:

第二阶段的计算结果a_i即为value_i对应的权重系数,然后进行加权求和即可得到Attention数值:

通过如上三个阶段的计算,即可求出针对Query的Attention数值,目前绝大多数具体的注意力机制计算方法都符合上述的三阶段抽象计算过程。

Attention机制的具体计算过程的通俗解读(二),从每个源、目标元素自身的特征生成query、key和value向量的计算都是可学习的(通常这个计算就是一个全连接层,分别称作query映射,key映射和value映射),因此经过大量训练之后,每个元素都会找到完成各自任务所需的最合适的query、key和value。下图对注意力机制的计算过程(点乘,加权平均)作一直观总结。

Self Attention 的通俗解读

解读一:传统attention是计算Q和K之间的依赖关系,而self attention则分别计算Q和K自身的依赖关系。

Self Attention与传统的Attention机制非常的不同:传统的Attention是基于source端和target端的隐变量(hidden state)计算Attention的,得到的结果是源端的每个词与目标端每个词之间的依赖关系。但Self Attention不同,它分别在source端和target端进行,仅与source input或者target input自身相关的Self Attention,捕捉source端或target端自身的词与词之间的依赖关系;然后再把source端的得到的self Attention加入到target端得到的Attention中,捕捉source端和target端词与词之间的依赖关系。因此,self Attention Attention比传统的Attention mechanism效果要好,主要原因之一是,传统的Attention机制忽略了源端或目标端句子中词与词之间的依赖关系,相对比,self Attention可以不仅可以得到源端与目标端词与词之间的依赖关系,同时还可以有效获取源端或目标端自身词与词之间的依赖关系,如图所示。

解读二:在一般任务的Encoder-Decoder框架中,输入Source和输出Target内容是不一样的,比如对于英-中机器翻译来说,Source是英文句子,Target是对应的翻译出的中文句子,Attention机制发生在Target的元素Query和Source中的所有元素之间。而Self Attention顾名思义,指的不是Target和Source之间的Attention机制,而是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力计算机制。其具体计算过程是一样的,只是计算对象发生了变化。

如果是常规的Target不等于Source情形下的注意力计算,其物理含义正如上文所讲,比如对于机器翻译来说,本质上是目标语单词和源语单词之间的一种单词对齐机制。那么如果是Self Attention机制,一个很自然的问题是:通过Self Attention到底学到了哪些规律或者抽取出了哪些特征呢?或者说引入Self Attention有什么增益或者好处呢?我们仍然以机器翻译中的Self Attention来说明,下图和下下图是可视化地表示Self Attention在同一个英语句子内单词间产生的联系。

从上面两张图可以看出,Self Attention可以捕获同一个句子中单词之间的一些句法特征(比如上上图展示的有一定距离的短语结构)或者语义特征(比如上图展示的its的指代对象Law)。
很明显,引入Self Attention后会更容易捕获句子中长距离的相互依赖的特征,因为如果是RNN或者LSTM,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小。
但是Self Attention在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征。除此外,Self Attention对于增加计算的并行性也有直接帮助作用。这是为何Self Attention逐渐被广泛使用的主要原因。

解读三:直观来讲,自注意力是让这个序列中每一个元素都有机会根据自身特征选择性地吸取整个序列中每一个其他元素的信息,这样达到的效果就是我们常说的元素间的“互动”。比如,在机器翻译中,在把源语言的单词翻译到目标语言之前,模型需要先理解每个词的语义,而有些词的语义需要上下文(context)才能确定。由于Transformer在应用之前需要对所有的元素进行embedding,所以经过自注意力机制处理序列时,词语之间相互推断信息过程就是序列中元素间交换信息的过程,也就是“互动”的过程。信息交换发挥着替代RNN中的记忆能力(memory)的功能,使得自注意力编码器也能像RNN一样对一个复杂的语句进行理解和解析。

方法1:transformer里面的self-attention

方法2:A Structured Self-Attentive Sentence Embedding

用自注意力进行序列编码和解码过程的通俗解读

第一部分提到,Transformer架构中,在编解码的环节上,用注意力机制替代了之前流行的RNN。编码的过程比较简单,只需要直接使用自注意力处理源序列的特征就可以了。要得到强大的表达能力,把这个自注意力编码器原样堆叠多层就好了(Transformer论文中使用了6层;后来的大规模预训练模型使用数十甚至上百层)。层跟层之间需要有非线性激活函数,Transformer的做法是在每个自注意力层之后加入一个前馈网络(feed-forward network),其实就是一个两层的MLP(中间由ReLU激活,带有drop out)。这个结构对序列中每个元素都独立计算,因此不会进行元素间的信息交换(元素间的互动完全靠自注意力),但有助于在注意力层进行元素间的信息交换之后,让每个元素消化整合自己的信息,为下一层再次通过自注意力交换信息做好准备。自注意力层和前馈层都使用残差连接的方式,也就是说,由注意力计算出(来自源元素value加权平均)的输出要和自注意力层的输入目标序列特征相加作为自注意力层的输出,前馈层也一样,其原理如同ResNet。

解码的过程也是类似,只不过在自注意力和前馈层之间,再额外加入一个对编码好的源序列的查询、对齐的过程,也是通过一个注意力层来实现。所以一个解码器层的源序列来自两部分,第一部分是解码器中上一层(即之前的自注意力层)的输出,第二部分是编码器的输出。这部分的连接关系需要图示才好理解。大家可以参考Transformer论文中的图1,而下图给出一个更详细的解读(这里编解码器各有两层)。

1、positional encoding

细心的同学可能已经发现了,上文介绍的注意力机制是对序列顺序不敏感的,也就是说,如果输入的源序列和目标序列被打乱顺序但没有任何元素的增删,输出结果不会受任何影响。这是因为注意力计算涉及的key、query和value都只来源于各个元素的自身特征,跟它们在序列中的位置没有关系。可以说,这样的注意力机制所操作的对象并不是两个序列,而是两个集合,只要集合中包含的元素确定了,输出结果就确定了。也就是说,词与词之间不存在顺序关系(打乱一句话,这句话里的每个词的词向量依然不会变),即现有的编码方式是没有包含原有序列包含的位置关系信息的。这种忽略序列中元素顺序的性质可能适合某些类型的任务(比如分析自动驾驶的车辆间互动),但对机器翻译这样的任务显然是不适合的(比如,“自动驾驶汽车”和“汽车自动驾驶”的语义很不一样)。为了在计算过程中不丢掉每个元素在序列中的位置信息,Transformer使用了一种叫做位置编码的机制。其思路其实也很直观,就是在每个元素的输入特征里,加上一个跟元素本身特征无关、但随该元素在序列中的位置而变化的“位置特征”。位置特征作为元素在序列中位置(序号)的函数,选择有很多,位置编码的方法也远不止一种。Transformer实践中使用的是一系列频率(随特征维度j)渐变的正余弦函数,设计思路上类似于傅立叶基函数(Fourier basis):

每两个维度(一对同相位的正弦和余弦)构成一个随序号递增而旋转的单位复数,维度越靠后(�越大)则角速度越慢;第一对的旋转角速度为1。下图展示了序列前10个元素的前20个特征(前10对正/余弦)的可视化(��=64)。

以对我爱你中的爱字编码为例:

爱在我爱你中pos = 1,用512维向量表示,奇数维度使用sin插入位置信息。(如果是编码我等偶数位置,则使用cos插入位置信息)。

在实际过程中采用的计算公式如下图,爱这个字经过加入位置信息的过程后(具体的操作是把预训练的embedding 加上位置信息编码),变会产生了新的512纬的新编码。这个编码就包含了位置信息(为什么会包含,我们后续会进一步解释)。

下面我们解释下为什么要用这种位置编码技术,在数学上为什么相加就加入了位置信息?

借助上述公式,我们可以得到一个特定位置的 d(model)维的位置向量,并且借助三角函数的性质:

可以看出,对于 pos+k 位置的位置向量某一维 2或 1而言,可以表示为,pos 位置与k位置的位置向量的2;与21维的线性组合,这样的线性组合意味着位置向量中蕴含了相对位置信息

2、Training process

因为NLP与CV的问题形式的差别,对于很多自动驾驶行业的同学来说,Transformer的训练过程不是很直观,所以我们在这一节里通过详细的可视化把这个过程展现出来,不嫌罗嗦。在机器翻译任务中,模型通常一个一个地生成目标语言的token,每一个token的生成就是一次上文中定义的序列转换操作,源序列就是源语言的语句,而目标序列是前面已经生成的目标语言的token序列(目标语言语句的前缀),输出序列则是包含下一个要生成的token的目标语言序列。下图详细地展示了一个中译英例子的推理过程。输入的中文语句“电脑在开车。”经过tokenization之后被拆解成为“电脑”,“在”,“开车”这三个中文token,加上<起始>和<终止>这两个特殊符号,构成长度为5个token的源序列;翻译的目标是英文语句“The computer is driving.”包含4个英文token,加上<起始>和<终止>,目标序列一共6个token。在推理(inference)时,Transformer(RNN网络也一样)要生成这个翻译序列需要进行5次推理计算:

每次推理时输入的目标序列是之前已经生成的部分,输出序列则是同一个序列再向前推进一个元素,这个多出来的元素就是这次推理预测的下一个token,下一次推理把这个token接到目标序列最后作为新的输入目标序列,如此循环。第一次推理的输入目标序列就是简单的一个元素<起始>。这个循环一直持续到推理输出的新元素是<终止>,表示模型认为这个语句翻译已经完成了。注意每次的目标序列和输出序列其实都是相同的序列(翻译的英文序列),但相差一个元素(目标序列等于输出序列右移一位),而正是这个元素之差决定了推理过程能够每次都有进展:输出序列的结尾总是有一个(本次推理预测出来的)目标序列里没有的新元素。这个元素位移是下一节masking机制的关键。

训练过程,从概念上来讲,就是简单地在上述推理过程的基础之上加上对每次推理预测的新元素的监督即可:

然而这样的训练有一个很大的缺点,就是后面的推理对之前推理的输出的依赖,除了不利于并行计算以外,还会导致训练效果不佳。这是因为在训练的早期,模型的输出还比较随机的时候,开始的一两次推理的随机输出会造成后续推理基本没法学到任何东西,浪费训练资源。对这个问题,比较常见的对策叫做“教师强制训练”(teacher forcing),就是在每次推理中,不使用前次推理的输出作为输入目标序列,而是使用训练标签的真值。这样可以保证对每次推理的监督训练都是从正确的输入出发,因而可以期待正确的结果。这种模式如下图所示:

教师强制训练也有自己的问题。模型的训练结果总是不可能完美的,最后总会有误差存在。在实际部署的推理中,是没有标签真值可用的,后面推理的输入不可避免地仍然要使用前次推理的输出,而如果模型在训练中一直使用真值作为输入,在推理时,前次推理输出的错误对下次推理来说就是在训练数据分布之外(out of distribution)的异常输入,这种错误会很快积累成一整个序列的毫无意义的垃圾输出。只有模型的一次次推理每一次都不出错,才能一直保持下次推理有正确的输入可用,就像走钢丝一样,一步走错就很难恢复。因为这种依赖效应,部署中的模型错误率(连续多次推理的复合错误率)会远低于训练中的错误率(单次推理的平均错误率)。这种因为模型在训练过程中没有经历过自己的错误造成的后果因而在部署中表现变差的现象,通常称为“经历偏差”(exposure bias),在NLP领域之外也经常见到,比如自动驾驶的模仿学习(imitation learning)等领域。有些方法,像“日程化采样”(scheduled sampling,让模型随着训练的进展逐步经历自己的推理结果),可以缓解这个问题,但这方面的研究还远没有结束。

3、教师强制训练中的masking

从上面的图中可以看到,使用教师强制训练后,各次推理之间的序贯关系被去掉了,因此所有推理可以同步并行,这是教师强制训练的另一个好处。在这个中译英的例子里,要想把5次推理变成一次,乍一看来只需要把整个英语语句(训练标签)作为目标序列输入到Transformer里,然后对输出序列的每个元素都计算loss进行监督就行了:

但是这样有一个问题,那就是这个推理过程中,在计算第一个输出“The”的时候,Transformer模型是能看到整个目标序列的,而上文里逐步推理的过程中,计算第一个输出的时候目标序列只有“<起始>”一个元素。如果这样训练,模型很容易学会偷懒,只需要把输入目标序列的第i+1个元素输出为第i个输出元素就可以了。换句话说,“天机不可泄露”:要是模型能未卜先知地知道自己下一步将要输出什么,它就不用费劲计算这个输出了,训练就没有效果。所以,要解决这个问题的关键就是让模型只能看到目标序列的一部分(前缀):在输出第i个元素的时候,不能看目标序列的第i+1个元素及其后面的部分。Transformer的具体做法是在计算注意力的时候,加入一个掩码(mask),这个mask是一个跟注意力分布矩阵一样形状的矩阵,可以单独调节每一个源元素与每一个目标元素之间的注意力强度。通过设计合适的mask,就可以实现在输出每一个元素的时候,切断它从未来获得信息的通路(把对应的注意力强制置零)。这可以通过在 �⋅� 点积上加上负无穷来实现,经过softmax计算出的权重就变成0了。下图展示了带有mask的Transformer的工作过程。

到这里,Transformer的结构就已经全部介绍完了。你学会了吗?回到前面写好的源代码处,试试把多头注意力、尺度补偿、位置编码、训练mask这些改进都加上,然后一个全副武装的Transformer就完成了!

有哪些变形的attention机制?

(1)Soft/Hard Attention

两种Attention Mechanism都有各自的优势,但目前更多的研究和应用还是更倾向于使用Soft Attention,因为其可以直接求导,进行梯度反向传播。

soft attention:传统attention,可被嵌入到模型中去进行训练并传播梯度。

ESIM模型计算句子蕴含关系时的local inference 之前需要将两句话进行 alignment,就是使用 soft_align_attention。

hard attention:不计算所有输出,依据概率对encoder的输出采样,在反向传播时需采用蒙特卡洛进行梯度估计。具体地:Hard Attention是一个随机的过程。Hard Attention不会选择整个encoder的输出作为其输入,Hard Attention会依概率Si来采样输入端的隐状态一部分来进行计算,而不是整个encoder的隐状态。为了实现梯度的反向传播,需要采用蒙特卡洛采样的方法来估计模块的梯度。

关键词提取的硬自注意力机制:取N个权重最高的词并按权重大小排序,把每一个词的embedding表示拼接输入一个全链接层后得到句子的表示。

(2)Global/Local Attention

global attention:传统attention,对所有encoder输出进行计算

local attention:介于soft和hard之间,会预测一个位置并选取一个窗口进行计算

具体地:

Global Attention:传统的Attention model一样。所有的hidden state都被用于计算Context vector 的权重,即变长的对齐向量at,其长度等于encoder端输入句子的长度。

Local Attention:Global Attention有一个明显的缺点就是,每一次,encoder端的所有hidden state都要参与计算,这样做计算开销会比较大,特别是当encoder的句子偏长,比如,一段话或者一篇文章,效率偏低。因此,为了提高效率,Local Attention应运而生。

Local Attention是一种介于Kelvin Xu所提出的Soft Attention和Hard Attention之间的一种Attention方式,即把两种方式结合起来。

总之,Global Attention和Local Attention各有优劣,在实际应用中,Global Attention应用更普遍,因为local Attention需要预测一个位置向量p,这就带来两个问题:1、当encoder句子不是很长时,相对Global Attention,计算量并没有明显减小。2、位置向量pt的预测并不非常准确,这就直接计算的到的local Attention的准确率。

(3)multi-headed attention

多头注意力是在上述注意力机制的基础上进行的一个简单的改进。“多头”指的是把每个query,key和value沿个各自的维度分割成若干段,形成若干独立的query-key-value分组,每个分组内进行点积运算和value加权平均,之后再把各组产生的结果连接起来。

这样设计的好处是,允许目标元素使用不止一种选择源元素的标准,因为不同组之间的注意力分布可以不同。比如,上文中译英的例子里,输出“an”的注意力分布中不但关注输入中的“一套”,也关注“自动驾驶”,但这两个通路的含义是完全不同的,一个是提取语义,一个是通过首字母决定不定冠词的变体,因此与其强迫用同一个query同时匹配到“一套”的key和“自动驾驶”的key,不如学习两个不同的query,一个去寻找源序列中的语义对应(注意力在“一套”),一个去定位下文的拼写信息(注意力在“自动驾驶”),然后从各自value中提取相应的信息。多头注意力就提供了这样的支持,使得每个目标元素可以根据多种选择标准(query,key)来灵活地选择源元素中的各类特征信息。下图展示了分组数量�=2的多头注意力的计算过程。

点积注意力在高维度下的尺度补偿

在Transformer之前,点积注意力在高维度下表现不太好,Transformer作者之一Shazeer认为可能跟query和key的尺度缩放问题有关。由于query和key都是独立学习的,query和key向量中的每个系数都有大致固定的尺度(这取决于计算query和key的全连接层参数,因此是不太可控的;作为向量,query和key都没有特意做归一化),因此每个对应系数的积也有固定的尺度(也就是固定的方差),也就是说,维度越高,点积 �⋅� 值的尺度就会越大(点积方差正比于维度数量)。表示复杂的语义概念需要很高的query和key维度,造成很大的点积绝对值,会在softmax中造成问题,因为其中用到指数运算,绝对值很大的点积在训练中会收到几乎为0的梯度,导致训练进展缓慢。对此,Transformer的解决方法是在点积 �⋅� 上除以 �� ,这就正好抵消了维度增加造成的点积尺度放大效应,保证了不论维度多高,点积的方差都是恒定的。这是一个简单的小技巧,但背后反映了在对底层计算的细致观察,在深度学习领域,这样的小技巧造就性能卓越的模型的例子比比皆是。

(4)Hierarchical Attention

Hierarchical Attention构建了两个层次的Attention Mechanism,第一个层次是对句子中每个词的attention,即word attention;第二个层次是针对文档中每个句子的attention,即sentence attention。

整个网络结构由四个部分组成:一个由双向RNN(GRU)构成的word sequence encoder,然后是一个关于词的word-level的attention layer;基于word attention layar之上,是一个由双向RNN构成的sentence encoder,最后的输出层是一个sentence-level的attention layer。

(5)Attention over Attention

两个输入,一个Document和一个Query,分别用一个双向的RNN进行特征抽取,得到各自的隐状态h(doc)和h(query),然后基于query和doc的隐状态进行dot product,得到query和doc的attention关联矩阵。然后按列(colum)方向进行softmax操作,得到query-to-document的attention 值a(t);按照行(row)方向进行softmax操作,得到document-to-query的attention值b(t),再按照列方向进行累加求平均得到平均后的attention值b(t)。最后再基于上一步attention操作得到a(t)和b(t),再进行attention操作,即attention over attention得到最终query与document的关联矩阵。

(6)Multi-step Attention:ConvS2S

完全基于CNN的Seq2Seq模型需要通过层叠多层来获取输入句子中词与词之间的依赖关系,特别是当句子非常长的时候,我曾经实验证明,层叠的层数往往达到10层以上才能取得比较理想的结果。针对每一个卷记得step(输入一个词)都对encoder的hidden state和decoder的hidden state进行dot product计算得到最终的Attention 矩阵,并且基于最终的attention矩阵去指导decoder的解码操作。

这里还用到了Position Embeddings,加入位置向量,给予模型正在处理哪一位置的信息,e = (w1 + p1, . . . , wm + pm)。transformer可能是学这个的吧。

(7)Multi-dimensional Attention

Multi-dimensional attention把一维的Attention扩展到多维,多维Attention可以捕捉输入多个维度重要性,具有更强的描述能力。如上图所示,输入序列“Fish burger is the best dish it tastes fresh”,每个输入token的Attention由两个部分concat而成,比如,计算h8(tastes)关于Aspect的Attention时,Attention分为Aspect的Attention u(t~a)和Opinion的Attention u(t~p),当m=a时,m-=p,当m=p时,m-=a。Attention同时考虑了输入token作为Aspect和Opinion的重要程度。Multi-dimension Attention捕捉多维度的Attention,为了避免Attention过于集中,对Attention加入F范数约束,使Attention集中序列的不同部分。

(8)Memory-based Attention

Memory机制在NLP中应用也比较广泛,比如对话系统中。Memory-based Attention借用了这一思想,假设输入为q,Memory中以(k,v)形式存储着我们需要的上下文。例如在QA问题中,Memory-based Attention可以通过不停地迭代地更新Memory来将注意力转移到答案所在的位置。初始q为问题question,然后计算a与memory中k的Attention关系,得到此时最相关的C,将q与c拼接到一起作为新的q,再次计算C,不停地迭代,直到得到最终的答案。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值