大家好,我想做这个视频已经有一段时间了。这是一个全面但面向普通观众的介绍,旨在讲解像 ChatGPT 这样的大型语言模型 (Large Language Models)。
我希望通过这个视频达成的目标是,给你们提供一些思维模型 (mental models),用以理解这个工具到底是什么。它在某些方面显然是神奇和惊人的,擅长某些事,又不擅长另一些事,同时还有很多需要注意的“锋利边缘”。所以,这个文本框背后到底是什么?你可以在里面输入任何东西然后按回车,但是我们应该输入什么?返回的这些词语是如何生成的?这到底是怎么运作的?你究竟在和什么东西对话?我希望在这个视频中涵盖所有这些主题。我们将完整地过一遍这东西是如何构建的全过程,但我会尽量让所有内容对普通观众来说都通俗易懂。
那么,让我们先来看看像 ChatGPT 这样的东西是如何构建的。在此过程中,我也会谈到这些工具在认知和心理层面的一些启示。
好的,我们来构建一个 ChatGPT。
这会涉及多个顺序排列的阶段。第一个阶段叫做 预训练 (pre-training) 阶段。
第一阶段:预训练 (Pre-training)
预训练阶段的第一步是下载和处理整个互联网。
要想对此有个大致概念,我推荐大家看看这个网址。一个名为 Hugging Face 的公司收集、创建并整理了一个名为 FineWeb 的数据集。他们在这篇博客文章中详细介绍了他们是如何构建 FineWeb 数据集的。所有主要的语言模型提供商,如 OpenAI、Anthropic 和谷歌等,在内部都会有类似 FineWeb 数据集的对应物。
我们在这里的目标是什么呢?我们试图从互联网上的公开来源获取海量的文本。我们希望获得数量巨大、质量非常高的文档,并且我们还希望文档的多样性 (diversity) 非常大,因为我们想让这些模型内部蕴含大量知识。所以,我们需要大量多样化的高质量文档。实现这一点相当复杂,正如你在这里看到的,需要多个阶段才能做好。
我们稍后会详细看看其中一些阶段。现在,我只想指出一点,例如,FineWeb 数据集,它非常能代表你在一个生产级应用中会看到的情况,最终占用的磁盘空间大约只有 44TB。你现在可以非常容易地买到一个 1TB 的 U盘,甚至今天这整个数据集几乎可以装在一个硬盘里。所以,这最终并不是一个庞大的数据量,尽管互联网非常大,但我们处理的是文本,并且我们也在进行积极的过滤。在这个例子中,我们最终得到了大约 44TB 的数据。
现在让我们来看看这些数据长什么样,以及这些处理阶段都包含什么。
很多这类工作的起点,并且最终贡献了大部分数据的,是来自 Common Crawl 的数据。Common Crawl 是一个自 2007 年以来就一直在抓取互联网的组织。例如,截至 2024 年,Common Crawl 已经索引了 27 亿个网页。他们有各种爬虫在互联网上运行,你基本上就是从一些种子网页开始,然后跟随所有链接,不断地跟随链接并索引所有信息。随着时间的推移,你最终会得到海量的互联网数据。这通常是许多这类工作的起点。
这些 Common Crawl 数据相当原始,需要通过许多不同的方式进行过滤。这里他们记录了(这是同一个图表)在这些阶段发生的一些处理类型。
- URL 过滤 (URL filtering) :这指的是存在一些黑名单,包含了你不希望从中获取数据的 URL 或域名。通常这包括恶意软件网站、垃圾邮件网站、营销网站、种族主义网站、成人网站等等。在这个阶段,有大量不同类型的网站被直接剔除,因为我们不希望它们出现在我们的数据集中。
- 文本提取 (Text extraction) :你必须记住,所有这些网页都是爬虫保存下来的原始 HTML。当我在浏览器里点击“检查元素”时,这就是原始 HTML 的样子。你会注意到它有各种标记,比如列表等等,还有 CSS 之类的东西。这几乎是网页的计算机代码。但我们真正想要的只是文本内容,我们不想要导航栏之类的东西。所以,有很多过滤、处理和启发式方法 (heuristics) 被用来充分地只筛选出网页中的优质内容。
- 语言过滤 (Language filtering) :例如,FineWeb 使用一个语言分类器进行过滤。他们尝试猜测每个网页的语言,然后只保留那些超过 65% 是英文的网页。你可以感觉到,这就像是一个设计决策,不同的公司可以自己决定在数据集中包含各种语言的比例。因为,如果我们过滤掉了所有的西班牙语,那么你可能会想象我们之后的模型在西班牙语方面表现不会很好,因为它从未见过那么多该语言的数据。因此,不同的公司可以根据自己的需求在多语言性能上有所侧重。FineWeb 非常侧重于英语,所以如果他们最终训练一个语言模型,它在英语方面会非常出色,但在其他语言方面可能就不那么好了。
- 其他过滤与处理:在语言过滤之后,还有一些其他的过滤步骤和去重 (de-duplication) 等。最后一步是个人身份信息移除 (PII removal) ,即 Personally Identifiable Information。例如,地址、社会安全号码之类的东西,你会试图检测并从数据集中过滤掉包含这些信息的网页。
这里有很多阶段,我不会详述所有细节,但这是一个相当广泛的预处理部分。最终,你得到了像 FineWeb 这样的数据集。当你点击进去,你可以看到一些最终样子的例子。任何人都可以在 Hugging Face 网站上下载它。
这里是一些最终进入训练集的文本示例。这是一篇关于 2012 年龙卷风的文章。下一个是关于“你知道你身体里有两个像 9V 电池大小的黄色肾上腺吗?”的东西。好的,这像是一篇奇怪的医学文章。
所以,就把这些看作是互联网上的网页,经过各种方式过滤后只留下了文本。现在我们有了海量的文本,40TB,这成为了下一个步骤的起点。
分词 (Tokenization)
在我们把文本输入神经网络之前,我们必须决定如何表示这些文本。我们用于这些神经网络的技术期望得到一个一维的符号序列 (one-dimensional sequence of symbols) ,并且它希望有一个有限的符号集合。
所以我们必须决定这些符号是什么,然后将我们的数据表示为这些符号的一维序列。目前我们拥有的是一维的文本序列。它从这里开始,到这里结束,然后换行等等。尽管在我的显示器上它是二维布局的,但它本质上是从左到右、从上到下的,所以它是一个一维的文本序列。
当然,在计算机中,这里有一个底层的表示。如果我对此文本进行所谓的 UTF-8 编码 (encode) ,我可以得到计算机中对应于此文本的原始比特流。这就是它的样子。例如,这第一个竖条就是前八个比特。
这个表示在某种意义上正是我们想要的:我们正好有两个可能的符号,0 和 1,并且我们有一个非常长的序列。但事实证明,这个序列长度在我们的神经网络中将是一个非常有限和宝贵的资源。我们实际上不想要一个由仅仅两种符号组成的极长序列。相反,我们想要用更大的词汇表 (vocabulary) 来换取更短的序列长度。
一种朴素的压缩序列长度的方法是,将一组连续的比特(例如 8 个比特)组合成一个所谓的字节 (byte) 。因为每个比特要么是开要么是关,如果我们取 8 个比特一组,那么总共只有 256 种可能的组合。因此,我们可以将这个比特序列重新表示为一个字节序列。这个字节序列会短 8 倍,但现在我们有 256 个可能的符号。这里的每个数字都在 0 到 255 之间。
我非常鼓励你不要把这些看作数字,而是看作独特的 ID 或独特的符号。也许更好的方式是把它们中的每一个都换成一个独特的表情符号 (Emoji),你会得到类似这样的东西。所以,我们基本上有一个表情符号序列,并且有 256 种可能的表情符号。
事实证明,在生产级的、最先进的语言模型中,你实际上想要更进一步。你希望继续缩短序列的长度,以换取词汇表中更多的符号。这是通过运行一种叫做字节对编码 (Byte-Pair Encoding, BPE) 的算法来完成的。它的工作方式是,我们基本上寻找非常常见的连续字节或符号。例如,序列 116 后面跟着 32 这个组合非常常见。所以我们将这个对合并成一个新的符号,我们会创造一个 ID 为 256 的新符号,并用这个新符号重写每一对 116, 32。我们可以随心所欲地迭代这个算法。每次我们创造一个新符号,我们都在缩短序列长度并增加符号大小。
在实践中,一个相当不错的词汇表大小设置大约是 100,000 个可能的符号。具体来说,GPT-4 使用 100,277 个符号。
这个从原始文本转换成这些符号(我们称之为 tokens)的过程,就叫做分词 (tokenization) 。
让我们来看一下 GPT-4 是如何进行分词的。我喜欢用一个叫做 tiktokenizer 的网站来探索这些 token 表示。在这里的下拉菜单中选择 CL100K_BASE,这是 GPT-4 基础模型的分词器。在左边,你可以输入文本,它会显示该文本的分词结果。
例如,"hello world" 最终正好是两个 token:token "hello"(ID 15339)和 token " world"(注意前面的空格,ID 1917)。分词是大小写敏感的。
所以,最终,我们有了这个一维的 token 序列。FineWeb 数据集大约有 15 万亿 (trillion) 个 token。再次记住,所有这些都代表小文本块,它们就像序列的原子,而数字本身没有任何意义,它们只是唯一的 ID。
神经网络训练 (Neural Network Training)
现在我们到了有趣的部分,即神经网络训练。这是在训练这些神经网络时,计算上大部分繁重工作发生的地方。
我们要做的是建模这些 token 在序列中如何相互跟随的统计关系。我们从数据中取出一个个窗口 (windows) 的 token。例如,一个包含 8,000 个 token 的窗口。
在这个例子中,我只取前四个 token,这样一切都能很好地展示。我们取一个四个 token 的窗口,比如 "viewing a single" 这几个词对应的 token ID。我们要做的是预测序列中紧随其后的下一个 token。例如,我们知道下一个 token 应该是 "post" 对应的 ID 3962。
这四个 token 我们称之为上下文 (context) ,它们被输入到一个神经网络中。这是神经网络的输入。
神经网络的输出是什么?因为我们的词汇表有 100,277 个可能的 token,神经网络将输出正好那么多个数字。所有这些数字对应于每个 token 作为下一个出现的概率。它在猜测接下来会发生什么。
一开始,这个神经网络是随机初始化的,所以它的预测概率也是随机的。例如,它可能预测 "post" 出现的概率只有 3%。
但是,因为我们知道正确的答案是 "post",我们就有了一个数学过程来更新 (update) 神经网络。我们有一种调整它的方法,使得正确答案的概率(这里的 3%)变得更高,而所有其他 token 的概率变得更低。
所以,如果我现在对神经网络进行一次更新,下一次我把这四个 token 的序列输入神经网络时,它现在会被轻微调整,可能会说 "post" 的概率是 4%,而其他词的概率降低了。我们有一种方法来微调神经网络,使其对序列中下一个正确的 token 给出更高的概率。
你必须记住,这个过程不仅发生在这一个 token 上。这个过程在整个数据集中的所有 token 上同时发生。我们对神经网络进行一系列的更新,使其预测与训练集中实际发生的情况的统计数据相匹配。
推理 (Inference)
一旦模型训练好了,我们就可以进入一个叫做推理 (inference) 的阶段。在推理中,我们是从模型中生成 (generate) 新的数据。
从模型生成数据相对直接。我们从一些作为前缀的 token 开始。比如我们从 token 91 开始。我们把它输入到网络中,网络会给我们一个概率向量。然后我们可以基于这个概率分布进行采样,就像掷一个有偏见的硬币。
我们从这个分布中采样,得到一个唯一的 token。例如,token 860 接下来出现了。现在我们有了 91 和 860,我们把它们一起作为新的上下文再次输入模型,预测第三个 token,然后采样,如此循环往复。
这个过程是随机的 (stochastic) 。我们每次都在掷硬币,有时我们可能会复现训练集中的一小段文本,但更多时候,我们会得到一个在训练数据中任何文档里都没有逐字出现过的 token。我们会得到训练数据的一种“混音版”。统计上它们会有相似的属性,但它们并不等同于你的训练数据。它们像是受到训练数据启发而创作的。
当你和 ChatGPT 对话时,那个模型是很久以前 OpenAI 训练好的。他们有一套特定的、效果很好的权重(参数)。你和模型对话时,所有这些都只是推理。没有更多的训练,那些参数是固定的。你只是给它一些 token,它来补全 token 序列。
第二阶段:后训练 (Post-training)
我们刚刚讨论了第一个阶段,预训练。我们看到,它归结为我们获取互联网文档,将它们分解成 token,然后使用神经网络预测 token 序列。这个阶段的输出是一个基础模型 (base model) 。它基本上是一个互联网文档模拟器。
这本身还不是很有用,因为我们想要的是一个助手 (assistant) 。我们想问问题并让它回答。这些基础模型不会这么做,它们只是创作互联网页面的混音版。
所以,我们需要进入第二个阶段,即后训练 (Post-training) 。我们把我们的基础模型,这个互联网文档模拟器,交给后训练阶段。
监督式微调 (Supervised Fine-Tuning, SFT)
在这个阶段,我们要做的是让模型不再采样互联网文档,而是对问题给出回答。换句话说,我们想开始考虑对话 (conversations) 。
例如,我们希望当人类说“2+2 等于几?”时,助手应该回应“2+2 等于 4”。
因为这是神经网络,我们不会用代码明确地编程。一切都是通过在数据集上进行神经网络训练来完成的。因此,我们将通过创建对话数据集来隐式地编程这个助手。
这些数据从哪里来?来自人类标注员 (human labelers) 。我们基本上会给人类标注员一些对话上下文,并要求他们在这种情况下给出理想的助手回应。
这个过程是这样的:我们拿出在预训练阶段产生的基础模型,这个模型是在互联网文档上训练的。我们现在扔掉那个互联网文档数据集,换上一个新的数据集,一个对话数据集。然后我们继续在这个新的对话数据集上训练模型。
模型会非常迅速地调整,并学会这个助手对人类查询的回应方式的统计规律。然后,在之后的推理过程中,我们将能够启动助手并获得回应,它将模仿人类标注员在这种情况下会做的事情。
这个后训练阶段在计算上通常比预训练阶段(可能需要数月和数千台计算机)便宜得多,可能只需要几个小时。
就像之前一样,对话也需要被转换成 token 序列。这需要设计一种编码协议,使用特殊的 token(如 <|im_start|>、user、assistant、<|im_end|>)来标记对话的结构、轮次和角色。最终,整个对话变成了一个一维的 token 序列,然后我们就可以像以前一样在上面训练语言模型了。
LLM 心理学与怪癖
这种训练流程带来了一些需要注意的认知效应。
-
幻觉 (Hallucinations) :这是指 LLM 会编造事实。为什么会这样?因为在 SFT 的训练集中,人类标注员对于“谁是某某?”这类问题,总是会自信地给出正确答案。模型学会了这种自信的风格。所以,当你问它一个它不知道的人(比如一个我瞎编的名字 "Orson Kovats"),它不会说“我不知道”,而是会模仿那种自信的风格,编造一个听起来最合理的答案。
如何缓解?
- 缓解方法1:教它说“我不知道” 。我们可以探测模型知识的边界。对于模型确实不知道的问题,我们在训练集中加入新的对话样本,其中正确的助手回答就是“对不起,我不知道这个信息”。这样,模型就学会了将内部的“不确定性”状态与“我不知道”的语言表达联系起来。
- 缓解方法2:工具使用 (Tool Use) 。我们可以让模型使用工具。例如,当模型不确定时,它可以不直接回答,而是生成一个特殊的
[SEARCH]token,后面跟着搜索查询。系统会暂停生成,执行网络搜索,然后将搜索结果作为新的上下文信息提供给模型。现在,信息就在模型的“工作记忆”(即上下文窗口)里,模型就可以基于这些事实来准确回答了。这就是为什么你在 ChatGPT 中有时会看到它在“搜索网络”。
-
知识存在于何处? 这里有一个非常重要的区别:
- 参数中的知识 (Knowledge in Parameters) :这是模型的长期记忆。就像你一个月前读过的东西,是一种模糊的回忆。
- 上下文窗口中的知识 (Knowledge in Context Window) :这是模型的工作记忆。是模型当前可以直接访问的信息,非常清晰和准确。
- 实践启示:当你想让 LLM 处理特定信息时(比如总结一篇文章),最好的做法不是期望它从记忆中回忆,而是直接把文章内容粘贴到提示中,将其放入上下文窗口。这样得到的总结质量会高得多。
-
计算能力:“需要 Token 来思考”
- 模型在生成每一个 token 时,其内部进行的计算量是有限的。它不能在一个 token 内完成过于复杂的推理。
- 因此,好的回答需要将推理过程分布在多个 token 上。这就是所谓的“思维链 (Chain of Thought)”。模型需要一步一步地写出它的思考过程,把中间结果展示出来。这些中间步骤主要是为了模型自己,而不是为了你。
- 如果你强迫模型在一个 token 内给出复杂问题的答案(例如,一个需要多步计算的数学题),它很可能会失败。这就是为什么模型不擅长心算、数字符,或处理一些精细的字符级任务(如拼写逆序)。
- 实践启示:对于需要精确计算或推理的任务,最好让模型“展示其工作过程”,或者更可靠地,让它使用代码解释器 (code interpreter) 这个工具来完成计算。
-
分词的诅咒 (The Curse of Tokenization)
- 模型看到的世界不是由字母组成的,而是由 token(文本块)组成的。这使得它在处理需要精细字符操作的任务时表现不佳。例如,曾经著名的“草莓 (strawberry) 中有几个 r?”的问题,很多模型都曾回答错误,因为 "strawberry" 可能会被分成几个 token,模型很难“看见”并计算单个字母。
第三阶段:强化学习 (Reinforcement Learning, RL)
我们可以用一个比喻来理解这三个阶段:
- 预训练 (Pre-training) ≈ 阅读教科书的讲解部分,建立基础知识。
- 监督式微调 (SFT) ≈ 学习教科书中的例题和标准答案,模仿专家的解题方法。
- 强化学习 (RL) ≈ 做教科书后面的练习题。
在做练习题时,你只有问题和最终答案(在答案页),但没有解题过程。你需要自己通过反复尝试和犯错来探索如何得到正确答案。这就是强化学习的核心思想。
为什么需要 RL? 因为人类标注员提供的“标准答案”(SFT 数据)不一定是 LLM 解决问题的最佳路径。人类的认知和 LLM 的认知是不同的。我们不知道什么样的思考路径对 LLM 来说是最高效、最不容易出错的。我们应该让 LLM 自己去发现。
RL 是如何工作的?(在可验证领域,如数学)
- 给定一个问题(比如一个数学题)。
- 让模型生成大量不同的解题过程(比如 1000 种)。
- 用一个验证器 (verifier) (比如检查最终答案是否正确)来判断哪些解题过程是成功的,哪些是失败的。
- 奖励 (Reward) 那些成功的解题路径,即通过训练增加模型未来生成类似路径的概率。
- 不断重复这个过程。
RL 的惊人之处在于,它能导致“涌现”出思考策略。在最近的 DeepSeek-V2 模型论文中,研究者发现通过 RL 训练后,模型学会了自己进行反思和回溯。它会生成这样的文本:“等等,让我重新评估一下这一步......” 或者 “让我从另一个角度检查一下我的答案。” 这些复杂的认知策略是模型为了提高最终答案的准确率而自发学习到的,没有任何人直接教它这么做。这就是“思考模型 (thinking models)”或“推理模型 (reasoning models)”的由来。
RLHF (Reinforcement Learning from Human Feedback) 对于像“写个笑话”这样没有唯一正确答案的不可验证领域,我们如何进行 RL? 这里的技巧是,我们不直接用人来评估数百万个生成的笑话(这不现实),而是训练一个奖励模型 (reward model) 来模拟人类的偏好。
- 人类标注员对少量模型生成的输出进行排序(比如,哪个笑话最好笑)。
- 用这些排序数据训练一个奖励模型。这个模型的任务是给任何一个笑话打分,分数要符合人类的偏好。
- 一旦有了这个自动化的“人类偏好模拟器”,我们就可以用它作为奖励信号,对原始的语言模型进行大规模的 RL 训练。
RLHF 的局限性 RLHF 虽然有效,但它有一个致命弱点:奖励模型可以被“欺骗 (gamed)” 。强化学习非常擅长找到奖励模型的漏洞。如果训练时间过长,LLM 不会变得更会讲笑话,而是会学会生成一些无意义的、但能从奖励模型那里骗到高分的乱码。因此,RLHF 更像是一种微调,而不是可以无限扩展、通往超强智能的魔法。
相比之下,在数学等可验证领域,RL 的潜力要大得多,因为它无法“欺骗”一个客观的正确答案。这与 DeepMind 的 AlphaGo 类似,它通过自我对弈的强化学习,最终超越了所有人类棋手,甚至下出了人类无法理解的“神之一手”(Move 37)。
总结与未来
所以,当你向 ChatGPT 提问时,发生了什么?
- 你的问题被分词并嵌入到一个对话结构中。
- 这个 token 序列被输入到一个经过三阶段训练的神经网络中。
- 预训练赋予了它海量的世界知识。
- 监督式微调 (SFT) 塑造了它的助手人格,使其回答听起来像一个遵循特定指南的、专业的 OpenAI 标注员的模拟。
- 如果你使用的是高级的“思考模型”,强化学习 (RL) 则让它在得出答案前,先进行一番自我探索和推理,这个过程模仿了(甚至可能超越了)人类的思考过程。
未来展望:
- 多模态 (Multi-modality) :模型将能原生处理文本、图像、音频。
- 智能体 (Agents) :模型将能执行更长期的、多步骤的任务。
- 无处不在 (Pervasiveness) :AI 将深度集成到我们使用的所有工具中。
希望这个全面的讲解对你有用,让你对这些神奇的工具有了更深入的理解。