B 站视频讲解
Transformer 是谷歌大脑在 2017 年底发表的论文 attention is all you need 中所提出的 seq2seq 模型。现在已经取得了大范围的应用和扩展,而 BERT 就是从 Transformer 中衍生出来的预训练语言模型
这篇文章分为以下几个部分
Transformer 直观认识 Positional Encoding Self Attention Mechanism 残差连接和 Layer Normalization Transformer Encoder 整体结构 Transformer Decoder 整体结构 总结 参考文章
0. Transformer 直观认识
Transformer 和 LSTM 的最大区别,就是 LSTM 的训练是迭代的、串行的,必须要等当前字处理完,才可以处理下一个字。而 Transformer 的训练时并行的,即所有字 是同时训练的,这样就大大增加了计算效率。Transformer 使用了位置嵌入 (Positional Encoding) 来理解语言的顺序,使用自注意力机制(Self Attention Mechanism)和全连接层进行计算,这些后面会讲到
Transformer 模型主要分为两大部分,分别是 Encoder 和 Decoder 。Encoder 负责把输入(语言序列)隐射成隐藏层 (下图中第 2 步用九宫格代表的部分),然后解码器再把隐藏层映射为自然语言序列。例如下图机器翻译的例子
本篇文章大部分内容在于解释 Encoder 部分,即把自然语言序列映射为隐藏层的数学表达的过程 。理解了 Encoder 的结构,再理解 Decoder 就很简单了
上图为 Transformer Encoder Block 结构图,注意:下面的内容标题编号分别对应着图中 1,2,3,4 个方框的序号
1. Positional Encoding
由于 Transformer 模型没有 循环神经网络的迭代操作, 所以我们必须提供每个字的位置信息给 Transformer,这样它才能识别出语言中的顺序关系
现在定义一个位置嵌入 的概念,也就是 Positional Encoding,位置嵌入的维度为[max_sequence_length, embedding_dimension]
, 位置嵌入的维度与词向量的维度是相同的,都是embedding_dimension
。max_sequence_length
属于超参数,指的是限定每个句子最长由多少个词构成
注意,我们一般以字 为单位训练 Transformer 模型。首先初始化字编码的大小为[vocab_size, embedding_dimension]
,vocab_size
为字库中所有字的数量,embedding_dimension
为字向量的维度,对应到 PyTorch 中,其实就是nn.Embedding(vocab_size, embedding_dimension)
论文中使用了 sin 和 cos 函数的线性变换来提供给模型位置信息:
P E ( p o s , 2 i ) = s i n ( p o s / 10000 2 i / d model ) P E ( p o s , 2 i + 1 ) = c o s ( p o s / 10000 2 i / d model ) 序号增大,位置嵌入函数的周期变化越来越平缓
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import math
def get_positional_encoding (max_seq_len, embed_dim) : positional_encoding = np.array([ [pos / np.power(10000 , 2 * i / embed_dim) for i in range(embed_dim)] if pos != 0 else np.zeros(embed_dim) for pos in range(max_seq_len)])
positional_encoding[<span class="hljs-number">1</span>:, <span class="hljs-number">0</span>::<span class="hljs-number">2</span>] = np.sin(positional_encoding[<span class="hljs-number">1</span>:, <span class="hljs-number">0</span>::<span class="hljs-number">2</span>]) <span class="hljs-comment"># dim 2i 偶数</span>
positional_encoding[<span class="hljs-number">1</span>:, <span class="hljs-number">1</span>::<span class="hljs-number">2</span>] = np.cos(positional_encoding[<span class="hljs-number">1</span>:, <span class="hljs-number">1</span>::<span class="hljs-number">2</span>]) <span class="hljs-comment"># dim 2i+1 奇数</span>
<span class="hljs-keyword">return</span> positional_encoding
positional_encoding = get_positional_encoding(max_seq_len=100 , embed_dim=16 ) plt.figure(figsize=(10 ,10 )) sns.heatmap(positional_encoding) plt.title(“Sinusoidal Function” ) plt.xlabel(“hidden dimension” ) plt.ylabel(“sequence length” )
plt.figure(figsize=(8 , 5 ))
plt.plot(positional_encoding[1 :, 1 ], label=“dimension 1” )
plt.plot(positional_encoding[1 :, 2 ], label=“dimension 2” )
plt.plot(positional_encoding[1 :, 3 ], label=“dimension 3” )
plt.legend()
plt.xlabel(“Sequence length” )
plt.ylabel(“Period of Positional Encoding” )
2. Self Attention Mechanism
对于输入的句子 X 为 Decoder 中 Masked Self-Attention 的输出
6. 总结
到此为止,Transformer 中 95% 的内容已经介绍完了,我们用一张图展示其完整结构。不得不说,Transformer 设计的十分巧夺天工
下面有几个问题,是我从网上找的,感觉看完之后能对 Transformer 有一个更深的理解
Transformer 为什么需要进行 Multi-head Attention?
原论文中说到进行 Multi-head Attention 的原因是将模型分为多个头,形成多个子空间,可以让模型去关注不同方面的信息,最后再将各个方面的信息综合起来。其实直观上也可以想到,如果自己设计这样的一个模型,必然也不会只做一次 attention,多次 attention 综合的结果至少能够起到增强模型的作用,也可以类比 CNN 中同时使用多个卷积核 的作用,直观上讲,多头的注意力有助于网络捕捉到更丰富的特征 / 信息
Transformer 相比于 RNN/LSTM,有什么优势?为什么?
RNN 系列的模型,无法并行计算,因为 T 时刻的计算依赖 T-1 时刻的隐层计算结果,而 T-1 时刻的计算依赖 T-2 时刻的隐层计算结果 Transformer 的特征抽取能力比 RNN 系列的模型要好
为什么说 Transformer 可以代替 seq2seq?
这里用代替这个词略显不妥当,seq2seq 虽已老,但始终还是有其用武之地,seq2seq 最大的问题在于将 Encoder 端的所有信息压缩到一个固定长度的向量中 ,并将其作为 Decoder 端首个隐藏状态的输入,来预测 Decoder 端第一个单词 (token) 的隐藏状态。在输入序列比较长的时候,这样做显然会损失 Encoder 端的很多信息,而且这样一股脑的把该固定向量送入 Decoder 端,Decoder 端不能够关注到其想要关注的信息 。Transformer 不但对 seq2seq 模型这两点缺点有了实质性的改进 (多头交互式 attention 模块),而且还引入了 self-attention 模块,让源序列和目标序列首先“自关联” 起来,这样的话,源序列和目标序列自身的 embedding 表示所蕴含的信息更加丰富,而且后续的 FFN 层也增强了模型的表达能力,并且 Transformer 并行计算的能力远远超过了 seq2seq 系列模型
7. 参考文章