最通俗易懂的Transformer教程

文章目录

    • 一. transformer encoder
      • 0. Tansformer 整体感受
      • 1.
        positional encodingpositional \ encoding

        positional encoding, 即**位置嵌入**(或位置编码);

      • 2.
        self attention mechanismself \ attention \ mechanism

        self attention mechanism, **自注意力机制**

      • 3. 残差连接和
        Layer NormalizationLayer \ Normalization

        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

  1. transformertransformer

    transformer模型的直觉, 建立直观认识;

  2. positional encodingpositional \ encoding

    positional encoding, 即位置嵌入(或位置编码);

  3. self attention mechanismself \ attention \ mechanism

    self attention mechanism, 即自注意力机制注意力矩阵可视化;

  4. Layer NormalizationLayer \ Normalization

    Layer Normalization和残差连接.

  5. transformer encodertransformer \ encoder

    transformer encoder整体结构.

0. Tansformer 整体感受

首先来说一下transformerLSTM的最大区别, 就是LSTM的训练是迭代的, 是一个接一个字的来, 当前这个字过完LSTM单元, 才可以进下一个字, 而transformer的训练是并行了, 就是所有字是全部同时训练的, 这样就大大加快了计算效率, transformer使用了位置嵌入

(positional encoding)(positional \ encoding)

(positional encoding)来理解语言的顺序, 使用自注意力机制和全连接层来进行计算, 这些后面都会详细讲解.
transformer模型主要分为两大部分, 分别是编码器解码器, 编码器负责把自然语言序列映射成为隐藏层(下图中第2步用九宫格比喻的部分), 含有自然语言序列的数学表达. 然后解码器把隐藏层再映射为自然语言序列, 从而使我们可以解决各种问题, 如情感分类, 命名实体识别, 语义关系抽取, 摘要生成, 机器翻译等等, 下面我们简单说一下下图的每一步都做了什么:

  1. 输入自然语言序列到编码器: Why do we work?(为什么要工作);
  2. 编码器输出的隐藏层, 再输入到解码器;
  3. 输入
    <start>

    (起始)符号到解码器;

  4. 得到第一个字"为";
  5. 将得到的第一个字"为"落下来再输入到解码器;
  6. 得到第二个字"什";
  7. 将得到的第二字再落下来, 直到解码器输出
    <end>

    (终止符), 即序列生成完成.

在这里插入图片描述

1.

positional encodingpositional \ encoding

positional encoding, 即位置嵌入(或位置编码);

由于transformer模型没有循环神经网络的迭代操作, 所以我们必须提供每个字的位置信息给transformer, 才能识别出语言中的顺序关系.

现在定义一个位置嵌入的概念, 也就是

positional encodingpositional \ encoding

positional encoding, 位置嵌入的维度为

[max sequence length, embedding dimension][max \ sequence \ length, \ embedding \ dimension]

[max sequence length, embedding dimension], 嵌入的维度同词向量的维度,

max sequence lengthmax \ sequence \ length

max sequence length属于超参数, 指的是限定的最大单个句长.

注意, 我们一般以字为单位训练transformer模型, 也就是说我们不用分词了, 首先我们要初始化字向量为

[vocab size, embedding dimension][vocab \ size, \ embedding \ dimension]

[vocab size, embedding dimension],

vocab sizevocab \ size

vocab size为总共的字库数量,

embedding dimensionembedding \ dimension

embedding dimension为字向量的维度, 也是每个字的数学表达.

在这里论文中使用了

sinesine

sine和

cosinecosine

cosine函数的线性变换来提供给模型位置信息:

PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)(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}

PE(pos,2i)?=sin(pos/100002i/dmodel?)PE(pos,2i+1)?=cos(pos/100002i/dmodel?)(eq.1)
上式中

pospos

pos指的是句中字的位置, 取值范围是

[0, max sequence length)[0, \ max \ sequence \ length)

[0, max sequence length),

ii

i指的是词向量的维度, 取值范围是

[0, embedding dimension)[0, \ embedding \ dimension)

[0, embedding dimension), 上面有

sinsin

sin和

coscos

cos一组公式, 也就是对应着

embedding dimensionembedding \ dimension

embedding dimension维度的一组奇数和偶数的序号的维度, 例如

0,10, 1

0,1一组,

2,32, 3

2,3一组, 分别用上面的

sinsin

sin和

coscos

cos函数做处理, 从而产生不同的周期性变化, 而位置嵌入在

embedding dimensionembedding \ dimension

embedding dimension维度上随着维度序号增大, 周期变化会越来越慢, 而产生一种包含位置信息的纹理, 就像论文原文中第六页讲的, 位置嵌入函数的周期从

2π2 \pi

2π到

10000?2π10000 * 2 \pi

10000?2π变化, 而每一个位置在

embedding dimensionembedding \ dimension

embedding dimension维度上都会得到不同周期的

sinsin

sin和

coscos

cos函数的取值组合, 从而产生独一的纹理位置信息, 模型从而学到位置之间的依赖关系和自然语言的时序特性.
下面画一下位置嵌入, 可见纵向观察, 随着

embedding dimensionembedding \ dimension

embedding dimension增大, 位置嵌入函数呈现不同的周期变化.

在这里插入图片描述
在这里插入图片描述

对于同一个位置的字,也就是seq_len相同,在不同的hidden dimension上的明暗变化是不同的。在同一个hidden dimension维度上,沿着seq_len的维度是呈周期变化的。这样通过sin和cos函数就把位置信息进行了编码。

注意力矩阵是一个seq_len * seq_len的矩阵,下图是注意力矩阵可视化后的效果:
在这里插入图片描述
上图中, 我们用位置编码矩阵乘以(矩阵乘)他本身的转置, 也就是

PE: [seq_len, embedding_dim]PE: \ [seq\_len, \ embedding\_dim ]

PE: [seq_len, embedding_dim], 我们求

PEPETPEPE^T

PEPET, 得出的维度是

[seq_len, seq_len][seq\_len, \ seq\_len ]

[seq_len, seq_len]. 我们看到上图中, 矩阵的对角线隆起, 也就是值比较大, 是因为一个矩阵乘以他本身的转置之后, 形成的矩阵的对角线正是这个矩阵的每一行

(row)(row)

(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 mechanismself \ attention \ mechanism

self attention mechanism, 自注意力机制

在这里插入图片描述
在这里插入图片描述
Attention mask:在softmax之前,需要根据seq_len来进行mask,防止padding位置上也有信息。
在这里插入图片描述
注意, 在上面

self attentionself \ attention

self attention的计算过程中, 我们通常使用

mini batchmini \ batch

mini batch来计算, 也就是一次计算多句话, 也就是

XX

X的维度是

[batch size, sequence length][batch \ size, \ sequence \ length]

[batch size, sequence length],

sequence lengthsequence \ length

sequence length是句长, 而一个

mini batchmini \ batch

mini batch是由多个不等长的句子组成的, 我们就需要按照这个

mini batchmini \ batch

mini batch中最大的句长对剩余的句子进行补齐长度, 我们一般用

00

0来进行填充, 这个过程叫做

paddingpadding

padding.
但这时在进行

softmaxsoftmax

softmax的时候就会产生问题, 回顾

softmaxsoftmax

softmax函数

σ(z)i=ezij=1Kezj\sigma (\mathbf {z} )_{i}={\frac {e^{z_{i}}}{\sum _{j=1}^{K}e^{z_{j}}}}

σ(z)i?=∑j=1K?ezj?ezi??,

e0e^0

e0是1, 是有值的, 这样的话

softmaxsoftmax

softmax中被

paddingpadding

padding的部分就参与了运算, 就等于是让无效的部分参与了运算, 会产生很大隐患, 这时就需要做一个

maskmask

mask让这些无效区域不参与运算, 我们一般给无效区域加一个很大的负数的偏置, 也就是:

zillegal=zillegal+biasillegalz_{illegal} = z_{illegal} + bias_{illegal}

zillegal?=zillegal?+biasillegal?

biasillegal?bias_{illegal} \to -\infty

biasillegal?→?∞

ezillegal0e^{z_{illegal}} \to 0

ezillegal?→0
经过上式的

maskingmasking

masking我们使无效区域经过

softmaxsoftmax

softmax计算之后还几乎为

00

0, 这样就避免了无效区域参与计算.

3. 残差连接和

Layer NormalizationLayer \ Normalization

Layer Normalization

1). 残差连接:
我们在上一步得到了经过注意力矩阵加权之后的

VV

V, 也就是

Attention(Q, K, V)Attention(Q, \ K, \ V)

Attention(Q, K, V), 我们对它进行一下转置, 使其和

XembeddingX_{embedding}

Xembedding?的维度一致, 也就是

[batch size, sequence length, embedding dimension][batch \ size, \ sequence \ length, \ embedding \ dimension]

[batch size, sequence length, embedding dimension], 然后把他们加起来做残差连接, 直接进行元素相加, 因为他们的维度一致:

Xembedding+Attention(Q, K, V)X_{embedding} + Attention(Q, \ K, \ V)

Xembedding?+Attention(Q, K, V)
在之后的运算里, 每经过一个模块的运算, 都要把运算之前的值和运算之后的值相加, 从而得到残差连接, 训练的时候可以使梯度直接走捷径反传到最初始层:

X+SubLayer(X)(eq. 5)X + SubLayer(X) \tag{eq. 5}

X+SubLayer(X)(eq. 5)
2).

LayerNormLayerNorm

LayerNorm:

LayerNormalizationLayer Normalization

LayerNormalization的作用是把神经网络中隐藏层归一为标准正态分布, 也就是

i.i.di.i.d

i.i.d独立同分布, 以起到加快训练速度, 加速收敛的作用:

μi=1mi=1mxij\mu_{i}=\frac{1}{m} \sum^{m}_{i=1}x_{ij}

μi?=m1?i=1∑m?xij?
上式中以矩阵的行

(row)(row)

(row)为单位求均值;

σj2=1mi=1m(xij?μj)2\sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1}
(x_{ij}-\mu_{j})^{2}

σj2?=m1?i=1∑m?(xij??μj?)2
上式中以矩阵的行

(row)(row)

(row)为单位求方差;

LayerNorm(x)=αxij?μiσi2+?+β(eq.6)LayerNorm(x)=\alpha \odot \frac{x_{ij}-\mu_{i}}
{\sqrt{\sigma^{2}_{i}+\epsilon}} + \beta \tag{eq.6}

LayerNorm(x)=α⊙σi2?+??xij??μi??+β(eq.6)
然后用每一行每一个元素减去这行的均值, 再除以这行的标准差, 从而得到归一化后的数值,

?\epsilon

?是为了防止除

00

0;
之后引入两个可训练参数

α, β\alpha, \ \beta

α, β来弥补归一化的过程中损失掉的信息, 注意

\odot

⊙表示元素相乘而不是点积, 我们一般初始化

α\alpha

α为全

11

1, 而

β\beta

β为全

00

0.

4. Feed Forward

这一部分比较简单,把上一步中的输出,经过了一个放缩,先把维度扩大到之前的3倍,再缩小到之前的hidden_size,再经过激活函数。得到一个[seq_len, hidden_size]大小的矩阵,再经过残差连接和Layer Normalizaion。

5. Transformer整体架构

经过上面4个步骤, 我们已经基本了解到来

transformertransformer

transformer编码器的主要构成部分, 我们下面用公式把一个

transformer blocktransformer \ block

transformer block的计算过程整理一下:
1). 字向量与位置编码:

X=EmbeddingLookup(X)+PositionalEncoding(eq.2)X = EmbeddingLookup(X) + PositionalEncoding \tag{eq.2}

X=EmbeddingLookup(X)+PositionalEncoding(eq.2)

XRbatch size ? seq. len. ? embed. dim.X \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}

X∈Rbatch size ? seq. len. ? embed. dim.
2). 自注意力机制:

Q=Linear(X)=XWQQ = Linear(X) = XW_{Q}

Q=Linear(X)=XWQ?

K=Linear(X)=XWK(eq.3)K = Linear(X) = XW_{K} \tag{eq.3}

K=Linear(X)=XWK?(eq.3)

V=Linear(X)=XWVV = Linear(X) = XW_{V}

V=Linear(X)=XWV?

Xattention=SelfAttention(Q, K, V)(eq.4)X_{attention} = SelfAttention(Q, \ K, \ V) \tag{eq.4}

Xattention?=SelfAttention(Q, K, V)(eq.4)
3). 残差连接与

Layer NormalizationLayer \ Normalization

Layer Normalization

Xattention=X+Xattention(eq. 5)X_{attention} = X + X_{attention} \tag{eq. 5}

Xattention?=X+Xattention?(eq. 5)

Xattention=LayerNorm(Xattention)(eq. 6)X_{attention} = LayerNorm(X_{attention}) \tag{eq. 6}

Xattention?=LayerNorm(Xattention?)(eq. 6)
4). 下面进行

transformer blocktransformer \ block

transformer block结构图中的第4部分, 也就是

FeedForwardFeedForward

FeedForward, 其实就是两层线性映射并用激活函数激活, 比如说

ReLUReLU

ReLU:

Xhidden=Activate(Linear(Linear(Xattention)))(eq. 7)X_{hidden} = Activate(Linear(Linear(X_{attention}))) \tag{eq. 7}

Xhidden?=Activate(Linear(Linear(Xattention?)))(eq. 7)
5). 重复3).:

Xhidden=Xattention+XhiddenX_{hidden} = X_{attention} + X_{hidden}

Xhidden?=Xattention?+Xhidden?

Xhidden=LayerNorm(Xhidden)X_{hidden} = LayerNorm(X_{hidden})

Xhidden?=LayerNorm(Xhidden?)

XhiddenRbatch size ? seq. len. ? embed. dim.X_{hidden} \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}

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