保姆级教程:图解Transformer

2021年1月19日13:50:12 5 9,253 °C
摘要

Transformer的原理剖析,注意力机制、位置嵌入详解。

保姆级教程:图解Transformer

本文 GitHub https://github.com/Jack-Cherish/PythonPark 已收录,有技术干货文章,整理的学习资料,一线大厂面试经验分享等,欢迎 Star 和 完善。

一、前言

大家好,我是 Jack。

本文是图解 AI 算法系列教程的第二篇,今天的主角是 Transformer。

Transformer 可以做很多有趣而又有意义的事情。

比如我写过的《用自己训练的AI玩王者荣耀是什么体验?》。

再比如 OpenAI 的 DALL·E,可以魔法一般地按照自然语言文字描述直接生成对应图片!

输入文本:鳄梨形状的扶手椅。

AI 生成的图像:

保姆级教程:图解Transformer

两者都是多模态的应用,这也是各大巨头的跟进方向,可谓大势所趋

Transformer 最初主要应用于一些自然语言处理场景,比如翻译、文本分类、写小说、写歌等。

随着技术的发展,Transformer 开始征战视觉领域,分类、检测等任务均不在话下,逐渐走上了多模态的道路。

保姆级教程:图解Transformer

Transformer 近两年非常火爆,内容也很多,要想讲清楚,还涉及一些基于该结构的预训练模型,例如著名的 BERTGPT,以及刚出的 DALL·E 等。

它们都是基于 Transformer 的上层应用,因为 Transformer 很难训练,巨头们就肩负起了造福大众的使命,开源了各种好用的预训练模型

我们都是站在巨人肩膀上学习,用开源的预训练模型在一些特定的应用场景进行迁移学习

篇幅有限,本文先讲解 Transformer 的基础原理,希望每个人都可以看懂。

后面我会继续写 BERTGPT 等内容,更新可能慢一些,但是跟着学,绝对都能有所收获。

还是那句话:如果你喜欢这个 AI 算法系列教程,一定要让我知道,转发在看支持,更文更有动力!

二、Transformer

Transformer 是 Google 在 2017 年提出的用于机器翻译的模型。

保姆级教程:图解Transformer

Transformer 的内部,在本质上是一个 Encoder-Decoder 的结构,即 编码器-解码器

保姆级教程:图解Transformer

Transformer 中抛弃了传统的 CNN 和 RNN,整个网络结构完全由 Attention 机制组成,并且采用了 6 层 Encoder-Decoder 结构。

保姆级教程:图解Transformer

显然,Transformer 主要分为两大部分,分别是编码器解码器

整个 Transformer 是由 6 个这样的结构组成,为了方便理解,我们只看其中一个Encoder-Decoder 结构。

以一个简单的例子进行说明:

保姆级教程:图解Transformer

Why do we work?,我们为什么工作?

左侧红框是编码器,右侧红框是解码器

编码器负责把自然语言序列映射成为隐藏层(上图第2步),即含有自然语言序列的数学表达。

解码器把隐藏层再映射为自然语言序列,从而使我们可以解决各种问题,如情感分析、机器翻译、摘要生成、语义关系抽取等。

简单说下,上图每一步都做了什么:

  • 输入自然语言序列到编码器: Why do we work?(为什么要工作);
  • 编码器输出的隐藏层,再输入到解码器;
  • 输入 <𝑠𝑡𝑎𝑟𝑡> (起始)符号到解码器;
  • 解码器得到第一个字"为";
  • 将得到的第一个字"为"落下来再输入到解码器;
  • 解码器得到第二个字"什";
  • 将得到的第二字再落下来,直到解码器输出 <𝑒𝑛𝑑> (终止符),即序列生成完成。

解码器和编码器的结构类似,本文以编码器部分进行讲解。即把自然语言序列映射为隐藏层的数学表达的过程,因为理解了编码器中的结构,理解解码器就非常简单了。

为了方便学习,我将编码器分为 4 个部分,依次讲解。

保姆级教程:图解Transformer

1、位置嵌入(𝑝𝑜𝑠𝑖𝑡𝑖𝑜𝑛𝑎𝑙 𝑒𝑛𝑐𝑜𝑑𝑖𝑛𝑔)

我们输入数据 X 维度为[batch size, sequence length]的数据,比如我们为什么工作

batch size 就是 batch 的大小,这里只有一句话,所以 batch size1sequence length 是句子的长度,一共 7 个字,所以输入的数据维度是 [1, 7]

我们不能直接将这句话输入到编码器中,因为 Tranformer 不认识,我们需要先进行字嵌入,即得到图中的 \(X_{\text {embedding }}\)。

简单点说,就是文字->字向量的转换,这种转换是将文字转换为计算机认识的数学表示,用到的方法就是 Word2VecWord2Vec 的具体细节,对于初学者暂且不用了解,这个是可以直接使用的。

得到的 \(X_{\text {embedding }}\) 的维度是 [batch size, sequence length, embedding dimension]embedding dimension 的大小由 Word2Vec 算法决定,Tranformer 采用 512 长度的字向量。所以 \(X_{\text {embedding }}\) 的维度是 [1, 7, 512]

至此,输入的我们为什么工作,可以用一个矩阵来简化表示。

保姆级教程:图解Transformer

我们知道,文字的先后顺序,很重要。

比如吃饭没没吃饭没饭吃饭吃没饭没吃,同样三个字,顺序颠倒,所表达的含义就不同了。

文字的位置信息很重要,Tranformer 没有类似 RNN 的循环结构,没有捕捉顺序序列的能力。

为了保留这种位置信息交给 Tranformer 学习,我们需要用到位置嵌入

加入位置信息的方式非常多,最简单的可以是直接将绝对坐标 0,1,2 编码。

Tranformer 采用的是 sin-cos 规则,使用了 sin 和 cos 函数的线性变换来提供给模型位置信息:

$$\begin{aligned} P E_{(p o s, 2 i)} &=\sin \left(p o s / 10000^{2 i / d_{\text {model }}}\right) \\ P E_{(\text {pos }, 2 i+1)} &=\cos \left(\text { pos } / 10000^{2 i / d_{\text {model }}}\right) \end{aligned}$$

上式中 pos 指的是句中字的位置,取值范围是 [0, 𝑚𝑎𝑥 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛𝑔𝑡ℎ)i 指的是字嵌入的维度, 取值范围是 [0, 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛)。 就是 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 的大小。

上面有 sin 和 cos 一组公式,也就是对应着 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 维度的一组奇数和偶数的序号的维度,从而产生不同的周期性变化。

可以用代码,简单看下效果。

可以看到,位置嵌入在 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛 (也是hidden dimension )维度上随着维度序号增大,周期变化会越来越慢,而产生一种包含位置信息的纹理。

保姆级教程:图解Transformer

就这样,产生独一的纹理位置信息,模型从而学到位置之间的依赖关系和自然语言的时序特性。

最后,将 \(X_{\text {embedding }}\) 和 位置嵌入 相加,送给下一层。

2、自注意力层(𝑠𝑒𝑙𝑓 𝑎𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛 𝑚𝑒𝑐ℎ𝑎𝑛𝑖𝑠𝑚)

直接看下图笔记,讲解的非常详细。

保姆级教程:图解Transformer

多头的意义在于,\(Q K^{T}\) 得到的矩阵就叫注意力矩阵,它可以表示每个字与其他字的相似程度。因为,向量的点积值越大,说明两个向量越接近。

保姆级教程:图解Transformer

我们的目的是,让每个字都含有当前这个句子中的所有字的信息,用注意力层,我们做到了。

需要注意的是,在上面 𝑠𝑒𝑙𝑓 𝑎𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛 的计算过程中,我们通常使用 𝑚𝑖𝑛𝑖 𝑏𝑎𝑡𝑐ℎ,也就是一次计算多句话,上文举例只用了一个句子。

每个句子的长度是不一样的,需要按照最长的句子的长度统一处理。对于短的句子,进行 Padding 操作,一般我们用 0 来进行填充。

保姆级教程:图解Transformer

3、残差链接和层归一化

加入了残差设计和层归一化操作,目的是为了防止梯度消失,加快收敛。

1) 残差设计

我们在上一步得到了经过注意力矩阵加权之后的 𝑉, 也就是 𝐴𝑡𝑡𝑒𝑛𝑡𝑖𝑜𝑛(𝑄, 𝐾, 𝑉),我们对它进行一下转置,使其和 𝑋𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 的维度一致, 也就是 [𝑏𝑎𝑡𝑐ℎ 𝑠𝑖𝑧𝑒, 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛𝑔𝑡ℎ, 𝑒𝑚𝑏𝑒𝑑𝑑𝑖𝑛𝑔 𝑑𝑖𝑚𝑒𝑛𝑠𝑖𝑜𝑛] ,然后把他们加起来做残差连接,直接进行元素相加,因为他们的维度一致:

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

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

$$X + SubLayer(X) $$

2) 层归一化

作用是把神经网络中隐藏层归一为标准正态分布,也就是 𝑖.𝑖.𝑑 独立同分布, 以起到加快训练速度, 加速收敛的作用。

$$\mu_{i}=\frac{1}{m} \sum^{m}_{i=1}x_{ij}$$

上式中以矩阵的行 (𝑟𝑜𝑤) 为单位求均值:

$$\sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1}
(x_{ij}-\mu_{j})^{2}$$

上式中以矩阵的行 (𝑟𝑜𝑤) 为单位求方差:

$$LayerNorm(x)=\alpha \odot \frac{x_{ij}-\mu_{i}}
{\sqrt{\sigma^{2}_{i}+\epsilon}} + \beta $$

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

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

代码层面非常简单,单头 attention 操作如下:

Multi-Head Attention 实现在 ScaledDotProductAttention 基础上构建:

4、前馈网络

这个层就没啥说的了,非常简单,直接看代码吧:

最后,回顾下 𝑡𝑟𝑎𝑛𝑠𝑓𝑜𝑟𝑚𝑒𝑟 𝑒𝑛𝑐𝑜𝑑𝑒𝑟 的整体结构。

经过上文的梳理,我们已经基本了解了 𝑡𝑟𝑎𝑛𝑠𝑓𝑜𝑟𝑚𝑒𝑟 编码器的主要构成部分,我们下面用公式把一个 𝑡𝑟𝑎𝑛𝑠𝑓𝑜𝑟𝑚𝑒𝑟 𝑏𝑙𝑜𝑐𝑘 的计算过程整理一下:

1) 字向量与位置编码

$$X = EmbeddingLookup(X) + PositionalEncoding$$
$$X \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.} $$

2) 自注意力机制

$$Q = Linear(X) = XW_{Q}$$
$$K = Linear(X) = XW_{K}$$
$$V = Linear(X) = XW_{V}$$
$$X_{attention} = SelfAttention(Q, \ K, \ V)$$

3) 残差连接与层归一化

$$X_{attention} = X + X_{attention}$$
$$X_{attention} = LayerNorm(X_{attention})$$

4) 前向网络

其实就是两层线性映射并用激活函数激活,比如说\(ReLU\):

$$X_{hidden} = Activate(Linear(Linear(X_{attention})))$$

5) 重复3)

$$X_{hidden} = X_{attention} + X_{hidden}$$
$$X_{hidden} = LayerNorm(X_{hidden})$$
$$X_{hidden} \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.} $$

三、絮叨

至此,我们已经讲完了 Transformer 编码器的全部内容,知道了如何获得自然语言的位置信息,注意力机制的工作原理等。

本文以原理讲解为主,后续我会继续更新实战内容,教大家如何训练我们自己的有趣又好玩的模型。

本文硬核,肝了很久,如果喜欢,还望转发、再看多多支持。

我是 Jack ,我们下期见。

文章持续更新,可以微信公众号搜索【JackCui-AI】第一时间阅读,本文 GitHub https://github.com/Jack-Cherish/PythonPark 已经收录,有大厂面试完整考点,欢迎Star。

weinxin
微信公众号
分享技术,乐享生活:微信公众号搜索「JackCui-AI」关注一个在互联网摸爬滚打的潜行者。
Jack Cui

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:5   其中:访客  5   博主  0

    • avatar Misaka 来自天朝的朋友 谷歌浏览器 Windows 10 北京市 移动 0

      解码器没有讲解吗

      • avatar 阿虎 来自天朝的朋友 谷歌浏览器 Windows 7 新疆博尔塔拉州博乐市 电信 0

        太深奥,看不懂! :sad:

        • avatar MAT_ 来自天朝的朋友 谷歌浏览器 Windows 10 北京市 电信 2

          哈哈哈,我正好看完,jack大佬这篇就是论文解析的感觉,没理解的地方也讲到了,好强

          • avatar hstk30 来自天朝的朋友 谷歌浏览器 Mac OS X Lion 10_15_7 浙江省杭州市 电信 0

            请教一下,我看这篇blog 【https://jalammar.github.io/illustrated-transformer/】说 multi-head attention是 N 倍的Q, K, V,但你这里说是将Q, K, V分成N份,不知道哪个是对的,还是说你在W_q, W_k, W_v时先将这些矩阵设置为 N 倍?
            (刚学几天bert,不要见怪)

            • avatar MAT_ 来自天朝的朋友 谷歌浏览器 Windows 10 北京市 电信 2

              contiguous()操作一般什么时候用呢?我理解的是更换张量存储的位置吗?在Multi-Head Attention代码的第51行的位置.