文章目录
- 一. transformer encoder
- 0. Tansformer 整体感受
- 1.
positional encoding, 即**位置嵌入**(或位置编码);
- 2.
self attention mechanism, **自注意力机制**
- 3. 残差连接和
Layer Normalization
- 4. Feed Forward
- 5. Transformer整体架构
Transformer是谷歌大脑在2017年底发表的论文attention is all you need中所提出的seq2seq模型. 现在已经取得了大范围的应用和扩展, 而BERT就是从transformer中衍生出来的预训练语言模型。在NLP领域目前已经成了标配,语言模型, 命名实体识别, 机器翻译, 可能很多人想到的LSTM等循环神经网络, 但目前其实LSTM起码在自然语言处理领域已经过时了, 在Stanford阅读理解数据集**(SQuAD2.0)榜单里, 机器的成绩已经超人类表现, 这很大程度要归功于transformer的BERT预训练模型**。
一. transformer encoder
-
transformer模型的直觉, 建立直观认识;
-
positional encoding, 即位置嵌入(或位置编码);
-
self attention mechanism, 即自注意力机制与注意力矩阵可视化;
-
Layer Normalization和残差连接.
-
transformer encoder整体结构.
0. Tansformer 整体感受
首先来说一下transformer和LSTM的最大区别, 就是LSTM的训练是迭代的, 是一个接一个字的来, 当前这个字过完LSTM单元, 才可以进下一个字, 而transformer的训练是并行了, 就是所有字是全部同时训练的, 这样就大大加快了计算效率, transformer使用了位置嵌入
(positional encoding)来理解语言的顺序, 使用自注意力机制和全连接层来进行计算, 这些后面都会详细讲解.
transformer模型主要分为两大部分, 分别是编码器和解码器, 编码器负责把自然语言序列映射成为隐藏层(下图中第2步用九宫格比喻的部分), 含有自然语言序列的数学表达. 然后解码器把隐藏层再映射为自然语言序列, 从而使我们可以解决各种问题, 如情感分类, 命名实体识别, 语义关系抽取, 摘要生成, 机器翻译等等, 下面我们简单说一下下图的每一步都做了什么:
- 输入自然语言序列到编码器: Why do we work?(为什么要工作);
- 编码器输出的隐藏层, 再输入到解码器;
- 输入
(起始)符号到解码器; - 得到第一个字"为";
- 将得到的第一个字"为"落下来再输入到解码器;
- 得到第二个字"什";
- 将得到的第二字再落下来, 直到解码器输出
(终止符), 即序列生成完成.
1.
positional encoding, 即位置嵌入(或位置编码);
由于transformer模型没有循环神经网络的迭代操作, 所以我们必须提供每个字的位置信息给transformer, 才能识别出语言中的顺序关系.
现在定义一个位置嵌入的概念, 也就是
positional encoding, 位置嵌入的维度为
[max sequence length, embedding dimension], 嵌入的维度同词向量的维度,
max sequence length属于超参数, 指的是限定的最大单个句长.
注意, 我们一般以字为单位训练transformer模型, 也就是说我们不用分词了, 首先我们要初始化字向量为
[vocab size, embedding dimension],
vocab size为总共的字库数量,
embedding dimension为字向量的维度, 也是每个字的数学表达.
在这里论文中使用了
sine和
cosine函数的线性变换来提供给模型位置信息:
PE(pos,2i)?=sin(pos/100002i/dmodel?)PE(pos,2i+1)?=cos(pos/100002i/dmodel?)(eq.1)
上式中
pos指的是句中字的位置, 取值范围是
[0, max sequence length),
i指的是词向量的维度, 取值范围是
[0, embedding dimension), 上面有
sin和
cos一组公式, 也就是对应着
embedding dimension维度的一组奇数和偶数的序号的维度, 例如
0,1一组,
2,3一组, 分别用上面的
sin和
cos函数做处理, 从而产生不同的周期性变化, 而位置嵌入在
embedding dimension维度上随着维度序号增大, 周期变化会越来越慢, 而产生一种包含位置信息的纹理, 就像论文原文中第六页讲的, 位置嵌入函数的周期从
2π到
10000?2π变化, 而每一个位置在
embedding dimension维度上都会得到不同周期的
sin和
cos函数的取值组合, 从而产生独一的纹理位置信息, 模型从而学到位置之间的依赖关系和自然语言的时序特性.
下面画一下位置嵌入, 可见纵向观察, 随着
embedding dimension增大, 位置嵌入函数呈现不同的周期变化.
对于同一个位置的字,也就是seq_len相同,在不同的hidden dimension上的明暗变化是不同的。在同一个hidden dimension维度上,沿着seq_len的维度是呈周期变化的。这样通过sin和cos函数就把位置信息进行了编码。
注意力矩阵是一个seq_len * seq_len的矩阵,下图是注意力矩阵可视化后的效果:
上图中, 我们用位置编码矩阵乘以(矩阵乘)他本身的转置, 也就是
PE: [seq_len, embedding_dim], 我们求
PEPET, 得出的维度是
[seq_len, seq_len]. 我们看到上图中, 矩阵的对角线隆起, 也就是值比较大, 是因为一个矩阵乘以他本身的转置之后, 形成的矩阵的对角线正是这个矩阵的每一行
(row)点乘这一行本身, 所以是值最大的区域(红色部分). 对于位置编码来说, 也就是当前位置与当前位置本身相关程度最高. 再往对角线两边看, 发现以对角线(红色山峰)区域为中心, 两边属于缓慢下降趋势, 这就说明了随着离当前位置越远, 其位置编码的相关程度就越低. 由此可见, 位置编码建立在时间维度的关联关系.
后边有人指出,transformer只进行了位置上的编码,但是不能体现方向上的信息,导致transformer结构直接用于NER任务时,效果不佳,在MSRA数据集上只有86%的f1值,经过改进可以达到92.7%的f1值,比使用char-bigram-bilstm-crf的效果(92%)好一些。Adapted Transformer Encoder for Named Entity Recognition,论文链接:https://arxiv.org/abs/1911.04474
2.
self attention mechanism, 自注意力机制
Attention mask:在softmax之前,需要根据seq_len来进行mask,防止padding位置上也有信息。
注意, 在上面
self attention的计算过程中, 我们通常使用
mini batch来计算, 也就是一次计算多句话, 也就是
X的维度是
[batch size, sequence length],
sequence length是句长, 而一个
mini batch是由多个不等长的句子组成的, 我们就需要按照这个
mini batch中最大的句长对剩余的句子进行补齐长度, 我们一般用
0来进行填充, 这个过程叫做
padding.
但这时在进行
softmax的时候就会产生问题, 回顾
softmax函数
σ(z)i?=∑j=1K?ezj?ezi??,
e0是1, 是有值的, 这样的话
softmax中被
padding的部分就参与了运算, 就等于是让无效的部分参与了运算, 会产生很大隐患, 这时就需要做一个
mask让这些无效区域不参与运算, 我们一般给无效区域加一个很大的负数的偏置, 也就是:
zillegal?=zillegal?+biasillegal?
biasillegal?→?∞
ezillegal?→0
经过上式的
masking我们使无效区域经过
softmax计算之后还几乎为
0, 这样就避免了无效区域参与计算.
3. 残差连接和
Layer Normalization
1). 残差连接:
我们在上一步得到了经过注意力矩阵加权之后的
V, 也就是
Attention(Q, K, V), 我们对它进行一下转置, 使其和
Xembedding?的维度一致, 也就是
[batch size, sequence length, embedding dimension], 然后把他们加起来做残差连接, 直接进行元素相加, 因为他们的维度一致:
Xembedding?+Attention(Q, K, V)
在之后的运算里, 每经过一个模块的运算, 都要把运算之前的值和运算之后的值相加, 从而得到残差连接, 训练的时候可以使梯度直接走捷径反传到最初始层:
X+SubLayer(X)(eq. 5)
2).
LayerNorm:
LayerNormalization的作用是把神经网络中隐藏层归一为标准正态分布, 也就是
i.i.d独立同分布, 以起到加快训练速度, 加速收敛的作用:
μi?=m1?i=1∑m?xij?
上式中以矩阵的行
(row)为单位求均值;
σj2?=m1?i=1∑m?(xij??μj?)2
上式中以矩阵的行
(row)为单位求方差;
LayerNorm(x)=α⊙σi2?+?
?是为了防止除
0;
之后引入两个可训练参数
α, β来弥补归一化的过程中损失掉的信息, 注意
⊙表示元素相乘而不是点积, 我们一般初始化
α为全
1, 而
β为全
0.
4. Feed Forward
这一部分比较简单,把上一步中的输出,经过了一个放缩,先把维度扩大到之前的3倍,再缩小到之前的hidden_size,再经过激活函数。得到一个[seq_len, hidden_size]大小的矩阵,再经过残差连接和Layer Normalizaion。
5. Transformer整体架构
经过上面4个步骤, 我们已经基本了解到来
transformer编码器的主要构成部分, 我们下面用公式把一个
transformer block的计算过程整理一下:
1). 字向量与位置编码:
X=EmbeddingLookup(X)+PositionalEncoding(eq.2)
X∈Rbatch size ? seq. len. ? embed. dim.
2). 自注意力机制:
Q=Linear(X)=XWQ?
K=Linear(X)=XWK?(eq.3)
V=Linear(X)=XWV?
Xattention?=SelfAttention(Q, K, V)(eq.4)
3). 残差连接与
Layer Normalization
Xattention?=X+Xattention?(eq. 5)
Xattention?=LayerNorm(Xattention?)(eq. 6)
4). 下面进行
transformer block结构图中的第4部分, 也就是
FeedForward, 其实就是两层线性映射并用激活函数激活, 比如说
ReLU:
Xhidden?=Activate(Linear(Linear(Xattention?)))(eq. 7)
5). 重复3).:
Xhidden?=Xattention?+Xhidden?
Xhidden?=LayerNorm(Xhidden?)
Xhidden?∈Rbatch size ? seq. len. ? embed. dim.
通过sin和cos函数对不同维度上的信息的周期是不同的,把位置信息与word embedding信息相加,再求了一个attention matrix,维度是[seq_len, seq_len]的,每一行是归一化的,是当前字与整个句子每个字的相似度的概率分布。因为用了点积,本质上是自己与自己作点积得到的概率分布。再对embedding的信息作加权,得到残差。
multi-head是为了提取不同的语义信息。
一个transformer层用了两次残差连接和两次Layer Normalization。
transformer的本质是multi-head的self-attention。
参考:
https://github.com/aespresso/a_journey_into_math_of_ml