序言
本节作为整篇的收官之作,将介绍全球首家推动人工智能生成人类语言的公司——OpenAI 的 GPT 模型的基本原理。理解该模型是进入 AI 行业的基础。OpenAI 的创始团队包括科技巨头 Elon Musk 以及 Geoffrey Hinton 的学生伊利亚·苏茨克弗(Ilya Sutskever)。OpenAI 最初公开了 ChatGPT-2 的语言模型(LLM)源代码,但在随后的 ChatGPT-3 及之后的版本中停止了开源。本节介绍的模型由 OpenAI 参与者、现任斯坦福大学教授李飞飞的学生 Andrej Karpathy 基于 ChatGPT-3 模型而来。
GPT 架构
接下来谈谈 GPT 架构。大多数 GPT 模型(尽管有不同的变化)都使用这种架构。使用框图表示法,这就是 GPT 架构的高级示意图:
此时,除了'GPT Transformer 块',其他模块我们都已详细讨论过。这里的 + 号只是表示两个向量相加(这意味着两个嵌入必须同样大小)。来看一下这个 GPT Transformer 块:
就是这样。之所以称之为'Transformer',是因为它源自并属于一种 Transformer 架构——我们将在下一节中详细了解。理解上没有影响,因为这里展示的所有模块我们都已讨论过。让我们回顾一下到目前为止构建这个 GPT 架构的过程:
- 我们了解到神经网络接收数字并输出其他数字,权重是可训练的参数
- 我们可以对这些输入/输出数字进行解释,赋予神经网络现实世界的意义
- 我们可以串联神经网络创建更大的网络,并可以将每一个称为'块',用框来表示以简化图解。每个块的作用都是接收一组数字并输出另一组数字
- 我们学习了很多不同类型的块,每种块都有其不同的作用
- GPT 只是这些块的一个特殊排列,如上图所示,解释方式在第一部分已讨论过
随着时间的推移,人们在此基础上做出了各种修改,使得现代 LLM 更加强大,但基本原理保持不变。
现在,这个 GPT Transformer 实际上在原始 Transformer 论文中被称为'解码器'。让我们看看这一点。
Transformer 架构
这是驱动语言模型能力迅速提升的关键创新之一。Transformer 不仅提高了预测准确性,还比先前的模型更高效(更容易训练),允许构建更大的模型。这是 GPT 架构的基础。
观察 GPT 架构,你会发现它非常适合生成序列中的下一个词。它基本遵循我们在第一部分讨论的逻辑:从几个词开始,然后逐个生成词。但是,如果你想进行翻译呢?比如,你有一句德语句子(例如'Wo wohnst du?' = 'Where do you live?'),你希望将其翻译成英语。我们该如何训练模型来完成这项任务?
第一步,我们需要找到一种输入德语单词的方法,这意味着我们要扩展嵌入,包含德语和英语。一种简单的输入方式是将德语句子和生成的英文句子连接起来,并将其输入上下文。为了让模型更容易理解,我们可以添加一个分隔符。每一步看起来像这样:
这可以工作,但仍有改进空间:
- 如果上下文长度固定,有时会丢失原始句子
- 模型需要学习很多内容。包括两种语言,还需要知道是分隔符,它应该在此处开始翻译
- 每次生成一个词时,都需要处理整个德语句子,存在不同偏移。这意味着相同内容的内部表示不同,模型应该能够通过这些表示进行翻译
Transformer 最初就是为此任务创建的,它由'编码器'和'解码器'组成——基本上是两个独立的模块。一个模块仅处理德语句子,生成中间表示(仍然是数值集合)——这被称为编码器。第二个模块生成单词(我们已经见过很多)。唯一的区别是,除了将已生成的单词输入解码器外,还将编码器输出的德语句子作为额外输入。也就是说,在生成语言时,它的上下文是已生成的所有单词加上德语句子。这个模块被称为解码器。
这些编码器和解码器由一些块组成,尤其是夹在其他层之间的注意力块。我们来看'Attention is all you need'论文中的 Transformer 架构示意图并尝试理解它:
左侧的竖直块集合称为'编码器',右侧的称为'解码器'。让我们逐个理解每个部分:
前馈网络
前馈网络是没有循环的网络。第一部分中讨论的原始网络就是一个前馈网络。事实上,这个块采用了非常相似的结构。它包含两个线性层,每个层之后都有一个 ReLU(见第一部分关于 ReLU 的介绍)和一个 Dropout 层。请记住,这个前馈网络适用于每个位置独立。也就是说,位置 0 有一个前馈网络,位置 1 有一个,依此类推。但是位置 x 的神经元不会与位置 y 的前馈网络相连。这样做的重要性在于防止网络在训练时'偷看'前方信息。
交叉注意力
你会注意到解码器有一个多头注意力,其箭头来自编码器。这里发生了什么?记得自注意力和多头注意力中的 value、key、query 吗?它们都来自同一个序列。事实上,query 只是序列的最后一个词。那么,如果我们保留 query,但将 value 和 key 来自一个完全不同的序列会怎样?这就是这里发生的情况。value 和 key 来自编码器的输出。数学上没有任何改变,只是 key 和 value 的输入来源发生了变化。
Nx 堆叠
Nx 表示这个块重复 N 次。基本上,你在将一个块层层堆叠,前一个块的输出作为下一个块的输入。这样可以使神经网络更深。从图上看,编码器输出如何传递给解码器可能让人困惑。假设 N=5。我们是否将每层编码器输出传递给对应的解码器层?不是的。实际上你只需运行一次编码器,然后将同一表示提供给 5 个解码器层。


