Transformer是谷歌大脑在2017年底发表的论文
attention is all you need 中所提出的seq2seq模型. 现在已经取得了大范围的应用和扩展, 而BERT就是从transformer中衍生出来的预训练语言模型。在NLP领域目前已经成了标配,语言模型, 命名实体识别, 机器翻译, 可能很多人想到的
LSTM等循环神经网络 , 但目前其实LSTM起码在自然语言处理领域已经过时了, 在Stanford阅读理解数据集**(SQuAD2.0)
榜单里, 机器的成绩已经超人类表现, 这很大程度要归功于 transformer的BERT预训练模型**。
一. transformer encoder
t r a n s f o r m e r transformer t r a n s f o r m e r 模型的直觉, 建立直观认识;
p o s i t i o n a l e n c o d i n g positional \ encoding p o s i t i o n a l e n c o d i n g , 即位置嵌入 (或位置编码);
s e l f a t t e n t i o n m e c h a n i s m self \ attention \ mechanism s e l f a t t e n t i o n m e c h a n i s m , 即自注意力机制 与注意力矩阵可视化 ;
L a y e r N o r m a l i z a t i o n Layer \ Normalization L a y e r N o r m a l i z a t i o n 和残差连接.
t r a n s f o r m e r e n c o d e r transformer \ encoder t r a n s f o r m e r e n c o d e r 整体结构.
0. Tansformer 整体感受
首先来说一下transformer 和LSTM 的最大区别, 就是LSTM的训练是迭代的, 是一个接一个字的来, 当前这个字过完LSTM单元, 才可以进下一个字, 而transformer的训练是并行了, 就是所有字是全部同时训练的, 这样就大大加快了计算效率, transformer使用了位置嵌入( p o s i t i o n a l e n c o d i n g ) (positional \ encoding) ( p o s i t i o n a l e n c o d i n g ) 来理解语言的顺序, 使用自注意力机制和全连接层来进行计算, 这些后面都会详细讲解.
transformer模型主要分为两大部分 , 分别是编码器 和解码器 , 编码器 负责把自然语言序列映射成为隐藏层 (下图中第2步 用九宫格比喻的部分), 含有自然语言序列的数学表达. 然后解码器把隐藏层再映射为自然语言序列, 从而使我们可以解决各种问题, 如情感分类, 命名实体识别, 语义关系抽取, 摘要生成, 机器翻译等等, 下面我们简单说一下下图的每一步都做了什么:
输入自然语言序列到编码器: Why do we work?(为什么要工作);
编码器输出的隐藏层, 再输入到解码器;
输入< s t a r t > <start> < s t a r t > (起始)符号到解码器;
得到第一个字"为";
将得到的第一个字"为"落下来再输入到解码器;
得到第二个字"什";
将得到的第二字再落下来, 直到解码器输出< e n d > <end> < e n d > (终止符), 即序列生成完成.
1. p o s i t i o n a l e n c o d i n g positional \ encoding p o s i t i o n a l e n c o d i n g , 即位置嵌入 (或位置编码);
由于transformer模型没有 循环神经网络的迭代操作, 所以我们必须提供每个字的位置信息给transformer, 才能识别出语言中的顺序关系.
现在定义一个位置嵌入的概念, 也就是p o s i t i o n a l e n c o d i n g positional \ encoding p o s i t i o n a l e n c o d i n g , 位置嵌入的维度为[ m a x s e q u e n c e l e n g t h , e m b e d d i n g d i m e n s i o n ] [max \ sequence \ length, \ embedding \ dimension] [ m a x s e q u e n c e l e n g t h , e m b e d d i n g d i m e n s i o n ] , 嵌入的维度同词向量的维度, m a x s e q u e n c e l e n g t h max \ sequence \ length m a x s e q u e n c e l e n g t h 属于超参数, 指的是限定的最大单个句长.
注意, 我们一般以字为单位训练transformer模型, 也就是说我们不用分词了, 首先我们要初始化字向量为[ v o c a b s i z e , e m b e d d i n g d i m e n s i o n ] [vocab \ size, \ embedding \ dimension] [ v o c a b s i z e , e m b e d d i n g d i m e n s i o n ] , v o c a b s i z e vocab \ size v o c a b s i z e 为总共的字库数量, e m b e d d i n g d i m e n s i o n embedding \ dimension e m b e d d i n g d i m e n s i o n 为字向量的维度, 也是每个字的数学表达.
在这里论文中使用了s i n e sine s i n e 和c o s i n e cosine c o s i n e 函数的线性变换来提供给模型位置信息:
P E ( p o s , 2 i ) = s i n ( p o s / 1000 0 2 i / d model ) P E ( p o s , 2 i + 1 ) = c o s ( p o s / 1000 0 2 i / d model ) (eq.1) PE_{(pos,2i)} = sin(pos / 10000^{2i/d_{\text{model}}}) \quad PE_{(pos,2i+1)} = cos(pos / 10000^{2i/d_{\text{model}}})\tag{eq.1} P E ( p o s , 2 i ) = s i n ( p o s / 1 0 0 0 0 2 i / d model ) P E ( p o s , 2 i + 1 ) = c o s ( p o s / 1 0 0 0 0 2 i / d model ) ( e q . 1 )
上式中p o s pos p o s 指的是句中字的位置, 取值范围是[ 0 , m a x s e q u e n c e l e n g t h ) [0, \ max \ sequence \ length) [ 0 , m a x s e q u e n c e l e n g t h ) , i i i 指的是词向量的维度, 取值范围是[ 0 , e m b e d d i n g d i m e n s i o n ) [0, \ embedding \ dimension) [ 0 , e m b e d d i n g d i m e n s i o n ) , 上面有s i n sin s i n 和c o s cos c o s 一组公式, 也就是对应着e m b e d d i n g d i m e n s i o n embedding \ dimension e m b e d d i n g d i m e n s i o n 维度的一组奇数和偶数的序号的维度, 例如0 , 1 0, 1 0 , 1 一组, 2 , 3 2, 3 2 , 3 一组, 分别用上面的s i n sin s i n 和c o s cos c o s 函数做处理, 从而产生不同的周期性变化, 而位置嵌入在e m b e d d i n g d i m e n s i o n embedding \ dimension e m b e d d i n g d i m e n s i o n 维度上随着维度序号增大, 周期变化会越来越慢, 而产生一种包含位置信息的纹理, 就像论文原文中第六页讲的, 位置嵌入函数的周期从2 π 2 \pi 2 π 到10000 ∗ 2 π 10000 * 2 \pi 1 0 0 0 0 ∗ 2 π 变化, 而每一个位置在e m b e d d i n g d i m e n s i o n embedding \ dimension e m b e d d i n g d i m e n s i o n 维度上都会得到不同周期的s i n sin s i n 和c o s cos c o s 函数的取值组合, 从而产生独一的纹理位置信息, 模型从而学到位置之间的依赖关系和自然语言的时序特性.
下面画一下位置嵌入, 可见纵向观察, 随着e m b e d d i n g d i m e n s i o n embedding \ dimension e m b e d d i n g d i m e n s i o n 增大, 位置嵌入函数呈现不同的周期变化.
对于同一个位置的字,也就是seq_len相同,在不同的hidden dimension上的明暗变化是不同的。在同一个hidden dimension维度上,沿着seq_len的维度是呈周期变化的。这样通过sin和cos函数就把位置信息进行了编码。
注意力矩阵是一个seq_len * seq_len的矩阵,下图是注意力矩阵可视化后的效果:
上图中, 我们用位置编码矩阵乘以(矩阵乘)他本身的转置, 也就是P E : [ s e q _ l e n , e m b e d d i n g _ d i m ] PE: \ [seq\_len, \ embedding\_dim ] P E : [ s e q _ l e n , e m b e d d i n g _ d i m ] , 我们求P E P E T PEPE^T P E P E T , 得出的维度是[ s e q _ l e n , s e q _ l e n ] [seq\_len, \ seq\_len ] [ s e q _ l e n , s e q _ l e n ] . 我们看到上图中, 矩阵的对角线隆起, 也就是值比较大, 是因为一个矩阵乘以他本身的转置之后, 形成的矩阵的对角线正是这个矩阵的每一行( r o w ) (row) ( r o w ) 点乘这一行本身, 所以是值最大的区域(红色部分). 对于位置编码来说, 也就是当前位置与当前位置本身相关程度最高. 再往对角线两边看, 发现以对角线(红色山峰)区域为中心, 两边属于缓慢下降趋势, 这就说明了随着离当前位置越远, 其位置编码的相关程度就越低. 由此可见, 位置编码建立在时间维度的关联关系.
后边有人指出,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. s e l f a t t e n t i o n m e c h a n i s m self \ attention \ mechanism s e l f a t t e n t i o n m e c h a n i s m , 自注意力机制
Attention mask:在softmax之前,需要根据seq_len来进行mask,防止padding位置上也有信息。
注意, 在上面s e l f a t t e n t i o n self \ attention s e l f a t t e n t i o n 的计算过程中, 我们通常使用m i n i b a t c h mini \ batch m i n i b a t c h 来计算, 也就是一次计算多句话, 也就是X X X 的维度是[ b a t c h s i z e , s e q u e n c e l e n g t h ] [batch \ size, \ sequence \ length] [ b a t c h s i z e , s e q u e n c e l e n g t h ] , s e q u e n c e l e n g t h sequence \ length s e q u e n c e l e n g t h 是句长, 而一个m i n i b a t c h mini \ batch m i n i b a t c h 是由多个不等长的句子组成的, 我们就需要按照这个m i n i b a t c h mini \ batch m i n i b a t c h 中最大的句长对剩余的句子进行补齐长度, 我们一般用0 0 0 来进行填充, 这个过程叫做p a d d i n g padding p a d d i n g .
但这时在进行s o f t m a x softmax s o f t m a x 的时候就会产生问题, 回顾s o f t m a x softmax s o f t m a x 函数σ ( z ) i = e z i ∑ j = 1 K e z j \sigma (\mathbf {z} )_{i}={\frac {e^{z_{i}}}{\sum _{j=1}^{K}e^{z_{j}}}} σ ( z ) i = ∑ j = 1 K e z j e z i , e 0 e^0 e 0 是1, 是有值的, 这样的话s o f t m a x softmax s o f t m a x 中被p a d d i n g padding p a d d i n g 的部分就参与了运算, 就等于是让无效的部分参与了运算, 会产生很大隐患, 这时就需要做一个m a s k mask m a s k 让这些无效区域不参与运算, 我们一般给无效区域加一个很大的负数的偏置, 也就是:
z i l l e g a l = z i l l e g a l + b i a s i l l e g a l z_{illegal} = z_{illegal} + bias_{illegal} z i l l e g a l = z i l l e g a l + b i a s i l l e g a l
b i a s i l l e g a l → − ∞ bias_{illegal} \to -\infty b i a s i l l e g a l → − ∞
e z i l l e g a l → 0 e^{z_{illegal}} \to 0 e z i l l e g a l → 0
经过上式的m a s k i n g masking m a s k i n g 我们使无效区域经过s o f t m a x softmax s o f t m a x 计算之后还几乎为0 0 0 , 这样就避免了无效区域参与计算.
3. 残差连接和L a y e r N o r m a l i z a t i o n Layer \ Normalization L a y e r N o r m a l i z a t i o n
1). 残差连接 :
我们在上一步得到了经过注意力矩阵加权之后的V V V , 也就是A t t e n t i o n ( Q , K , V ) Attention(Q, \ K, \ V) A t t e n t i o n ( Q , K , V ) , 我们对它进行一下转置, 使其和X e m b e d d i n g X_{embedding} X e m b e d d i n g 的维度一致, 也就是[ b a t c h s i z e , s e q u e n c e l e n g t h , e m b e d d i n g d i m e n s i o n ] [batch \ size, \ sequence \ length, \ embedding \ dimension] [ b a t c h s i z e , s e q u e n c e l e n g t h , e m b e d d i n g d i m e n s i o n ] , 然后把他们加起来做残差连接, 直接进行元素相加, 因为他们的维度一致:
X e m b e d d i n g + A t t e n t i o n ( Q , K , V ) X_{embedding} + Attention(Q, \ K, \ V) X e m b e d d i n g + A t t e n t i o n ( Q , K , V )
在之后的运算里, 每经过一个模块的运算, 都要把运算之前的值和运算之后的值相加, 从而得到残差连接, 训练的时候可以使梯度直接走捷径反传到最初始层:
X + S u b L a y e r ( X ) (eq. 5) X + SubLayer(X) \tag{eq. 5} X + S u b L a y e r ( X ) ( e q . 5 )
2). L a y e r N o r m LayerNorm L a y e r N o r m :
L a y e r N o r m a l i z a t i o n Layer Normalization L a y e r N o r m a l i z a t i o n 的作用是把神经网络中隐藏层归一为标准正态分布, 也就是i . i . d i.i.d i . i . d 独立同分布, 以起到加快训练速度, 加速收敛的作用:
μ i = 1 m ∑ i = 1 m x i j \mu_{i}=\frac{1}{m} \sum^{m}_{i=1}x_{ij} μ i = m 1 i = 1 ∑ m x i j
上式中以矩阵的行( r o w ) (row) ( r o w ) 为单位求均值;
σ j 2 = 1 m ∑ i = 1 m ( x i j − μ j ) 2 \sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1}
(x_{ij}-\mu_{j})^{2} σ j 2 = m 1 i = 1 ∑ m ( x i j − μ j ) 2
上式中以矩阵的行( r o w ) (row) ( r o w ) 为单位求方差;
L a y e r N o r m ( x ) = α ⊙ x i j − μ i σ i 2 + ϵ + β (eq.6) LayerNorm(x)=\alpha \odot \frac{x_{ij}-\mu_{i}}
{\sqrt{\sigma^{2}_{i}+\epsilon}} + \beta \tag{eq.6} L a y e r N o r m ( x ) = α ⊙ σ i 2 + ϵ x i j − μ i + β ( e q . 6 )
然后用每一行 的每一个元素 减去这行的均值 , 再除以这行的标准差 , 从而得到归一化后的数值, ϵ \epsilon ϵ 是为了防止除0 0 0 ;
之后引入两个可训练参数α , β \alpha, \ \beta α , β 来弥补归一化的过程中损失掉的信息, 注意⊙ \odot ⊙ 表示元素相乘而不是点积, 我们一般初始化α \alpha α 为全1 1 1 , 而β \beta β 为全0 0 0 .
4. Feed Forward
这一部分比较简单,把上一步中的输出,经过了一个放缩,先把维度扩大到之前的3倍,再缩小到之前的hidden_size,再经过激活函数。得到一个[seq_len, hidden_size]大小的矩阵,再经过残差连接和Layer Normalizaion。
5. Transformer整体架构
经过上面4个步骤, 我们已经基本了解到来t r a n s f o r m e r transformer t r a n s f o r m e r 编码器的主要构成部分, 我们下面用公式把一个t r a n s f o r m e r b l o c k transformer \ block t r a n s f o r m e r b l o c k 的计算过程整理一下:
1). 字向量与位置编码:
X = E m b e d d i n g L o o k u p ( X ) + P o s i t i o n a l E n c o d i n g (eq.2) X = EmbeddingLookup(X) + PositionalEncoding \tag{eq.2} X = E m b e d d i n g L o o k u p ( X ) + P o s i t i o n a l E n c o d i n g ( e q . 2 )
X ∈ R b a t c h s i z e ∗ s e q . l e n . ∗ e m b e d . d i m . X \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.} X ∈ R b a t c h s i z e ∗ s e q . l e n . ∗ e m b e d . d i m .
2). 自注意力机制:
Q = L i n e a r ( X ) = X W Q Q = Linear(X) = XW_{Q} Q = L i n e a r ( X ) = X W Q
K = L i n e a r ( X ) = X W K (eq.3) K = Linear(X) = XW_{K} \tag{eq.3} K = L i n e a r ( X ) = X W K ( e q . 3 )
V = L i n e a r ( X ) = X W V V = Linear(X) = XW_{V} V = L i n e a r ( X ) = X W V
X a t t e n t i o n = S e l f A t t e n t i o n ( Q , K , V ) (eq.4) X_{attention} = SelfAttention(Q, \ K, \ V) \tag{eq.4} X a t t e n t i o n = S e l f A t t e n t i o n ( Q , K , V ) ( e q . 4 )
3). 残差连接与L a y e r N o r m a l i z a t i o n Layer \ Normalization L a y e r N o r m a l i z a t i o n
X a t t e n t i o n = X + X a t t e n t i o n (eq. 5) X_{attention} = X + X_{attention} \tag{eq. 5} X a t t e n t i o n = X + X a t t e n t i o n ( e q . 5 )
X a t t e n t i o n = L a y e r N o r m ( X a t t e n t i o n ) (eq. 6) X_{attention} = LayerNorm(X_{attention}) \tag{eq. 6} X a t t e n t i o n = L a y e r N o r m ( X a t t e n t i o n ) ( e q . 6 )
4). 下面进行t r a n s f o r m e r b l o c k transformer \ block t r a n s f o r m e r b l o c k 结构图中的第4部分 , 也就是F e e d F o r w a r d FeedForward F e e d F o r w a r d , 其实就是两层线性映射并用激活函数激活, 比如说R e L U ReLU R e L U :
X h i d d e n = A c t i v a t e ( L i n e a r ( L i n e a r ( X a t t e n t i o n ) ) ) (eq. 7) X_{hidden} = Activate(Linear(Linear(X_{attention}))) \tag{eq. 7} X h i d d e n = A c t i v a t e ( L i n e a r ( L i n e a r ( X a t t e n t i o n ) ) ) ( e q . 7 )
5). 重复3).:
X h i d d e n = X a t t e n t i o n + X h i d d e n X_{hidden} = X_{attention} + X_{hidden} X h i d d e n = X a t t e n t i o n + X h i d d e n
X h i d d e n = L a y e r N o r m ( X h i d d e n ) X_{hidden} = LayerNorm(X_{hidden}) X h i d d e n = L a y e r N o r m ( X h i d d e n )
X h i d d e n ∈ R b a t c h s i z e ∗ s e q . l e n . ∗ e m b e d . d i m . X_{hidden} \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.} X h i d d e n ∈ R b a t c h s i z e ∗ s e q . l e n . ∗ e m b e d . d i m .
通过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