跳转至

What Is ChatGPT Doing … and Why Does It Work?

Stephen Wolfram, February 14, 2023

1. It’s Just Adding One Word at a Time

ChatGPT 可以自动生成读起来甚至表面上像人类书写的文本,这一点非常了不起,也出乎意料。但它是如何做到的呢?它为什么能做到?我在这里的目的是粗略地介绍一下 ChatGPT 内部的情况,然后探讨它为什么能如此出色地生成我们认为有意义的文本。首先,我想说的是,我将专注于事情的全貌--虽然我会提到一些工程细节,但我不会深入探讨。(我所说内容的精髓同样适用于当前的其他 "大型语言模型"[LLMs],也适用于 ChatGPT)。

首先要解释的是,ChatGPT 从根本上说一直在努力做的事情就是对目前所获得的文本进行 "合理的延续",这里的 "合理 "指的是 "在看到人们在数十亿个网页上所写的内容等之后,人们可能期望某人写出的内容"。

比方说,我们有这样一段文字:"The best thing about AI is its ability to"。想象一下,扫描数十亿页人类书写的文本(比如网络上和数字化书籍中的文本),然后找到这些文本的所有实例--然后再看看下一个词出现的频率是多少。ChatGPT 就能有效地做到这一点,只不过(正如我将解释的那样)它并不看文字的字面意思,而是寻找在某种意义上 "意义匹配 "的东西。但最终结果是,它会生成一个可能排在后面的词的排序列表以及 "概率":

Alt text

最了不起的是,当 ChatGPT 做一些类似于写作文的事情时,它所做的本质上只是一遍又一遍地询问 "鉴于目前的文本,下一个词应该是什么?"--每次都添加一个词。(更准确地说,正如我将要解释的那样,它是在添加一个 "token",这可能只是一个词的一部分,这就是为什么它有时会 "创造新词")。

但是,好吧,在每个步骤中,它都会得到一个带概率的单词表。但它究竟应该选择哪个词添加到它正在写的文章(或其他东西)中呢?人们可能会认为应该是 "排名最高 "的词(即 "概率 "最高的词)。但就在这时,一些巫术开始悄然出现。因为出于某种原因--也许有一天我们会对这种原因有科学式的理解--如果我们总是选择排名最高的单词,我们通常会得到一篇非常 "平淡 "的作文,似乎从未 "表现出任何创造性"(甚至有时会逐字重复)。但如果有时(随意)选取排名较低的词,我们就会得到一篇 "更有趣 "的文章。

这里的随机性意味着,如果我们多次使用同一个提示,每次都可能得到不同的作文。而且,为了与巫术的理念保持一致,还有一个所谓的 "temperature" 参数,它决定了排名较低的词被使用的频率,而对于作文生成来说,0.8 的 "temperature" 似乎是最好的。(值得强调的是,这里并没有使用任何 "理论";这只是一个在实践中行之有效的问题。举例来说,"温度 "的概念之所以存在,是因为恰好使用了统计物理学中熟悉的指数分布,但这与 "物理 "并无关联--至少就我们所知是这样)。

在我们继续之前,我应该解释一下,为了便于说明,我通常不会使用 ChatGPT 中的完整系统;相反,我通常会使用更简单的 GPT-2 系统,它有一个很好的特点,就是足够小,可以在标准的台式电脑上运行。因此,在我展示的所有内容中,我都会包含明确的 Wolfram 语言代码,您可以在自己的电脑上立即运行。(点击这里的任何图片,即可复制其背后的代码)。

例如,下面是获取上述概率表的方法。首先,我们必须检索底层的 "语言模型 "神经网络:

Alt text

稍后,我们将进入这个神经网络,了解它是如何工作的。但现在,我们只需将这个 "网络模型 "作为一个黑盒子,应用到我们目前的文本中,然后根据概率找出该模型认为应该遵循的前 5 个单词:

Alt text

这样就可以将结果转换成格式化的 "数据集":

Alt text

下面是重复 "应用模型 "的结果--每一步都添加概率最高的单词(在此代码中指定为模型中的 "decision"):

Alt text

如果时间再长一点会怎样?在这种("zero temperature")情况下,很快就会出现混乱和重复:

Alt text

但是,如果不总是选择 "顶级 "词,而是有时随机选择 "非顶级 "词("随机性 "相当于 "temperature "0.8),又会怎样呢?同样可以建立文本:

而每次这样做,都会做出不同的随机选择,文本也会不同,就像这 5 个例子一样:

值得注意的是,即使在第一步,也有很多可能的 "下一个词 "可供选择(温度为 0.8),尽管它们的概率下降得很快(是的,对数图上的直线对应于 \(n^{-1}\) 的 "幂律 "衰减,这是语言统计的一般特征):

那么,如果时间更长会发生什么呢?下面是一个随机例子。这个例子比最前面那个词(zero temperature)的情况要好一些,但还是有点奇怪:

这是用最简单的 GPT-2 型号(2019 年)完成的。使用更新、更大的 GPT-3 型号,效果会更好。下面是使用同样的 "提示",但使用最大的 GPT-3 型号时产生的顶部字词(zero temperature)文本:

下面是一个 "温度 0.8 "的随机例子:

2. Where Do the Probabilities Come From?

好了,ChatGPT 总是根据概率来选择下一个单词。但这些概率从何而来呢?让我们从一个更简单的问题开始。让我们考虑一次生成一个字母(而不是单词)的英文文本。我们如何计算出每个字母的概率呢?

我们可以做的最简单的事情就是抽取英文文本样本,计算不同字母在其中出现的频率。例如,计算维基百科中关于 "猫 "的文章中出现的字母:

这对 "狗 "来说也是一样:

结果相似,但不尽相同("o "无疑在 "dogs "冠词中更常见,因为毕竟它出现在 "dog "一词本身中)。尽管如此,如果我们抽取的英语文本样本足够多,我们还是可以期待最终得到至少相当一致的结果:

下面是我们用这些概率生成字母序列的示例:

我们可以通过添加空格将其分解为 "单词",就像添加具有一定概率的字母一样:

我们可以通过迫使 "单词长度 "的分布与英语中的分布相一致,在制造 "单词 "方面做得更好一些:

我们在这里并没有得到任何 "actual words",但结果看起来略有好转。不过,要想更进一步,我们需要做的不仅仅是随机挑选每个字母。举例来说,我们知道,如果有一个 "q",那么下一个字母基本上就是 "u"。

下面是字母单独出现的概率图:

下面的图表显示了典型英语文本中成对字母("2-grams")的概率。可能出现的第一个字母显示在页面的横向,第二个字母显示在页面的纵向:

例如,我们可以看到,除了 "u "行之外,"q "列是空白的(概率为零)。好了,现在我们不再一次生成一个字母的 "单词",而是使用这些 "2-gram "概率,一次生成两个字母的 "单词"。下面是一个结果样本--其中恰好包含了一些 "实际单词":

有了足够多的英文文本,我们不仅可以对单个字母或字母对(2-grams)的概率,还可以对较长的字母组合的概率做出相当准确的估计。如果我们生成的 "随机单词 "的 n-gram 概率逐渐变长,我们就会发现它们逐渐变得 "more realistic":

但是,让我们现在假设--或多或少像 ChatGPT 所做的那样--我们处理的是整个单词,而不是字母。英语中大约有 4 万个比较常用的单词。通过查看大量的英语文本语料库(比如几百万本书,共几千亿个单词),我们可以估算出每个单词的常用程度。利用这一点,我们就可以开始生成 "句子",其中的每个单词都是独立随机抽取的,与出现在语料库中的概率相同。下面是我们得到的一个样本:

毫不奇怪,这是无稽之谈。那么,我们怎样才能做得更好呢?就像处理字母一样,我们不仅可以开始考虑单词的概率,还可以考虑词对或更长的 n-gram 的概率。下面是我们从 "cat"(猫)这一单词开始得到的 5 个例子:

它看起来越来越 "合理 "了。我们可以想象,如果我们能够使用足够长的 n-gram,我们基本上就能 "得到一个 ChatGPT"--也就是说,我们会得到一个东西,它能生成具有 "正确的整体作文概率 "的作文长单词序列。但问题是:我们根本没有足够多的英语文章来推导出这些概率。

在对网络的抓取中,可能有几千亿个单词;在已经数字化的书籍中,可能又有几千亿个单词。但是,即使是 40,000 个常用词,可能出现的 2-grams 的数量也已经达到了 16 亿,而可能出现的 3-grams 的数量更是高达 60 万亿。因此,我们根本无法从现有的文本中估算出所有这些词的概率。而到了 20 个单词的 "文章片段 "时,可能性的数量已经超过了宇宙中粒子的数量,所以从某种意义上说,我们永远不可能把它们全部写下来。

那么我们能做些什么呢?最重要的想法是建立一个模型,让我们能够估算出序列出现的概率--即使我们从未在所查看的文本语料库中明确看到过这些序列。而 ChatGPT 的核心正是一个所谓的 "大型语言模型"(LLM),它的建立可以很好地估计这些概率。

3. What Is a Model?

假设你想知道(就像伽利略在 15 世纪晚期所做的)从比萨斜塔的每一层投下一颗炮弹到落地需要多长时间。那么,你可以测量每种情况下的时间,并将结果列成表格。或者,你也可以采用理论科学的精髓:建立一个模型,给出计算答案的某种程序,而不仅仅是测量和记住每种情况。

假设我们有炮弹从不同楼层落下所需的时间数据(有点理想化):

我们如何计算出从我们没有明确数据的地板上掉下来需要多长时间?在这种特殊情况下,我们可以利用已知的物理定律来计算。但是,如果我们只有数据,而不知道有什么基本定律支配着它。那么,我们可以做一个数学上的猜测,比如,也许我们应该用一条直线作为模型:

我们可以选择不同的直线。但这是平均而言最接近所给数据的一条直线。根据这条直线,我们可以估算出任何楼层的下降时间。

我们怎么会知道在这里使用直线呢?在某种程度上,我们并不知道。这只是数学上很简单的东西,而我们已经习惯了这样一个事实,即我们测量的很多数据都被数学上简单的东西很好地拟合了。我们可以尝试一些数学上更复杂的方法--比如说 \(a + bx + cx^2\) --然后在这种情况下,我们会做得更好:

不过也有可能出错。比如,我们用 \(a + b/x + c\sin(x)\) 的话最好只能做成这样:

值得理解的是,从来没有 "model-less model"。你所使用的任何模型都有一些特定的底层结构,然后有一组 "你可以转动的旋钮"(即你可以设置的参数)来适应你的数据。在 ChatGPT 中,使用了大量这样的 "旋钮"--实际上有 1750 亿个。

但难能可贵的是,ChatGPT 的底层结构--"仅仅 "有这么多参数--就足以建立一个模型,"足够好地 "计算下一个词的概率,从而为我们提供合理的长篇文章。

4. Models for Human-Like Tasks

我们上面举的例子涉及为数字数据建立模型,而数字数据基本上来自简单物理学--我们几个世纪以来就知道 "简单数学适用"。但对于 ChatGPT,我们必须建立一个由人脑生成的人类语言文本模型。而对于这样的东西,我们(至少目前)还没有类似于 "简单数学 "的东西。那么,它的模型会是什么样的呢?

在谈语言之前,我们先来谈谈另一项类似人类的任务:识别图像。作为一个简单的例子,让我们来看看数字图像(没错,这是一个典型的机器学习例子):

我们可以做的一件事是为每个数字获取大量样本图像:

那么,要想知道我们输入的图像是否对应于某个特定的数字,我们只需与现有的样本进行明确的逐像素比较即可。但作为人类,我们似乎可以做得更好--因为我们仍然可以识别数字,即使它们是手写的,而且有各种修改和变形:

当我们为上面的数字数据建立模型时,我们可以利用给定的数值 \(x\),针对特定的 \(a\)\(b\) 计算出 \(a + b x\)。那么,如果我们把这里每个像素的灰度值看作某个变量 \(x_i\),是否所有这些变量的某个函数在求值时都能告诉我们图像对应哪个数字?事实证明,构建这样一个函数是可能的。不过,它并不是特别简单,这一点也不奇怪。一个典型的例子可能涉及 50 万次数学运算。

但最终的结果是,如果我们将图像的像素值集合输入这个函数,就会得到一个数字,说明我们得到的是哪位数字的图像。稍后,我们将讨论如何构建这样一个函数,以及神经网络的概念。但现在,让我们把这个函数当作一个黑盒子,输入手写数字的图像(像素值数组),然后得到这些图像对应的数字:

但这究竟是怎么回事呢?假设我们逐渐模糊一个数字。有一小段时间,我们的函数仍能 "识别 "它,这里是 "2"。但很快它就 "失去了它",开始给出 "错误 "的结果:

但为什么我们说这是 "错误 "的结果呢?在这种情况下,我们知道我们通过模糊 "2 "得到了所有图像。但是,如果我们的目标是建立一个人类识别图像的模型,那么真正要问的问题是,如果人类在不知道图像来源的情况下看到其中一张模糊的图像,会怎么做。

如果我们从函数中得到的结果通常与人类的说法一致,那么我们就有了一个 "好模型"。而一个非同小可的科学事实是,对于像这样的图像识别任务,我们现在基本上已经知道如何构建能做到这一点的函数。

我们能 "从数学上证明 "它们有效吗?嗯,不能。因为要做到这一点,我们必须对人类的行为有一套数学理论。以 "2 "图像为例,改变几个像素。我们可能会想,如果只有几个像素 "错位",我们还是应该把图像看作 "2"。但这应该做到什么程度呢?这是一个人类视觉感知的问题。是的,对于蜜蜂或章鱼来说,答案无疑是不同的,而对于假想的外星人来说,答案也可能完全不同。

5. Neural Nets

那么,我们用于图像识别等任务的典型模型究竟是如何工作的呢?目前最流行也是最成功的方法是使用神经网络。神经网络发明于 20 世纪 40 年代,其形式与今天的神经网络非常接近,可以看作是大脑工作方式的简单理想化。

人脑中约有 1,000 亿个神经元(神经细胞),每个神经元每秒能产生大约 1000 次电脉冲。这些神经元连接成一个复杂的网络,每个神经元都有树状分支,可以将电信号传递给成千上万个其他神经元。粗略估计,任何一个神经元在某一时刻是否会产生电脉冲,取决于它从其他神经元接收到的脉冲信号--不同的连接会产生不同的 "权重"。

当我们 "看到图像 "时,图像中的光子落到我们眼球后部的细胞("感光器")上,就会在神经细胞中产生电信号。这些神经细胞与其他神经细胞相连,最终这些信号会通过一连串的神经元层。正是在这个过程中,我们 "识别 "了图像,最终 "形成了 "我们 "看到了一个 2 "的想法(也许最后我们会做一些事情,比如大声说出 "2 "这个词)。

上一节中的 "黑盒 "函数就是这种神经网络的 "数学化 "版本。它恰好有 11 层(虽然只有 4 个 "核心层"):

这个神经网络并没有什么特别的 "理论来源";它只是在 1998 年作为一项工程被构建出来,并被发现可以工作的东西。(当然,这与我们将大脑描述为生物进化过程中产生的并无太大区别)。

好吧,但这样的神经网络是如何 "识别事物 "的呢?关键在于 attractors 的概念。想象一下,我们有手写的 "1 "和 "2 "的图像:

我们希望所有的 1 都 "被吸引到一个地方",而所有的 2 都 "被吸引到另一个地方"。或者换一种说法,如果一个图像 "更接近 1 "而不是 "更接近 2",我们希望它最终出现在 "1 的位置",反之亦然。

打个简单的比方,假设我们在平面上有一些用点表示的位置(在现实生活中,这些点可能是咖啡店的位置)。那么我们可以想象,从平面上的任何一点出发,我们总是希望到达最近的点(即我们总是去最近的咖啡店)。我们可以将平面划分为若干区域("吸引盆地 -- attractor basins"),这些区域被理想化的 "分水岭 -- watersheds"分隔开来:

我们可以把这看作是在执行一种 "识别任务",在这项任务中,我们并不是在识别给定图像 "最像 "哪位数字,而是直接查看给定点最接近哪个点。(我们在这里展示的 "Voronoi diagram "设置是在二维欧几里得空间中将点分开;数字识别任务可以被认为是在做非常类似的事情--但却是在由每幅图像中所有像素的灰度级组成的 784 维空间中)。

那么,我们该如何让神经网络 "完成识别任务 "呢?让我们来看看这个非常简单的例子:

我们的目标是获取与位置 {x,y} 相对应的 "输入",然后将其 "识别 "为最接近的三个点中的任何一个。换句话说,我们希望神经网络能计算出 {x,y} 的函数,比如

那么,我们如何利用神经网络来实现这一目标呢?归根结底,神经网络是理想化 "神经元 "的连接集合--通常分层排列--一个简单的例子就是:

每个 "神经元 "实际上都是用来评估一个简单的数字函数。要 "使用 "这个网络,我们只需在顶层输入数字(如坐标 x 和 y),然后让每一层的神经元 "评估它们的函数",并将结果通过网络向前传递--最终在底层产生最终结果:

在传统的(受生物学启发的)设置中,每个神经元实际上都有一组来自上一层神经元的 "输入连接",每个连接都被赋予一定的 "权重"(可以是正数或负数)。将 "前一个神经元 "的值乘以相应的权重,然后相加,再加上一个常数,最后应用一个 "阈值 -- thresholding"(或 "激活 -- activation")函数,就能确定某个神经元的值。用数学术语来说,如果一个神经元的输入为 \(x = {x1、x2 ... }\),那么我们就可以计算 \(f[w . x + b]\),其中权重 \(w\) 和常数 \(b\) 通常是为网络中的每个神经元选择的,但函数 \(f\) 通常是相同的。

计算 \(w . x + b\) 只需矩阵乘法和加法。激活函数 " \(f\) 引入了非线性(并最终导致了非凡的行为)。通常会使用各种激活函数,这里我们只使用 Ramp(或 ReLU):

对于我们希望神经网络执行的每项任务(或者说,对于我们希望它评估的每个整体功能),我们都会有不同的权重选择。(正如我们稍后将讨论的那样,这些权重通常是通过使用机器学习的方法从我们想要的输出示例中 "训练 "神经网络来确定的)。

归根结底,每个神经网络都对应着某种整体数学函数--虽然写出来可能很乱。对于上面的例子,它应该是:

ChatGPT 的神经网络也只是对应于这样一个数学函数,但实际上有数十亿个项。

不过,让我们回到单个神经元上来。下面是一些例子,说明神经元在有两个输入(代表坐标 x 和 y)的情况下,通过选择不同的权重和常数(以及作为激活函数的 Ramp)可以计算的函数:

但从上面看,更大的网络又是怎样的呢?下面是它的计算结果:

虽然还不是很 "正确",但已经接近我们上面展示的 "最近点 "函数了。

让我们看看其他神经网络的情况。在每种情况下,正如我们稍后要解释的,我们都使用机器学习来找到最佳的权重选择。然后,我们在这里展示使用这些权重的神经网络的计算结果:

更大的网络通常能更好地逼近我们的目标函数。在 "每个吸引子盆地 (attractor basin) 的中间",我们通常能得到我们想要的答案。但在边界--神经网络 "很难下定决心 "的地方--情况可能会更加混乱。

在这个简单的数学式 "识别任务 "中,"正确答案 "是什么一目了然。但在识别手写数字的问题上,就不那么清楚了。如果有人把 "2 "写得像 "7 "怎么办?尽管如此,我们仍然可以问神经网络是如何区分数字的--这给出了一个提示:

我们能 "从数学角度 "说明网络是如何进行区分的吗?并不能。它只是 "做了神经网络所做的事"。但事实证明,通常情况下,这似乎与我们人类所做的区分相当吻合。

让我们举一个更详细的例子。假设我们有猫和狗的图像。我们有一个训练有素的神经网络来区分它们。下面是它在一些例子中的表现:

现在,"正确答案 "是什么就更不清楚了。一只穿着猫咪衣服的狗怎么样?等等。无论输入什么信息,神经网络都会产生一个答案,而且答案的方式与人类的方式相当一致。正如我在上文所说,这并不是我们可以 "从第一原理中推导出 "的事实。至少在某些领域,这只是经验证明的事实。但这正是神经网络有用的一个关键原因:它们以某种方式捕捉到了 "类似人类 "的做事方式。

给自己看一张猫的照片,然后问 "为什么那是一只猫?也许你会说 "嗯,我看到了它的尖耳朵等等"。但要解释你是如何认出那是一只猫并不容易。只是你的以某种方式想到了这一点。但对于大脑来说,我们没有办法(至少现在还没有办法)"进入 "它的内部,看看它是怎么想出来的。那么(人工)神经网络呢?当你展示一张猫的图片时,可以直接看到每个 "神经元 "在做什么。但即使是获得基本的可视化效果,通常也非常困难。

在我们用于上述 "最近点 "问题的最终网络中,有 17 个神经元。在识别手写数字的网络中,有 2190 个神经元。而在我们用来识别猫和狗的网络中,有 60650 个神经元。通常情况下,要将 60650 个维度的空间可视化是非常困难的。但由于这是一个为处理图像而建立的网络,它的许多神经元层都被组织成数组,就像它正在观察的像素数组一样。

如果我们以一只典型的猫为例

那么,我们就可以用一组衍生图像来表示第一层神经元的状态--我们可以很容易地将其中许多图像解释为 "没有背景的猫 "或 "猫的轮廓":

到了第 10 层,就很难解释发生了什么:

但一般来说,我们可以说神经网络是在 "挑选出某些特征"(也许尖耳朵就是其中之一),并利用这些特征来确定图像的内容。但是,这些特征是我们有名称的特征吗,比如 "尖耳朵"?大多不是。

我们的大脑在使用类似的功能吗?大多数情况下我们并不清楚。但值得注意的是,像我们在这里展示的这种神经网络的前几层似乎能识别出图像的某些方面(如物体的边缘),这似乎与我们所知的大脑第一层视觉处理所识别出的特征相似。

但是,假设我们想要一个神经网络的 "猫识别理论"。我们可以说"看,这个特定的网络就能做到这一点"--这立刻就能让我们感觉到这是一个 "有多难的问题"(例如,可能需要多少个神经元或多少层)。但至少到目前为止,我们还没有办法 "娓娓道来 "地描述网络在做什么。也许这是因为它在计算上确实是不可还原的,除了明确地追踪每一步之外,没有一般的方法可以找到它在做什么。或者,这只是因为我们还没有 "搞清楚这门科学",还没有找出 "自然法则 "来让我们概括发生了什么。

当我们讨论用 ChatGPT 生成语言时,也会遇到同样的问题。同样,我们也不清楚是否有办法 "总结它在做什么"。但语言的丰富性和细节(以及我们的经验)可能会让我们比图像走得更远。

6. Machine Learning, and the Training of Neural Nets

到目前为止,我们一直在讨论 "已经知道 "如何完成特定任务的神经网络。但神经网络之所以如此有用(大概也适用于大脑),是因为它们不仅原则上可以完成各种任务,而且还可以逐步 "通过实例训练 "来完成这些任务。

当我们制作一个神经网络来区分猫和狗时,我们实际上并不需要编写一个程序来(比如说)明确地找到胡须;相反,我们只需要展示大量关于什么是猫什么是狗的例子,然后让网络从这些例子中 "机器学习 "如何区分它们。

问题的关键在于,训练有素的网络能从所展示的特定例子中 "归纳 "出来。正如我们在上文所看到的,这并不是说神经网络能够识别出所展示的猫咪图像中的特定像素模式,而是说神经网络能够根据我们所认为的某种 "general catness" 来区分图像。

那么,神经网络训练究竟是如何进行的呢?从根本上说,我们一直在努力寻找能让神经网络成功重现我们给出的例子的权重。然后,我们依靠神经网络以 "合理 "的方式在这些例子之间进行 "插值"(或 "泛化")。

让我们来看一个比上述最近点问题更简单的问题。让神经网络来学习函数:

为此,我们需要一个只有一个输入和一个输出的网络,例如

但我们应该使用什么样的权重呢?每给一组可能的权重,神经网络都会计算对应的函数。举例来说,下面是随机选择的几组权重的计算结果:

是的,我们可以清楚地看到,在所有这些情况下,它都无法重现我们想要的函数。那么,我们该如何找到能重现函数的权重呢?

其基本思想是提供大量的 "输入 → 输出 "示例来进行 "学习",然后尝试找到能够重现这些示例的权重。下面就是用逐渐增多的示例进行学习的结果:

在 "训练 "的每个阶段,网络中的权重都会逐步调整--我们看到,最终我们得到的网络能够成功地再现我们想要的功能。那么,我们该如何调整权重呢?我们的基本思路是在每个阶段查看离我们想要的功能 "还有多远",然后更新权重,使其更接近我们想要的功能。

为了找出 "我们离得有多远",我们要计算通常所说的 "损失函数 -- loss function"(有时也称为 "代价函数 -- cost function")。在这里,我们使用的是一个简单的(L2)损失函数,它只是我们得到的值与真实值之差的平方和。我们可以看到,随着训练过程的进行,损失函数会逐渐减小(遵循一定的 "学习曲线",不同的任务有不同的学习曲线)--直到我们达到一个点,网络(至少是一个很好的近似值)成功地再现了我们想要的函数:

好了,最后要解释的是如何调整权重以减小损失函数。正如我们所说,损失函数给出了我们得到的值与真实值之间的 "距离"。但是,"我们得到的值 "在每个阶段都是由当前版本的神经网络和其中的权重决定的。但现在想象一下,权重是变量,比如 \(w_i\)。我们想知道如何调整这些变量的值,以最小化取决于它们的损失。

例如,想象一下(对实际应用中的典型神经网络进行惊人的简化),我们只有两个权重 \(w_1\)\(w_2\)。那么我们可能会有这样一个损失,它是 \(w_1\)\(w_2\) 的函数:

在这种情况下,数值分析提供了多种寻找最小值的技术。不过,一种典型的方法是从之前的 \(w_1\)\(w_2\) 开始,沿着最陡峭下降的路径逐步求取:

就像水从山上流下一样,我们只能保证这个过程最终会到达曲面的某个局部最小值("高山湖泊"),而很可能达不到最终的全局最小值。

要在 "权重景观 "上找到最陡峭的下降路径,并不是明显可行的。不过,微积分可以帮上忙。如上所述,我们可以将神经网络看作是在计算一个数学函数--它取决于输入和权重。但现在我们可以考虑对这些权重进行微分。事实证明,微积分的链式法则实际上可以让我们 "解开 "神经网络中连续各层所做的运算。其结果是,我们可以--至少在某些局部近似情况下--"反转 "神经网络的操作,并逐步找到权重值,使输出相关的损失最小化。

上图显示了在只有 2 个权重的不切实际的简单情况下,我们可能需要做的最小化。但事实证明,即使使用更多的权重(ChatGPT 使用了 1,750 亿个权重),我们仍有可能实现最小化,至少在某种程度上是近似的。事实上,"深度学习 "在 2011 年前后取得的重大突破,就是因为人们发现,从某种意义上说,当权重较多时,比权重较少时更容易实现最小化(至少是近似最小化)。

换句话说--有点违背直觉--用神经网络解决更复杂的问题比解决更简单的问题更容易。这其中的大致原因似乎是,当 "权重变量 "较多时,就会产生一个高维空间,其中有 "许多不同的方向 "可以引导我们找到最小值,而当变量较少时,就很容易陷入局部最小值("高山湖泊"),没有 "出路"。

值得指出的是,在通常情况下,有许多不同的权重集合都能使神经网络获得基本相同的性能。而在实际的神经网络训练中,通常会有很多随机选择,从而产生 "不同但等效的解决方案",就像这些:

但是,每一种 "不同的解决方案 "至少会有略微不同的行为。而且,如果我们要求在我们提供训练示例的区域之外进行 "外推法",我们可能会得到截然不同的结果:

但哪一种才是 "正确 "的呢?真的无从说起。它们都 "符合观察到的数据"。但它们都对应着不同的 "先天""思维 "方式,即 "跳出框框 "来 "思考 "该怎么做。在我们人类看来,有些可能比另一些 "更合理"。

7. The Practice and Lore of Neural Net Training

特别是在过去十年中,神经网络训练的艺术取得了许多进步。没错,这基本上就是一门艺术。有时--尤其是回过头来看--人们至少能看到 "科学解释 "的曙光。但大多数情况下,我们都是通过不断尝试和犯错来发现问题,不断添加新的想法和技巧,从而逐步建立起关于如何使用神经网络的重要知识(lore)。

有几个关键部分。首先是针对特定任务应该使用哪种结构的神经网络。然后是如何获得训练神经网络的数据这一关键问题。越来越多的情况下,我们并不是要从头开始训练一个神经网络:相反,一个新的神经网络可以直接吸收另一个已经训练过的神经网络,或者至少可以利用那个神经网络为自己生成更多的训练实例。

人们可能会认为,对于每一种特定的任务,都需要不同结构的神经网络。但我们发现,即使是表面上截然不同的任务,相同的架构似乎也能奏效。在某种程度上,这让人想起通用计算的概念(以及我的计算等价原则),但正如我稍后将要讨论的,我认为这更多地反映了一个事实,即我们通常试图让神经网络完成的任务是 "类人 "任务,而神经网络可以捕捉到相当普遍的 "类人过程"。

在神经网络发展的早期,人们往往认为应该 "让神经网络做尽可能少的事情"。例如,在将语音转换为文本时,人们认为应该首先分析语音音频,将其分解为音素等。但人们发现,至少对于 "类人任务 "来说,通常最好是尝试训练神经网络处理 "端到端问题",让它自己 "发现 "必要的中间特征、编码等。

还有人认为,应该在神经网络中引入复杂的单个组件,让它实际上 "明确实现特定的算法思想"。但事实再次证明,这在大多数情况下是不值得的;相反,最好的办法是处理非常简单的组件,让它们 "自我组织"(尽管通常是以我们无法理解的方式),以实现(大概)这些算法思想。

这并不是说没有与神经网络相关的 "结构思想"。因此,举例来说,具有局部连接的二维神经元阵列至少在处理图像的早期阶段非常有用。在处理人类语言等问题时,例如在 ChatGPT 中,具有集中于 "序列回溯 "的连接模式似乎也很有用--我们稍后会看到这一点。

但神经网络的一个重要特点是,与一般计算机一样,它们最终只是在处理数据。而目前的神经网络--目前的神经网络训练方法--专门处理数字阵列。但在处理过程中,这些数组可以完全重新排列和重塑。举例来说,我们上面用于识别数字的网络从二维 "图像 "阵列开始,迅速 "增厚 "到多个通道,然后 "缩小 "到一维阵列,最终包含代表不同可能输出数字的元素:

但是,好吧,怎样才能知道特定任务需要多大的神经网络呢?这是一门艺术。在某种程度上,关键是要知道 "任务有多难"。但对于类似人类的任务来说,这通常很难估算。是的,也许有一种系统的方法可以让计算机非常 "机械 "地完成任务。但我们很难知道,是否存在人们可能认为的窍门或捷径,能让我们至少在 "类人水平 "上更轻松地完成任务。要 "机械地 "玩某个游戏,可能需要枚举一棵巨大的游戏树;但要达到 "人类水平的游戏",可能有更简单("启发式")的方法。

在处理微小神经网络和简单任务时,有时我们可以清楚地看到 "无法从这里到达那里"。例如,在上一节的任务中,我们用几个小神经网络来完成这个任务,似乎最好只能做到这样:

我们发现,如果网络太小,就无法再现我们想要的功能。但超过一定的规模,它就没有问题了--至少只要训练的时间足够长,训练的例子足够多。顺便说一句,这些图片说明了神经网络的一个传说:如果中间有一个 "挤压",迫使所有东西都要经过较少的中间神经元数量,那么通常就可以使用较小的网络。(值得一提的是,"无中间层"--或所谓的 "感知器"--网络只能学习基本的线性函数,但只要有一个中间层,原则上就可以任意逼近任何函数,至少只要有足够多的神经元。尽管为了使其具有可训练性,通常需要进行某种正则化或归一化处理)

好了,假设我们已经确定了某种神经网络架构。现在的问题是如何获得训练网络所需的数据。围绕神经网络和机器学习的许多实际挑战都集中在获取或准备必要的训练数据上。在许多情况下("监督学习"),人们希望获得输入和预期输出的明确示例。因此,举例来说,人们可能希望通过图像中的内容或其他属性来标记图像。也许我们需要明确地去做标记,通常要花费很大的力气。但很多时候,我们可以利用已经完成的工作,或者将其作为某种代理。例如,我们可以使用网络上为图片提供的alt标签。或者,在另一个领域,我们可以使用为视频制作的封闭式字幕。或者,在语言翻译训练中,我们可以使用不同语言版本的平行网页或其他文档。

你需要向神经网络展示多少数据才能训练它完成特定任务?同样,这也很难根据第一原理进行估算。当然,通过使用 "迁移学习 "来 "迁移 "另一个网络已经学习过的重要特征列表等,可以大大降低要求。但一般来说,神经网络需要 "看到大量示例 "才能得到良好的训练。至少对于某些任务来说,神经网络的一个重要经验是,例子可以重复得令人难以置信。事实上,向神经网络展示所有示例是一种标准策略,而且是一遍又一遍地重复。在每一轮 "训练"(或 "epochs")中,神经网络至少会处于略微不同的状态,而以某种方式 "提醒它 "某个特定的例子,有助于让它 "记住那个例子"。(是的,这也许类似于人类记忆中重复的作用)。

但是,仅仅重复同样的例子往往是不够的。还需要向神经网络展示这些示例的变化。神经网络的一个特点是,这些 "数据增强 "变化并不一定要很复杂才有用。只需用基本的图像处理方法对图像稍作修改,就能使其在神经网络训练中 "焕然一新"。同样,当用于训练自动驾驶汽车的实际视频等耗尽时,我们可以继续在一个类似于视频游戏的模型环境中运行模拟,从中获取数据,而不需要真实世界场景的所有细节。

像 ChatGPT 这样的软件怎么样?它有一个很好的特点,就是可以进行 "无监督学习",从而更容易获得训练用的示例。回想一下,ChatGPT 的基本任务是找出如何续写一段给定的文本。因此,要让它获得 "训练示例",只需获取一段文本,屏蔽掉文本结尾,然后将其作为 "训练输入"--"输出 "则是完整的、未屏蔽的文本。我们稍后会详细讨论这个问题,但重点是,与学习图像中的内容不同,不需要 "明确标记";ChatGPT 实际上可以直接从给定的文本示例中学习。

那么,神经网络的实际学习过程又是怎样的呢?归根结底,就是要确定什么样的权重能最好地捕捉所给的训练示例。有各种各样的细节选择和 "超参数设置"(之所以称为 "超参数设置",是因为权重可以被看作是 "参数"),可以用来调整学习过程。损失函数有不同的选择(平方和、绝对值总和等)。损失最小化有不同的方法(每一步在权重空间中移动多远等)。还有一些问题,比如需要展示多大 "批量 "的示例,才能获得试图最小化的损失的每次连续估计值。是的,我们可以应用机器学习(例如,我们在 Wolfram 语言中就是这样做的)来自动进行机器学习,并自动设置超参数等。

但最终,整个训练过程可以通过观察损失是如何逐步减少来体现(如 Wolfram 语言对一个小训练的进度监控):

我们通常会看到,损失会在一段时间内减少,但最终会在某个恒定值上趋于平缓。如果这个值足够小,那么就可以认为训练是成功的;否则,这可能是一个信号,表明我们应该尝试改变网络架构。

能知道 "学习曲线 "需要多长时间才能趋于平缓吗?就像许多其他事情一样,似乎存在近似的幂律比例关系,这取决于神经网络的大小和使用的数据量。但总的结论是,训练神经网络很难--需要耗费大量的计算精力。而实际上,绝大多数的计算工作都是在对数组进行运算,而这正是 GPU 所擅长的--这就是为什么神经网络训练通常会受到 GPU 可用性的限制。

未来,是否会有从根本上更好的方法来训练神经网络,或者做神经网络能做的事情?我认为几乎可以肯定。神经网络的基本理念是利用大量简单(基本相同)的组件创建一个灵活的 "计算结构",而且这个 "结构 "可以逐步修改,以便从实例中学习。在当前的神经网络中,人们基本上是在使用微积分的思想--应用于实数--来进行增量修改。但越来越明显的是,拥有高精度的数字并不重要;即使使用目前的方法,8 bits 或更少可能就足够了。

像元胞自动机这样的计算系统,基本上是在许多单独的比特上并行操作的,如何进行这种增量修改一直都不清楚,但没有理由认为不可能。事实上,就像 "2012 年深度学习的突破 "一样,这种增量修改在更复杂的情况下可能比在简单的情况下更容易。

神经网络--或许有点像大脑--被设定为拥有一个基本固定的神经元网络,改变的是它们之间连接的强度("权重")。(也许至少在年轻的大脑中,大量全新的连接也可以生长)。不过,虽然这可能是一种方便的生物学设置,但我们并不清楚这是否是实现我们所需的功能的最佳方法。而一种相当于渐进式网络重写的方法(也许会让人想起我们的物理项目)最终可能会更好。

但即使是在现有的神经网络框架内,目前也存在一个关键的限制:目前的神经网络训练从根本上说是按顺序进行的,每批实例的效果都会传播回去,以更新权重。事实上,在目前的计算机硬件条件下,即使考虑到 GPU,神经网络在训练过程中的大部分时间都是 "闲置 "的,每次只更新一部分。从某种意义上说,这是因为我们目前的计算机往往拥有独立于 CPU(或 GPU)的内存。但在大脑中,情况可能有所不同--每一个 "内存元素"(即神经元)同时也是一个潜在的活跃计算元素。如果我们能以这种方式设置未来的计算机硬件,就有可能更高效地进行训练。

8. “Surely a Network That’s Big Enough Can Do Anything!”

像 ChatGPT 这样的功能看起来如此令人印象深刻,以至于人们可能会想象,如果可以 "继续下去",训练出越来越大的神经网络,那么它们最终就能 "无所不能"。如果我们关注的是那些容易被人类即时思维所利用的事物,那么情况很有可能就是这样。但过去几百年科学发展的教训是,有些东西可以通过形式化的过程来解决,但人类的即时思维却无法轻易触及。

非一般性(Nontrivial)的数学就是一个很好的例子。但一般情况实际上是计算问题。归根结底,问题在于计算的不可约性(computational irreducibility)。有些计算,人们可能会认为需要很多步骤才能完成,但事实上却可以 "简化 "为非常直接的事情。但计算不可约性的发现意味着,这并不总是可行的。相反,有些过程--很可能就像下面这个--要想知道发生了什么,必然需要对每个计算步骤进行本质上的追踪:

我们通常用大脑做的事情,大概都是为了避免计算的不可还原性而特别选择的。用大脑做数学题需要特别的努力。而实际上,仅靠大脑 "思考来穿过 "任何非简单程序的操作步骤基本上是不可能的。

当然,为此我们有计算机。有了计算机,我们就可以轻而易举地完成漫长的、计算上不可还原的事情。关键在于,一般来说,这些都没有捷径可走。

是的,我们可以记住很多特定计算系统中发生的具体例子。也许我们甚至可以看到一些("计算可还原性")模式,让我们可以做一些概括。但问题是,计算的不可还原性意味着我们永远无法保证意外不会发生--只有明确地进行计算,你才能知道在任何特定情况下实际发生了什么。

归根结底,可学习性与计算不可还原性之间存在着根本性的矛盾。学习实际上是通过利用规律性来压缩数据。但计算的不可还原性意味着,规则性的存在最终是有限度的。

在实践中,我们可以想象将一些小的计算设备,如元胞自动机(cellular automata)或图灵机,构建成神经网络这样的可训练系统。事实上,这些设备可以作为神经网络的良好 "工具",就像 Wolfram|Alpha 可以作为 ChatGPT 的良好工具一样。但计算的不可还原性意味着,我们不能指望 "进入 "这些设备,让它们学习。

或者换一种说法,在计算能力和可训练性之间存在一个最终的权衡:你越想让一个系统 "真正利用 "它的计算能力,它就越会表现出计算上的不可重复性,它的可训练性也就越低。而从根本上说,可训练性越强,它就越无法进行复杂的计算。

(对于目前的 ChatGPT 来说,情况实际上要极端得多,因为用于生成每个输出标记的神经网络是一个纯粹的 "前馈 "网络,没有循环,因此无法进行任何具有非简单 "控制流 "的计算)。

当然,有人可能会问,能够进行不可还原的计算是否真的很重要?事实上,在人类历史的大部分时间里,这并不特别重要。但是,我们的现代科技世界是建立在至少使用数学计算的工程学基础之上的,而且还越来越多地使用更广泛的计算。如果我们看看自然界,它充满了不可还原的计算--我们正在慢慢了解如何模仿并用于我们的技术目的。

是的,神经网络当然可以注意到自然世界中的各种规律性,我们也可以通过 "无辅助人类思维 "轻易地注意到这些规律性。但是,如果我们想解决数学或计算科学范畴内的问题,神经网络是做不到的--除非它有效地 "使用 "一个 "普通 "的计算系统作为工具。

但是,这一切有可能让人感到困惑。过去有很多任务,包括写作文,我们都认为对计算机来说 "从根本上来说太难了"。而现在,当我们看到像 ChatGPT 这样的软件能完成这些任务时,我们往往会突然想到,计算机一定是变得更加强大了,尤其是超越了它们原本基本上就能做到的事情(比如逐步计算像元胞自动机这样的计算系统的行为)。

但这并不是正确的结论。计算上不可还原的过程仍然是计算上不可还原的过程,对计算机来说从根本上说仍然是困难的--即使计算机可以很容易地计算它们的各个步骤。相反,我们应该得出的结论是,那些我们人类可以完成,但我们认为计算机无法完成的任务,比如写作文,其实在某种意义上比我们想象的更容易计算。

换句话说,神经网络之所以能在写作文方面取得成功,是因为写作文原来是一个比我们想象的 "计算上更浅显 "的问题。从某种意义上说,这让我们更接近于 "拥有一套理论",来解释我们人类是如何完成写作文这样的事情,或者是如何处理语言的。

如果你有一个足够大的神经网络,那么,是的,人类能做的事情你也许都能做。但是,你无法捕捉到自然界所能做到的一切,也无法捕捉到我们从自然界中创造出来的工具所能做到的一切。正是这些工具的使用--无论是实用的还是概念性的--让我们在近几个世纪里超越了 "纯粹的无辅助的人类思维 "所能达到的界限,并为人类捕捉到了物理和计算宇宙中更多的东西。

9. The Concept of Embeddings

神经网络--至少就目前的设置而言--从根本上说是基于数字的。因此,如果我们要用神经网络来处理像文本这样的内容,就需要一种用数字来表示文本的方法。当然,我们可以从给字典中的每个单词分配一个数字开始(就像 ChatGPT 所做的那样)。但有一个重要的想法--例如,它是 ChatGPT 的核心--远不止于此。这就是 "嵌入 (embeddings)"的概念。我们可以把 "嵌入 "看作是一种尝试用数字数组来表示事物 "本质 "的方法--其特性是 "附近的事物 "用附近的数字来表示。

因此,举例来说,我们可以把单词嵌入看作是试图在一种 "意义空间 "中布局单词,在这种 "意义空间 "中,在某种程度上 "意义相近 "的单词会在嵌入中出现。实际使用的嵌入--比如在 ChatGPT 中--往往涉及大量的数字列表。但是,如果我们将其投影到二维空间,就可以举例说明单词是如何被嵌入的:

是的,我们看到的东西在捕捉典型的日常印象方面做得非常好。但我们如何才能构建这样的嵌入呢?大致的思路是查看大量文本(这里是来自网络的 50 亿个单词),然后看看不同单词出现的 "环境""有多相似"。例如,"alligator"(鳄鱼)和 "crocodile"(鳄鱼)经常会在其他相似的句子中交替出现,这意味着它们会被放在嵌入的附近。但是,"萝卜 "和 "鹰 "不会经常出现在其他相似的句子中,因此它们在嵌入式中的位置会相距甚远。

但是,如何使用神经网络真正实现这样的功能呢?首先,我们要讨论的不是文字嵌入,而是图像嵌入。我们希望找到某种方法,通过数字列表来描述图像的特征,从而为 "我们认为相似的图像 "分配相似的数字列表。

如何判断是否应该 "认为图像相似"?比如说,如果我们的图像是手写数字,那么如果两个图像的数字相同,我们就可以 "认为相似"。前面我们讨论过一个神经网络,它经过训练可以识别手写数字。我们可以认为,这个神经网络的最终输出是将图像放入 10 个不同的分区,每个数字一个分区。

但是,如果我们在做出 "是'4'"的最终决定之前 "截取 "神经网络内部发生的事情呢?我们可能会想到,在神经网络中会有一些数字来描述图像 "大部分像 4,但也有一点像 2 "之类的特征。而我们的想法就是拾取这些数字作为嵌入的元素。

概念是这样的。我们不直接尝试描述 "什么图像靠近其他什么图像",而是考虑一个明确定义的任务(在本例中为数字识别),我们可以为此获得明确的训练数据--然后利用这样一个事实,即在完成这项任务时,神经网络隐含地必须做出相当于 "近似性决策 "的决定。因此,我们不必明确谈论 "图像的近似性",而只是谈论图像代表哪个数字的具体问题,然后 "让神经网络 "来隐含地确定这意味着什么 "图像的近似性"。

那么,在数字识别网络中,这又是如何详细实现的呢?我们可以将该网络视为由 11 个连续层组成,并将其图标化(激活函数显示为独立层):

一开始,我们将实际图像输入第一层,图像由像素值的二维数组表示。在最后一层,我们将得到一个包含 10 个值的数组,我们可以将其理解为网络 "有多确定 "图像对应的是 0 到 9 中的每一位数字。

换句话说,神经网络此时已经 "非常确定 "这幅图像是 4--要真正得到输出 "4",我们只需找出神经元中数值最大的那个位置。

但如果我们再往前看一步呢?网络的最后一个操作是所谓的软最大值,它试图 "强制确定性 (force certainty)"。但在这之前,神经元的值是

代表 "4 "的神经元仍然具有最高的数值。但其他神经元的数值中也蕴含着信息。我们可以预期,这一系列数字在某种意义上可以用来描述图像的 "本质",从而提供一些我们可以用作嵌入的信息。因此,举例来说,这里的每个 4 都有略微不同的 "特征"(或 "特征嵌入")--都与 8 非常不同:

在这里,我们基本上使用 10 个数字来描述图像的特征。但通常情况下,使用更多的数字会更好。例如,在我们的数字识别网络中,我们可以通过前一层得到一个包含 500 个数字的数组。这可能是一个合理的 "图像嵌入 "数组。

如果我们想对手写数字的 "图像空间 "进行明确的可视化,就需要 "降维",即有效地将我们得到的 500 维向量投射到三维空间中:

我们刚才谈到,图像的特征描述(以及嵌入)有效地基于通过确定图像是否(根据我们的训练集)对应于相同的手写数字来识别图像的相似性。如果我们有一个训练集,可以识别出每张图像属于 5000 种常见物体类型(猫、狗、椅子......)中的哪一种,那么我们就可以更普遍地对图像做同样的事情。通过这种方式,我们就能根据对常见物体的识别 "锚定 "图像嵌入,然后再根据神经网络的行为 "泛化"。关键在于,只要这种行为与我们人类感知和解释图像的方式一致,那么这种嵌入最终就会 "在我们看来是正确的",并且在执行 "类似人类判断 "的任务时非常有用。

那么,我们如何用同样的方法来寻找单词的嵌入呢?关键在于,我们要从一项有关单词的任务出发,而我们可以很容易地对这项任务进行训练。标准的此类任务就是 "词语预测"。想象一下,我们得到了"the ___ cat"。基于大量的文本语料库(比如网络文本内容),可能 "填空 "的不同词语的概率是多少?或者,给定"black",不同 "侧翼词 "的概率是多少?

我们如何为神经网络设置这个问题?最终,我们必须用数字来表示一切。其中一种方法就是为英语中的 50,000 个左右常用词各分配一个唯一的数字。例如,"the "可能是 914,而 "cat"(前有空格)可能是 3542。(对于 "the ___ cat "这个问题,我们的输入可能是{914, 3542}。输出应该是什么样的呢?嗯,它应该是一个包含 50,000 个左右数字的列表,这些数字有效地给出了每个可能的 "填词 "的概率。同样,为了找到嵌入,我们要在神经网络 "得出结论 "之前 "拦截 "它的 "内部"--然后拾取出现在那里的数字列表,我们可以将其视为 "每个单词的特征"。

那么,这些特征是什么样的呢?在过去的 10 年里,已经开发出了一系列不同的系统(word2vec、GloVe、BERT、GPT......),每个系统都基于不同的神经网络方法。但归根结底,所有这些系统都是通过数百到数千个数字的列表来表征单词。

这些 "嵌入向量 "的原始形式信息量很小。例如,下面是 GPT-2 为三个特定单词生成的原始嵌入向量:

如果我们测量这些向量之间的距离,就能发现词语的 "近似度"。稍后,我们将更详细地讨论这种嵌入的 "认知 "意义。但现在的重点是,我们有办法将单词转化为 "神经网络友好 "的数字集合。

但实际上,我们不仅可以用数字集合来表征单词,还可以对单词序列或整个文本块进行表征。ChatGPT 就是这样处理问题的。它将目前得到的文本生成一个嵌入向量来表示它。然后,它的目标就是找出接下来可能出现的不同单词的概率。它将答案表示为一串数字,这些数字基本上给出了 50,000 个左右可能出现的单词中每个单词的概率。

严格来说,ChatGPT 并不处理单词,而是处理 "标记 -- tokens"--方便的语言单位,可能是整个单词,也可能只是像 "pre"、"ing "或 "ized "这样的片段。使用 "标记 "可以让 ChatGPT 更容易地处理罕见词、复合词和非英语词汇,有时还能发明新词(无论好坏)。

10. Inside ChatGPT

好了,我们终于可以讨论 ChatGPT 的内部结构了。没错,归根结底,它是一个巨大的神经网络--目前是所谓的 GPT-3 网络的一个版本,拥有 1750 亿个权值。在很多方面,它与我们讨论过的其他神经网络非常相似。但它是一个特别为处理语言而设置的神经网络。它最显著的特征是一个名为 "转换器 (transformer)" 的神经网络架构。

在我们上面讨论的第一种神经网络中,任何给定层上的每个神经元基本上都与前一层上的每个神经元相连(至少有一定的权重)。但是,如果要处理的数据具有特定的已知结构,这种全连接网络(大概)就显得多余了。因此,例如在处理图像的早期阶段,通常会使用所谓的卷积神经网络("convnets"),在这种网络中,神经元被有效地布局在类似于图像像素的网格上,并且只与网格上附近的神经元相连。

Transformer 的理念是对组成文本的标记序列做一些至少类似的处理。但 transformer 并不只是在序列中定义一个固定的区域来建立连接,而是引入了 "注意力 (attention)" 的概念 -- 以及对序列中某些部分的 "注意力 "比对其他部分的 "注意力 "更多的概念。也许有一天,只需启动一个通用的神经网络,然后通过训练来完成所有的定制工作会更有意义。但至少从目前来看,"模块化 (modularize)" 在实践中似乎至关重要 -- transformer 是这样,我们的大脑可能也是这样。

好了,那么 ChatGPT(或者说,它所基于的 GPT-3 网络)究竟是如何工作的?回想一下,它的总体目标是根据它所接受的训练(包括查看数十亿页的网络文本等),以 "合理 "的方式续写文本。因此,在任何给定的时间点,它都有一定数量的文本作为输入 -- 它的目标是为下一个要添加的标记 (token) 提出一个合适的选择。

它的运行分为三个基本阶段。 - 首先,它获取与迄今为止的文本相对应的标记序列,并找到代表这些标记的嵌入(即数字数组)。 - 然后,它以 "标准神经网络方式 "对该嵌入进行操作,使数值 "波及 "网络中的连续层,从而产生一个新的嵌入(即一个新的数字数组)。 - 然后,它提取这个数组的最后一部分,并从中生成一个包含约 50,000 个值的数组,这些值将转化为下一个不同可能词块的概率(没错,使用的词块数量恰好与英语中的常用词数量相同,不过只有约 3000 个词块是完整的单词,其余的都是片段)。

关键的一点是,这一管道的每个部分都是由神经网络实现的,其权重由网络的端到端训练决定。换句话说,实际上,除了整体架构之外,没有任何其他部分是 "明确设计 "的;一切都只是从训练数据中 "学习 "出来的。

不过,在架构的设置方式上还有很多细节 -- 反映了各种经验和神经网络的知识 (lore)。尽管这肯定会让人一头雾水,但我认为谈论其中的一些细节还是很有帮助的,尤其是可以让人了解构建 ChatGPT 的过程。

首先是嵌入模块。下面是它在 GPT-2 中的 Wolfram 语言示意图:

输入是由 n 个词组组成的向量(如上一节所述,用 1 到约 50,000 之间的整数表示)。每个标记都会被单层神经网络转换成一个嵌入向量(GPT-2 的嵌入向量长度为 768,ChatGPT 的 GPT-3 的嵌入向量长度为 12288)。与此同时,还有一个 "次级路径",它获取标记的(整数)位置序列,并根据这些整数创建另一个嵌入向量。最后,将标记值和标记位置的嵌入向量相加,生成嵌入模块的最终嵌入向量序列。

为什么只是将标记值和标记位置嵌入向量相加呢?我不认为这有什么特别的科学道理。我们只是尝试了各种不同的方法,而这似乎是行之有效的一种。从某种意义上说,只要设置 "大致正确",通常就可以通过充分的训练来确定细节,而不需要真正 "从工程层面理解 "神经网络是如何进行自我配置的,这也是神经网络传说 (lore) 的一部分。

下面是嵌入模块对字符串 hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello bye bye bye bye bye bye 的操作:

每个标记的嵌入向量元素都显示在页面下方,在整个页面上,我们首先看到的是 "hello "嵌入,然后是 "bye "嵌入。上面的第二个数组是位置嵌入 -- 其结构看起来有点随机,但这只是 "碰巧学到的"(在本例中是在 GPT-2 中)。

好了,在嵌入模块之后,就是 transformer 的 "重头戏 "了:一连串所谓的 "注意力模块"(GPT-2 为 12 个,ChatGPT 的 GPT-3 为 96 个)。这一切都非常复杂,让人想起典型的难以理解的大型工程系统,或者生物系统。无论如何,下面是单个 "注意力区块"(GPT-2)的示意图:

每个注意力区块内都有一系列 "注意力头"(GPT-2 为 12 个,ChatGPT 的 GPT-3 为 96 个)-- 每个注意力头都对嵌入向量中的不同值块独立运行。(是的,我们不知道为什么分割嵌入向量是个好主意,也不知道它的不同部分有什么 "含义";这只是 "行之有效 "的方法之一)。

好了,注意头有什么作用呢?基本上,它们是在标记序列(即目前生成的文本)中 "回望 "的一种方式,并将过去 "打包 "成有助于找到下一个标记的形式。在上文第一节中,我们谈到了使用 2-gram 概率来根据前一个词挑选单词。转换器中的 "关注 "机制允许 "关注 "甚至更早的词,因此有可能捕捉到动词指代名词的方式,而名词在句子中出现的时间比动词早很多。

更具体地说,注意力头的作用是以一定的权重重新组合嵌入向量中与不同标记相关的块。因此,举例来说,对于上面的 "hello, bye "字符串,第一个注意力区块(GPT-2 中)中的 12 个注意力头的 "重组权重 (recombination weights)" 模式如下("一路往回看,直到字符串序列的开头"):

经过注意头处理后,得到的 "重新加权嵌入向量"(GPT-2 的长度为 768,ChatGPT 的 GPT-3 的长度为 12,288)会通过一个标准的 "全连接 "神经网层。我们很难了解这一层在做什么。下面是它使用的 768×768 权重矩阵图(这里是 GPT-2 的权重矩阵图):

以 64×64 平均移为例,开始出现一些(随机漫步式的)结构:

是什么决定了这种结构?归根结底,大概是人类语言特征的某种 "神经网络编码"。但到目前为止,这些特征是什么还是个未知数。实际上,我们是在 "打开 ChatGPT 的大脑"(或至少是 GPT-2),然后发现,是的,里面很复杂,而且我们不理解它 -- 尽管最终它产生了可识别的人类语言。

好了,在通过一个注意力区块后,我们就得到了一个新的嵌入向量,然后这个向量又会连续通过更多的注意力区块(GPT-2 共 12 个;GPT-3 共 96 个)。每个注意力区块都有自己特定的 "注意力 "和 "全连接 "权重模式。下面是 GPT-2 第一个注意力头对 "hello, bye" 输入产生的注意力权重序列:

下面是全连接层的(移动平均)"矩阵":

奇怪的是,尽管不同注意力区块中的这些 "权重矩阵 "看起来非常相似,但权重大小的分布却可能有些不同(而且并不总是高斯分布):

那么,在经历了所有这些注意力区块之后,transformer 的最终效果是什么呢?从根本上说,它是将标记序列的原始嵌入式集合转换为最终集合。然后,ChatGPT 的特殊工作方式就是提取这个集合中的最后一个嵌入,并对其进行 "解码",从而得出下一个标记的概率列表。

以上就是 ChatGPT 的概要内容。它看似复杂(尤其是因为它有许多不可避免的、有些随意的 "工程选择"),但实际上所涉及的最终元素却非常简单。因为归根结底,我们要处理的只是一个由 "人工神经元 "组成的神经网络,每个神经元都在进行简单的操作,即接收一组数字输入,然后将它们与特定权重相结合。

ChatGPT 的原始输入是一个数字数组(迄今为止 tokens 的嵌入向量),当 ChatGPT "运行 "生成一个新 token 时,这些数字会 "波及 "神经网络的各个层,每个神经元都会 "做自己的事情",并将结果传递给下一层的神经元。没有循环或 "返回"。一切都只是在网络中 "前馈"。

这种设置与典型的计算系统--如图灵机--截然不同,后者的结果会被相同的计算元素反复 "再处理"。在这里,至少在生成一个给定的输出标记时,每个计算元素(即神经元)只使用一次。

但从某种意义上说,即使在 ChatGPT 中,仍然存在一个重复使用计算元素的 "外循环"。因为当 ChatGPT 要生成一个新 token 时,它总是会 "读取"(即把之前的整个标记序列作为输入),包括 ChatGPT 自己之前 "写入 "的 token。我们可以认为,这种设置意味着 ChatGPT 确实--至少在其最外层--涉及一个 "反馈循环",尽管在这个循环中,每一次迭代都明确地显示为一个 token,出现在它生成的文本中。

但让我们回到 ChatGPT 的核心:反复用于生成每个标记的神经网络。在某种程度上,它非常简单:一整套相同的人工神经元。网络的某些部分只是由("全连接")神经元层组成,其中给定层上的每个神经元都与前一层上的每个神经元相连(有一定权重)。但 ChatGPT 的 transformer 架构则具有更多的结构,其中只有不同层上的特定神经元才会被连接。(当然,我们仍然可以说 "所有神经元都连接在一起"--只是有些神经元的权重为零)。

此外,在 ChatGPT 中,神经网络的某些方面并不被自然地认为仅仅由 "同质 "层组成。举例来说,正如上面的图标摘要所示,在注意力区块内部,有一些地方会对输入的数据进行 "多份复制",然后每份数据都会经过不同的 "处理路径",可能涉及不同数量的层,之后才会重新组合。不过,虽然这可能是一种方便的表述方式,但至少在原则上,"密集填充 "各层也是可能的,只不过有些权重为零而已。

如果查看 ChatGPT 的最长路径,大约有 400 个(核心)层参与其中--从某种程度上来说,这并不是一个庞大的数字。但这里有数百万个神经元--总共有 1750 亿个连接,因此也就有 1750 亿个权重。要知道,每当 ChatGPT 生成一个新的标记时,它都要对每一个权重进行计算。从实现上来说,这些计算可以 "按层 "组织成高度并行的数组运算,方便地在 GPU 上完成。但每生成一个标记,仍要进行 1750 亿次计算(最后还要更多一点)--因此,用 ChatGPT 生成一段长文本需要一段时间也就不足为奇了。

但归根结底,最了不起的是,所有这些操作--虽然各自都很简单--却能以某种方式共同完成如此出色的 "类人 "文本生成工作。必须再次强调的是(至少就我们所知),没有任何 "终极理论理由 "可以解释为什么这样的事情会成功。事实上,正如我们将要讨论的那样,我认为我们必须把这看作是一个可能令人吃惊的科学发现:在像 ChatGPT 这样的神经网络中,我们有可能捕捉到人类大脑在生成语言方面所做工作的本质。

11. The Training of ChatGPT

好了,现在我们已经大致介绍了 ChatGPT 安装后的工作原理。但它是如何建立起来的呢?神经网络中的 1750 亿个权重是如何确定的?从根本上说,这些权重是基于人类撰写的大量网络、书籍等文本语料进行大规模训练的结果。正如我们所说,即使有了这么多的训练数据,神经网络能否成功生成 "类人 "文本也并不明显。而且,要做到这一点,似乎还需要详细的工程设计。但是,ChatGPT 的最大惊喜和发现就在于它是可行的。实际上,一个 "只有 "1750 亿权重的神经网络就能为人类书写的文本建立一个 "合理的模型"。

在现代,有大量人类撰写的文本以数字形式存在。公共网络上至少有几十亿人类撰写的网页,总共可能有一万亿字的文本。如果把非公开网页也包括在内,这个数字可能至少要大 100 倍。迄今为止,已有 500 多万本数字化书籍面世(在已出版的 1 亿本左右的书籍中),这又提供了 1 000 亿字左右的文本。这还不包括从视频等语音中提取的文本(作为个人比较,我一生发表的材料总产量略低于 300 万字,在过去 30 年里,我写了大约 1500 万字的电子邮件,总共打了大约 5000 万字--仅在过去几年里,我在现场直播中就讲了超过 1000 万字。是的,我会从所有这些中训练出一个机器人)。

但是,好吧,有了这些数据,我们该如何从中训练神经网络呢?基本过程就像我们在上面的简单例子中讨论的那样。先呈现一批示例,然后调整网络中的权重,使网络在这些示例上产生的误差("损失")最小。从误差 "反向传播 "的主要代价是,每次这样做时,网络中的每个权重通常都会至少发生一点点变化,因此需要处理的权重非常多。(实际的 "反向计算 "通常只比正向计算难一个很小的常数因子)。

利用现代 GPU 硬件,可以直接并行计算成千上万个实例的结果。但在实际更新神经网络中的权重时,目前的方法基本上需要逐批更新。(是的,这可能是真正的大脑--其计算和内存元素相结合--至少目前在架构上具有优势的地方)。

即使在我们之前讨论过的学习数字函数这种看似简单的情况下,我们也发现经常需要使用数百万个示例才能成功训练一个网络,至少要从零开始。那么,这意味着我们需要多少例子才能训练出一个 "类人语言 "模型呢?似乎没有任何基本的 "理论 "方法可以知道。但在实践中,ChatGPT 成功地在几千亿字的文本上进行了训练。

有的文本它看了好几遍,有的只看了一遍。但不知何故,它从看到的文本中 "得到了它需要的东西"。但是,考虑到需要学习的文本数量,它需要多大的网络才能 "学得好 "呢?同样,我们还没有一个基本的理论方法来回答这个问题。归根结底--我们将在下文中进一步讨论--人类语言和人类通常所说的话应该有一定的 "总算法内容"。但接下来的问题是,神经网络在根据算法内容建立模型时会有多高效。同样,我们也不知道--虽然 ChatGPT 的成功表明它的效率相当高。

最后,我们可以注意到,ChatGPT 使用了几千亿个权重来完成它的工作--在数量上与它所获得的训练数据的单词(或标记)总数相当。从某种程度上说,看起来运行良好的 "网络规模 "与 "训练数据规模 "相当,这或许令人惊讶(尽管我们也在 ChatGPT 的较小类似系统中观察到了经验)。毕竟,这肯定不是说 "ChatGPT 内部 "以某种方式 "直接存储 "了来自网络和书籍等的所有文本。因为 ChatGPT 内部实际上是一堆精确度不到 10 位的数字,它们是对所有文本的总体结构进行的某种分布式编码。

换句话说,我们可以问,人类语言的 "有效信息内容 "是什么,通常用它来表达什么。这就是语言实例的原始语料库。然后是 ChatGPT 神经网络中的表征。这种表征很可能与 "算法最小 "的表征相去甚远(我们将在下文讨论)。但它是神经网络可以随时使用的表征。而且在这种表示法中,训练数据的 "压缩 "似乎并不明显;平均看来,一个神经网络的权重只需略低于一个神经网络的权重,就能承载一个词的训练数据的 "信息内容"。

当我们运行 ChatGPT 生成文本时,基本上每个权重都要使用一次。因此,如果有 n 个权重,我们就有 n 个计算步骤要做--尽管在实践中,许多步骤通常都可以在 GPU 中并行完成。但是,如果我们需要大约 n 个字的训练数据来设置这些权重,那么根据上文所述,我们可以得出结论,我们需要大约 \(n^2\) 个计算步骤来完成网络的训练,这就是为什么使用当前的方法,我们最终需要花费数十亿美元来进行训练。

12. Beyond Basic Training

在训练 ChatGPT 的过程中,大部分精力都花在向它 "展示 "大量来自网络、书籍等的现有文本上。但事实证明,还有另一个显然相当重要的部分。

一旦完成对原始文本语料库的 "原始训练",ChatGPT 内的神经网络就可以开始生成自己的文本,并根据提示等继续生成文本。不过,虽然这样做的结果往往看似合理,但它们往往会以非人类的方式 "偏离",尤其是对于较长的文本片段。比方说,通过对文本进行传统的统计,我们并不能轻易发现这种情况。但实际阅读文本的人却很容易注意到这一点。

而构建 ChatGPT 的一个关键想法是,在 "被动阅读 "网络等事物之后,再迈出一步:让人类主动与 ChatGPT 互动,看看它产生了什么,并向它反馈 "如何成为一个优秀的聊天机器人"。但神经网络如何利用这些反馈呢?第一步是让人类对神经网络的结果进行评分。然后再建立另一个神经网络模型,试图预测这些评分。但现在,这个预测模型可以在原始网络上运行--本质上就像一个损失函数,实际上就是通过人类的反馈来 "调整 "该网络。而实际结果似乎对系统成功产生 "类人 "输出有很大影响。

总的来说,有趣的是,"最初训练过的 "网络似乎不需要什么 "刺激",就能朝着特定的方向有效地运行。人们可能会认为,要让网络表现得像是 "学到了新东西",就必须运行训练算法,调整权重等。

但事实并非如此。相反,似乎只需告诉 ChatGPT 一次,作为你给出的提示的一部分,然后它就能在生成文本时成功利用你告诉它的内容。再一次,我认为这一事实是了解 ChatGPT "真正在做什么 "以及它与人类语言和思维结构之间关系的重要线索。

当然,它也有一些相当像人类的地方:至少在它接受了所有前期训练之后,你只需告诉它一次,它就能 "记住"--至少 "足够长的时间 "来用它生成一段文字。那么在这种情况下会发生什么呢?可能是 "你要告诉它的一切都已经在那里的某个地方"--而你只是把它引到了正确的地方。但这似乎说不通。相反,更有可能的情况是,是的,这些元素已经在那里了,但具体细节是由 "这些元素之间的轨迹 "之类的东西定义的,而这正是你在告诉它某些东西时所引入的。

事实上,就像人类一样,如果你告诉它一些完全不符合它已知框架的奇异和意想不到的东西,它似乎并不能成功地 "整合 "这些东西。只有当它基本上以一种相当简单的方式骑在 (riding) 它已有的框架之上时,它才能 "整合 "它。

还值得再次指出的是,神经网络所能 "接收 "的东西不可避免地存在 "算法限制"。如果告诉神经网络 "此到彼 "等形式的 "浅层 "规则,神经网络很可能就能很好地表示和再现这些规则--事实上,神经网络从语言中 "已经知道 "的东西会给它提供一个可立即遵循的模式。但是,如果试图为实际的 "深度 "计算提供规则,而这种计算涉及许多潜在的计算上不可还原的步骤,那就行不通了。(请记住,在每一个步骤中,它都只是在其网络中 "向前输入数据",除了生成新的标记外,永远不会循环)。

当然,网络可以学习特定 "不可还原 "计算的答案。但是,一旦存在组合数量的可能性,这种 "查表式 "的方法就行不通了。因此,是的,就像人类一样,现在是神经网络 "触网 "并使用实际计算工具的时候了。(是的,Wolfram|Alpha 和 Wolfram 语言非常适合,因为它们是为 "谈论世界上的事物 "而构建的,就像语言模型神经网络一样)。

13. What Really Lets ChatGPT Work?

14. Meaning Space and Semantic Laws of Motion

15. Semantic Grammar and the Power of Computational Language

16. So … What Is ChatGPT Doing, and Why Does It Work?

17. Thanks

参考

Wolfram, S. (2023, February 14). What is ChatGPT doing ... and why does it work?. Stephen Wolfram Writings. writings.stephenwolfram.com/2023/02/what-is-chatgpt-doing-and-why-does-it-work.