文章前言
从问题出发来了解背景
问题一.为什么要提出Transformer模型
Transformer模型是基于注意力机制的,不用到RNN和CNN等神经网络结构,同时更加并行,训练时间更少。固有的序列性质排除了训练示例中的并行化, 因为内存限制限制了示例的批处理,使得这个约束在较长的句子中尤为致命。Transformer采取的这种注意力机制允许对依赖关系进行建模,而不去考虑它们在输出或者输入中的距离。
问题二.Transformer与LSTM最大的区别
LSTM的训练是迭代的,是一个字接一个字的来,当前这个字过完LSTM单元才可进行下一个字。 而Transformer的训练是并行的,所有字是同时训练的。这样就加快了计算效率,transformer使用了位置编码来理解语言的顺序,使用Self-Attention和全连接层来进行计算。
问题三.Transformer模型核心Self-Attention简介
自注意(Self-attention),有时也称为内部注意(intra-attention),是一种将单个序列的不同位置联系起来,以计算该序列的表示的注意机制。自我注意已经成功地用于各种任务,包括阅读理解、抽象总结、文本蕴涵和学习任务独立的句子表征。Transformer是第一个完全依靠自我注意来计算其输入和输出的表示而不使用序列对齐的rnn或卷积的转导模型。
从整体宏观上来理解Transformer
一.总的模型结构 Transformer模型主要由编码器(Encoder)和解码器(Decoder)构成,其中编码器和解码器可以有N个。 Encoder负责把自然语言序列映射为隐藏层,含有自然语言序列的数学表达,然后Decoder把隐藏层层再映射为自然语言序列,从而使得我们解决各种问题。
Encoder(编码器)和Decoder(解码器)
-
Encoder的输入是一个向量,输出也是一个向量。第一个Encoder输入的是词向量,后面层的Encoder的输入是前一个Encoder的输出。 -
Encoder由两部分组成: Self-Attention Layer Feed Forward Neural Network(全连接前馈神经网络,FFNN) -
Decoder和Encoder类似,就只比Encoder多了一层Encoder-Decoder Attention层,这个层能帮助解码器聚集于输入句子的相关部分。
Position Embedding(位置编码)
-
由于Transformer模型没有RNN的迭代操作,所以我们必须提供给每个字的位置信息给Transformer,才能识别出语言中的顺序关系。 -
例如Input是X,它的维度是[batch_size, sequence_length],词嵌入之后得到的X维度是[batch_size, sequence_length,embedding_dimension],Positional encoding的维度[max_sequence_length,embedding_dimension],其中max_sequence_length为一个序列的最大长度,embedding_dimension应该与字向量的维度相同。 -
论文中的位置编码采取了sin和cos函数的线性变换来提供给模型位置的信息,其中pos为单词在句子中的位置,d_model为字嵌入的维度,i为词向量的维度。 -
论文中的sin和cos是一组公式,也就是对应着embedding dimension中的奇偶数序列的维度。
Self-Attention机制
一.宏观理解
例如我们要翻译这么一个句子:
The animal didn’t cross the street because it was too tired
我们自然是知道句子中的it指代前面的animal。但是电脑就不那么容易理解。当模型在处理(翻译)it 的时候,Self Attention机制能够让模型把it和animal关联起来。 同理,当模型处理句子中的每个词时,Self Attention机制使得模型不仅能够关注这个位置的词,而且能够关注句子中其他位置的词,作为辅助线索,进而可以更好地编码当前位置的词。 从下面图中可以看出来在翻译it时,有一部分注意力集中在了The animal上,并融合进了it中。 二.细节理解
第一步:先定义Q、K、V三个向量。这三个向量是词向量分别与三个矩阵相乘得到的,这三个矩阵就是我们学习的参数。那Q表示的是query,就是与我这个单词相匹配的单词的属性,K是key,就是表示我这个单词的本身的属性,V是value,表示的是我这个单词的包含的信息本身。 例如下图:X1向量Wq得到q1,X2向量Wq得到q2。
第二步:计算Attention Score 假设要计算词“Thinking”的Attention Score,需要根据Thinking这个词,对句子中的没一个词都计算一个分数。这个根数决定了我们再编码Thinking这个词时,需要对句子中其他位置的词放置多少的注意力。 分数 = 该词对应的Query向量.(点乘)每个词的Key向量
第三步:将分数除以sqrt(d_key) d_key是key向量的长度,除以一个数,是为了在反向传播时,求取梯度更加稳定。
第四步:归一化处理 将这些分数经过softmax,使得分数都是正数,并且相加为1
第五步:将每个分数与每个Value相乘 对于分数高的位置,相乘后的值越大,我们就会把更多的注意力放到它们的身上
第六步:相加 将上一步得到的向量相加,就得到了“Thinking”在self-Attention层的输出。
三.矩阵计算Attention 第一步是计算 Query,Key,Value 的矩阵。首先,我们把所有词向量放到一个矩阵 X 中,然后分别和3 个权重矩阵 𝑊𝑄,𝑊𝐾𝑊𝑉 相乘,得到 Q,K,V 矩阵。 接着将第二步到第六步合并
多头注意力机制(Multi-Head Attention)
一.这种机制增强了Attention层的能力
- 扩展了模型关注不同位置的能力
- 多头注意力机制赋予Attention层多个“子表示空间”。多头注意力机制会有多组的WQ、WK、WV权重矩阵。每一组的注意力的权重矩阵都是随机初始化的。经过训练之后,每一组的WQ、Wk、Wv、可以看做是把输入的向量映射到一个“子表示空间”。
二.步骤(假设有8个head) 1.将每组的qkv分别通过自注意力机制得到八个输出Z 2.把 8 个矩阵 {Z0,Z1…,Z7} 拼接起来 3.把拼接后的矩阵和 WO 权重矩阵相乘 4.得到最终的矩阵 Z,这个矩阵包含了所有 attention heads(注意力头) 的信息。这个矩阵会输入到 FFNN (Feed Forward Neural Network)层。
代码实现Attention
Pytorch中实现的函数
torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None)
参数说明: embed_dim:最终输出的 K、Q、V 矩阵的维度,这个维度需要和词向量的维度一样 num_heads:设置多头注意力的数量。如果设置为 1,那么只使用一组注意力。如果设置为其他数值,那么 - - num_heads 的值需要能够被 embed_dim 整除 dropout:这个 dropout 加在 attention score 后面
num_heads 的值之所以是要能够被embed_dim 整除,是为了把词的隐向量长度平分到每一组,这样多组注意力也能够放到一个矩阵里,从而并行计算多头注意力。 例如,我们前面说到,8 组注意力可以得到 8 组 Z 矩阵,然后把这些矩阵拼接起来,得到最终的输出。如果不能够整除,那么这些向量的长度就无法平均分配。
forward(query, key, value, key_padding_mask=None, need_weights=True, attn_mask=None)
参数说明如下: query:对应于 Key 矩阵,形状是 (L,N,E) 。其中 L 是输出序列长度,N 是 batch size,E 是词向量的维度 key:对应于 Key 矩阵,形状是 (S,N,E) 。其中 S 是输入序列长度,N 是 batch size,E 是词向量的维度 value:对应于 Value 矩阵,形状是 (S,N,E) 。其中 S 是输入序列长度,N 是 batch size,E 是词向量的维度 key_padding_mask:如果提供了这个参数,那么计算 attention score 时,忽略 Key 矩阵中某些 padding 元素,不参与计算 attention。形状是 (N,S)。其中 N 是 batch size,S 是输入序列长度。 如果 key_padding_mask 是 ByteTensor,那么非 0 元素对应的位置会被忽略 如果 key_padding_mask 是 BoolTensor,那么 True 对应的位置会被忽略 attn_mask:计算输出时,忽略某些位置。形状可以是 2D (L,S),或者 3D (N?numheads,L,S)。其中 L 是输出序列长度,S 是输入序列长度,N 是 batch size。 如果 attn_mask 是 ByteTensor,那么非 0 元素对应的位置会被忽略 如果 attn_mask 是 BoolTensor,那么 True 对应的位置会被忽略
残差连接
- 编码器的每个子层(Self Attention 层和 FFNN)都有一个残差连接和层标准化(layer-normalization)。
- 残差连接 将 Self-Attention 层的层标准化(layer-normalization)和向量都进行可视化
Decoder
-
decoding阶段的每一个时间步都输出一个翻译后的单词。(例子为英语翻译) -
接下来重复这个过程,直到输出一个结束符,Transformer就算完成了所有的输出。每一步的输出都会在下一个时间步输入到下一个解码器。 -
decoder中的Self-Attention层只允许关注到输出序列中早于当前位置之前的单词。 具体做法:在Self-Attention分数经过Softmax层之前,屏蔽当前位置之后的那些位置。 -
Encoder-Decoder Attention层是使用前一层输出来构造Query矩阵,而Key矩阵和Value矩阵是来自于编码器的最终输出。
最后的先行层和softmax层
- Decoder最终的输出是一个向量,其元素是浮点数,由softmax层后面的先行层来实现向量转换为单词。
- 先行层就是一个普通的全连接神经网络,可以把Decoder输出的向量转换映射到一个更长的向量,这个向量就称作logit向量。
eg:假设我们的模型有 10000 个英语单词(模型的输出词汇表),这些单词是从训练集中学到的。因此 logits 向量有 10000 个数字,每个数表示一个单词的分数。我们就是这样去理解线性层的输出。然后,Softmax 层会把这些分数转换为概率(把所有的分数转换为正数,并且加起来等于 1。然后选择最高概率的那个数字对应的词,就是这个时间步的输出单词。
|