Transfomer

1. Transformer 模型

参考链接 - ShusenWang - Transformer 模型

transformer 模型完全基于 attention,attention 原本是用在 RNN 上的,这里我们把 RNN 去掉,只保留attention。

  transformer 模型是 Ashish Vaswani 等人在 NIPS 2017 中论文 Attention is all you need 提出。transformer 是一种 Seq2Seq 模型,它有一个 encoder 和一个 decoder,很适合做机器翻译。transformer 不是循环神经网络网络,transformer 没有循环的结构,transformer 只有 attention全连接层

  transformer 的实验效果非常惊人,可以完爆最好的 RNN 加 attention。机器翻译问题已经没有人用 RNN ,业界都用 transformer 加 bert。

思考一个问题,如果把 RNN 去掉,只保留 attention,然后用 attention 来搭一个深度神经网络来代替 RNN,该怎么做?

1.1 Attention for Seq2Seq Model

  这一章从零开始起搭一个基于attention 的神经网络,从前面的 RNN 加 attention 模型入手,剥离 RNN ,保留 attention,然后搭建 attention 层与 self-attention 层。后面章节会把这些层组装起来,搭一个深度的 Seq2Seq 模型,搭出来的模型就是 transformer

Seq2Seq 模型有一个 encoder 和一个 decoder。encoder 的输入是m 个向量 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm encoder 把这些输入的信息压缩到状态向量 h h h 中,最后一个状态 h m h_m hm 是对所有输入的概括。

decoder 是一个文本生成器,依次生成状态 s s s ,然后根据状态 s s s 生成单词,把新生成的单词作为下一个输入 x ′ \mathbf{x}^{\prime} x 。如果有 attention 的话,还需要计算 Context vector c c c ,每算出一个状态 s s s ,就算一个 c c c

在这里插入图片描述
Context vector c c c 的计算如下,首先把 decoder 当前状态 s j s_j sj ,与 encoder 所有 m 个状态 h 1 h_1 h1 h m h_m hm 做对比。用 align 函数计算它们的相关性,把算出的数字 α i j \alpha_{ij} αij 作为权重,这里的 i i i 是 encoder 状态 h h h 的下标,j是decoder 状态 s s s 的下标。

  每算一个 Context vector c c c ,就要计算出 m 个权重 α \alpha α α 1 j \alpha_{1j} α1j 一直到 α m j \alpha_{mj} αmj ,每个 α \alpha α 对应一个状态 h h h

  • 权重 α \alpha α 的计算,权重是 h i h_i hi s j s_j sj 的函数,把向量 h i h_i hi 乘到矩阵 W K \mathbf{W}_{K} WK 上,得到向量 k : i \mathbf{k}_{:i} k:i ;把向量 s j s_j sj 乘到矩阵 W Q \mathbf{W}_{Q} WQ 上,得到向量 q : j \mathbf{q}_{:j} q:j 。这里的矩阵 W K \mathbf{W}_{K} WK W Q \mathbf{W}_{Q} WQalign 函数的参数,参数需要从训练数据里学习。

  • 我们要把 s j s_j sj 这一个向量,与 encoder 所有 m 个状态向量 h h h 做对比,有 m m m h i h_i hi 向量,所以有 m m m k : i \mathbf{k}_{:i} k:i 向量。用这些 k : i \mathbf{k}_{:i} k:i 向量组成 K \mathbf{K} K 矩阵,每个 k : i \mathbf{k}_{:i} k:i 向量都是矩阵 K \mathbf{K} K 的列。

  • 计算矩阵 K T \mathbf{K}^{T} KT 与向量 q : j \mathbf{q}_{:j} q:j 的乘积,结果是个 m 维的向量,然后用 Softmax 函数,输出一个 m m m 维的向量 α : j \alpha_{:j} α:j 。把 α : j \alpha_{:j} α:jm 个元素记为 α 1 j \alpha_{1j} α1j α 2 j \alpha_{2j} α2j 一直到 α m j \alpha_{mj} αmj,这些元素全都介于 0~1 之间,而且它们相加等于1,这样我们就得到 m m m 个权重。

在这里插入图片描述
  刚才把 decoder 状态 s j s_j sj 还有 encoder 状态 h i h_i hi , 分别做线性变换,得到向量 q : j \mathbf{q}_{:j} q:j k : i \mathbf{k}_{:i} k:i ,它们被称为 Query Key Query 的意思是用来匹配 Key 值, Key 的意思是用来被 Query 匹配。

  拿一个 Query 向量 q : j \mathbf{q}_{:j} q:j 去对比所有m Key 向量,算出 m m m 个权重 α : j \alpha_{:j} α:j,这 m m m α \alpha α 就说明 Query 和每个 Key 的匹配程度,匹配程度越高,权重 α \alpha α 就越大。

在这里插入图片描述  除此之外,还要算 Value 向量 v : i \mathbf{v}_{:i} v:i ,把 h i h_i hi 乘到矩阵 W V \mathbf{W}_{V} WV 上,得到向量 v : i \mathbf{v}_{:i} v:i ,矩阵 W V \mathbf{W}_{V} WV 也是个参数。attention 中一共有三个参数矩阵 W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK 以及 W V \mathbf{W}_{V} WV ,它们都要从训练数据中学习。


W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV 计算过程实例:

  上面说明了 Query Key Value 三种向量是怎么计算出来的,下面回过头来看这个例子。我们先把 decoder 当前状态 s j s_j sj 映射到 Query 向量。具体是这么做的,拿参数矩阵 W Q \mathbf{W}_{Q} WQ 与 decoder 状态 s j s_j sj 相乘,得到 Query 向量 q : j \mathbf{q}_{:j} q:j

  然后把 encoder 所有 m 个状态, h 1 h_1 h1 h m h_m hm 映射到 m m m Key 向量。具体这么做,拿参数矩阵 W K \mathbf{W}_{K} WK 与 第 i i i 的状态 h i h_i hi 相乘得到 Key 向量 k : i \mathbf{k}_{:i} k:i 。对 h 1 h_1 h1 h m h_m hm ,所有 m 个状态向量都做这种变换,得到 m m m Key 向量,把这 m m m Key 向量表示为矩阵 K \mathbf{K} K ,向量 k : i \mathbf{k}_{:i} k:i 是矩阵 K \mathbf{K} K 的第 i i i 列。

  用矩阵 K \mathbf{K} K 与向量 q : j \mathbf{q}_{:j} q:j 计算出 m 维的权重向量 α : j \alpha_{:j} α:j。向量 α : j \alpha_{:j} α:j 的m 个元素分别是 α 1 j \alpha_{1j} α1j α 2 j \alpha_{2j} α2j 一直到 α m j \alpha_{mj} αmj,每一个元素对应一个 h h h 向量。

  接下来计算 Value 向量 v : i \mathbf{v}_{:i} v:i ,那 encoder 的第 i i i 个状态向量 h i h_i hi 与参数矩阵 W V \mathbf{W}_{V} WV 相乘,得到一个 Value 向量 v : i \mathbf{v}_{:i} v:i 。把所有 m 个状态 h 1 h_1 h1 h m h_m hm 都做这种变换,得到 mValue 向量叫做 v : 1 \mathbf{v}_{:1} v:1 v : 2 \mathbf{v}_{:2} v:2 v : 3 \mathbf{v}_{:3} v:3 一直到 v : m \mathbf{v}_{:m} v:m ,每个 v : i \mathbf{v}_{:i} v:i 对应一个 encoder 状态 h i h_i hi

在这里插入图片描述
  现在我们有了 m 个权重值 α \alpha α,以及 m m mValue 向量 v \mathbf{v} v 。 现在用 α \alpha α 做权重,把这 mValue 向量 v \mathbf{v} v 做加权平均,把结果作为新的 Context vector c j c_{j} cj Context vector c j c_{j} cj = = = α 1 j \alpha_{1j} α1j × \times × v : 1 \mathbf{v}_{:1} v:1 + ⋯ + +\cdots+ ++ α m j \alpha_{mj} αmj × \times × v : m \mathbf{v}_{:m} v:m ,这种计算权重 α \alpha αContext vector c j c_{j} cj 的方法就是 transformer 里面用的。

1.2 Attention without RNN

attention 原本是用在 RNN 上的,思考如何才能剥离 RNN ,只保留 attention ?这样可以得到一个attention 层与 self-attention 层,transformer 就是由 attention 层与 self-attention 层组成的。

1.2.1 Attention Layer

  我们先设计一个 attention 层,用于 Seq2Seq 模型。我们移除了 RNN 现在开始搭建 attention,考虑 Seq2Seq 模型,它有一个 encoder 和一个 decoder,encoder 的输入向量是 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm decoder 的输入是 x 1 ′ \mathbf{x}_1^{\prime} x1 x t ′ \mathbf{x}_t^{\prime} xt

  举个例子,把英语翻译成德语,英语句子里有 m 个单词变成了词向量 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm decoder 一次生成德语单词,把当前生成的德语单词作为下一轮的输入,现在已经生成了 t 个德语单词,把它们作为输入来生成下一个单词,新生成的德语单词会作为第 t + 1 t+1 t+1 个输入。

  我们不用 RNN ,只用 attention。首先 encoder 的输入 x 1 \mathbf{x}_1 x1 x 2 \mathbf{x}_2 x2 x m \mathbf{x}_m xm 来计算 Key Value 向量。用矩阵 W K \mathbf{W}_{K} WK 把向量 x i \mathbf{x}_i xi 变成 k : i \mathbf{k}_{:i} k:i ,用矩阵 W V \mathbf{W}_{V} WV 把向量 x i \mathbf{x}_i xi 变成 v : i \mathbf{v}_{:i} v:i 。于是 x 1 \mathbf{x}_1 x1 就被映射成了向量 k : 1 \mathbf{k}_{:1} k:1 v : 1 \mathbf{v}_{:1} v:1 x 2 \mathbf{x}_2 x2 就被映射成了 k : 2 \mathbf{k}_{:2} k:2 v : 2 \mathbf{v}_{:2} v:2 …… 同样道理,我们可以得到 m m m k \mathbf{k} k 向量和 v \mathbf{v} v 向量。

在这里插入图片描述
  然后把 decoder 的输入 x 1 ′ \mathbf{x}_1^{\prime} x1 x 2 ′ \mathbf{x}_2^{\prime} x2 一直到 x t ′ \mathbf{x}_t^{\prime} xt 做线性变换,用矩阵 W Q \mathbf{W}_{Q} WQ x j ′ \mathbf{x}_j^{\prime} xj 映射到 Query 向量 q : j \mathbf{q}_{:j} q:j 。decoder 有 t 个 输入向量,所以得到 t t t Query 向量 q : 1 \mathbf{q}_{:1} q:1 q : 2 \mathbf{q}_{:2} q:2 q : 3 \mathbf{q}_{:3} q:3 一直到 q : t \mathbf{q}_{:t} q:t

  注意,一共有 3 个参数矩阵,encoder 中有两个,分别是矩阵 W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV ,用来计算 Key Value 。decoder 中有一个 W Q \mathbf{W}_{Q} WQ ,用来计算 Query


现在开始计算权重 α \alpha α ,拿第一个 Query 向量 q : 1 \mathbf{q}_{:1} q:1 与所有 m Key 向量做对比,通过比较 q : 1 \mathbf{q}_{:1} q:1 k : 1 \mathbf{k}_{:1} k:1 k : 2 \mathbf{k}_{:2} k:2 k : 3 \mathbf{k}_{:3} k:3 一直到 k : m \mathbf{k}_{:m} k:m m m m 个向量的相关性,算出 m m m 个权重值,记为这个 m m m 维 的向量 α : 1 \alpha_{:1} α:1

在这里插入图片描述
  具体是用这个公式计算出来的,把所有 m m m Key 向量表示为矩阵 K \mathbf{K} K K T \mathbf{K}^T KT 乘以向量 q : 1 \mathbf{q}_{:1} q:1 ,再做 Softmax 变换,得到这个 m m m 维 的向量 α : 1 \alpha_{:1} α:1

然后计算 Context vector c : 1 \mathbf{c}_{:1} c:1 ,需要用到权重向量 α : 1 \alpha_{:1} α:1 与所有 m Value 向量 v : 1 \mathbf{v}_{:1} v:1 v : 2 \mathbf{v}_{:2} v:2 v : 3 \mathbf{v}_{:3} v:3 一直到 v : m \mathbf{v}_{:m} v:m c : 1 \mathbf{c}_{:1} c:1 是指 m m m v \mathbf{v} v 向量的加权平均,权重就是向量 α : 1 \alpha_{:1} α:1 m m m 个元素分别是 α 11 \alpha_{11} α11 α 21 \alpha_{21} α21一直到 α m 1 \alpha_{m1} αm1。把向量 v : 1 \mathbf{v}_{:1} v:1 v : m \mathbf{v}_{:m} v:m 作为矩阵 V \mathbf{V} V 的列,那么这个加权平均就可以写成矩阵 V \mathbf{V} V 乘以向量 α : 1 \alpha_{:1} α:1

在这里插入图片描述
现在计算权重向量 α : 2 \alpha_{:2} α:2, 要用到第二个 Query 向量 q : 2 \mathbf{q}_{:2} q:2 ,以及所有 m 个k 向量 k : 1 \mathbf{k}_{:1} k:1 k : 2 \mathbf{k}_{:2} k:2 , k : 3 \mathbf{k}_{:3} k:3 一直到 k : m \mathbf{k}_{:m} k:m

在这里插入图片描述
然后计算Context vector c : 2 \mathbf{c}_{:2} c:2 , 要用到权重 α : 2 \alpha_{:2} α:2 以及所有 m Value 向量 v : 1 \mathbf{v}_{:1} v:1 v : 2 \mathbf{v}_{:2} v:2 v : 3 \mathbf{v}_{:3} v:3 一直到 v : m \mathbf{v}_{:m} v:m c : 2 \mathbf{c}_{:2} c:2 m m m v \mathbf{v} v 向量的加权平均,权重就是向量 α : 2 \alpha_{:2} α:2 m m m 个元素。

在这里插入图片描述
用类似的方法计算出所有的 Context vector c \mathbf{c} c 。每个 c \mathbf{c} c 对应一个 x ′ \mathbf{x}^{\prime} x c : 1 \mathbf{c}_{:1} c:1 对应 x 1 ′ \mathbf{x}_1^{\prime} x1 c : 2 \mathbf{c}_{:2} c:2 对应 x 2 ′ \mathbf{x}_2^{\prime} x2 ,以此类推,decoder 的输入一共有 t x ′ \mathbf{x}^{\prime} x 向量,所以计算出 t t t c \mathbf{c} c 向量,这 t t tContext vector c \mathbf{c} c 就是最终的输出。可以用矩阵 C \mathbf{C} C 来表示这些向量,这些 c \mathbf{c} c 向量 c : 1 \mathbf{c}_{:1} c:1 c : 2 \mathbf{c}_{:2} c:2 c : 3 \mathbf{c}_{:3} c:3 一直到 c : t \mathbf{c}_{:t} c:t 都是矩阵 C \mathbf{C} C 的列。

在这里插入图片描述
  上图中计算 c : j \mathbf{c}_{:j} c:j 公式表明,想要计算一个向量 c : j \mathbf{c}_{:j} c:j ,要用到所有的 v \mathbf{v} v 向量,(即 V \mathbf{V} V );所有的 k \mathbf{k} k 向量,(即 K \mathbf{K} K );以及一个 q : j \mathbf{q}_{:j} q:j 向量。比如向量 c : 2 \mathbf{c}_{:2} c:2 依赖于 q : 2 \mathbf{q}_{:2} q:2 以及所有的 v \mathbf{v} v 向量 和 k \mathbf{k} k 向量,所以 c : 2 \mathbf{c}_{:2} c:2 依赖于 decoder 一个输入 x 2 ′ \mathbf{x}_2^{\prime} x2 以及 encoder 全部输入 x 1 \mathbf{x}_1 x1 x 2 \mathbf{x}_2 x2 x 3 \mathbf{x}_3 x3 一直到 x m \mathbf{x}_m xm

  • 举个例子,假如我们想把英语翻译成德语,可以把 m 个英文单词输入encoder,然后用 decoder 来依次产生德语单词。第二个Context vector c : 2 \mathbf{c}_{:2} c:2 可以通过 Key Value 看到所有 m 个英文单词, c : 2 \mathbf{c}_{:2} c:2 还能看到 x 2 ′ \mathbf{x}_2^{\prime} x2 ,也就是当前输入的德语单词。

  • 我们可以把 c : 2 \mathbf{c}_{:2} c:2 作为特征向量,输入 Softmax 分类器,计算出一个概率分布 p 2 \mathbf{p}_{2} p2 ,然后通过 p 2 \mathbf{p}_{2} p2 抽样得到第 3 个德语单词,编码成 x 3 ′ \mathbf{x}_3^{\prime} x3 作为下一轮的输入。

在这里插入图片描述
  不难看出,用 Attention Layer 做机器翻译跟用 RNN 非常类似,用 RNN 会把状态 h h h 作为特征向量,用 attention 的话会把Context vector c \mathbf{c} c 作为特征向量。不论是用 attention ,还是用 RNN 来搭一个 Seq2Seq 模型,输入与输出的大小都是一样的,所以显然可以用 attention layer 来代替 RNN。Attention layer 的好处是 不会遗忘

  • 向量 c : 2 \mathbf{c}_{:2} c:2 是直接用所有 m 个英文词向量 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm 算出来的,所以 c : 2 \mathbf{c}_{:2} c:2 知道整句英语。

  把 Attention Layer 记为函数 Attn, 输入是矩阵 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X X \mathbf{X} X 的列是 encoder m m m 个输入向量 x 1 \mathbf{x}_1 x1 x 2 \mathbf{x}_2 x2 一直到 x m \mathbf{x}_m xm X ′ \mathbf{X}^{\prime} X 的列是 decoder t t t 个输入向量 x 1 ′ \mathbf{x}_1^{\prime} x1 x 2 ′ \mathbf{x}_2^{\prime} x2 一直到 x t ′ \mathbf{x}_t^{\prime} xt Attention 层有 3 个参数矩阵, W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV ,它们要靠训练数据来学习。attention 层的输出是矩阵 C \mathbf{C} C ,它的列向量是 c : 1 \mathbf{c}_{:1} c:1 c : 2 \mathbf{c}_{:2} c:2 c : 3 \mathbf{c}_{:3} c:3 一直到 c : t \mathbf{c}_{:t} c:t t t t 个向量。

在这里插入图片描述
  可以这样表示 attention 层,有两个输入序列 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X ,有一个输出序列 C \mathbf{C} C ,每个 c \mathbf{c} c 向量对应一个 x ′ \mathbf{x}^{\prime} x 向量。

1.3 Self-Attention without RNN

  前面我们研究了 Seq2Seq 模型,我们删掉了 RNN,并且搭建了一个 attention 层,可以用 attention 层来做机器翻译。接下来搭建一个 Self-Attention 层,原理完全一样,仍可以用 self-attention 来取代 RNN。

  前面介绍搭的 Attention Layer,attention 层用于 Seq2Seq,它有两个输入序列,比如左边是英语,右边是翻译出来的德语。我们用函数 Attn 来表示 attention 层,函数有 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X 两个输入。

Self-attention 层不是 Seq2Seq,它 只有一个输入序列 ,这就像是普通的 RNN 一样。self-attention 层也可以用 Attn 函数表示,这个函数跟前面的 Attn 完全一样,区别在于函数的输入,attention 层的输入是 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X 两个矩阵。

  用 self-attention 层,Attn 函数的两个输入都是 X \mathbf{X} X ,输出序列是 c : 1 \mathbf{c}_{:1} c:1 c : m \mathbf{c}_{:m} c:m ,输出序列的长度跟输入是一样的,都是 m 。每个 c \mathbf{c} c 向量都对应一个 x \mathbf{x} x 向量,但是要 注意 c : i \mathbf{c}_{:i} c:i 并非只依赖于 x i \mathbf{x}_i xi ,而是依赖于所有 m x \mathbf{x} x 向量。改变任何一个 x \mathbf{x} x c : i \mathbf{c}_{:i} c:i 就会发生变化。

在这里插入图片描述
Self-Attention Layer 的原理跟前面的 Attention Layer 完全一样,只是输入不一样。Self-Attention Layer 只有一个输入序列 x 1 \mathbf{x}_1 x1 x 2 \mathbf{x}_2 x2 一直到 x m \mathbf{x}_m xm

  1️⃣ 第一步是做三种变换,把 x i \mathbf{x}_i xi 映射到 q : i \mathbf{q}_{:i} q:i k : i \mathbf{k}_{:i} k:i v : i \mathbf{v}_{:i} v:i 三个向量,参数矩阵还是 W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV。线性变换之后, x 1 \mathbf{x}_1 x1 被映射到 q : 1 \mathbf{q}_{:1} q:1 k : 1 \mathbf{k}_{:1} k:1 v : 1 \mathbf{v}_{:1} v:1 x 2 \mathbf{x}_2 x2 被映射到 q : 2 \mathbf{q}_{:2} q:2 k : 2 \mathbf{k}_{:2} k:2 v : 2 \mathbf{v}_{:2} v:2 。每个 x \mathbf{x} x 向量都会被映射成 q \mathbf{q} q k \mathbf{k} k v \mathbf{v} v 三个向量。

在这里插入图片描述
  2️⃣ 然后是计算权重向量 α \alpha α,公式还是一样。矩阵 K T \mathbf{K}^T KT 乘以 q : j \mathbf{q}_{:j} q:j 向量,然后做 Softmax,得到 m 维向量 α : j \alpha_{:j} α:j。下面这张图, α : 1 \alpha_{:1} α:1 依赖于 q : 1 \mathbf{q}_{:1} q:1 ,以及所有 k \mathbf{k} k 向量 k : 1 \mathbf{k}_{:1} k:1 k : 2 \mathbf{k}_{:2} k:2 k : 3 \mathbf{k}_{:3} k:3 一直到 k : m \mathbf{k}_{:m} k:m

在这里插入图片描述
  权重向量 α : 2 \alpha_{:2} α:2 依赖于 q : 2 \mathbf{q}_{:2} q:2 ,以及所有 k \mathbf{k} k 向量, k : 1 \mathbf{k}_{:1} k:1 k : 2 \mathbf{k}_{:2} k:2 k : 3 \mathbf{k}_{:3} k:3 一直到 k : m \mathbf{k}_{:m} k:m 。用同样的公式计算出所有权重向量 α \alpha α,一共有 m α \alpha α 向量,每个向量都是 m m m 维 的。

在这里插入图片描述
  3️⃣ 现在可以开始计算 Context vector c \mathbf{c} c c : 1 \mathbf{c}_{:1} c:1 是所有m v \mathbf{v} v 向量的加权平均,权重都是 α \alpha α。下面图中显示 c : 1 \mathbf{c}_{:1} c:1 依赖于权重向量 α : 1 \alpha_{:1} α:1,以及所有 m m m v \mathbf{v} v 向量 v : 1 \mathbf{v}_{:1} v:1 v : 2 \mathbf{v}_{:2} v:2 v : 3 \mathbf{v}_{:3} v:3 一直到 v : m \mathbf{v}_{:m} v:m

在这里插入图片描述
  同样的方法计算 c : 2 \mathbf{c}_{:2} c:2 ,把所有的 m v \mathbf{v} v 向量 v : 1 \mathbf{v}_{:1} v:1 v : 2 \mathbf{v}_{:2} v:2 v : 3 \mathbf{v}_{:3} v:3 一直到 v : m \mathbf{v}_{:m} v:m 做加权平均,权重是向量 α : 2 \alpha_{:2} α:2 m m m 个元素。

在这里插入图片描述
  做同样的操作,计算出所有m c \mathbf{c} c 向量,得到 c : 1 \mathbf{c}_{:1} c:1 c : 2 \mathbf{c}_{:2} c:2 c : 3 \mathbf{c}_{:3} c:3 一直到 c : m \mathbf{c}_{:m} c:m ,这 m m m c \mathbf{c} c 向量就是 self-attention 层的输出。

j j j 个输出 c : j \mathbf{c}_{:j} c:j 是这样算出来的,它依赖于矩阵 V \mathbf{V} V 、 矩阵 K \mathbf{K} K 以及向量 q : j \mathbf{q}_{:j} q:j 。因为 c : j \mathbf{c}_{:j} c:j 依赖于所有的 k \mathbf{k} k 和所有的 v \mathbf{v} v ,所以 c : j \mathbf{c}_{:j} c:j 依赖于所有 m x \mathbf{x} x 向量, x 1 \mathbf{x}_1 x1 一直到 x m \mathbf{x}_m xm

在这里插入图片描述
  上图里面,每个输入 x i \mathbf{x}_i xi 的位置上都对应一个输出 c : i \mathbf{c}_{:i} c:i 注意 c : i \mathbf{c}_{:i} c:i 并非只依赖于 x i \mathbf{x}_i xi ,而是依赖于所有的 x \mathbf{x} x 。改变任何一个 x \mathbf{x} x ,输出的 c : i \mathbf{c}_{:i} c:i 都会发生变化。

Self-attention Layer 的构造,输入是一个序列,从 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm 这个 m 这个向量,这一层有 3 个参数矩阵 W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV ,这 3 个矩阵可以把每个 x \mathbf{x} x 映射到 q \mathbf{q} q k \mathbf{k} k v \mathbf{v} v 三个向量。输出也是一个序列,从 c : 1 \mathbf{c}_{:1} c:1 c : m \mathbf{c}_{:m} c:m m 个向量,每个 x \mathbf{x} x 的位置上都有一个对应的 c \mathbf{c} c

attentionself-attention 都用 Attn 这个函数来表示,这个函数有两个输入矩阵,attention 层的两个输入是 不同 的矩阵 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X self-attention 层的两个输入是 相同 的矩阵,都是 X \mathbf{X} X

在这里插入图片描述
总结
attention 最早用于改进 Seq2Seq 模型;后来发现 attention 并不局限于 Seq2Seq 模型,而是可以用在所有的 RNN 上。如果只有一个 RNN 网络,那么 attention 就叫做 self-attention。后来有人发现根本不需要 RNN ,直接单独用 attention,反而效果更好,就是《attention is all you need 》这篇论文提出的 transformer 模型。

1.4 Multi-Head Attention

  下面用构造的 attention layer 和 self-attention layer 来搭建一个深度神经网络。首先使用 attention 来组建 multi-head attention。

self-attention 层输入是一个序列 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm ,self-attention 层有 3 个参数矩阵 W Q \mathbf{W}_{Q} WQ W K \mathbf{W}_{K} WK W V \mathbf{W}_{V} WV ,输出是一个序列 c : 1 \mathbf{c}_{:1} c:1 c : m \mathbf{c}_{:m} c:m 。这样的 self-attention 层被称为 single-head self-attention

multi-head self-attention 是用 l l l 个 single-head self-attention 组成的,它们各自有各自的参数,不共享参数。每个 single-head self-attention 有 3 个参数矩阵,所以 multi-head self-attention 一共有 3 l 3l 3l 个参数矩阵。

  所有 single-head self-attention 都有相同的输入,输入都是 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm 序列。但是他们的参数矩阵各不相同,所以输出的 c \mathbf{c} c 序列也各不相同。把 l l l 个single-head self-attention 输出的序列做 concatnation 堆叠起来,作为 multi-head self-attention 的最终输出,堆叠起来的 c \mathbf{c} c 向量变得更高。如果每个单头的输出都是 d × m d\times m d×m 的矩阵,那么多头的输出就是 l d × m ld\times m ld×m 的矩阵。

在这里插入图片描述
  上面用 l l l 个单头 self-attention 构造出 多头self-attention。同样可以用 l l l 个 single-head attention 构造multi-head attention 。所有单头 attention 的输入都是这两个序列, x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm 以及 x 1 ′ \mathbf{x}_1^{\prime} x1 x m ′ \mathbf{x}_m^{\prime} xm ,每个单头 attention 都有各自的参数矩阵。它们不共享参数,每个单头都有自己的输出序列 c \mathbf{c} c ,把单头输出的 c \mathbf{c} c 给堆叠起来,就是多头的输出。

在这里插入图片描述

1.4.1 Stacked Self-Attention Layers

  已经构造出了 multi-head self-attentionmulti-head attention,接下来要用这两种层搭建一个深度神经网络。

  首先用 multi-head self-attention全连接层 来搭建一个 encoder 的网络。多头 self-attention 层输入是序列 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm ,输出是序列 c : 1 \mathbf{c}_{:1} c:1 c : m \mathbf{c}_{:m} c:m 。然后搭一个全连接层,把向量 c : 1 \mathbf{c}_{:1} c:1 作为输入,全连接层把 c : 1 \mathbf{c}_{:1} c:1 乘到参数矩阵 W U \mathbf{W}_{U} WU上,然后再用 ReLU 或其他激活函数得到输出向量 u : 1 \mathbf{u}_{:1} u:1

  把同一个全连接层用到 c : 2 \mathbf{c}_{:2} c:2 上,得到输出 u : 2 \mathbf{u}_{:2} u:2 u : 2 \mathbf{u}_{:2} u:2 的计算方法与 u : 1 \mathbf{u}_{:1} u:1 完全相同。注意 两个全连接层是 完全相同 的,它们的参数矩阵都是 W U \mathbf{W}_{U} WU

  然后把全连接层用到 c : 3 \mathbf{c}_{:3} c:3 上,输出 u : 3 \mathbf{u}_{:3} u:3 。以此类推,得到 m m m 个输出向量 u \mathbf{u} u 。这些全连接层都是完全一样的,有同一个参数矩阵 W U \mathbf{W}_{U} WU

在这里插入图片描述
  刚才搭了两层,一个multi-head self-attention,一个全连接层,输入是 m 个向量 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm ,输出也是 m m m 个向量 u : 1 \mathbf{u}_{:1} u:1 u : m \mathbf{u}_{:m} u:m u : i \mathbf{u}_{:i} u:i 向量在 x i \mathbf{x}_i xi 向量的位置上,但是 u : i \mathbf{u}_{:i} u:i 不仅仅依赖于 x i \mathbf{x}_i xi u : i \mathbf{u}_{:i} u:i 依赖于所有 m m m x \mathbf{x} x 向量。 改变任何一个 x \mathbf{x} x 向量, u : i \mathbf{u}_{:i} u:i 都会发生变化。当然对 u : i \mathbf{u}_{:i} u:i 影响最大的还是 x i \mathbf{x}_i xi

在这里插入图片描述
  可以继续搭更多层,可以再搭一个multi-head attention 层和一个全连接层。想搭多少层都可以,这样就可以搭建出一个深度神经网络,道理跟 多层 RNN 是一样的。

在这里插入图片描述
  现在来搭建 transformer 模型的 encoder 网络。一个 block 有两层,一个multi-head attention 层和一个全连接层。输入是 512 × m 512\times m 512×m 的矩阵,输出也是 512 × m 512\times m 512×m 的矩阵。这里的m 是输入序列 x \mathbf{x} x 的长度,每个 x \mathbf{x} x 向量都是 512 维的。

在这里插入图片描述
  左边是 encoder 网络的结构,输入是 512 × m 512\times m 512×m 的矩阵 X \mathbf{X} X X \mathbf{X} X 的每一列都是 512 维的词向量。右边是先前定义的一个block,它有两层,一个 self-attention 层和一个 Dense 层,它的输出也是 512 × m 512\times m 512×m 的矩阵。输入和输出的大小一样,所以可以用 ResNet 那种 skip-connection,把输入加到输出上。

  然后搭第二个block ,输出还是 512 × m 512\times m 512×m 的矩阵,想搭多少个 block 都可以。transformerencoder 网络一共有 6 个blocks,每个 block 有两层,每个 block 都有自己的参数,blocks 之间不共享参数。最终的输出是 512 × m 512\times m 512×m 的矩阵,输出与输入的大小是一样的。

在这里插入图片描述
  上面通过堆叠 multi-head self-attention 与全连接层搭建出 encoder 网络,encoder 网络有 6 个blocks,每个 block 有两层,现在进一步用到 attention 层,从而搭建 transformer 的 decoder 网络。

  transformer 是一个 Seq2Seq 模型,它有一个 encoder 和一个 decoder,输入是两个序列。如果我们想要把英语翻译成德语。那么 x \mathbf{x} x 序列是英语的词向量, x ′ \mathbf{x}^{\prime} x 是德语的词向量。

  刚才已经搭建的 encoder 网络,有 6 个blocks,每个block 有两层。encoder 的输入和输出都是 512 维的向量,输入序列有 m 个词向量, x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm ,输出序列也有 m 个向量 u : 1 \mathbf{u}_{:1} u:1 u : m \mathbf{u}_{:m} u:m

在这里插入图片描述


  现在我们开始搭建 decoder 网络的一个 block。1️⃣ block 的第一层是个 multi-head self-attention ,输入是序列 x 1 ′ \mathbf{x}_1^{\prime} x1 x t ′ \mathbf{x}_t^{\prime} xt ,输出是序列 c : 1 \mathbf{c}_{:1} c:1 c : t \mathbf{c}_{:t} c:t ,它们全都是 512 维的向量。

在这里插入图片描述

  2️⃣ 第二层是multi-head attention,这一层的输入是两个序列,一个序列是 u : 1 \mathbf{u}_{:1} u:1 u : m \mathbf{u}_{:m} u:m ,它们是 encoder 网络输出;另一个序列是 c : 1 \mathbf{c}_{:1} c:1 c : t \mathbf{c}_{:t} c:t 。多头 attention 层的输出是 z : 1 \mathbf{z}_{:1} z:1 z : t \mathbf{z}_{:t} z:t ,它们也都是 512 维的向量。

在这里插入图片描述
  3️⃣ 最后再搭一个全连接层,输入是 512 维的向量 z : 1 \mathbf{z}_{:1} z:1 ,输出是 512 维的向量 s : 1 \mathbf{s}_{:1} s:1 。全连接层都一样,都是把参数矩阵 W S \mathbf{W}_{S} WS 与输入的 z \mathbf{z} z 向量相乘,然后用 ReLU 激活函数得到向量 s \mathbf{s} s

  把 z : 2 \mathbf{z}_{:2} z:2 作为输入全连接层输出 s : 2 \mathbf{s}_{:2} s:2 。用同样的方法,把所有的 z \mathbf{z} z 向量都映射到 s \mathbf{s} s 向量。

在这里插入图片描述
  我们已经搭建了decoder 网络的一个block,这个block 有三层,分别是 self-attention 层、attention 层以及全连接层。这三层组成 decoder 的一个 block

在这里插入图片描述
  这个 block 需要两个输入序列,两个序列都是 512 维的向量,两个序列的长度分别是 mt。如果是把英语翻译成德语,那么 m m m 是英语句子的长度, t t t 是已经生成德语单词的数量。 这个block 的输出序列长度是 t ,每个向量都是 512 维的。把这个block 简化成右图所示,它有两个输入序列,一个输出序列。

在这里插入图片描述

1.5 Put Everything Together

  现在我们已经有了所有模块,可以把这些模块拼起来,得到最终的 transformer 模型。

  我们已经搭建好了encoder 网络,encoder 网络很简单,依次叠加 6 个blocks,每个 block 有两层,encoder 网络的输入是矩阵 X \mathbf{X} X , 它有 m 列,每列都是 512 维的词向量,输出是矩阵 U \mathbf{U} U ,它跟输入矩阵 X \mathbf{X} X 的大小完全一样,都是 512 × m 512 \times m 512×m

  前面我们已经搭好了 decoder 网络的一个 block,它的输入是两个序列,输出是一个序列。下图(右)中 decoder 网络最底层的一个模块 Block 1 ,左边输入序列是 512 × m 512 \times m 512×m 的矩阵 U \mathbf{U} U ,也就是 encoder 网络的输出,右边的输入序列是 512 × t 512 \times t 512×t 的矩阵 X ′ \mathbf{X}^{\prime} X

在这里插入图片描述
  在英语翻译德语的例子中, X ′ \mathbf{X}^{\prime} X 包含 t 个德语词向量,这个模块输出一个 512 × t 512 \times t 512×t 的矩阵,大小跟右边的输入 X ′ \mathbf{X}^{\prime} X 一样。

  再来一个 block,左边的输入序列还是矩阵 U \mathbf{U} U , 也就是 encoder 网络的输出,右边输入序列是 decoder 上一个模块的输出。第二个block 的输出还是 512 × t 512 \times t 512×t 的矩阵。

  一共搭了 6 个blocks,组成了 decoder 网络,每个block 有 3 层,分别是 self-attention 层、attention 层以及全连接层。每个block 有两个输入序列,一个输出序列。左边输入序列都是矩阵 U \mathbf{U} U ,也就是 encoder 网络的输出,右边输入序列是前一个 block 的输出。

在这里插入图片描述
  左边 encoder 网络和右边 decoder 网络组合起来就是 transformer 模型,最终输出的序列是t个向量,每个向量都是 512 维的。


总结

RNN Seq2Seq 模型有两个输入序列,encoder 的输入序列是 x 1 \mathbf{x}_1 x1 x m \mathbf{x}_m xm decoder 输入序列是 x 1 ′ \mathbf{x}_1^{\prime} x1 x t ′ \mathbf{x}_t^{\prime} xt

transformer 模型也有这两个输入序列,decoder 输出 t 个状态,向量 s : 1 \mathbf{s}_{:1} s:1 s : t \mathbf{s}_{:t} s:t RNN Seq2Seq 模型与 transformer 模型这两者的输入大小完全一样,输出大小也完全一样。

  所以怎么样用 RNN 的,现在就可以怎么样用 transformer ,所有 RNN Seq2Seq 模型能做的,transformer 模型也同样能做。

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

2. BERT 模型

参考链接 - ShusenWang - Transformer 模型

  Bidirectional Encoder Representations from Transformers (BERT) 是用来预训练 transformer 模型,BERT 在 2018 年提出,文章 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 在 ACL 2019 上发表。BERT 的目的,数据训练transformer 模型的 encoder 的网络,从而大幅提高准确率。

  BERT 的基本想法有两个,一个想法是随机遮挡一个或者多个单词,让 encoder 网络根据上下文来预测被遮挡的单词,第二个想法是把两个句子放在一起,让 encoder 网络判断两句话是不是原文里相邻的两句话。BERT 用这两个任务来预训练 transformer 模型中的 encoder 网络。

2.1 Task 1: Predict Masked Words

  transformer 的 encoder 网络,输入是一句话,被分成很多个单词。Embedding 层把每一个单词映射到一个词向量,然后是 encoder 网络,它是由多个blocks 垒起来,每个 block 有两层,分别是 self-attention 层和 全连接层。

the cat set on the mat 这句输入有 6 个单词,Embedding 层把这 6 个单词映射成 6 个词向量 x 1 \mathbf{x}_1 x1 x 6 \mathbf{x}_6 x6 encoder 网络的输入是 6 个词向量,所以最终输出 6 个向量 u : 1 \mathbf{u}_{:1} u:1 u : m \mathbf{u}_{:m} u:m

在这里插入图片描述
BERT 的第一个任务是预测被遮挡的单词,随机遮挡一个或者多个单词,然后问神经网络被遮住的单词是什么?比如下面这个例子里,第二个单词被遮住,于是让神经网络来预测第二个单词。

在这里插入图片描述

具体这么做,把输入的第二个单词替换成 [mask] 符号,[mask] 符号会被embedding 层编码成词向量到 x M \mathbf{x}_{\mathbf{M}} xM 。把 [mask] 位置的输出记作向量 u M \mathbf{u}_{\mathbf{M}} uM 注意 transformer 的 encoder 网络不是一对一映射,而是多对一。

u M \mathbf{u}_{\mathbf{M}} uM 向量不仅依赖于 x M \mathbf{x}_{\mathbf{M}} xM ,而且依赖于所有 x \mathbf{x} x 向量 u M \mathbf{u}_{\mathbf{M}} uM 在mask 位置上,但是 u M \mathbf{u}_{\mathbf{M}} uM 知道整句话的信息。 u M \mathbf{u}_{\mathbf{M}} uM 包含上下文信息,所以可以用 u M \mathbf{u}_{\mathbf{M}} uM 来预测被遮挡的单词。

  把 u M \mathbf{u}_{\mathbf{M}} uM 作为特征向量,输入一个 Softmax 分类器,分类器的输出是一个概率分布 p \mathbf{p} p ,字典里每个单词都有一个概率值,通过概率值就能判断被遮挡单词是什么。

在这里插入图片描述
  这个例子里遮住了 cat 这个单词。训练的时候,希望分类器的输出 p \mathbf{p} p 向量,接近单词 cat 的 one-hot 向量。把 cat 的 one-hot 向量记作 e \mathbf{e} e ,e 是 ground truth。先前得到的向量 p ,是分类器输出的概率分布。我们希望预测接近 ground truth,也就是让 p 接近 e。把 e \mathbf{e} e p \mathbf{p} p CrossEntropy 作为损失函数,用反向传播算出损失函数,关于模型参数的梯度,然后梯度下降来更新模型参数。

在这里插入图片描述
BERT 会随机遮挡单词,把遮住的单词作为标签,BERT 预训练不需要人工标注的数据集,可以用任何书籍或者维基百科作为训练数据,可以自动生成标签。这样一来,训练数据要多少有多少,足以训练出一个非常大的模型。

2.2 Task 2: Predict the Next Sentence

  第一句话是 “ calculus is a branch of math ” ,“微积分是数学的一个分支”。再告诉你第二句话是“ it was developed by newton and leibniz ”,“它是由牛顿和莱布尼兹发展起来的”。现在让你做一个判断,这两句话是否是原文中相邻的两句话?

  很有可能是,因为微积分与牛顿、莱布尼兹的相关性非常大。神经网络可以从海量的训练数据中学出这种相关性。所以神经网络有能力做出正确的判断。

  假如第一句话不变,还是 “ calculus is a branch of math ” ,第二句话换成 “ panda is native to south central china ” ,这两句话是否是原文中相邻的两句话呢?

  很可能不是,第一句话讲的是微积分和数学,第二句话讲的是熊猫,这两句话之间没有任何关联。


  可以这样准备训练数据,把两句话给拼接起来,两句话之间用 [SEP] 符号给分开,在最前面放一个[CLS] 符号占一个位置。[CLS] 符号的意思是 classification 分类。

  生成训练数据的时候,有 50% 是原文里真实相邻的两句话,另外 50% 的第二句话是从训练数据中随机抽取的句子。

“ calculus is a branch of math ” “ it was developed by newton and leibniz ” 这两句话是真实的原文,所以标签设置 为 True。第二句话也可以是随机选取的句子。这里的第二句话“ panda is native to south central china ” 是随机选取的,所以标签是False

在这里插入图片描述
  一条训练数据包含两句话,两句话都被分割成很多个符号。在最前面放了一个 [CLS] 符号,占一个位置,这个位置上的输出叫做向量 c \mathbf{c} c。向量 c \mathbf{c} c[CLS] 的位置上,但是 c \mathbf{c} c 并不只依赖于 [CLS] 符号,向量 c \mathbf{c} c 包含两句话的全部信息,所以靠 c \mathbf{c} c 向量就能判断出两句话是否真实的相邻。

  把 c \mathbf{c} c 作为特征向量,输入一个分类器,分类器的输出是介于 0 ~ 1 之间的值 f f f 1代表 True,分类器认为两句话是从原文里拿出来的,0代表 False, 分类器认为第二句话是假的,两句话不相关。可以用 CrossEntropy 作为损失函数来衡量预测 f f f 与真实标签之间的区别,有了损失函数就可以计算梯度,然后用梯度下降来更新模型参数。

在这里插入图片描述
这样做预训练有什么用呢? 相邻两句话通常有关联。这样做 二分类 可以强化这种关联,让 Embedding 词向量包含这种关联,比如微积分和牛顿的词向量就应该有某种关联。

encoder 网络中有self-attention 层,self-attention 的作用就是找相关性。这种分类任务可以训练 self-attention 找到正确的相关性。

2.3 Combining the two methods

  前面介绍了两个任务,一个是预测遮住的单词,另一个是判断两句话是否在原文里真实相邻。BERT 把两个任务结合起来,用来预训练 transformer。

  把两句话给拼接起来,然后随机遮挡 15% 的单词,这条训练数据里碰巧有 2 个被遮挡的单词,一共有三个任务,第一个任务是判断两句话是否真的相邻?由于这两句话是从原文里取出来的,所以标签设置为True。另外两个任务是预测 2 个被遮住的单词,标签是真实的单词 branchwas

在这里插入图片描述
  下面这条训练数据里碰巧只有 1个被遮挡的单词,所以一共有两个任务。由于第二句话是随机抽样得到的,所以是假的,标签设置为 False。被遮挡的单词是 south,所以标签是单词 south

在这里插入图片描述
  假如有两个单词被遮挡,那么就有三个任务,就需要定义三个损失函数。第一个任务是 二分类,所以第一个损失函数就是 binary CrossEntropy。第二、三个任务是 预测单词,这是多分类任务,所以损失函数是 CrossEntropy。目标函数是 3 个损失函数的加和,把目标函数关于模型参数求梯度,然后做梯度下降来更新模型参数。

在这里插入图片描述
BERT 的好处是不需要人工标注数据,人工标注数据非常贵,得花几十万甚至上百万才能标注一个比较大的数据集。BERT 两种任务的标签都是自动生成的,这是个非常好的性质,可以用书,用论文,用网页等等数据来做 预训练。反正标签都是自动生成的,无需人工标注,BERT 论文里用了英文维基百科,长度是二十五亿个单词。

BERT 的想法简单,而且非常有效,但是计算代价超级大。论文里有两种模型,小模型有 1.1 亿个参数,其实它一点也不小。大模型更大,有 2.35 亿个参数,训练小模型用 16 块 TPU ,花了四天时间,这只是跑一遍的时间,还不算调参数,要是调参数的话,时间还要多很多倍。训练大模型代价要大 4 倍更夸张。不过训练出来的模型参数都是公开的。想要用 transformer 可以直接去下载 BERT 预训练的模型和参数就好了。


参考链接 - ShusenWang - Transformer 模型
参考链接 - ShusenWang’s github - DeepLearning - 课件

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值