目录
一.背景
说到自然语言处理,语言模型,命名实体识别,机器翻译,可能很多人想到的LSTM等循环神经网络,但目前其实LSTM起码在自然语言处理领域已经过时了,在Stanford阅读理解数据集(SQuAD2.0)榜单里,机器的成绩已经超人类表现,这很大程度要归功于transformer的BERT预训练模型。
transformer是谷歌大脑在2017年底发表的论文中所提出的seq2seq模型(从序列到序列的模型).现在已经取得了大范围的应用和扩展,而BERT就是从transformer中衍生出来的预训练语言模型。
这里介绍一下在目前的自然语言处理领域,transformer模型已经得到广泛认可和应用,而应用的方式主要是先进行预训练语言模型(上游任务),然后把预训练的模型适配给下游任务(在自然语言处理中要完成的实际的任务,如情感分析,分类,机器翻译等),以完成各种不同的任务,如分类,生成,标记等等,预训练模型非常重要,预训练的模型的性能直接影响下游任务的性能。就好比要培养一个孩子成为作家,不可能上来就直接让他开始写作,在写作之前肯定会让他识字,看很多的大作家的作品汲取精华,最后才可以进行写作。
二.transformer编码器
- 1. t r a n s f o r m e r transformer transformer模型直觉,建立直观认识;
- 2. p o s i t i o n a l positional positional e n c o d i n g encoding encoding,即位置嵌入(或位置编码);
- 3. s e l f self self a t t e n t i o n attention attention m e c h a n i s m mechanism mechanism,即自注意力机制与注意力矩阵可视化;
- 4. L a y e r Layer Layer N o r m a l i z a t i o n Normalization Normalization和残差连接;
- 5. t r a n s f o r m e r transformer transformer e n c o d e r encoder encoder整体结构。
1. t r a n s f o r m e r transformer transformer模型直觉,建立直观认识;
首先来说一下transformer和LSTM/RNN的最大区别,就是LSTM/RNN的训练是迭代的,是一个接一个字的来,当前这个字过完LSTM单元/RNN层,才可以进下一个字,而transformer的训练是并行的,一般以字为单位训练的,transformer模型就是所有字是全部同时训练的,这样就大大加快了计算效率, transformer使用了位置嵌入(positional encoding)来理解语言的顺序(获取时间序列关系),使用自注意力机制和全连接层来进行计算。
transformer模型主要分为两大部分,分别是编码器和解码器,编码器负责把自然语言序列映射成为隐藏层(下图中第2步用九宫格比喻的部分),含有自然语言序列的数学表达.然后解码器把隐藏层再映射为自然语言序列,从而使我们可以解决各种问题,如情感分类,命名实体识别,语义关系抽取,摘要生成,机器翻译等等,下面我们简单说一下下图的每一步都做了什么:
1.1transformer的流程
- <1>
Inputs
是经过padding
的输入数据,大小(shape)是[batch_size, max_sequence]
(batch_size个max_sequence长度的句子,同时输入) - <2>初始化
embedding matrix
,通过embedding lookup
(到字向量表中去查找对应的字向量)将Inputs
映射成token embedding
,大小是[batch_size, max_sequence, embedding_size]
- <3>通过
sin
和cos
函数创建positional encoding
,表示一个token
(这里的token就是目标词(字)的意思)的绝对位置信息,并且加入到token embedding
中,然后dropout
- <4>
multi-head attention
(多头注意力机制) - <4.1>输入
token embedding
,通过Dense生成Q,K,V,大小是[batch size, max seq length, embedding size]
,然后按第2维split成num heads份并按第0维concat,生成新的Q,K,V,大小是[num heads*batch size, max seq length, embedding size/num heads]
,完成multi-head的操作。 - <4.2>将K的第1维和第2维进行转置,然后Q和转置后的K的进行点积,结果的大小是
[num heads*batch size, max seq length, max seq length]
。 - <4.3> 将<4.2>的结果除以
hidden size
的开方(在transformer中,hidden size=embedding size
),完成scale
的操作。 - <4.4> 将<4.3>中padding的点积结果置成一个很小的数(-2^32+1),完成mask操作,后续
softmax
对padding
的结果就可以忽略不计了。 - <4.5> 将经过
mask
的结果进行softmax
操作。 - <4.6> 将
softmax
的结果和V
进行点积,得到attention
的结果,大小是[num heads*batch size, max seq length, hidden size/num heads]
。 - <4.7> 将
attention
的结果按第0维split
成num heads
份并按第2维concat
,生成multi-head attention
的结果,大小是[batch size, max seq length, hidden size]
。Figure 2上concat
之后还有一个linear
的操作,但是代码里并没有。 - <5> 将
token embedding
和multi-head attention
的结果相加,并进行Layer Normalization
。 - <6> 将<5>的结果经过2层Dense,其中第1层的
activation=relu
,第2层activation=None
。 - <7> 功能和<5>一样。
- 未完待续
1.2transformer-encoder部分
这里主要分析编码器的结构,主要是Bert预训练语言模型只用到了编码器部分。
Transformer Block结构图:
注意,为了方便查看,下边的内容分别对应着图中第1,2,3,4个方框的序号
图表分析:
- 这里输入一个 X X X(多个句子)得到一个 X h i d d e n X_{hidden} Xhidden, X h i d d e n X_{hidden} Xhidden就是通过编码器输出得到的隐藏层。先看一下第一步
Input Embedding
,先从字向量表里边查到对应的字向量,它的维度就变成了[batch_size , Sequence_length , embedding_dimmension]
,这里的input Embedding
就是从这个表中找到每一个字的数学表达。
字向量表的结构:
这里是一个字的向量查询表(哪里来的?),本文第一幅图中的第二步中初始化embedding matrix,就是通过embedding lookup
(到字向量表中去查找对应的字向量)将Inputs
映射成token embedding
,大小(shape)是[batch_size, max_sequence, embedding_size]
接下来引入position encoding的概念,即给每一个字设定一个位置
2. p o s i t i o n a l e n c o d i n g positional \ encoding positional encoding, 即位置嵌入(或位置编码)😭?)
由于transformer模型没有循环神经网络的迭代操作, 所以我们必须提供每个字的位置信息给transformer, 才能识别出语言中的顺序关系。
现在定义一个位置嵌入的概念,也就是positional encoding,位置嵌入的维度为[max_sequencelength, embedding_dimension]
,嵌入的维度的1维度等于词向量的embedding_size维度, max_ sequencelength属于超参数,指的是限定的最大单个句长.
注意,我们一般以字为单位训练transformer模型,也就是说我们不用分词了,首先我们要初始化字向量为[vocabsize, embedding_dimensionl], vocab_size为总共的字库数量, embedding_dimension为字向量的维度,也是每个字的数学表达
在这里论文中使用了sin和cos函数的线性变换来提供给模型位置信息:
这里作者添加位置编码的方法是:构造一个跟输入embedding维度相同的矩阵,然后跟输入embedding相加得到multi-head attention的输入。
P E ( pos , 2 i ) = sin ( pos / 1000 0 2 i / d model ) P E (pos, 2 i + 1 ) = cos ( pos / 1000 0 2 i / d model ) P E_{(\text {pos}, 2 i)}=\sin \left(\text {pos} / 10000^{2 i / d_{\text {model }}}\right) \quad P E_{ \text {(pos, }2 i+1)}=\cos \left(\text {pos} / 10000^{2 i / d_{\text {model }}}\right) PE(pos,2i)=sin(pos/100002i/dmodel )PE(pos, 2i+1)=cos(pos/100002i/dmodel )
- 其中PE为二阶矩阵,大小跟输入embedding的维度一样,行表示词语,列表示词向量;
- p o s pos pos表示词语在句子中的位置;
- d m o d e l d_{model} dmodel表示词向量的维度;
- i i i表示词向量的位置。
因此上述表示在每个词语的词向量的偶数位置添加sin变量,奇数位置添加cos变量,以此来填满整个PE矩阵,然后加到input embedding中去,这样便完成了位置编码的引入了。
使用 s i n sin sin编码和 c o s cos cos编码的原因是可以得到词语之间的相对位置,因为:
s i n ( α + β ) = s i n α c o s β + c o s α s i n β sin(α+β)=sinαcosβ+cosαsinβ sin(α+β)=sinαcosβ+cosαsinβ
c o s ( α + β ) = c o s α c o s β − s i n α s i n β cos(α+β)=cosαcosβ−sinαsinβ cos(α+β)=cosαcosβ−sinαsinβ
即由 s i n ( p o s + k ) sin(pos+k) sin(po