GPT 模型的工作原理 你知道吗?
动动发财的小手,点个赞吧!
简介
当我使用 GPT 模型编写我的前几行代码时是 2021 年,那一刻我意识到文本生成已经到了一个拐点。在此之前,我在研究生院从头开始编写语言模型,并且我有使用其他文本生成系统的经验,所以我知道让它们产生有用的结果是多么困难。作为我在 Azure OpenAI 服务中发布 GPT-3 的公告工作的一部分,我很幸运能够及早使用 GPT-3,并且我尝试了它以准备它的发布。我让 GPT-3 总结了一份长文档,并尝试了少量提示。我可以看到结果比以前的模型先进得多,这让我对这项技术感到兴奋,并渴望了解它是如何实施的。而现在后续的 GPT-3.5、ChatGPT 和 GPT-4 模型正在迅速获得广泛采用,该领域的更多人也对它们的工作原理感到好奇。虽然其内部运作的细节是专有且复杂的,但所有 GPT 模型都共享一些不太难理解的基本思想。我这篇文章的目标是解释一般语言模型的核心概念,特别是 GPT 模型,并针对数据科学家和机器学习工程师进行解释。
生成语言模型的工作原理
让我们从探索生成语言模型的工作原理开始。最基本的想法如下:他们将 n 个标记作为输入,并产生一个标记作为输出。
这似乎是一个相当简单的概念,但为了真正理解它,我们需要知道token
是什么。
token
是一段文本。在 OpenAI GPT 模型的上下文中,常用词和短词通常对应于单个标记,例如下图中的“我们”一词。长的和不常用的词通常被分解成几个标记。例如,下图中的“拟人化”一词被分解为三个标记。像“ChatGPT”这样的缩写可以用单个标记表示,也可以分解成多个,具体取决于字母一起出现的常见程度。你可以去 OpenAI 的 Tokenizer 页面,输入你的文本,然后看看它是如何被分割成标记的。您可以在用于文本的“GPT-3”标记化和用于代码的“Codex”标记化之间进行选择。我们将保留默认的“GPT-3”设置。
您还可以使用 OpenAI 的开源 tiktoken 库使用 Python 代码进行标记化。 OpenAI 提供了一些不同的分词器,每个分词器的行为都略有不同。在下面的代码中,我们使用“davinci”的分词器(一种 GPT-3 模型)来匹配您使用 UI 看到的行为。
import tiktoken
# Get the encoding for the davinci GPT3 model, which is the "r50k_base" encoding.
encoding = tiktoken.encoding_for_model("davinci")
text = "We need to stop anthropomorphizing ChatGPT."
print(f"text: {text}")
token_integers = encoding.encode(text)
print(f"total number of tokens: {encoding.n_vocab}")
print(f"token integers: {token_integers}")
token_strings = [encoding.decode_single_token_bytes(token) for token in token_integers]
print(f"token strings: {token_strings}")
print(f"number of tokens in text: {len(token_integers)}")
encoded_decoded_text = encoding.decode(token_integers)
print(f"encoded-decoded text: {encoded_decoded_text}")
text: We need to stop anthropomorphizing ChatGPT.
total number of tokens: 50257
token integers: [1135, 761, 284, 2245, 17911, 25831, 2890, 24101, 38, 11571, 13]
token strings: [b'We', b' need', b' to', b' stop', b' anthrop', b'omorph', b'izing', b' Chat', b'G', b'PT', b'.']
number of tokens in text: 11
encoded-decoded text: We need to stop anthropomorphizing ChatGPT.
您可以在代码的输出中看到这个分词器包含 50,257 个不同的分词,并且每个分词都在内部映射到一个整数索引。给定一个字符串,我们可以将其拆分为整数标记,然后我们可以将这些整数转换为它们对应的字符序列。对字符串进行编码和解码应该始终返回原始字符串。
这让您对 OpenAI 的分词器如何工作有一个很好的直觉,但您可能想知道他们为什么选择这些分词长度。让我们考虑一些其他的标记化选项。假设我们尝试最简单的实现,其中每个字母都是一个标记。这使得将文本分解为标记变得容易,并使不同标记的总数保持较小。然而,我们无法像 OpenAI 的方法那样编码尽可能多的信息。如果我们在上面的例子中使用基于字母的标记,11 个标记只能编码“我们需要”,而 OpenAI 的 11 个标记可以编码整个句子。事实证明,当前的语言模型对它们可以接收的令牌的最大数量有限制。因此,我们希望在每个令牌中包装尽可能多的信息。
现在让我们考虑每个单词都是一个标记的场景。与 OpenAI 的方法相比,我们只需要七个标记来表示同一个句子,这似乎更有效率。按词拆分也很容易实现。然而,语言模型需要有一个它们可能遇到的标记的完整列表,而这对于整个单词来说是不可行的——不仅因为字典中的单词太多,而且很难跟上领域的步伐——特定术语和发明的任何新词。
因此,OpenAI 选择介于这两个极端之间的解决方案也就不足为奇了。其他公司已经发布了遵循类似方法的分词器,例如 Google 的 Sentence Piece。
现在我们对令牌有了更好的理解,让我们回到我们原来的图表,看看我们是否能更好地理解它。生成模型接受 n 个标记,可以是几个词、几段或几页。他们输出一个标记,可以是一个短词或一个词的一部分。
现在这更有意义了。
但是如果你玩过 OpenAI 的 ChatGPT,你就会知道它会产生很多令牌,而不仅仅是一个令牌。那是因为这个基本思想应用于扩展窗口模式。你给它 n 个令牌,它产生一个令牌输出,然后它将该输出令牌合并为下一次迭代输入的一部分,产生一个新的令牌输出,等等。此模式不断重复,直到达到停止条件,表明它已完成生成您需要的所有文本。
例如,如果我输入“We need to”作为模型的输入,算法可能会产生如下所示的结果:
在玩 ChatGPT 时,您可能还注意到该模型不是确定性的:如果您两次问完全相同的问题,您可能会得到两个不同的答案。那是因为该模型实际上并没有产生单个预测标记;相反,它返回所有可能标记的概率分布。换句话说,它返回一个向量,其中每个条目都表示选择特定标记的概率。然后模型从该分布中采样以生成输出标记。
该模型如何得出该概率分布?这就是训练阶段的目的。在训练期间,该模型会接触到大量文本,并且在给定输入标记序列的情况下调整其权重以预测良好的概率分布。 GPT 模型使用大部分互联网进行训练,因此他们的预测反映了他们所看到的信息的组合。
您现在对生成模型背后的想法有了很好的理解。请注意,虽然我只是解释了这个想法,但我还没有给你一个算法。事实证明,这个想法已经存在了几十年,并且多年来一直使用几种不同的算法来实现。接下来我们将看看其中的一些算法。
生成语言模型简史
隐马尔可夫模型 (HMM) 在 1970 年代开始流行。它们的内部表示对句子(名词、动词等)的语法结构进行编码,并在预测新词时使用这些知识。然而,因为它们是马尔可夫过程,所以它们在生成新令牌时只考虑最近的令牌。因此,他们实现了一个非常简单的“n 个标记输入,一个标记输出”想法,其中 n = 1。因此,它们不会生成非常复杂的输出。让我们考虑以下示例:
如果我们将“The quick brown fox jumps over the”输入到语言模型,我们会期望它返回“lazy”。然而,HMM 只会看到最后一个标记“the”,并且在信息如此少的情况下,它不太可能给出我们期望的预测。随着人们对 HMM 进行试验,很明显语言模型需要支持多个输入标记才能生成良好的输出。
N-gram 在 1990 年代开始流行,因为它们通过将多个标记作为输入来修复 HMM 的主要限制。 n-gram 模型可能会很好地预测前面示例中的“懒惰”一词。
n-gram 的最简单实现是具有基于字符的标记的二元语法,给定单个字符,能够预测序列中的下一个字符。您只需几行代码就可以创建其中一个,我鼓励您尝试一下。首先,计算训练文本中不同字符的数量(我们称之为 n),并创建一个用零初始化的 n x n 二维矩阵。通过选择对应于第一个字符的行和对应于第二个字符的列,每对输入字符可用于定位该矩阵中的特定条目。在解析训练数据时,对于每一对字符,只需将一个字符添加到相应的矩阵单元格即可。例如,如果您的训练数据包含单词“car”,您可以向“c”行和“a”列中的单元格添加一个,然后向“a”行和“r”列中的单元格添加一个柱子。一旦您累积了所有训练数据的计数,将每一行转换为概率分布,方法是将每个单元格除以该行的总数。
然后要进行预测,您需要给它一个字符作为开头,例如“c”。您查找对应于“c”行的概率分布,并对该分布进行采样以生成下一个字符。然后你选择你制作的角色,重复这个过程,直到你达到停止条件。高阶 n-gram 遵循相同的基本思想,但它们能够通过使用 n 维张量来查看更长的输入标记序列。
N-grams 很容易实现。然而,由于矩阵的大小随着输入标记数量的增加呈指数增长,因此它们不能很好地扩展到更大数量的标记。而且只有几个输入令牌,它们无法产生好的结果。需要一种新技术来继续在该领域取得进展。
在 2000 年代,递归神经网络 (RNN) 变得非常流行,因为它们能够接受比以前的技术多得多的输入标记。特别是作为 RNN 类型的 LSTM 和 GRU,得到了广泛的应用,并被证明能够产生相当好的结果。
RNN 是一种神经网络,但与传统的前馈神经网络不同,它们的架构可以适应接受任意数量的输入并产生任意数量的输出。例如,如果我们给 RNN 输入标记“We”、“need”和“to”,并希望它生成更多标记直到达到一个完整点,RNN 可能具有以下结构:
上面结构中的每个节点都具有相同的权重。您可以将其视为连接到自身并重复执行的单个节点(因此称为“循环”),或者您可以将其视为上图中显示的扩展形式。在基本 RNN 上添加到 LSTM 和 GRU 的一个关键功能是存在一个从一个节点传递到下一个节点的内部存储单元。这使后面的节点能够记住先前节点的某些方面,这对于做出良好的文本预测至关重要。
然而,RNN 在处理非常长的文本序列时存在不稳定问题。模型中的梯度趋向于呈指数增长(称为“爆炸梯度”)或减小为零(称为“消失梯度”),从而阻止模型继续从训练数据中学习。 LSTM 和 GRU 可以缓解梯度消失问题,但不能完全阻止它。因此,即使在理论上他们的架构允许任何长度的输入,但实际上对该长度有限制。再次,文本生成的质量受到算法支持的输入令牌数量的限制,需要新的突破。
2017 年,介绍 Transformers 的论文被谷歌发布,我们进入了文本生成的新时代。 Transformers 中使用的架构允许大幅增加输入令牌的数量,消除了 RNN 中出现的梯度不稳定问题,并且高度可并行化,这意味着它能够利用 GPU 的强大功能。 Transformers 在今天被广泛使用,它们是 OpenAI 为其最新的 GPT 文本生成模型选择的技术。
Transformer 基于“注意力机制”,它允许模型比其他输入更多地关注某些输入,而不管它们出现在输入序列中的什么位置。例如,让我们考虑以下句子:
在这种情况下,当模型预测动词“bought”时,它需要匹配动词“went”的过去式。为了做到这一点,它必须非常关注“去了”的令牌。事实上,它可能更关注标记“went”而不是标记“and”,尽管事实上“went”在输入序列中出现得更早。
GPT 模型中的这种选择性注意行为是由 2017 年论文中的一个新颖想法实现的:使用“蒙面多头注意”层。让我们分解这个术语,并深入研究它的每个子术语:
-
Attention:“注意”层包含一个权重矩阵,表示输入句子中所有标记位置对之间的关系强度。这些权重是在训练期间学习的。如果一对仓位对应的权重很大,那么这两个仓位中的token相互影响很大。这种机制使 Transfomer 能够比其他标记更多地关注某些标记,而不管它们出现在句子中的什么位置。
-
Masked:如果矩阵仅限于每个标记位置与输入中较早位置之间的关系,则注意力层被“屏蔽”。这就是 GPT 模型用于文本生成的内容,因为输出标记只能依赖于它之前的标记。
-
Multi-head:Transformer 使用蒙面的“多头”注意层,因为它包含多个并行操作的蒙面注意层。
LSTM 和 GRU 的记忆单元还使后来的标记能够记住早期标记的某些方面。但是,如果两个相关的标记相距很远,梯度问题可能会成为障碍。变形金刚没有这个问题,因为每个标记都直接连接到它之前的所有其他标记。
现在您了解了 GPT 模型中使用的 Transformer 架构的主要思想,让我们看一下当前可用的各种 GPT 模型之间的区别。
不同的 GPT 模型是如何实现的
在撰写本文时,OpenAI 最新发布的三种文本生成模型分别是 GPT-3.5、ChatGPT 和 GPT-4,它们均基于 Transformer 架构。事实上,“GPT”代表“Generative Pre-trained Transformer”。
GPT-3.5 是一个作为完成式模型训练的转换器,这意味着如果我们给它几个词作为输入,它能够生成更多可能在训练数据中跟随它们的词。
另一方面,ChatGPT 被训练为一种对话式模型,这意味着当我们与它交流时,它表现得最好,就像我们在进行对话一样。它基于与 GPT-3.5 相同的 transformer 基础模型,但它使用对话数据进行了微调。然后使用人类反馈强化学习 (RLHF) 对其进行进一步微调,这是 OpenAI 在其 2022 InstructGPT 论文中引入的一项技术。在这种技术中,我们给模型两次相同的输入,取回两个不同的输出,然后询问人类排序者它更喜欢哪个输出。然后使用该选择通过微调改进模型。这种技术使模型的输出与人类期望保持一致,这对 OpenAI 最新模型的成功至关重要。
另一方面,GPT-4 可用于完成和对话,并拥有自己全新的基础模型。该基础模型还使用 RLHF 进行了微调,以更好地符合人类期望。
编写使用 GPT 模型的代码
你有两种选择来编写使用 GPT 模型的代码:你可以直接使用 OpenAI API,或者你可以在 Azure 上使用 OpenAI API。无论哪种方式,您都使用相同的 API 调用编写代码,您可以在 OpenAI 的 API 参考页面中了解这些内容。
两者之间的主要区别在于 Azure 提供了以下附加功能:
- 自动化负责任的 AI 过滤器,可减少 API 的不道德使用
- Azure 的安全功能,例如专用网络
- 区域可用性,以便在与 API 交互时获得最佳性能
如果您正在编写使用这些模型的代码,则需要选择要使用的特定版本。这是一个快速备忘单,其中包含 Azure OpenAI 服务中当前可用的版本:
- GPT-3.5: text-davinci-002, text-davinci-003
- ChatGPT: gpt-35-turbo
- GPT-4: gpt-4, gpt-4–32k
两个 GPT-4 版本的区别主要在于它们支持的令牌数量:gpt-4 支持 8,000 个令牌,而 gpt-4–32k 支持 32,000 个。相比之下,GPT-3.5 模型仅支持 4,000 个令牌。
由于 GPT-4 目前是最昂贵的选择,因此最好从其他模型之一开始,并仅在需要时升级。有关这些模型的更多详细信息,请查看文档。
总结
在本文中,我们介绍了所有生成语言模型共有的基本原则,尤其是来自 OpenAI 的最新 GPT 模型的独特之处。
在此过程中,我们强调了语言模型的核心思想:“n 个标记输入,一个标记输出”。我们探讨了代币是如何分解的,以及为什么要以这种方式分解。我们追溯了语言模型从早期的隐马尔可夫模型到最近的基于 Transformer 的模型长达数十年的演变。最后,我们描述了来自 OpenAI 的三个最新的基于 Transformer 的 GPT 模型,每个模型是如何实现的,以及如何编写使用它们的代码。
到现在为止,您应该已经准备好就 GPT 模型进行有见地的对话,并开始在您自己的编码项目中使用它们。我计划写更多关于语言模型的解释,所以请关注我,让我知道你希望看到哪些主题!感谢您的阅读!
本文由mdnice多平台发布