一、从 RNN 到基于注意力的 NLP 模型
1.1 基于注意力的RNN模型
2016年时NLP领域常用双向LSTM对句子进行编码(如翻译下面的句子),把输出定义为一个序列。然后用LSTM解码。最后使用注意力以便灵活地访问编码的隐藏状态(即memory)。
基于注意力机制的seq2seq,RNN模型
以上就是2014年~2017年的RNN模型,2021年的今天,我们可以用不同的模型。
1.2 RNN循环模型的问题
(1)线性交互距离
循环模型的缺点:线性交互距离。
RNN是“从左到右”展开的,对线性局部性进行编码,这也启发我们:邻近的单词会影响彼此的意思。如句子The pizza with the tasty berverage is expensive.,pizza 和 tasty 就能够增加彼此的语义效应。RNN 采取 O(序列长度)步长来使远距离的词对进行交互:
RNN 模型的远距离的词对交互问题
远距离词对交互的 O(序列长度)步长意味着:
难以学习长距离依赖(因为梯度问题!)
单词的线性顺序不是思考句子的正确方式
(2)缺乏并行性
循环模型的缺点:缺乏并行性。
向前和向后传递具有 O(序列长度)非并行的操作。本来,GPU 可以一次执行一堆独立的计算! 但是,在过去的 RNN 隐藏状态被算完之前,未来的 RNN 隐藏状态是不能被完全计算出来的。所以循环模型禁止对非常大的数据集进行训练。
1.3 单词窗口
如果不用循环模型,用单词窗口咋样?
word窗口模型聚合本地上下文(也称为 1D卷积convolution,后面会说这个)
非并行的操作数量不会增加序列长度
单词窗口的长距离依赖情况如何呢?
堆叠单词窗口层允许更远单词之间的交互,如上图二。
最大交互距离 = 序列长度 / 窗口大小(但如果序列太长,你就会忽略长距离上下文)。
1.4 注意力
注意力将每个单词的表示视为:一个查询(Q)去访问,和合并来自一组值(V)的信息。
上个lecture看到了从编码器到解码器的注意力,这里单独考虑一个句子,考虑注意力。
不可并行操作的数量不会增加序列长度。
最大交互距离:O(1),因为所有单词在每一层都交互。
数字表示在一个状态被计算之前的最小步数:
注意力的表达示意图
1.5 自注意力
(1)位置问题的方案:序列顺序编码
self-attention是对集合的操作,没有固有的顺序概念,它不知道输入单词的顺序:
- 由于 self-attention 不建立在顺序信息中,我们需要在键、查询和值中对句子的顺序进行编码。
解决方案:
通过余弦和正弦曲线,将位置表示为向量。
正弦位置表示:连接不同周期的正弦函数:
位置信息的正弦表示法
优点:周期性表明“绝对位置”可能不那么重要,也许可以在周期重新开始时推断出更长的序列!
缺点:不可学习;推断也不起作用!注意,位置编码是写死的,不会更新,没有可训练学习的参数。
所以, 要从头开始学习 (learned from scratch) 位置表示的向量。学习的绝对位置表示: 让所有 p i p_{i}p
i
成为可学习的参数。学习一个矩阵p ∈ R d × T p \in R^{d \times T}p∈R
d×T
,并让每一个p i p_ip
i
成为该矩阵的一列;
优点(Pros):灵活性,学习每个位置以适应数据
缺点(Cons):肯定(definitely)不能外推到 1,…,T 之外的索引。
大多数系统都用这种方式。
更灵活的位置表示:
相对线性位置注意力,参见[Shaw et al., 2018];
基于依存句法的位置,参见[Wang et al., 2019]。
(2)线性问题的方案:非线性激活函数
自注意力在深度学习网络中没有非线性元素,堆叠更多的自注意力层也只是对 V 向量的再平均, 一切都只是加权平均值,始终是线性操作。所以,为了增加非线性操作,有如下方案:
添加FF网络
(3)预测问题的方案:掩码矩阵
第3个障碍:窥视未来。
在作序列预测时,我们需要确保不会“展望未来”(就像机器翻译或语言建模一样)。
为了在解码器中使用自注意力,我们需要确保我们不能窥视未来。 如果在每个时间步,我们都更改 K 和 Q 的设置,使其仅包含过去的单词,那么效率就太低了! 所以,为了实现并行化,我们要掩盖对未来单词的注意力,将注意力得分设置为-∞。
1.6 总结
解决方案:
位置问题的解决方案:向输入添加位置表示。
线性问题的解决方案:对每个自注意力输出应用相同的前馈网络。
预测问题的解决方案:通过人为地将注意力权重设置为 0来掩盖未来。
自注意力构建块(不完全代表transformer模型)的几个必要条件:
自注意力:Transformer 方法的基础。
位置表示:指定序列顺序,因为自注意力是其输入的一个无序函数。
非线性(ReLU):在自注意力模块的输出,经常作为简单的前馈网络(FFN)实施。
掩码:为了在不看未来的情况下并行化操作,防止有关未来的信息“泄漏”到过去(不能偷看未来)
二、详解transformer
2.1 transformer多级视图
图:transformer的多级概念视图
(1)模块视图:编码器栈是由 6 个编码器模块组成,解码器栈也是由 6 个解码器模块组成(模块数量可增减);
(2)子层视图: 每个编码器模块包括两个子层:
每个编码器模块包括两个子层:
子层 1:包括:注意力①、(残差+层归一化);
子层 2:包括:前馈网络、(残差+层归一化);
每个解码器模块包括三个子层:
子层 1:包括:注意力②、(残差+层归一化);
子层 2:包括:注意力③、(残差+层归一化);
子层 3:包括:前馈网络、(残差+层归一化);
2.2 transformer 编码器
还需要解决的问题:
K-Q-V 注意力:我们如何从单个词嵌入中得到 K、Q、V 向量?
多头注意力:在单层中关注多个地方。
帮助训练的技巧(这些技巧并不能改善模型的能力,但它们有助于改善训练过程):
残差连接
层归一化
缩放点积
(1)K-Q-V注意力
1)自注意力机制
自注意力是指键(K)、查询(Q)和值(V)来自同一数据来源,即 K=Q=V。
Transformer 以特定方式执行此操作:
(2)多头注意力
如果想一次查询(Q)句子中的多个位置:
因此, 每个头都可以 “看” 不同的事物, 并以不同的方式构建值 ( V \mathrm{V}V ) 向量。多头注意力和单头自注意力有相同的计算量。最简单的多头注意力,即两头注意力(如下图的c):
(3)残差连接
(4)层归一化
Ba et al.[2016]提出层归一化(如上图的b),其主要作用是帮助模型更快训练。
想法:通过对每一层的单位均值和标准偏差进行归一化,减少隐藏向量值中的无信息含量的变异。 LayerNorm 的成功可能归功于它的归一化梯度 [Xu 等人,2019]。
(5)点积缩放
Vaswani et al.[2017]提出点积缩放,它是帮助 Transformer 训练的最终变体。 当维度 d 变大时,向量之间的点积越来越大,使得 softmax 函数的输入可能很大,从而使梯度变小。 所以,我们不是使用前面见过的自注意力函数:
2.3 transformer 解码器
(1)掩码注意力
解码器的多头掩码自注意力,Q、K、V 度来来同一数据源,即 Q=K=V,注意力的计算也是四个步骤: 点积、缩放、分布、输出(见见2.2的5.点积缩放)。注意,掩码操作(见1.5的3掩码矩阵)在缩放计算之后。
(2)交叉注意力
1)解码器的多头交叉注意力
即: 首先, 在一个矩阵乘法中取查询键点积: Z Q ( H K ) T Z Q(H K)^{\mathrm{T}}ZQ(HK)
T
,其次,softmax,并用另一个矩阵乘法计算加权平均值。 由此可知,交叉注意力的计算也是标准的四个步骤:点积、缩放、分布、输出。
2.4 transformer 总体架构
上图是在原始论文中 Transformer 架构图的基础上,稍作修改,为了方便描述和记忆,这里对源代 码1中的 12 个类(class)名进行了编号:
C12:EncoderDecoder C12 是编码器解码器,它是个总类,包括 2 个 C1、2 个 C2、1 个 C9、1 个 C10、1 个 C11;
C11:Generator C11 是输出,包括一个全连接层和一个 softmax 层,负责把 top-N 候选输出序列找出来;
C10:Decoder C10 是解码器栈,包含 6 个 C7 和 1 个 C8;
C9:Encoder C9 是编码器栈,包含 6 个 C6 和 1 个 C8;
C8:LayerNorm C8 是层归一化(带有可训练参数的,批归一化);
C7:DecoderLayer C7 是解码器,包含 1 个 C3、2 个 C4 和 1 个 C5;
C6:EncoderLayer C6 是编码器,包含 1 个 C3、2 个 C4 和 1 个 C5;
C5:PositionwiseFeedForward C5 是前馈网络(两层,全连接)
C4:SubLayerConnection C4 是残差连接和层归一化;
C3:MultiHeadedAttention C3 是多头注意力,包含三种:标准自注意力、掩码自注意力、交叉注意力;
C2:PositionalEncoding C2 是位置编码;
C1:Embeddings C1 是词嵌入。
哈佛大学的学者根据《Attention is All You Need》论文而编写的 Transformer 的 PyTorch 代码网址: http://nlp.seas.harvard.edu/2018/04/03/attention.html
2.5 transformer的12个类
(1)embeddings
Embedding 就是根据独热向量和学习嵌入 (word2vec) , 将输入符记和输出符记转换为维度 d model \mathrm{d}_{\text {model }}d
model
( = 512 ) (=512)(=512) 的向量。我们还使用通常学习的线性变换和 softmax 函数将解码器输出转换为预测的下一个符记 概率。在我们的模型中, 我们在两个嵌入层和 pre-softmax 线性变换之间共享相同的权重矩阵。在嵌入层 中, 我们将这些权重乘以 d model \sqrt{d_{\text {model }}}
d
model
。
(2)Positional Encoding
位置编码的作用是为模型提供当前时间步的前后出现顺序的信息。 RNN 最大的优点就是在时间序列上对数据的抽象,有天然的先后顺序。 但是,因为 Transformer 抛弃了 RNN,即没有位置信息(就像词袋),所以必须加入位置信息。
为了让模型利用序列的顺序,需要注入一些关于符记在序列中的相对或绝对位置的信息。为此, 我们在编码器和解码器堆栈底部的输入嵌入中添加了“位置编码”。
位置编码与嵌入具有相同的维度 d m o d e l d_{model}d
model
, 因此可以将两者相加。位置编码有多种选择,诸如学到的和固定的。位置编码可以分为绝对位置和相对位置(Relative Position Representations ,RPR)。Shaw 等人(2018)指出,在同一个 sequence 中使用相对位置更好。
为了进行位置编码,使用不同频率的正弦和余弦函数:
其中 pos 是位置, i \mathrm{i}i 是维度。也就是说, 位置编码的每个维度对应一个正弦曲线。波长形成从 2 π 2 \pi2π 到 10000 ∗ 2 π 10000 * 2 \pi10000∗2π 的几何级数。我们选择这个函数是因为我们假设它可以让模型很容易地学会通过相对位置来关注, 因为对于任何固定的偏移量 k , P E p o s + k \mathrm{k}, \mathrm{PE}_{\mathrm{pos}+\mathrm{k}}k,PE
pos+k
可以表示为线性函数 P E p o s \mathrm{PE}_{\mathrm{pos}}PE
pos
。
此外, 我们将 dropout 应用于编码器和解码器堆栈中的嵌入和位置编码的总和。对于基本模型, 我们 使用 P drop = 0.1 P_{\text {drop }}=0.1P
drop
=0.1 的比率。
(3)Multi-Headed Attention
(4)SubLayerConnection
(5)PositionwiseFeedForward
(6)EncoderLayer
这个类 C6 只对一个编码器进行封装,包括两个子层:
子层 1:C3 和 C4;
子层 2:C5 和 C4。
(7)DecoderLayer
这个类 C7 只对一个解码器进行封装,包括三个子层:
子层 1:C3 和 C4;
子层 2:C3 和 C4;
子层 3:C5 和 C4。
(8)LayerNorm
(9)Encoder
这个类 C9 对六个编码器 C6 和一个层归一化类 C8 进行封装。
(10)Decoder
这个类 C10 对六个解码器 C7 和一个层归一化类 C8 进行封装。
(11)Generator
这个类的作用是:通过对数值进行 linage+softmax 转换,预测下一个单词。
解码器栈的输出是一个 float 向量。我们怎么把这个向量转换为一个词呢?通过一个线性层再加上一 个 Softmax 层实现转换,转换后的维度对应着输出类别的个数。若是翻译任务,则对应的是字典的大小。
首先,线性层是一个简单的全连接神经网络,将解码器栈的输出向量映射到一个更长的向量,这个向 量被称为 logits 向量。假设词典有 10000 个单词,则 logits 向量有 10000 个数值,每个数表示一个单词 的分数。
然后,Softmax 层会把这些分数转换为概率(把所有的分数转换为正数,并且加起来等于 1)。
最后,选择最高概率所对应的单词,作为这个时间步的输出。
(12)EncoderDecoder
这个类 C12 是总类,对输出模块 C11、解码器 10、编码器 C9、输入模块 C1 和 C2 进行封装。
三、Transformers强大结果
3.1 机器翻译
3.2 文档生成
在文档生成方面,[Liu et al., 2018]研究表明:生成英文维基百科文章可以作为源文档的多文档 摘要来处理。我们使用提取摘要来粗略地识别显着信息和神经抽象模型来生成文章。对于抽象模型,我们 引入了一种仅含解码器架构,该架构可以扩展地处理非常长的序列,比序列转换中使用的典型编码器解码 器架构长得多。该模型可以生成流畅、连贯的多句段落,甚至可以生成整个维基百科文章。当给定参考文 件时,它可以提取相关的事实信息,如困惑度、ROUGE 分数和人工评估所反映的那样,见表:
3.3 预训练
不久之后,大多数 Transformers 结果还包括预训练。 Transformer 的可并行性允许有效的预训练,并使它们成为事实上的标准。 通用语言理解评估基准(GLUE)是用于评估和分析多种已有自然语言理解任务的模型性能的工具。
模型基于在所有任务的平均准确率进行评估。当前最佳结果可以在公开 GLUE 排行榜上查看。 在这个流行的基准上,所有顶级模型都是基于 Transformer(和预训练)的,见表:
基于 Transformer 和预训练模型的 GLUE 排行榜
四、Drawbacks and variants of Transformers
4.1 优点:并行计算
自注意力相对于循环的好处之一是它是高度可并行化的。
4.2 缺点:平方计算
自注意力中的平方计算: 计算所有交互对意味着我们的计算量与序列长度成二次方增长!而对于循环 模型, 它只是线性增长!
作为序列长度函数的平方计算, 它的操作总数随着 O ( T 2 d ) O\left(T^{2} d\right)O(T
2
d) 增长, 其中 T TT 是序列长度, 而 d dd 是维度。 需要计算所有的交互对, 时间复杂度是 O ( T 2 d ) O\left(T^{2} d\right)O(T
2
d), 见图:
例如, 假设 d = 1000 \mathrm{d}=1000d=1000, 那么, 对于单个 (较短的) 句子, T ⩽ 30 , T 2 ⩽ 900 \mathrm{T} \leqslant 30, \mathrm{~T}^{2} \leqslant 900T⩽30, T
2
⩽900 。
在实践中, 我们一般设置了一个界限, 如 T = 512 \mathrm{T}=512T=512 。
但是, 如果我们想要处理长文档, T ⩾ 10 , 000 \mathrm{T} \geqslant 10,000T⩾10,000, 那么计算成本巨大。
4.3 探索:位置表示
简单的绝对索引是否是我们可以用来表示位置的最佳方法吗?因此,有人作了以下探讨:
(1) 相对线性位置注意力 [Shaw et al., 2018];
(2) 基于依存句法的位置 [Wang et al., 2019]。