LLMs 系列科普文(2)

上文中,我们将文本表示为小的文本片段,它们是整个 LLMs 中信息处理的基本单位,就像这个世界是由原子组成的那样,但请注意,这些经过 tokenizer 后产生的数字本身没有任何意义,它们只是唯一的标识符,如标号 4438 的 token 表示为 ‘How’.
好了,现在让我们进入有趣的部分——神经网络的预训练。
二、模型预训练与推理
顾名思义,这里是网络的预先训练阶段,这意味着后续通常还存在其他额外的训练。我们目前只聚焦于前期训练过程,这里正是训练神经网络时大量计算工作发生的核心环节。在此步骤中,我们的目标是建模这些 token 在序列中如何相互跟随的统计关系。
这涉及到 LLM 中 LM 的实际含义表达,LM 即 Language Mode,语言模型,何谓语言模型,语言模型是一种类似文字接龙的游戏,即给定上文,我们需要在此基础上预测下一个词是什么,再下一个词是什么,所以,当你看到如下内容:“我爱北京天”,请问,下一个字应该是什么,“安”,然后是“门” ……
具体操作是:我们提取数据中的 token 窗口进行分析。因此,我们会从这些数据中随机抽取一个 token 窗口。窗口的长度可以从零个 token 开始,一直延伸到我们设定的某个最大值。例如,在实际操作中,你可能会看到 8000 个 token 的窗口。理论上,我们可以使用任意长度的 token 窗口。但处理非常长的窗口序列在计算上会非常昂贵。因此,我们通常会选择一个合适的数字,比如 8000、4000 或 16000,并在指定长度处进行截断。

在这个例子中,我将选取前四个标记以确保内容整齐呈现。我们将截取这四个标记——“How”、" AP"、" reported" 和 " in"(即这些 token ID)作为一个四 token 窗口。现在我们要做的,本质上是尝试预测这个序列中下一个即将出现的 token。
1、模型输入与输出
所以接下来应该是 682,我们将这四个 token 的组合称为上下文,它们会被输入到神经网络中。这就是神经网络的输入。我们暂且先不关注模型的具体结构,因为当前最重要的是理解神经网络的输入和输出。输入是可变长度的 token 序列,长度范围从零到某个最大值,比如 8,000。
现在的输出是对接下来内容的预测。由于我们的词汇表包含 100,277 个可能的 token,神经网络将输出恰好对应这些 token 数量的预测数值,每个数值都代表该 token 作为序列中下一个出现的概率。因此,它是在对接下来出现的内容进行预测。
最初,这个神经网络是随机初始化的,还不具备很好的预测能力。因此,在训练的最初阶段,这些概率值基本上只是一些比较随机的预测值分配。在上面的图中,我们显示了其中的三个预测值,但请记住实际上是预测了 100,277 个数字。当前神经网络给出的概率显示,这个 " Direction" 标记的出现概率暂时被预测为 4%。11,799 的概率为 2%。而此处,682(" in")的概率为 3%。
当然,由于我们已从数据集中对该窗口进行了抽样。因此我们已知道接下来的数字是 682 —— 这就是标签 —— 正确的答案是序列中接下来实际出现的数字。因此我们需要利用标签结果,对模型的预测结果进行修正,更新模型的参数,这涉及到神经网络参数更新的数学运算过程,我们后续再讨论这一点。总之,我们有一种数学方法来计算如何调整和更新神经网络,使正确答案的概率略微提高。

如果我现在对神经网络进行一次更新,下次当我将这组特定的四个 token 序列输入神经网络时,经过微调的神经网络可能会将 " in" 的概率输出为 4%," Case" 的概率可能是 1%,而 " Direction" 可能会变为 2% 或类似数值。因此,我们有一种微调方法,可以稍微更新神经网络,使其对序列中下一个正确 token 给出更高的概率预测。
现在我们需要记住的是,这一过程不仅仅发生在这里的这一个 token 上——即这四个输入预测出这一个的情况。实际上,该过程会同时作用于整个数据集中所有 token。就像我们可能在下一次采样到的 4 个 token 序列 “ AP”, " reported", " in", " all" 的基础上预测 ‘’ formats",因此在实践中,我们会采样小窗口,即分批处理这些小窗口数据。接着,在每一个这样的 token 处,我们都要调整神经网络,使得该 token 出现的概率略微提高。这一切都是在大批量 token 的并行处理中完成的。这就是神经网络的预训练过程。
这是一个不断更新的过程,目的是让模型的预测结果与训练集中实际发生的统计数据相匹配。其概率分布会逐步调整,以符合数据中这些 token 相互跟随的统计规律,所以这一切都是基于概率统计,并没有什么其他神秘之处。
2、网络内部结构
接下来让我们简要探讨这些神经网络的内部机制,以便你对它们的运作原理有个基本认识。
如前所述,我们接收的输入是 token 序列。本例中虽然只展示 4 个输入 token,但实际可处理范围从零到约 8,000 个 token 不等。理论上,这可以是一个无限数量的 token。只是处理无限数量的 token 在计算上过于昂贵。因此我们需要将其截断至特定长度,这就成为该模型的最大上下文长度。

现在,这些输入 x x x 与神经网络参数(或称权重)在一个庞大的数学表达式中混合运算。此处我展示了 6 个参数示例及其设定值,但实际上现代神经网络会拥有数十亿个这样的参数。最初,这些参数是完全随机设定的。现在,在参数随机设置的情况下,你可能会预期这个神经网络会做出随机预测,事实也确实如此。一开始,它的预测完全是随机的。
但正是通过这种不断更新网络的迭代过程——我们称之为神经网络的训练——这些参数的设置得以调整,从而使神经网络的输出与训练集中观察到的模式保持一致。你可以把这些参数想象成 DJ 调音台上的旋钮。当你转动这些旋钮时,针对每个可能的 token 序列输入,都会得到不同的预测结果。
训练神经网络,其实就是寻找一组与训练集统计数据相吻合的参数。现在,我举个实例展示这个庞大数学表达式的样貌,让你有个直观感受。现代神经网络很可能是包含数万亿项的巨型表达式。这里只是想给你看一个简单的网络可能组成的表达式样子。大概就是这样的,其实也并不可怕,只是参数多了些,计算量大了些。在图示公式中我们有一些输入 x x x,比如 x 1 x_1 x1、 x 2 x_2 x2,它们会与网络的权重 w 0 w_0 w0、 w 1 w_1 w1、 w 2 w_2 w2、 w 3 w_3 w3 等混合。这种混合涉及简单的数学运算,如乘法、加法、指数运算、除法等。神经网络架构研究的主题就是设计具有诸多便利特性的有效数学表达式。它们表现力强、可优化、可并行化等等。本质上,它们通过将输入与参数混合来进行预测。而我们要做的是优化这个神经网络的参数,以使使得预测结果与训练集保持一致。

现在,我想向你们展示一个实际生产级别的神经网络示例。为此,我建议大家访问 LLM Visualization 网站1,它提供了这些神经网络非常直观的可视化效果。网页左侧,上半部分是典型的以 Transformer 架构为主的现代 LLMs 网络结构抽象示意图,下半部分是对每个组件功能的简单介绍,而右侧则会放到该组件的实际结构进行展示,这一切都是动态的,因此真心建议你可以自己动手玩一玩。
以这个具体模型为例,它大约包含 85,000 个参数。现在我们来看顶部结构:输入层接收 token 序列作为输入,信息随后在神经网络中向前传播,最终输出层会生成经过 softmax 处理的逻辑值。而这一切是为了预测下一个 token 是什么。在整个网络内部,有一系列的转换过程,它们共同作用以预测后续内容。
举例来说,这些 token 会被嵌入到所谓的分布式表示中,每个可能的 token 在神经网络内部都有一个代表它的向量。因此首先,我们将这些 token 经过 Embedding 层进行处理。然后,这些值会以某种方式在这个图中流动。每个组件单独来看都是非常简单的数学表达式。比如我们有层归一化、矩阵乘法、softmax 函数、注意力模块等等,再次建议你亲自动手把玩一下,随着左侧教程的展开,会在右侧以动画的形式直观的演示数据的计算过程,信息流在整个网络中的传递过程,这非常有趣。
但值得对大众读者说明的是,别太把它当神经元来理解,因为这些与你大脑中的神经元相比极其简单。你体内的生物神经元是具有记忆等功能的非常复杂的动态过程,而这个表达式里可没有记忆这回事。这是一个从输入到输出固定不变的数学表达式,没有记忆功能,完全无状态。因此,与生物神经元相比,这些神经元非常简单。但你可以大致将其视为一种人造的脑组织——如果你倾向于这样理解的话。信息流经所有这些神经元,直到我们得出预测结果。
关于网络的结构细节,我们在这里不会深入的探讨,你也不必纠结于其中的组件细节或转换过程中精确的数学细节,真正需要理解的是,整个网络只是一个数学函数。它由一组固定参数所定义,比如大约 85,000 个。
这是一种将输入转化为输出的方式。当我们调整参数时,会得到不同的预测结果。随后我们需要找到这些参数的合适配置,使预测结果能够与训练集中观察到的模式大致吻合。这就是 Transformer 模型。
3、模型推理
当我们在预训练阶段结束时,此时的模型参数已经经过大量的调整,使预测结果能够与训练集中观察到的模式大致吻合,言外之意就是,当我们再次喂给模型 “How”、" AP"、" reported"、" in" 时,模型将有较高的概率输出 " all"。
模型训练结束,模型网络参数已经固定,即我们已经得到了这个复杂的函数 f f f,当我们在使用它的时候,我们就称之为推理阶段,即我们期望可以让模型生成一些新的内容。

因此在推理过程中,我们所做的是从模型中生成新数据。我们本质上想观察模型通过其网络参数内化了哪些模式。从模型生成数据的过程相对直接。如上图所示,我们可以将一些基本内容作为前缀的 token 开始,比如你想要的起始内容。假设我们希望以 token 4438 开头,那么我们就将其输入网络。
记住,网络给出的是每个可能的 token 概率,共有十万多个可能的 token,因此这里给出的是大小为 10 万的概率向量。所以我们现在可以做的,本质上就是抛一个有偏见的硬币。也就是说,我们可以基于这个概率分布来采样一个 token。现在,10314 是一个相对可能的 token,但需要注意的是,即便 10314 的概率最大,在采样过程中,也未必一定会采样到它,这与采样策略有关,因此在这种情况下,10314 并不是唯一可能的 token,可能还有许多其他 token 可以被采样。但我们可以看到,10314 作为一个例子的确是一个相对可能的 token。
那么现在让我们继续这个过程。也就是说,4438 之后就是 10314。我们将其附加到输入中。我们再次询问,第三个 token 是什么?取样后假设它是 5068;再来一次,第四个可能的 token 是什么?采样到 304,再继续一次,我们采样到 896,实际上这并不是我们之前得到的 682,因此,在这种情况下,我们并没有完全重现训练数据中看到的序列。
所以请记住,这些系统是随机的。我们在采样,我们在掷硬币。有时我们运气不错,能重现训练集中的一小段文本。但有时我们得到的 token 并非训练数据中任何文档的逐字内容。所以我们将会得到类似于训练数据中的某种混杂版本。因为在每个步骤中,我们都可以进行掷硬币,得到稍微不同的 token。一旦这个 token 被采用,如果你继续采样下一个 token,以此类推,你很快就会开始生成与训练文档中出现的 token 流完全不同的内容。因此从统计学上看,它们会具有相似的特性,但并不与训练数据完全相同。它们更像是受到训练数据的启发。因此在这个案例中,我们得到了一个略有不同的序列。
那我们为什么会采样到 896 这个词呢?从某种程度上说,你可以想象在训练数据的某个地方,896 这个词跟随了这个上下文窗口,而我们恰好在这一步中采样到了它。
简单来说,推理就是依次从这些概率分布中进行预测,我们不断反馈并获取下一个 token。整个过程就像不断掷硬币。根据我们运气的好坏,从这些概率分布中采样时,可能会得到截然不同的模式。这就是推理。
在大多数常见情况下,下载互联网数据并进行 tokenizer 处理实际上是一个预处理步骤。然后,一旦你有了 token 序列,我们就可以开始训练网络了。在实际应用中,你会尝试训练许多不同的网络,它们有不同的设置、不同的排列方式和不同的大小。因此,你会进行大量的神经网络训练。
当你有了一个神经网络并训练它,并且得到一组令你满意的特定参数后,你就可以使用这个模型进行推理。实际上,你可以从模型中生成数据。当你在 ChatGPT 上与模型对话时,那个模型已经训练完成,可能是 OpenAI 在几个月前训练的。它们有一套特定的、效果很好的权重。当你与模型对话时,这一切都只是推理。不再有训练过程。这些参数是固定的。你基本上只是在和模型对话。你给它一些 token,它就会完成 token 序列。这就是你在 ChatGPT 上实际使用该模型时所看到的内容生成过程。因此,该模型仅执行推理任务。
LLM Visualization: https://bbycroft.net/llm ↩︎