Press "Enter" to skip to content

《深入理解GPT — I:文本生成的原理》

ChatGPT模型背后的简单解释

通过Midjourney创建的引入生成AI的年轻陀思妥耶夫斯基

经常与不同领域的同事进行交流,我喜欢向没有数据科学背景的人们传达机器学习概念的挑战。在这里,我尝试以书面形式用简单的语言解释GPT的工作原理。

在ChatGPT的魔力背后,有着不太受欢迎的逻辑。你给ChatGPT写一个提示,它就会生成文本,无论它是否准确,它都类似于人类的答案。它是如何能理解你的提示并生成连贯和可理解的答案的呢?

Transformer神经网络。这个架构设计用于处理大量的非结构化数据,我们这里指的是文本。当我们说架构时,我们指的实际上是一系列在几个层次上并行进行的数学操作。通过这个方程系统,我们引入了几个创新,帮助我们克服了长期以来的文本生成难题。这些是我们在5年前之前一直努力解决的挑战。

如果GPT已经存在了5年(确实,GPT论文于2018年发表),那么GPT不是旧闻了吗?为什么它最近变得非常流行?GPT 1、2、3、3.5(ChatGPT)和4之间有什么区别呢?

所有的GPT版本都建立在相同的架构上。然而,每个后续的模型都包含更多的参数,并使用更大的文本数据集进行训练。显然,后续的GPT版本中还有其他创新,特别是在训练过程中引入了人类反馈的强化学习,我们将在本系列博客的第三部分中解释。

向量、矩阵、张量。所有这些花哨的词实质上都是包含一系列数字的单元。这些数字经历一系列的数学运算(主要是乘法和求和),直到达到最佳输出值,即可能结果的概率。

输出值?从这个意义上说,它就是语言模型生成的文本,对吗?是的。那么,输入值是什么?是我的提示吗?是的,但不完全是。那么还有什么呢?

在继续讨论不同的文本解码策略之前,这将是下一篇博客文章的主题,消除歧义是有用的。我们回到开始时提出的根本问题。它是如何理解人类语言的呢?

生成预训练变换器。GPT缩写的三个单词。我们上面提到了Transformer部分,它代表了进行大量计算的架构。但我们具体计算什么?你又是从哪里得到这些数字的呢?它是一个语言模型,你所做的只是写文本并输入一些文本,你怎么能计算文本呢?

数据是不可知的。所有的数据,无论是文本、声音还是图像,都是相同的¹。

令牌。我们将文本分割成小块(令牌),并为每个令牌分配一个唯一的数字(令牌ID)。模型不知道词语、图像或音频记录。它们学习将它们表示为一系列巨大的数字(参数),这些数字作为工具用来以数值形式表示事物的特征。令牌是传达含义的语言单位,而令牌ID是编码令牌的唯一数字。

显然,我们如何对语言进行分词可能会有所不同。分词可以将文本分割成句子、单词、词的部分(子词)甚至单个字符。

让我们考虑一个场景,我们的语言语料库中有50,000个令牌(类似于GPT-2,它有50,257个令牌)。在分词之后,我们如何表示这些单位?

Sentence: "学生们用盛大的派对庆祝毕业"Token labels: ['[CLS]', '学生们', '用', '盛大的', '派对', '庆祝', '毕业', '[SEP]']Token IDs: tensor([[ 101, 2493, 8439, 1996, 7665, 2007, 1037, 2502, 2283,  102]])

上面是一个例句被拆分成单词的示例。在令牌化方法的实现中,令牌化方法可能会有所不同。现在对我们来说重要的是,我们通过相应的令牌ID获取语言单元(令牌)的数值表示。那么,既然我们有了这些令牌ID,我们能直接将它们输入到进行计算的模型中吗?

在数学中,基数很重要。101和2493作为令牌表示对模型很重要。因为请记住,我们所做的主要是大块数字的乘法和求和。因此,将一个数与101或2493相乘很重要。那么,我们如何确保用数字101表示的令牌不比2493不重要,只是因为我们恰好是任意地对其令牌化的?如何在不造成虚假排序的情况下对单词进行编码?

One-hot编码。令牌的稀疏映射。One-hot编码是一种将每个令牌投影为二进制向量的技术。这意味着向量中只有一个元素为1(“热”),其余元素为0(“冷”)。

图1:One-hot编码向量示例

令牌使用一个长度为语料库中令牌总数的向量表示。简单地说,如果我们的语言中有50k个令牌,那么每个令牌都由一个长度为50k的向量表示,其中只有一个元素为1,其余元素为0。由于这个投影中的每个向量只包含一个非零元素,因此被称为稀疏表示。然而,正如您可能想到的,这种方法非常低效。是的,我们设法消除了令牌ID之间的人为基数,但我们无法从稀疏向量中推导出有关单词语义的任何信息。我们无法确定单词“party”是指庆祝活动还是指政治组织。此外,用大小为50k的向量表示每个令牌意味着总共有50k个长度为50k的向量。这在所需的内存和计算方面非常低效。幸运的是,我们有更好的解决方案。

嵌入。令牌的密集表示。令牌化单元通过嵌入层,其中每个令牌被转换为固定大小的连续向量表示。例如,在GPT 3的情况下,每个令牌由一个768个数字的向量表示。这些数字是随机分配的,然后在看到大量数据(训练)后由模型进行学习。

令牌标签:“party”令牌:2283嵌入向量长度:768嵌入张量形状:([1, 10, 768])嵌入向量:tensor([ 2.9950e-01, -2.3271e-01,  3.1800e-01, -1.2017e-01, -3.0701e-01,        -6.1967e-01,  2.7525e-01,  3.4051e-01, -8.3757e-01, -1.2975e-02,        -2.0752e-01, -2.5624e-01,  3.5545e-01,  2.1002e-01,  2.7588e-02,        -1.2303e-01,  5.9052e-01, -1.1794e-01,  4.2682e-02,  7.9062e-01,         2.2610e-01,  9.2405e-02, -3.2584e-01,  7.4268e-01,  4.1670e-01,        -7.9906e-02,  3.6215e-01,  4.6919e-01,  7.8014e-02, -6.4713e-01,         4.9873e-02, -8.9567e-02, -7.7649e-02,  3.1117e-01, -6.7861e-02,        -9.7275e-01,  9.4126e-02,  4.4848e-01,  1.5413e-01,  3.5430e-01,         3.6865e-02, -7.5635e-01,  5.5526e-01,  1.8341e-02,  1.3527e-01,        -6.6653e-01,  9.7280e-01, -6.6816e-02,  1.0383e-01,  3.9125e-02,        -2.2133e-01,  1.5785e-01, -1.8400e-01,  3.4476e-01,  1.6725e-01,        -2.6855e-01, -6.8380e-01, -1.8720e-01, -3.5997e-01, -1.5782e-01,         3.5001e-01,  2.4083e-01, -4.4515e-01, -7.2435e-01, -2.5413e-01,         2.3536e-01,  2.8430e-01,  5.7878e-01, -7.4840e-01,  1.5779e-01,        -1.7003e-01,  3.9774e-01, -1.5828e-01, -5.0969e-01, -4.7879e-01,        -1.6672e-01,  7.3282e-01, -1.2093e-01,  6.9689e-02, -3.1715e-01,        -7.4038e-02,  2.9851e-01,  5.7611e-01,  1.0658e+00, -1.9357e-01,         1.3133e-01,  1.0120e-01, -5.2478e-01,  1.5248e-01,  6.2976e-01,        -4.5310e-01,  2.9950e-01, -5.6907e-02, -2.2957e-01, -1.7587e-02,        -1.9266e-01,  2.8820e-02,  3.9966e-03,  2.0535e-01,  3.6137e-01,         1.7169e-01,  1.0535e-01,  1.4280e-01,  8.4879e-01, -9.0673e-01,                                            …                                             …                                             …                           ])

上面是单词“party”的嵌入向量示例。

现在我们有大小为50,000×786的向量,与大小为50,000×50,000的独热编码相比,它更高效。

嵌入向量将成为模型的输入。由于密集的数值表示,我们能够捕捉单词的语义,相似的嵌入向量会更接近彼此。

你如何衡量上下文中两个语言单位的相似性?有几种函数可以衡量相同大小的两个向量之间的相似性。让我们用一个例子来解释。

考虑一个简单的例子,我们有单词“cat”、“dog”、“car”和“banana”的嵌入向量。为简化起见,让我们使用尺寸为4的嵌入大小。这意味着将有四个学习到的数字来表示每个标记。

import numpy as npfrom sklearn.metrics.pairwise import cosine_similarity# "cat"、"dog"、"car"和"banana"的示例单词嵌入embedding_cat = np.array([0.5, 0.3, -0.1, 0.9])embedding_dog = np.array([0.6, 0.4, -0.2, 0.8])embedding_car = np.array([0.5, 0.3, -0.1, 0.9])embedding_banana = np.array([0.1, -0.8, 0.2, 0.4])

使用上述向量,我们来计算使用余弦相似度的相似性分数。人类逻辑会发现“dogs”和“cats”这两个词更相关,而不是“banana”和“car”这两个词。我们能期望数学来模拟我们的逻辑吗?

# 计算余弦相似度similarity = cosine_similarity([embedding_cat], [embedding_dog])[0][0]print(f"‘cat’和‘dog’之间的余弦相似度:{similarity:.4f}")# 计算余弦相似度similarity_2 = cosine_similarity([embedding_car], [embedding_banana])[0][0]print(f"‘car’和‘banana’之间的余弦相似度:{similarity:.4f}")

我们可以看到,“cat”和“dog”这两个词的相似性得分非常高,而“car”和“banana”这两个词的相似性得分非常低。现在想象一下,对于我们语言语料库中的每个50000个标记,嵌入向量的长度为768。这就是我们能够找到彼此相关的单词的方式。

现在,让我们来看一下下面这两个具有更高语义复杂性的句子。

“学生们用盛大的派对庆祝毕业”“副领导在党内备受尊敬”

第一句和第二句中的“party”一词传达着不同的含义。大型语言模型如何能够区分作为政治组织的“party”和作为庆祝社交活动的“party”之间的区别呢?

我们能否依靠标记嵌入来区分同一标记的不同含义?事实是,尽管嵌入向量为我们提供了一系列优势,但它们不足以解决人类语言的整个语义挑战。

自注意力。解决方案再次由Transformer神经网络提供。我们生成一组新的权重,即查询、键和值矩阵。这些权重学习将标记的嵌入向量表示为一组新的嵌入。如何做到的?简单地通过对原始嵌入的加权平均。每个标记“关注”输入句子中的每个其他标记(包括自身),并计算一组注意权重,或者换句话说,新的所谓“上下文嵌入”。

它实际上只是通过使用标记嵌入计算出的一系列数字(注意权重)来映射输入句子中单词的重要性。

图2:不同上下文中标记的注意权重(BertViz注意头视图)

上面的可视化演示了“party”这个标记对两个句子中其他标记的“注意力”。连接的粗细表示标记的重要性或相关性。注意力和“关注”只是一系列数字及其大小,我们用数字来表示单词的重要性。在第一句中,单词“party”对单词“celebrate”的“关注”最多,而在第二句中,单词“deputy”的注意力最高。这就是模型通过检查周围的单词来融入上下文的方式。

正如我们在注意力机制中提到的,我们派生了一组新的权重矩阵,即:查询(Query)、键(Key)和值(Value)(简称q、k、v)。它们是相同大小(通常小于嵌入向量)的级联矩阵,引入到体系结构中以捕捉语言单元的复杂性。通过学习注意力参数,我们可以揭示单词、单词对、单词对对、单词对对对等之间的关系。下面是查询、键和值矩阵在查找最相关单词时的可视化。

查询、键和值矩阵及其最终概率(Softmax)的示意图

该可视化将查询和键向量表示为垂直条带,其中每个条带的粗细反映了其大小。标记之间的连接表示由注意力确定的权重,表明“party”的查询向量与“is”、“deputy”和“respected”的键向量最为显著地对齐。

为了使注意力机制以及查询、键和值的概念更具体,想象一下你去参加派对并听到一首你爱上的绝妙歌曲。派对结束后,你非常想找到这首歌并再次聆听,但你只记得歌词中的五个词和部分旋律(查询)。为了找到这首歌,你决定浏览派对播放列表(键)并听(相似度函数)播放列表中所有在派对上播放过的歌曲。当你最终认出这首歌时,你记录下了歌名(值)。

Transformer引入的最后一个重要技巧是将位置编码添加到向量嵌入中。这是因为我们希望捕捉单词的位置信息。这增加了我们准确预测下一个标记与真实句子上下文相关性的机会。这是重要的信息,因为交换单词往往会完全改变上下文。例如,“Tim chased clouds all his life”和“clouds chased Tim all his life”这两句话在本质上是完全不同的。

到目前为止,我们在基本层面上探索的所有数学技巧的目标都是预测下一个标记,给定输入标记序列。事实上,GPT训练的是一个简单任务,即文本生成或者换句话说,下一个标记的预测。在其核心问题上,我们测量一个标记在出现之前的标记序列中的概率。

也许你会想知道模型是如何从随机分配的数字中学习到最优数值的。这可能是另一篇博客文章的话题,但这实际上是理解的基础。此外,这也是你已经开始质疑基础知识的一个很好的迹象。为了消除不确定性,我们使用一种优化算法,根据称为损失函数的指标来调整参数。通过比较预测值与实际值,计算出这个指标。模型跟踪这个指标的变化,并根据损失的大小来调整数值。这个过程会一直进行,直到根据我们在算法中设置的超参数规则,损失无法再变小。超参数的一个示例可以是我们希望多频繁地计算损失和调整权重。这就是学习的基本思想。

希望在这篇简短的文章中,我至少能够稍微澄清一点。本博客系列的第二部分将重点讨论解码策略,即为什么您的提示很重要。第三部分将专门介绍ChatGPT成功的关键因素,即通过人类反馈进行强化学习。感谢阅读。下次再见。

参考文献:

A. Vaswani, N. Shazeer, N. Parmar, J. Uszkoreit, L. Jones, A. N. Gomez, Ł. Kaiser, and I. Polosukhin, “Attention Is All You Need,” in Advances in Neural Information Processing Systems 30 (NIPS 2017), 2017.

J. Vig, “A Multiscale Visualization of Attention in the Transformer Model,” In Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations, pp. 37–42, Florence, Italy, Association for Computational Linguistics, 2019.

L. Tunstall, L. von Werra, and T. Wolf, “Natural Language Processing with Transformers, Revised Edition,” O’Reilly Media, Inc., Released May 2022, ISBN: 9781098136796.

1 – 懒惰的程序员博客

Leave a Reply

Your email address will not be published. Required fields are marked *