天天看点

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

目录

  • 一、 transformer 直观认识
  • 二、positional encoding 位置嵌入(或位置编码)
  • 三、self attention mechanism 自注意力机制
  • 四、Layer Normalization 和残差连接
  • 五、transformer encoder 整体结构

目前LSTM起码在自然语言处理领域已经过时了, 在Stanford阅读理解数据集 (SQuAD2.0) 榜单里, 机器的成绩已经超人类表现, 这很大程度要归功于transformer的BERT预训练模型。

transformer是谷歌大脑在2017年底发表的论文attention is all you need中所提出的seq2seq模型。 现在已经取得了大范围的应用和扩展, 而BERT就是从transformer中衍生出来的预训练语言模型。

一、 transformer 直观认识

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) (positional encoding)来理解语言的顺序, 使用自注意力机制和全连接层来进行计算。

transformer模型主要分为两大部分, 分别是编码器和解码器, 编码器负责把自然语言序列映射成为隐藏层(下图中第2步用九宫格比喻的部分), 含有自然语言序列的数学表达。然后解码器把隐藏层再映射为自然语言序列, 从而使我们可以解决各种问题, 如情感分类, 命名实体识别, 语义关系抽取, 摘要生成, 机器翻译等等, 下面简单介绍一下下图的每一步都做了什么:

  1. 输入自然语言序列到编码器: Why do we work?(为什么要工作);
  2. 编码器输出的隐藏层, 再输入到解码器;
  3. 输入 < s t a r t > <start> <start>(起始)符号到解码器;
  4. 得到第一个字"为";
  5. 将得到的第一个字"为"落下来再输入到解码器;
  6. 得到第二个字"什";
  7. 将得到的第二字再落下来, 直到解码器输出 < e n d > <end> <end>(终止符), 即序列生成完成。
    【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

本文内容限于编码器部分, 即把自然语言序列映射为隐藏层的数学表达的过程, 因为理解了编码器中的结构, 理解解码器就非常简单了。最重要的是BERT预训练模型只用到了编码器的部分, 也就是先用编码器训练一个语言模型, 然后再把它适配给其他五花八门的下游任务。

Transformer Block结构图, 注意: 为方便查看, 下面的内容分别对应着上图第1, 2, 3, 4个方框的序号:

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

二、positional encoding 位置嵌入(或位置编码)

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

现在定义一个位置嵌入的概念, 也就是 p o s i t i o n a l   e n c o d i n g positional \ encoding positional encoding, 位置嵌入的维度为 [ 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] [max sequence length, embedding dimension], 嵌入的维度同词向量的维度, m a x   s e q u e n c e   l e n g t h max \ sequence \ length max sequence length属于超参数, 指的是限定的最大单个句长。

注意, 一般以字为单位训练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] [vocab size, embedding dimension], v o c a b   s i z e vocab \ size vocab size为总共的字库数量, e m b e d d i n g   d i m e n s i o n embedding \ dimension embedding dimension为字向量的维度, 也是每个字的数学表达。

在这里论文中使用了 s i n e sine sine和 c o s i n e cosine cosine函数的线性变换来提供给模型位置信息:

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

上式中 p o s pos pos指的是句中字的位置, 取值范围是 [ 0 ,   m a x   s e q u e n c e   l e n g t h ) [0, \ max \ sequence \ length) [0, max sequence length), 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, embedding dimension), 上面有 s i n sin sin和 c o s cos cos一组公式, 也就是对应着 e m b e d d i n g   d i m e n s i o n embedding \ dimension embedding dimension维度的一组奇数和偶数的序号的维度, 例如 0 , 1 0, 1 0,1一组, 2 , 3 2, 3 2,3一组, 分别用上面的 s i n sin sin和 c o s cos cos函数做处理, 从而产生不同的周期性变化, 而位置嵌入在 e m b e d d i n g   d i m e n s i o n embedding \ dimension embedding dimension维度上随着维度序号增大, 周期变化会越来越慢, 而产生一种包含位置信息的纹理, 就像论文原文中第六页讲的, 位置嵌入函数的周期从 2 π 2 \pi 2π到 10000 ∗ 2 π 10000 * 2 \pi 10000∗2π变化, 而每一个位置在 e m b e d d i n g   d i m e n s i o n embedding \ dimension embedding dimension维度上都会得到不同周期的 s i n sin sin和 c o s cos cos函数的取值组合, 从而产生独一的纹理位置信息, 模型从而学到位置之间的依赖关系和自然语言的时序特性。

下面画一下位置嵌入, 可见纵向观察, 随着 e m b e d d i n g   d i m e n s i o n embedding \ dimension embedding dimension增大, 位置嵌入函数呈现不同的周期变化。

输入:

# 导入依赖库
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
    # embed_dim: 字嵌入的维度
    # max_seq_len: 最大的序列长度
    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[1:, 0::2] = np.sin(positional_encoding[1:, 0::2])  # dim 2i 偶数
    positional_encoding[1:, 1::2] = np.cos(positional_encoding[1:, 1::2])  # dim 2i+1 奇数
    # 归一化, 用位置嵌入的每一行除以它的模长
    # denominator = np.sqrt(np.sum(position_enc**2, axis=1, keepdims=True))
    # position_enc = position_enc / (denominator + 1e-8)
    return 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")
           

输出:

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

输入:

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")
           

输出:

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

三、self attention mechanism 自注意力机制

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构
【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

Attention Mask

【BERT】Transformer 理论进阶一、 transformer 直观认识二、positional encoding 位置嵌入(或位置编码)三、self attention mechanism 自注意力机制四、Layer Normalization 和残差连接五、transformer encoder 整体结构

注意, 在上面 s e l f   a t t e n t i o n self \ attention self attention的计算过程中, 通常使用 m i n i   b a t c h mini \ batch mini batch来计算, 也就是一次计算多句话, 也就是 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] [batch size, sequence length], s e q u e n c e   l e n g t h sequence \ length sequence length是句长, 而一个 m i n i   b a t c h mini \ batch mini batch是由多个不等长的句子组成的, 就需要按照这个 m i n i   b a t c h mini \ batch mini batch中最大的句长对剩余的句子进行补齐长度, 一般用 0 0 0来进行填充, 这个过程叫做 p a d d i n g padding padding。

但这时在进行 s o f t m a x softmax softmax的时候就会产生问题, 回顾 s o f t m a x softmax softmax函数: σ ( 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=1K​ezj​ezi​​ e 0 e^0 e0是1, 是有值的, 这样的话 s o f t m a x softmax softmax中被 p a d d i n g padding padding的部分就参与了运算, 就等于是让无效的部分参与了运算, 会产生很大隐患, 这时就需要做一个 m a s k mask mask让这些无效区域不参与运算, 一般给无效区域加一个很大的负数的偏置, 也就是:

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} zillegal​=zillegal​+biasillegal​

b i a s i l l e g a l → − ∞ bias_{illegal} \to -\infty biasillegal​→−∞

e z i l l e g a l → 0 e^{z_{illegal}} \to 0 ezillegal​→0

经过上式的 m a s k i n g masking masking使无效区域经过 s o f t m a x softmax softmax计算之后还几乎为 0 0 0, 这样就避免了无效区域参与计算。

四、Layer Normalization 和残差连接

1). 残差连接:

在上一步得到了经过注意力矩阵加权之后的 V V V, 也就是 A t t e n t i o n ( Q ,   K ,   V ) Attention(Q, \ K, \ V) Attention(Q, K, V), 对它进行一下转置, 使其和 X e m b e d d i n g X_{embedding} Xembedding​的维度一致, 也就是 [ 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] [batch size, sequence length, embedding dimension], 然后把他们加起来做残差连接, 直接进行元素相加, 因为他们的维度一致:

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) Xembedding​+Attention(Q, K, V)

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

X + S u b L a y e r ( X ) (eq. 5) X + SubLayer(X) \tag{eq. 5} X+SubLayer(X)(eq. 5)

2). L a y e r N o r m LayerNorm LayerNorm:

L a y e r N o r m a l i z a t i o n Layer Normalization LayerNormalization的作用是把神经网络中隐藏层归一为标准正态分布, 也就是 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​=m1​i=1∑m​xij​

上式中以矩阵的行 ( r o w ) (row) (row)为单位求均值;

σ 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} σj2​=m1​i=1∑m​(xij​−μj​)2

上式中以矩阵的行 ( r o w ) (row) (row)为单位求方差;

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} LayerNorm(x)=α⊙σi2​+ϵ

​xij​−μi​​+β(eq.6)

然后用每一行的每一个元素减去这行的均值, 再除以这行的标准差, 从而得到归一化后的数值, ϵ \epsilon ϵ是为了防止除 0 0 0;

之后引入两个可训练参数 α ,   β \alpha, \ \beta α, β来弥补归一化的过程中损失掉的信息, 注意 ⊙ \odot ⊙表示元素相乘而不是点积, 一般初始化 α \alpha α为全 1 1 1, 而 β \beta β为全 0 0 0。

五、transformer encoder 整体结构

经过上面3个步骤, 已经基本了解了 t r a n s f o r m e r transformer transformer编码器的主要构成部分, 下面用公式把一个 t r a n s f o r m e r   b l o c k transformer \ block transformer block的计算过程整理一下:

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=EmbeddingLookup(X)+PositionalEncoding(eq.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∈Rbatch size ∗ seq. len. ∗ embed. dim.

2). 自注意力机制:

Q = L i n e a r ( X ) = X W Q Q = Linear(X) = XW_{Q} Q=Linear(X)=XWQ​

K = L i n e a r ( X ) = X W K (eq.3) K = Linear(X) = XW_{K} \tag{eq.3} K=Linear(X)=XWK​(eq.3)

V = L i n e a r ( X ) = X W V V = Linear(X) = XW_{V} V=Linear(X)=XWV​

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} Xattention​=SelfAttention(Q, K, V)(eq.4)

3). 残差连接与 L a y e r   N o r m a l i z a t i o n Layer \ Normalization Layer Normalization

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} Xattention​=X+Xattention​(eq. 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} Xattention​=LayerNorm(Xattention​)(eq. 6)

4). 下面进行 t r a n s f o r m e r   b l o c k transformer \ block transformer block结构图中的第4部分, 也就是 F e e d F o r w a r d FeedForward FeedForward, 其实就是两层线性映射并用激活函数激活, 比如说 R e L U ReLU ReLU:

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} Xhidden​=Activate(Linear(Linear(Xattention​)))(eq. 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} Xhidden​=Xattention​+Xhidden​

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}) Xhidden​=LayerNorm(Xhidden​)

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.} Xhidden​∈Rbatch size ∗ seq. len. ∗ embed. dim.

小结:

到现在位置已经学习完了transformer的编码器的部分, 了解到了transformer是怎样获得自然语言的位置信息的, 注意力机制是怎样的。其实举个语言情感分类的例子, 经过自注意力机制, 一句话中的每个字都含有这句话中其他所有字的信息, 那么可不可以添加一个空白字符到句子最前面, 然后让句子中的所有信息向这个空白字符汇总, 然后再映射成想要分的类别呢? 这就是BERT。

在BERT的预训练中, 给每句话的句头加一个特殊字符[CLS], 然后句末再加一个特殊字符[SEP], 之后模型预训练完毕之后, 就可以用句头的特殊字符的 h i d d e n   s t a t e hidden \ state hidden state完成一些分类任务了。

继续阅读