CMU-11-763-大模型推理笔记-全-

CMU 11-763 大模型推理笔记(全)

001:语言模型与推理导论 🧠

在本节课中,我们将要学习语言模型的基本概念以及什么是推理。我们会从最基础的定义开始,确保大家对后续课程中使用的术语和符号有统一的理解。


大家好,欢迎来到新学年,也欢迎来到这门新课。这是一门关于前沿主题的课程,这些主题在研究中发展非常迅速。我们非常欢迎大家就课程内容提供反馈,告诉我们哪些部分希望多讲或少讲。

这是一个新教室,这里有这块屏幕。我需要想办法处理这块屏幕,以免它妨碍教学。让我们开始吧。我需要确认我的屏幕已经共享。

是的,我正在录制。感谢提醒,因为我有时可能会忘记录制,所以大家每次都可以提醒我。

那么,我们开始吧。任何时候都欢迎大家举手提问或发表评论。我们努力将班级规模控制在可管理的范围内,以便每天都能进行讨论。


语言模型是什么?🤔

这是课程开始时必须展示的一张幻灯片。我保证这是唯一一张这样的幻灯片。我们每天都在使用语言模型,例如 ChatGPT、谷歌搜索、Copilot、Cursor 用于代码补全、Codex 等编码代理工具,以及各种专业应用。无论你在哪个行业,人们可能都在使用语言模型,例如法律行业的 Harvey,医疗行业的 Bridge 等。

对于语言模型,我假设班上几乎所有人都上过自然语言处理或深度学习课程。我假设大多数人都已经了解这些内容。但我希望我们在将要使用的符号和术语方面达成一致。因此,本课程的很多内容将是这方面的概述。

当我们谈论语言模型时,我们指的是一个为词元序列分配概率的模型。

这可能是以其他东西为条件的,例如一张图片。但大多数情况下,我们将讨论输出是一个词元序列的情况,无论是自然语言、代码还是其他类似的东西。

因此,我们计算一个序列 x 的概率,其中序列是 x₁, x₂, ..., xₗ


自回归模型 📈

最常见的方法(不是唯一的方法,但我们将主要讨论它)是自回归模型

自回归模型按如下方式分解整个序列的概率:下一个词元的概率取决于之前的所有词元。

整个序列的概率 P(x) 可以表示为:
P(x) = P(x₁) × P(x₂ | x₁) × P(x₃ | x₁, x₂) × ... × P(xₗ | x₁, x₂, ..., xₗ₋₁)

这个模型有一些反例。本课程将要讨论的一个主要反例是扩散模型。扩散模型是一种非常不同类型的模型,具有非常不同的推理算法。从推理的角度来看,它们在这方面很有趣。但这可能只是所有课程中的一节课。因此,除非我特别提到我们在做不同的事情,否则请假设我们讨论的是自回归模型。


无条件的文本生成示例

我们可以看一些无条件的文本补全示例。我使用了一个较小的模型 Qwen2-1.5B-Instruct,并生成了一些补全。

从句子开始词元(BOS)生成。实际上,这个模型没有明确的句子开始词元,所以我从句子结束词元(EOS)生成,但这大致相同。我将生成的100个序列分为高概率、中概率和低概率序列。此外,进行推理时,你需要决定何时停止。我遇到句号或换行符时就停止。

那么,如果从“空”开始采样,最高概率的词元序列可能是什么?

是的,这是一个词元序列,是数据词元,不仅仅是“the”这个词元。有什么想法吗?这是一个大的...这是一个棒球。它是...“F happy to help”之类的?不,这不是一个经过指令微调的模型。有什么想法吗?有些是中文,但这些不是最高概率的。好的,最高概率的是“三个句号”、“四个句号”和“零零括号加换行符”。是的,这些是相对高概率的。这是对数概率,所以你可以看到 -4.22 意味着如果你采样很多次,你会得到很多这样的序列。


中概率与低概率序列

中概率序列呢?这是来自 Qwen 模型的,如果大家熟悉 Qwen 的话。这可能有点不走运,或者有点人为因素,但我得到了很多代码。

这些是我采样的100个序列中概率居中的,也有很多数学内容。

然后是概率最低的序列(在采样的100个中仍然算高概率),它们大多是中文。我不知道这是好中文还是坏中文。政治性中文?好吧。你可以猜猜 Qwen 是在什么数据上训练的。


条件生成:更常见的用法 🎯

但实际上,大多数时候我们使用语言模型并不是为了无条件生成。相反,我们想做的是有用的事情,而通常有用的事情意味着以其他东西为条件。

当我们这样做时,基本上我们想要以前面的序列为条件,并生成下一个序列。

这也写作输入序列是 x,输出序列是 y。在这种情况下,我们将条件部分视为前缀查询,第二部分是我们想要生成的补全响应

从数学上讲,这本质上意味着如果我们有自回归分解,那么我们可以在乘积中省略前几个词元。通常当我们生成输出时,我们以前面的所有词元为条件,但现在我们可以直接省略它们,计算这个自回归分解。


条件生成示例

对于我的提示前缀,我使用了 “The best thing about Carnegie Mellon University is”。知道最高概率的词元可能是什么吗?

每个人都会说的是关于 CMU 最好的事情...计算机科学?这是一个有点奇怪的句子:“The best thing about Carnegie Mellon University is computer science.” 也许吧。

“师资力量”,那会是一个很好的答案,谢谢你恭维我。你得到了两个奖励分数。

“社区”。好的,我们得到了“它的多样性”和“它是一个社区”。“它是一个社区”这种赞美有点勉强,但也许可以。

中概率的序列要长一些。例如:“它在充满活力的社区中的位置”、“它建立的社区总有新东西可学”、“它让你有能力将教育提升到新的水平”。这些句子稍长一些,但可能仍然很典型。

如果我们看我采样的序列中概率最低的那些,它们关注于跨学科方法、学生主修计算机科学、数学和工程、你从中获得的社区感、如此多的人在做如此多不同的事情、不仅仅是学术和研究、工程、计算机科学和商业。还有“学生在课堂上创造的惊人的、非凡的经历”。所有这些都是相当合理的回答。计算机科学也出现了。

除了文本内容,你还注意到这些序列有什么其他特点吗?

长度,是的,完全正确。为什么较短的序列概率更高?

002:概率回顾与代码示例

在本节课中,我们将回顾概率论的基础知识,并通过代码示例展示如何实现一个简单的语言模型及其生成算法。课程内容分为四个部分:概率基础、Transformer模型的基本实现、生成算法介绍,以及元生成(重排序)算法。

概率基础回顾

上一节我们介绍了语言模型的基本定义。本节中,我们来看看支撑自回归语言模型的核心数学概念:条件概率。

自回归语言模型通过预测下一个词(token)的概率来为整个序列分配概率。这本质上是一个条件概率问题,即给定之前所有词的情况下,下一个词出现的概率。用公式表示如下:

P(下一个词 | 之前的词)

为了更具体地理解,我们来看一个简单的例子:N-gram模型。N-gram模型是一种基于马尔可夫假设的语言模型,它假设下一个词的概率只依赖于前 N-1 个词。

以下是构建一个简单的Bigram(二元语法)模型的步骤:

  1. 首先,我们定义一个非常小的词汇表,包含句子开始符号、一些名词、动词、介词、副词和句子结束符号。
  2. 然后,我们为每个词对(当前词,下一个词)分配一个概率。例如,给定句子开始符号后,出现“the”的概率是多少?给定“the”之后,出现“cat”的概率是多少?
  3. 这些概率构成了一个条件概率表,模型根据这个表来预测序列。

通过这个简单的模型,我们可以直观地理解条件概率和序列生成是如何工作的。

一个基础的Transformer模型实现

理解了概率基础后,我们来看看如何用代码实现一个更强大的模型——Transformer。本节将展示一个极简的Transformer模型实现,供大家在后续实验中使用。

虽然许多同学可能已经接触过Transformer,但这个实现旨在提供一个清晰、可运行的参考。我们使用PyTorch框架,并导入必要的库。

以下是模型构建的核心步骤概述:

  1. 环境设置:导入torchnumpy等必要库。
  2. 定义模型结构:包括嵌入层、注意力机制、前馈网络等核心组件。
  3. 实现前向传播:定义数据如何通过网络层进行计算。

这个简化实现帮助我们理解Transformer如何接收输入序列并输出下一个词的概率分布,这正是我们之前讨论的条件概率的具体计算过程。

生成算法与模型评估

现在我们已经有了一个可以输出概率的模型,本节我们来看看如何利用它来生成文本,并如何评估生成结果的质量。

生成算法决定了我们如何根据模型输出的概率分布来选择下一个词。评估则帮助我们判断生成文本的好坏。

以下是几种常见的生成算法:

  • 贪婪解码:在每一步都选择概率最高的词。这种方法简单高效,但可能导致重复或乏味的输出。
    next_token = torch.argmax(probabilities, dim=-1)
    
  • 束搜索:同时保留多个可能性最高的候选序列(称为“束”),在生成结束时选择总体概率最高的序列。这种方法比贪婪解码找到更好序列的可能性更高。
  • 采样:根据概率分布随机选择下一个词。这能产生更多样化的输出。
    next_token = torch.multinomial(probabilities, num_samples=1)
    
  • 核采样(Top-p采样):仅从累积概率超过阈值p的最小候选词集合中采样。这种方法能在多样性和可控性之间取得平衡。

为了评估这些生成结果,我们常使用“语言模型即评委”的方法,即用另一个(通常更强大的)语言模型来为生成的文本打分,评估其流畅性、相关性等指标。

元生成(重排序)算法

最后,我们探讨一种提升输出质量的高级技巧:元生成算法,或称重排序算法。

其核心思想是:先生成多个候选输出,然后使用一个评分函数从中选出最好的一个。这比单纯依赖单一生成路径(如束搜索)更有可能获得高质量结果。

以下是重排序算法的基本流程:

  1. 生成阶段:使用基础生成算法(如束搜索或采样)产生N个不同的候选序列。
  2. 评分阶段:使用一个评分函数(Scoring Function)对每个候选序列进行打分。这个函数可以是:
    • 生成模型本身的对数概率。
    • 一个专门训练的打分模型。
    • 基于规则或启发式的方法。
  3. 选择阶段:选择得分最高的候选序列作为最终输出。

这种方法将“生成”和“选择”解耦,允许我们使用更复杂、计算量更大的评分标准来挑选最佳结果,而不必在每一步生成时都受其约束。

总结

本节课中我们一起学习了语言模型推理的数学与编程基础。我们从条件概率这一核心概念出发,回顾了N-gram模型。接着,我们看了一个基础的Transformer模型代码实现。然后,我们介绍了包括贪婪解码、束搜索和采样在内的多种生成算法,以及如何使用语言模型进行评估。最后,我们探讨了通过重排序算法来进一步提升输出质量的策略。这些概念和工具构成了后续深入探讨更复杂推理算法的基础。

003:常见采样方法

在本节课中,我们将学习如何从语言模型中采样。我们将首先把模型视为概率分布,并讨论一些用于分析该分布的度量指标。然后,我们将利用这些知识,介绍两大类采样方法:第一类是简单、基于截断和温度的方法;第二类则是更复杂、更精细的方法,它们会用到我们首先讨论的那些概率分布度量指标。

模型作为概率分布

正如我们在第一节课中讨论的,我们可以从几个不同的角度来理解模型。但至少在今天这节课中,我们坚持的观点是:模型就是一个条件概率分布。我们今天并不特别关心这个分布是如何得到的,我们只需要知道,在给定某些输入和先前输出的标记后,模型会为下一个标记在词汇表空间上生成一个分布。

在这个定义下,我们可以对这个分布做一些说明。

局部归一化

我们今天讨论的模型都是局部归一化的。这意味着在解码的每一步,我们都会得到一个针对该步骤的标记分布,而不是在每一步得到一个可能不是分布的东西,然后在解码结束时再进行归一化。

一种理解方式是,我们的分数(概率)是单调非递增的。我们在第一节课讨论过,如果你每一步都乘以一个0到1之间的数,那么随着标记的增加,总概率会下降。这也意味着,如果你从一个看起来不太好的前缀开始,后面就无法再增加概率质量。

例如,在解码“2023年的美国总统是”这句话时,如果模型给出了“约瑟夫·拜登”和“巴拉克·奥巴马的前副总统”这两个不同的后续,我们可能会希望后者的总概率更高。但在局部归一化模型中,一旦模型为“约瑟夫·拜登”分配了相对较低的概率质量,后面就无法“回溯”并说“我认为这个应该得到更多概率质量”。虽然它可能仍然比另一个低概率输出更高,但无法从根本上改变初始分配的概率权重。

我们可以通过可视化来理解这一点。在局部归一化模型中,每个候选序列(如A和B)在每一步获得的概率会归一化,所有候选的概率和为1。而在全局归一化模型中,每一步得到的是一个分数,这些分数可以远大于1,从而可以在序列层面“提升”某个候选的可能性。我们采用局部归一化的原因有很多,其中之一是训练起来简单快速,不需要在序列层面考虑约束。但反过来,当你想在推理时施加全局约束时,就需要多花些心思。事实上,本学期我们将讨论的许多方法,都是在尝试对仅经过局部归一化训练的模型施加全局约束。

如果你对全局归一化模型感兴趣,幻灯片底部的链接是一篇很好的参考文献(虽然有点旧),介绍了如何在序列模型中实现这一点。你也可以学习概率图模型课程,其中有很多关于如何在图模型中实现全局归一化的内容。

校准性

我们关心的另一个分布特性是校准性。如果一个模型的置信度分数与其正确性的概率有很好的相关性,我们就说这个模型是校准的。例如,对于一个选择题,如果模型将50%的概率质量分配给了选项B,那么我们希望在实际情况中,B是正确答案的频率也大约是50%。这很有用,因为这样你就可以直接使用模型的逻辑概率作为置信度的度量。

那么,我们的模型通常具备这个属性吗?实际上,经过良好数据训练的预训练模型,其似然性与真实性(或该答案为真的可能性)有合理的对应关系。然而,当我们进行非常有用的后训练步骤(如RLHF)时,虽然通常能得到更好的输出,但却会破坏分布的校准性。因此,如果你观察一个经过后训练的模型,并不能保证它具有很好的校准性。

非零概率质量

概率分布的另一个特点是,它通常会对各种各样的事物分配非零的概率质量。特别是在那些存在半明显事实的场景中,模型至少会对一些不正确的输出分配一定的概率。例如,你的模型不会将100%的概率分配给“2+2=4”这个答案。这既有好处也有坏处。一些有趣的机器学习理论研究探讨了是否可能完全消除这种现象,答案是:如果你还希望模型保持校准性,那么完全消除对非事实内容分配概率质量是不可能的。

分布度量指标

现在,我们想计算一些关于分布的统计量,这些统计量对我们后面要讨论的方法很有用。

第一个你可能见过的度量是分布的。我们通常在解码的单个时间步上,在词汇表空间上计算它,公式为:

H(p) = - Σ p(x) * log p(x)

如果熵相对较高,这意味着什么?在信息论(香农熵)的意义上,高熵意味着我们需要更多的比特来编码信息,因为分布没有明显的偏向性。如果分布完全均匀,我们需要很多比特来指定分布中的任何一个特定项,所以熵会很高。如果熵非常低,则意味着分布非常“尖锐”。例如,如果一个分布将100%的概率质量集中在一个标记上,其他标记为0%,那么你可以用非常简洁的方式描述该分布的输出(总是标记X),所以熵会非常低。

交叉熵

我们关心的不仅仅是抽象的熵,还有相对于某个“真实”分布的熵。这里我们计算的是交叉熵,即相对于某个真实分布的熵。在语言模型中,“真实”分布通常指的是我们在自监督或半监督目标中使用的分布。具体来说,我们查看训练文档中实际的下一个标记是什么,然后计算模型预测分布与这个“真实”分布的交叉熵。

采样方法

在了解了模型作为概率分布及其度量之后,我们现在可以探讨如何从这个分布中采样。采样是从模型生成文本的基本操作。

以下是几种常见的采样方法:

1. 贪婪解码

贪婪解码在每一步都简单地选择概率最高的标记。这种方法简单高效,但可能导致重复、乏味的输出,因为它从不探索其他可能性。

2. 随机采样(或原始采样)

这是最基本的采样形式,直接从模型的概率分布中随机抽取下一个标记。每个标记被选中的概率与其概率质量成正比。这种方法能产生多样性,但有时会生成不连贯或无意义的文本。

3. 温度采样

温度采样通过引入一个温度参数 T 来调整分布的“尖锐”程度。具体做法是将模型的逻辑概率除以 T,然后重新进行softmax归一化:
p_t(x) = softmax(logits / T)

  • T < 1 时,分布变得更“尖锐”,高概率标记更突出,输出更确定、更保守。
  • T > 1 时,分布变得更“平坦”,低概率标记机会增加,输出更多样、更有创造性。
  • T = 1 时,就是原始的模型分布。

4. Top-k 采样

Top-k 采样首先从分布中选出概率最高的 k 个标记,然后在这个缩小的集合内重新归一化概率,并从中采样。这避免了从极不可能的标记中采样,在生成质量和多样性之间取得了平衡。k 是一个超参数。

5. Nucleus 采样(或 Top-p 采样)

Nucleus 采样不是固定候选标记的数量,而是动态地选择概率质量之和刚好超过阈值 p(例如0.9)的最小标记集合,然后在这个集合内重新归一化并采样。这种方法能根据分布的置信度自适应地调整候选集大小。

6. 典型采样

典型采样旨在生成“典型”的文本,即那些信息量(或惊喜度)与训练数据中平均信息量接近的文本。它通过计算每个标记的信息量(负对数概率),并倾向于选择那些信息量接近分布熵的标记来实现。这有助于生成更流畅、更像人类的文本。

总结

本节课中,我们一起学习了从语言模型中采样的基础知识。我们首先将语言模型框架为条件概率分布,并讨论了局部归一化、校准性等关键特性。接着,我们介绍了熵和交叉熵这两个重要的分布度量指标。最后,我们详细探讨了多种常见的采样方法,包括贪婪解码、随机采样、温度采样、Top-k采样、Nucleus采样和典型采样。理解这些方法及其背后的原理,是有效使用和优化语言模型生成文本的关键第一步。在接下来的课程中,我们将继续探讨更复杂的推理算法。

004:束搜索及其变体 🧠

在本节课中,我们将学习一种称为“模式搜索”的解码方法,其目标是找到模型输出分布中概率最高的单个序列。我们将重点介绍束搜索及其多种变体,并探讨束搜索的一些特性,包括所谓的“束搜索诅咒”。


上一节我们讨论了基于采样的解码方法。本节我们将从一个更偏向优化的视角来看待解码问题:我们不仅想要一个好的输出,更想要在给定模型参数分布下,最可能的那个输出序列。这被称为模式搜索或最大后验概率解码。

对于单个时间步的单个词元,找到最可能的输出很简单,只需对逻辑值执行 argmax 操作即可。然而,当我们想要解码一个完整的序列时,问题就变得复杂了。

贪心解码及其问题

一种简单的基线方法是贪心解码:在每个时间步,我们都选择当前概率最高的词元。其计算过程如下:

next_token = argmax(logits)

这种方法计算非常简单,但在扩展到多词元序列解码时,存在几个问题。

首先,贪心解码不一定能找到全局概率最高的序列。考虑以下情况:在某个时间步,两个词元“eats”和“cats”的概率相近。选择当前概率稍高的“eats”后,其后续词元的概率可能很低(例如0.1)。而选择当前概率稍低的“cats”,却可能有一个概率极高的后续词元(例如0.9)。最终,“cats”路径得到的序列总概率可能远高于“eats”路径,但贪心解码会错过它。

其次,贪心解码容易导致生成的文本看起来奇怪或陷入重复。例如,早期GPT-3模型在使用贪心解码时,可能会生成如下循环文本:

“I’m traveling forward. I’m traveling backward. I’m traveling sideways. I’m traveling forward. I’m traveling backward...”

以下是导致“重复陷阱”的原因:

  • 一旦模型开始重复某个模式(例如“I’m traveling X”),由于训练数据中可能存在类似的重复文本,模型会学习到“重复”是一个高概率的延续。
  • 在贪心解码下,模型总是选择当前最高概率的词元,这强化了重复趋势。重复两次后,第三次重复的概率变得更高,如此循环,难以跳出。
  • 通过指令微调或强化学习训练的较新、较大的模型,由于在训练中被明确引导避免无意义重复,因此较少陷入此陷阱。但对于容易重复的模型,在推理时选择合适的解码算法是必要的解决方案。

既然贪心解码在寻找全局最优序列和生成质量上存在不足,那么有没有更好的方法呢?接下来,我们将介绍束搜索,它是一种在计算资源和搜索质量之间取得平衡的经典算法。

束搜索

束搜索的核心思想是:在解码的每一步,保留概率最高的 k 个候选序列(称为“束宽”),而不是像贪心解码那样只保留一个。这样可以在一定程度上探索更多可能性,避免因早期局部最优选择而错过后期更优的序列。

束搜索的基本步骤如下:

  1. 初始化:从起始词元开始,有一个包含1个序列的集合。
  2. 扩展:对于当前集合中的每个序列,生成所有可能的下一个词元,并计算新序列的概率(通常是累积对数概率)。
  3. 修剪:从所有扩展得到的新序列中,选出概率最高的 k 个,作为下一步的候选集合。
  4. 重复:重复步骤2和3,直到所有序列都生成结束符或达到最大长度。

束搜索通过维护一个固定大小的候选集(束),在比穷举搜索更高效的情况下,找到比贪心解码更优的序列。


束搜索虽然有效,但也并非完美。接下来,我们将探讨束搜索的一些重要变体及其特性。

束搜索的变体与特性

束搜索有多种改进和变体,旨在解决其特定问题或适应不同需求。

束搜索诅咒

“束搜索诅咒”指的是一个现象:当使用束搜索生成较长序列时,最终选出的最高概率序列,其对数概率往往会随着序列长度增加而线性下降(即概率呈指数衰减)。这是因为束搜索倾向于选择由当前高概率词元组成的序列,但这些词元在长上下文中共同出现的联合概率可能很低。

更形式化地说,如果我们比较不同长度的序列,束搜索选出的序列分数可能不具有直接可比性,需要进行长度归一化等后处理。

其他变体

除了标准束搜索,常见的变体还包括:

  • 长度归一化束搜索:在比较序列分数时,将累积对数概率除以序列长度(或长度的某种函数),以缓解长序列分数偏低的问题,使不同长度的序列更具可比性。
  • 分集促进束搜索:在修剪步骤中,不仅考虑序列概率,还考虑候选序列之间的差异性,以避免束中的所有序列都过于相似,从而增加输出的多样性。
  • 随机束搜索:在扩展和修剪步骤中引入随机性,以一定的概率保留非最优的候选,从而探索更广阔的搜索空间。

本节课中,我们一起学习了模式搜索解码的核心思想。我们从简单的贪心解码入手,分析了它容易陷入局部最优和重复陷阱的问题。接着,我们深入探讨了束搜索算法,它通过维护一个固定大小的候选集,在效率和效果上取得了更好的平衡。最后,我们了解了束搜索的局限性,如“束搜索诅咒”,以及一些常见的改进变体,如长度归一化和分集促进。

理解这些基础解码算法,是掌握更高级、更复杂语言模型生成技术的重要一步。

005:A*与最佳优先搜索 🧭

在本节课中,我们将要学习两种更高级的搜索算法:最佳优先搜索和A*搜索。我们将探讨它们如何比贪心搜索和束搜索更智能地探索搜索空间,以找到概率更高的输出序列。

上一节我们介绍了贪心搜索和束搜索,它们各有优缺点。本节中我们来看看如何利用启发式信息来引导搜索,从而更高效地找到最优解。

搜索算法的发展背景

在深入细节之前,我想先谈谈我们当前讨论的算法在实际应用中的相对重要性。目前最常用的方法是采样贪心搜索,以及程度稍轻的束搜索。而今天要讨论的算法可能是我们所有方法中使用最少的。

我认为这有其原因。早期的语言模型,例如GPT-1和GPT-2,存在很多问题。如果你试图从中找出概率最高的输出,它们可能会产生不断重复的内容。如果你使用贪心搜索或束搜索,可能会得到一个长度为0的完全无用的输出。

为了解决这个问题,我们基本上“破坏”了推理过程。我们采用了不直接从分布中采样的策略,不使用贪心搜索,也不使用其他类似方法。那是一个“破坏搜索以获得更好结果”的时代。

现在,我们有了像GPT-3和GPT-4这样好得多的模型。GPT-3仍然不够好,但GPT-4已经足够好了。现在搜索算法实际上可以正常工作了,所以我们可以直接使用贪心输出。像OpenAI这样的公司,甚至不允许你使用温度参数不等于1的设置,它强制你从模型中采样。我认为很多其他公司也遵循了这一点,只使用温度参数为1的采样。

其理念是:我们直接根据模型的分布进行采样,因为模型已经被训练得足够好,当我们直接从分布中采样时,结果也相当不错。

但是,实际上,贪心搜索在我们最好的模型上确实有效,而且它比温度参数为1的采样效果更好。这让我觉得,现在是时候重新审视那些能有效搜索高模型分数输出的方法了。也许现在是重新考虑这些方法的时候了。

回顾:搜索空间与图表示

我将把搜索空间当作图来讨论,因为今天要讲的很多算法最初都是为图搜索而创建的。我们可以将其具体表示为一个加权有限状态自动机

一个加权有限状态自动机可以定义为:

WFSA = (S, Σ, δ, s0, F, W)

其中:

  • S 是一个有限的状态集合。
  • Σ 是一个有限的字母表(即词汇表)。
  • δ: S × Σ → S 是转移函数。
  • s0 ∈ S 是初始状态。
  • F ⊆ S 是终结状态集合。
  • W: δ → ℝ 是给每条转移边分配实数值权重的函数。为简化问题,通常将权重限制为全正或全负。

这是一个示例图:

初始状态 S0 --(A, 0.5)--> S1 --(B, 0.3)--> S2 --(C, 0.2)--> 终结状态 S3
          --(B, 0.3)--> S4 --(C, 0.7)--> S5
          --(C, 0.2)--> S6

这里,词汇表是 {A, B, C}。每条边都标有一个概率权重。由于是概率,从一个节点出发的所有边的权重之和应为1。

边权重的不同表示方式

有多种方式可以表示边的权重。从数学抽象的角度,这涉及到一个称为半环的概念。但简单来说,定义边权重时需要定义几个要素:

  1. 取值范围:例如,概率的取值范围是 [0, 1];对数概率的取值范围是 (-∞, 0]。
  2. 路径得分计算方式(加法函数 ⊕):在半环中,这抽象了“加法”操作,表示如何合并两条边的权重。
    • 对于概率,路径得分是沿路径所有边权重的乘积。寻找最佳路径是寻找乘积最大的路径。例如,一条路径的得分可能是 0.5 * 0.3 * 0.2
    • 对于对数概率,路径得分是沿路径所有边权重的。寻找最佳路径是寻找和最大的路径(因为对数概率为负,最大和即最接近零)。

最佳优先搜索

现在,让我们进入正题,看看最佳优先搜索

贪心搜索在每个步骤只选择当前概率最高的词元,它是短视的,可能做出糟糕的局部决策,而无法回溯或考虑其他假设。束搜索则同时维护Top-K个假设进行探索。

最佳优先搜索是一种更通用的图搜索算法。它的核心思想是始终扩展当前看来“最好”的节点。这个“好”的程度通过一个评估函数 f(n) 来衡量,其中 n 代表一个搜索节点(即一个部分生成的序列或图中的一个状态)。

算法维护一个优先队列(通常是最小堆或最大堆,取决于f(n)的优化方向),其中节点按照 f(n) 的值排序。

以下是算法的基本步骤:

  1. 将初始状态 s0 放入优先队列。
  2. 当队列不为空且未找到目标(或未满足停止条件)时:
    a. 从队列中取出 f(n) 值最优的节点 n
    b. 如果 n 是目标状态(终结状态),则成功返回路径。
    c. 否则,扩展节点 n,生成其所有后继节点。
    d. 对于每个后继节点 m,计算其评估值 f(m),并将其放入优先队列。

最佳优先搜索的关键在于 f(n) 的定义。如果 f(n) 只考虑从起始点到节点 n 的实际代价 g(n),那么它就退化成了Dijkstra算法,能保证找到全局最短路径,但可能探索很多不必要的节点。

A* 搜索

A 搜索* 是对最佳优先搜索的著名改进,它通过引入启发式函数来显著提高搜索效率。

在A*中,评估函数 f(n) 被定义为:

f(n) = g(n) + h(n)

其中:

  • g(n) 是从起始状态到节点 n实际累积代价(例如,负对数概率之和)。
  • h(n) 是从节点 n 到目标状态的估计代价,称为启发式函数

启发式函数 h(n) 是A*算法的灵魂。一个好的启发式函数应该满足以下条件:

  • 可采纳性h(n) 永远不会高估从节点 n 到目标的实际代价 h*(n)(即 h(n) ≤ h*(n))。这能保证A*找到最优解。
  • 一致性(或单调性):对于任意节点 n 及其后继节点 m,有 h(n) ≤ cost(n, m) + h(m)。这能保证算法的高效性。

如果 h(n) = 0,A* 就退化为 Dijkstra 算法。如果 h(n) 非常准确,A* 就能非常直接地找到目标,几乎不需要探索其他路径。

在语言模型的上下文中,g(n) 可以是我们已经生成的部分序列的负对数概率。h(n) 则是对完成剩余序列所需“代价”的估计,这可能基于一个更小、更快的模型,或者基于一些语言学启发规则(尽管设计一个可采纳的启发式函数对于文本生成非常困难)。

A* 搜索的流程与最佳优先搜索类似,只是 f(n) 的计算包含了启发式部分。它通过优先探索 f(n) 值最小的节点(假设代价是最小化问题),来平衡“已付出代价”和“预计未来代价”,从而高效地导向最优解。

总结

本节课中我们一起学习了两种基于启发式信息的搜索算法。

  • 最佳优先搜索 通过一个评估函数 f(n) 来指导搜索方向,始终扩展当前评估最优的节点。
  • A 搜索* 是其中最重要的一种,其评估函数 f(n) = g(n) + h(n) 结合了从起点到当前节点的实际代价 g(n) 和到目标节点的估计代价 h(n)。一个可采纳的启发式函数能保证A*找到全局最优解。

尽管这些算法在当前的LLM直接部署中使用不如采样或贪心搜索广泛,但随着模型质量的提高,为了寻找概率最高、质量最优的输出,重新审视这些高效的、有导向的搜索算法可能正当时。它们为在复杂的序列空间中进行智能探索提供了强大的理论框架。

006:其他受控生成方法

概述

在本节课中,我们将继续探讨如何控制或约束语言模型的生成过程。我们将介绍两种主要类型的约束:易于用允许或禁止的词汇列表描述的句法约束,以及更难以这种方式定义的语义约束。我们将学习如何通过修改概率分布或调整搜索过程来满足这些约束,并最终将讨论与强化学习对齐的概念联系起来。


回顾:已有的约束方法

上一节我们介绍了束搜索和A搜索等算法,它们已经包含了约束输出的方式。这通常通过调整搜索分数来实现,例如应用长度惩罚、在多样化束搜索中惩罚相似性,或者像神经逻辑A搜索那样加入启发式奖励估计。

本节我们将继续这一主题,探讨旨在生成高概率满足额外条件的输出的约束方法。我们将不仅考虑通过搜索来实现,也会探讨直接修改概率分布然后重新采样的方法。


约束的类型:可验证性

我们将从讨论可验证的约束开始。这里的“可验证”指的是:你可以编写一个确定性函数,在生成结束时检查约束是否被满足;或者,你可以列出所有允许的内容。

我们不仅关心生成结束时是否可验证,还关心在每个单独的生成步骤(token) 是否可验证。

  • 示例1(结束时可验证):输出必须恰好是10个token长。这很容易在最后检查,但在生成过程中,你无法确定每个选择是否最终能满足此约束。
  • 示例2(token级可验证):每个token必须以空格开头,或者所有输出必须是中文,或者输出必须是有效的JSON。在生成的任何步骤,你都能判断是否偏离了这些约束。

token级可验证的约束通常更容易处理。


从神经逻辑A*搜索的例子说起

为了与上一讲的内容衔接,我们直接使用神经逻辑A*搜索论文中的一个约束示例:写出一个包含“car”、“drive”和“snow”这三个概念的句子

这个约束在生成结束时是可验证的(检查句子是否包含这三个词),但不是token级可验证的。如果你从生成“the”开始,并不能保证最终会出现“car”或“snow”。

神经逻辑A*搜索的直觉是:它试图通过前瞻(look-ahead) 来估计在生成结束时满足约束的启发式可能性。例如,如果生成了前缀“I drive my car during the”,我们已经满足了三个约束中的两个。下一个token可能是“summer”或“winter”。

  • “summer”的概率可能略高,但会让我们更难在句末引入“snow”。
  • “winter”的概率可能略低,但更有可能最终生成“snow”。

通过A*搜索进行前瞻,我们可以选择更可能满足约束的路径。这是许多此类方法的核心思想。

现在,让我们暂时搁置这个复杂案例,从一个更简单的情况开始:token级可验证的约束


模板化生成:一个简单案例

考虑一些对语言模型输出的要求,例如:

  • 每次与用户对话时,消息应以“Hello”开头。
  • 每次输出都应以“Thanks for chatting with me”结尾。
  • 应始终输出有效的JSON。

能否通过训练让模型满足这些约束?理论上可以,例如提供大量总是以特定内容开头或结尾的训练数据。但无法保证模型在采样时总能做到,尤其是当输出过长被截断,或需要输出复杂结构(如JSON)时。此外,如果你想动态改变约束(例如将“Hello”改为“Bonjour”),重新训练整个模型是不现实的。

因此,对于这些相对简单、可以在推理时强制执行的约束,更好的做法是在推理时进行修改对生成过程施加约束

对于前两个简单例子(固定开头和结尾),如何在推理时强制执行?

  • 对于“Hello”:只需在模型生成的任何内容之前,强制添加“Hello”即可。
  • 对于结尾致谢:同样,在模型生成结束后,直接附加“Thanks for chatting with me”。

这些在系统层面很容易实现。但如何让模型始终输出有效的JSON呢?


为什么需要约束JSON生成?

在2025年,一个合理的首要问题是:不能直接要求模型这样做吗? 例如,向GPT-5提出请求“请将其转换为JSON”。GPT-5知道JSON格式,但其默认输出可能包含额外的解释性文字(例如:“嗨,我已为您完成此事...”)。如果你通过API调用并期望得到纯JSON结果,这可能不是你想要的。

当然,如果我们给出更清晰的指令(例如“仅输出JSON对象,不要任何额外文本”),前沿模型通常能够遵循。那么,为什么我们还要花时间讨论约束JSON生成呢?主要有两个原因:

  1. 格式一致性:模型可能选择“birth”作为键,但你的生产数据库使用的是“date_of_birth”。为了与现有格式无缝交互,需要保证输出结构完全符合预定模式。
  2. 可靠性保证:虽然GPT-5在请求有效JSON时不太可能输出灾难性错误,但在进行数百万次生成时,仍可能得到格式稍显奇怪或包含无关信息(如“抱歉,您已超过速率限制”)的输出。在大规模操作中,你需要某种验证机制来确保每次都能获得有效JSON,否则你就必须在得到输出后再进行JSON检查。

那么,我们该如何在生成过程中强制执行有效的JSON输出呢?


总结

本节课我们一起探讨了语言模型生成过程中的约束方法。我们区分了句法约束语义约束,并重点介绍了可验证性的概念,特别是token级可验证的约束,这类约束更容易在生成过程中进行控制。我们以固定开头/结尾和JSON生成为例,说明了为什么有时需要在推理时而非仅仅依靠训练来施加约束。接下来,我们将深入探讨如何具体实现这些约束,特别是保证JSON有效性的技术。

007:思维链与中间步骤

概述

在本节课中,我们将要学习思维链推理。这是一种在现代模型中广泛使用的基础技术,对于构建执行推理的模型至关重要。思维链是当前最重要的技术之一——推理模型的核心基础。我们将探讨其动机、工作原理以及如何帮助模型处理复杂任务。

动机:处理复杂推理任务

我们使用思维链的主要动机是为了完成复杂的推理任务。

例如,考虑这样一个问题:一块H100 GPU生成100个Llama 38B模型的token,最快需要多长时间?

如何开始解答这个问题?你可能会先计算生成一个token所需的时间。但这还不够,因为注意力机制的计算时间是二次的,并且解码过程无法完全利用GPU。此外,模型还需要回忆H100的FLOPS性能、Llama 38B的层大小,并最终执行计算。模型可以自己进行数学计算,也可以编写并运行一个程序来完成计算。我们将在后续课程中深入探讨这些能力。

核心挑战在于:并非所有问题的难度都相同

  • 简单问题(如 2 + 3)可以瞬间得出答案。
  • 中等难度问题(如 (3 + 4) * 5)可能需要稍加思考。
  • 复杂问题(如上面的GPU计算问题)则需要逐步推理,甚至借助外部工具。

因此,一个关键问题是:我们如何根据问题的难度来分配计算资源? 从理论上讲,有些问题的计算复杂度超过了语言模型单步预测所能进行的计算量,因此无法一步解决。但更常见的情况是,语言模型在解决计算问题时不够准确或高效。

历史背景:自适应计算时间

在讨论Transformer语言模型之前,需要指出思维链并非一夜之间出现的概念。其思想可以追溯到自适应计算时间,例如Graves在2016年针对循环神经网络语言模型提出的方法。

该方法的核心理念是:根据输入复杂度动态调整计算量

具体实现是:教会循环神经网络学习需要执行多少计算步骤。他们引入了一个停止机制。其工作流程如下:

  1. 获取循环神经网络的隐藏状态。
  2. 将其输入一个Sigmoid单元预测器,预测“应该停止”或“不应停止”。
  3. 根据预测结果决定是否停止计算。

此外,他们还使用了一种称为“ponder cost”的损失项来鼓励计算效率,这本质上是所执行步骤数的连续近似。论文中的图1展示了该网络的结构。

思维链提示

上一节我们回顾了自适应计算的历史,现在来看看现代语言模型中的关键方法:思维链提示。

思维链提示的核心思想是:在给出最终答案之前,引导模型生成一系列中间推理步骤

以下是思维链提示的一个标准示例:

标准提示:

问题:Roger有5个网球。他又买了2罐网球,每罐有3个。他现在总共有多少个网球?
答案:

模型可能直接输出:11

思维链提示:

问题:Roger有5个网球。他又买了2罐网球,每罐有3个。他现在总共有多少个网球?
让我们逐步思考。

模型可能会输出:

Roger一开始有5个网球。
2罐网球,每罐3个,所以是 2 * 3 = 6 个新网球。
总数为 5 + 6 = 11。
所以答案是11。

通过要求模型“逐步思考”,我们鼓励它展示其内部推理过程,这通常能显著提高其在复杂算术、常识推理和符号推理任务上的准确性。

思维链为何有效?

我们已经了解了思维链的基本形式,现在来探讨它为何有效。

思维链之所以有效,主要有以下几个原因:

  1. 模仿人类推理:它将多步问题分解为更易管理的子步骤,模仿了人类解决问题的方式。
  2. 减少幻觉:通过强制模型将答案建立在明确的中间步骤上,减少了“一步到位”可能产生的错误或胡言乱语。
  3. 利用预训练模式:大型语言模型在预训练时接触过大量包含推理步骤的文本(如教程、数学解题过程),思维链提示激活了这些模式。
  4. 误差定位:当答案错误时,检查中间步骤更容易定位错误发生在哪一环。

本质上,思维链提示利用了语言模型已有的分步推理能力,只是通过提示使其显式化。

进阶技术:自洽性

思维链提示的一个常见问题是,对于同一问题,不同的推理路径可能导向不同的答案。为了解决这个问题,研究者提出了“自洽性”方法。

自洽性的核心思想是:通过采样多条推理路径,并选择最一致的答案来提升鲁棒性

以下是其工作流程:

  1. 对于给定的问题,使用思维链提示从语言模型中采样生成 N 个不同的推理路径和答案。
  2. 从这 N 个结果中,选择出现频率最高的答案作为最终输出。

例如,对于一个数学问题,模型可能生成了3条推理链,分别得到答案 11, 11, 17。那么自洽性方法会选择 11 作为最终答案,因为它出现了两次。

这种方法通过“群体智慧”降低了单次推理中随机错误的影响,在数学和推理任务上显著提升了性能。其公式化表示如下:

最终答案 = argmax_{答案a} ( count( 采样路径中输出答案a的路径数量 ) )

总结

本节课我们一起学习了思维链推理及其相关技术。

我们首先探讨了处理复杂推理任务的动机,以及根据问题难度分配计算资源的必要性。接着,我们回顾了自适应计算时间的历史背景。然后,我们深入学习了思维链提示的核心方法,即引导模型生成中间推理步骤以提高答案准确性。最后,我们介绍了自洽性这一进阶技术,它通过集成多条推理路径的结果来进一步提升模型的鲁棒性和可靠性。

思维链是构建强大推理模型的基础,理解其原理对于后续学习更复杂的推理和工具使用模型至关重要。

008:自优化与自校正方法 🧠

在本节课中,我们将学习大语言模型(LLM)的自优化自校正方法。这些方法旨在通过让模型对自身输出进行迭代式地评估与修正,来提升生成结果的准确性和质量。我们将探讨其核心流程、关键设计决策,并通过几篇代表性论文来具体了解不同的实现方式。


自校正的基本流程

上一节我们介绍了思维链(Chain of Thought)方法,本节中我们来看看更显式的自优化与自校正方法。与人类写作或编程类似,我们通常不会一次性生成完美的结果,而是先完成初稿,然后反复检查和修改细节。自校正方法正是为了在语言模型中实现这一过程。

一般来说,自校正过程遵循一个循环结构,其核心步骤如下:

  1. 生成初始输出:模型根据输入生成第一个版本的答案。
  2. 评估输出:对当前生成的输出进行评估,判断其质量。
  3. 判断是否停止:如果评估认为输出已足够好,则停止循环并输出结果。
  4. 迭代修正:如果评估认为输出有待改进,则根据评估结果生成一个修正后的新版本,并回到第2步。

为了防止无限循环,通常会设置一个最大迭代次数作为硬性停止条件。

这个流程可以用以下伪代码表示:

def self_correction_loop(input, max_iterations):
    current_output = generate_initial_output(input)
    for i in range(max_iterations):
        critique = evaluate_output(input, current_output)
        if is_satisfactory(critique):
            return current_output
        current_output = refine_output(input, current_output, critique)
    return current_output # 达到最大迭代次数后返回

关键设计决策

为了使上述流程有效工作,不同的方法在实现细节上做出了不同的设计选择。以下是几个核心的设计维度:

  • 评估方式:如何对输出进行“评估”或“批判”?
    • 显式评估:明确地提示或训练一个模型(可以是同一个LLM)来评判输出的好坏,并生成具体的修改建议。
    • 隐式评估:不进行显式评判。例如,当模型在修正步骤中决定“无需修改”时,即视为输出已达标。
  • 训练需求:是否需要专门训练模型来执行自优化任务?
    • 需要训练:使用特定数据对模型进行微调,使其更擅长生成、评估和修正。
    • 无需训练:完全依赖预训练模型的能力,通过巧妙的提示工程(Prompt Engineering)来实现循环。
  • 修正条件:在生成修正版本时,模型应该参考哪些信息?
    • 可以基于原始输入当前输出评估结果或它们的组合。
  • 外部工具:评估过程是否依赖外部工具?
    • 纯语言模型:评估完全由LLM自身完成。
    • 结合外部工具:利用代码执行器、数学计算器、事实核查数据库等外部工具来辅助评估输出的正确性。

代表性方法介绍

接下来,我们将通过几篇论文来具体了解这些设计决策是如何被应用的。

1. 两阶段解码与“深思”修正 (2017)

这是一篇较早的论文,其核心思想非常简单:不满足于单次生成,而是先生成一个初稿,然后在拥有全局上下文的情况下对其进行精炼。

算法流程

  1. 第一遍生成:模型根据输入 x 生成初始序列 y‘
  2. 第二遍修正:模型将 xy‘ 共同作为输入,生成最终的优化序列 y

这个方法的关键在于,它训练了模型来进行这种两阶段生成。其训练目标是最大化最终输出 y 在给定输入 x 和中间输出 y‘ 下的对数概率:

L = log P(y | x, y‘; θ)

然而,这里存在一个计算难题:中间输出 y‘ 的可能性空间是离散且巨大的(所有可能的序列),无法直接对所有 y‘ 求和来计算梯度。

解决方案是采样:从第一遍模型中采样多个可能的中间输出 y‘,然后对于每个采样,计算它导致高质量最终输出 y 的概率。通过这种方式,模型可以学会偏好那些能导向更好最终结果的中间状态。用现代术语来说,这类似于基于奖励的优化思路。


2. 自优化 (Self-Refine)

自优化方法通常不依赖外部训练,而是通过设计提示词,让同一个LLM扮演“生成器”和“批判者”两个角色,进行多轮交互。

典型流程

  1. 生成:LLM根据问题生成一个答案。
  2. 批判:我们提示LLM:“请以批评家的身份,找出上述答案中可能存在的错误或可以改进的地方。”
  3. 修正:我们再次提示LLM:“根据刚才的批评,请重新生成一个改进后的答案。”
  4. 重复步骤2和3,直到满意或达到迭代上限。

这种方法无需训练使用显式评估,并且修正时同时参考了输入、当前输出和批判内容


3. 基于外部工具的自校正

对于一些需要精确性的任务(如数学计算、代码执行、事实核查),纯语言模型的评估可能不可靠。这类方法会引入外部工具来辅助评估。

例如,在解决数学问题时

  1. LLM生成一个解题步骤和答案。
  2. 系统将LLM生成的代码或计算式提交给Python解释器执行。
  3. 将执行结果与LLM的答案进行比对。如果不一致,则将错误信息(如执行报错或结果不符)反馈给LLM。
  4. LLM根据工具反馈的错误信息,修正其解题步骤。

这种方法将LLM的推理能力与工具的精确性结合起来,显著提升了复杂任务的成功率。


总结

本节课我们一起学习了LLM的自优化与自校正方法。我们首先了解了其迭代修正的基本流程,然后探讨了决定方法效果的几个关键设计决策:评估方式(显式/隐式)、训练需求、修正条件和是否使用外部工具。

通过分析从早期的两阶段解码到现代结合工具的自校正等多种方法,我们可以看到,让模型学会“反思”和“修正”是提升其输出可靠性的强大范式。这些方法可以与思维链、智能体(Agent)等技术结合,构建出更加强大和稳健的AI系统。

009:推理模型 🧠

在本节课中,我们将要学习什么是“推理模型”。我们将探讨其核心定义、训练方法,并通过一个名为“STaR”的经典论文来理解其工作原理。


概述

推理模型通常指通过强化学习等方法训练,能够利用长链思维过程来提升特定任务性能的模型。这些思维链不仅长,还具有自我修正等特定属性。通常,更长的推理序列与更好的性能相关,并且这类模型常使用可验证的奖励(如数学或代码的正确性)进行训练。

什么是推理模型?

你可以争论其定义,但目前大多数人使用的定义是:一种通常使用强化学习训练的模型,旨在利用长链思维来在特定任务上表现更好。这些长链思维不仅长,还具有特定属性,例如自我修正,即模型会回顾并重新审视之前的假设。一般来说,对于推理模型,更长的序列与更好的性能相关,这种相关性比短链思维更为显著。通常(但并非总是),这类模型使用可验证的奖励进行训练。

STaR:一个经典方法

第一个从大语言模型角度进行此类研究的论文名为 STaR。其核心思想是:给定一个问题,通过语言模型生成一个推理过程和一个答案。这个推理过程就是你的思维链。然后,根据答案运行一个可验证的奖励函数,检查其正确与否。

最典型的可验证奖励是数学代码。对于数学问题,这意味着提取数学问题的答案(通常写在类似 \boxed{} 的 LaTeX 命令中),并检查是否匹配。虽然有时会遇到等价表述的挑战,但这通常比检查自由文本的正确性要简单得多。

STaR 论文中一个特别的步骤是:如果答案错误,模型会得到一个提示(即正确答案),然后语言模型会生成一个与该答案匹配的推理过程。这个步骤并非所有推理模型都采用。

STaR 的工作流程

上一节我们介绍了 STaR 的基本概念,本节中我们来看看它的具体工作流程。

以下是 STaR 方法的核心步骤:

  1. 生成答案和推理过程:将问题输入语言模型,生成推理过程和答案。
  2. 基于奖励过滤正确链:使用可验证的奖励函数检查答案。
  3. 处理结果
    • 如果答案正确,则保留完整的思维链。
    • 如果答案错误,则给定正确答案,让模型生成与之匹配的推理过程。
  4. 微调模型:使用过滤后(或修正后)的数据对模型进行微调。

与强化学习的联系

这种方法非常接近强化学习,可以看作是一种特殊的强化学习形式:模型进行“推演”,获得结果和奖励。

另一种理解方式是通过策略梯度目标。策略梯度(如经典的 REINFORCE 算法)的基本形式是:根据获得的奖励,乘以输出对数似然的梯度。其通用公式可以表示为:

∇J(θ) ≈ E[ R * ∇ log π(a|s; θ) ]

其中,R 是奖励,π(a|s; θ) 是策略。

在 STaR 的简单场景中,如果答案正确,奖励为 1;如果错误,奖励为 0。这将丢弃错误推理路径的梯度。尽管方法简单,但论文证明这在几年前就是有效的。

实验与格式

他们在一个相对较小的模型(GPT-J 6B)上进行了测试,任务包括算术、常识问答和小学数学问题。

他们采用的数据格式很有趣,以少量示例开始。输入和目标格式如下:

输入格式示例:

问题:计算 123 + 456。

目标/输出格式示例:

推理(草稿纸):
3 + 6 = 9 (个位)
2 + 5 = 7 (十位), 加上进位 0, 还是 7
1 + 4 = 5 (百位), 加上进位 0, 还是 5
所以答案是 579。
答案:579

这种“草稿纸”格式允许模型展示逐步计算过程,例如加法中的进位处理。

总结

本节课中,我们一起学习了推理模型的核心概念。我们了解到,推理模型通过利用可验证奖励训练出的长链、可自我修正的思维过程来提升性能。我们以 STaR 论文为例,详细剖析了其“生成-验证-修正-微调”的工作流程,并探讨了其与强化学习策略梯度方法的联系。理解这些基础原理,是深入学习更复杂推理算法的重要一步。

010:工具集成 🛠️

在本节课中,我们将要学习如何为语言模型集成外部工具。我们将探讨工具的定义、分类、调用范式,并通过一个早期经典案例——WebGPT,来理解工具集成的实际应用与训练方法。

工具的定义与分类

上一节我们介绍了课程的基本安排,本节中我们来看看什么是工具。工具是指语言模型外部、作为其运行环境一部分的组件。

它们具有函数式接口,可以看作是编程语言中的函数,可以通过输入参数调用并获取输出结果,通常可以作为程序执行。

根据功能,工具主要可以分为以下三类:

以下是三种主要的工具类别:

  1. 感知工具:这类工具赋予语言模型访问其参数之外信息的能力。例如,从网络收集信息、读取文件系统或与用户交互。
  2. 行动工具:这类工具可以直接对环境产生影响和改变。例如,控制智能家居设备、发送邮件或修改数据库。
  3. 计算工具:这类工具不一定与外部环境交互,而是为语言模型提供其原本难以执行的计算能力。例如,调用计算器或Python解释器进行复杂运算。

以下是各类工具的一些具体示例:

  • 知识增强:调用搜索引擎、进行数据库查询。
  • 计算:使用计算器、Python解释器。
  • 世界信息:获取天气、查询日历事件。
  • 多模态交互:对图像进行视觉问答、将视频转换为文字稿、在Spotify上播放音乐。

工具调用范式

了解了工具的分类后,我们来看看语言模型调用工具的常见范式。虽然存在多种方式,但一种常见的方法是:在生成最终输出前,先调用一个函数。

典型的流程如下:

  1. 用户提出查询。
  2. 语言模型生成一个特殊的标签(如 <tool_call>),表示开始调用工具。
  3. 模型写出具体的工具调用指令(包括工具名和参数)。
  4. 模型生成结束标签(如 </tool_call>)。
  5. 系统解析工具调用,向远程服务器发送API请求。
  6. 获取API输出结果。
  7. 结果可以直接拼接到模型生成的文本流中,也可以作为“观察”反馈给模型,模型再基于此观察生成最终的回答。

经典案例:WebGPT

在大型语言模型时代早期,一个著名的工具集成案例是WebGPT。它解决了语言模型无法获取最新信息的问题。

其核心思想是赋予GPT模型进行基于文本的网络浏览的能力。模型可以通过工具执行以下操作:search()(搜索)、click()(点击链接)、scroll()(滚动页面)和 quote()(提取引用)。

WebGPT并非简单提示模型使用工具,而是通过强化学习来自人类反馈 进行训练。训练过程主要分为三步:

以下是WebGPT的训练步骤:

  1. 行为克隆:研究人员创建了一个网页界面,让人类执行信息搜集任务(搜索、点击、滚动)。他们记录下人类的所有操作数据,并用这些数据来训练一个初始的GPT模型(行为克隆模型)。
  2. 奖励建模:让行为克隆模型针对同一问题生成多个输出,由人类标注员选择他们更喜欢的答案,从而得到偏好数据。基于这些数据,训练一个奖励模型,用于评估模型生成的质量。
  3. 强化学习优化:使用上一步训练好的奖励模型作为信号,通过强化学习(如PPO算法)进一步优化语言模型的行为,使其生成的浏览和回答更符合人类偏好。

实验结果表明,经过训练的WebGPT模型在提供事实性、综合性答案方面,其表现优于未经搜索增强的模型,甚至超过了当时Reddit上随机获得高赞的回答者。


本节课中我们一起学习了语言模型工具集成的基本概念。我们明确了工具是模型外部的可调用函数,并将其分为感知、行动和计算三类。我们介绍了一种常见的工具调用范式,即通过特殊标签触发调用。最后,我们深入分析了WebGPT这一经典案例,了解了如何通过RLHF训练框架,使语言模型学会使用搜索工具来获取并整合最新信息,从而提升回答质量。

011:智能体与多智能体通信

概述

在本节课中,我们将学习智能体(Agents)与多智能体通信的基本概念。课程将聚焦于智能体在推理过程中面临的独特挑战,包括规划、环境表示和评估等,并会介绍相关的核心架构与工具。


课程开始前的通知

在开始之前,有两项关于作业的通知。

第一项,作业二预计将在今天发布。这里的“今天”指的是地球上的任何地方。希望你们明天醒来时就能看到它。

第二项,我们非常希望获得关于作业一的反馈。例如,有些人反映作业耗时较长,或者某些部分比较困难。我们希望确保作业内容不仅具有启发性,能帮助大家学习课堂讨论的知识,同时难度设置合理。

为此,我们准备了一份匿名反馈表。我们尝试在其中融入一些博弈论思想来设计评分机制:如果班级中超过50%的人填写了反馈表,那么每个人都能获得一次测验的满分。这是一份匿名反馈表。如果每个人都填写了,你们将在测验成绩中获得额外的3分(满分3分),这将使获得高分变得更容易一些。但这一奖励的前提是班级中有一半的人参与填写。因此,你们可以鼓励朋友们去填写。

我们这样做的原因是,我们希望在保持匿名的前提下,鼓励大家提供反馈。

如果没有问题,我们就开始今天的课程。今天有两场展示。


智能体简介

接下来,我将讨论智能体。我不会过多讨论如何通过架构或语言模型的变化来训练或设计智能体以提升准确性,而是主要聚焦于与智能体推理相关的内容,因为这是一门推理课程。当然,不可避免地会涉及一些架构方面的内容。

智能体是一个迭代式使用工具以完成任务的系统。它并非一次性使用工具,而是反复使用工具。

如今,几乎所有的智能体都由大语言模型驱动。可能还存在一些不由大语言模型驱动的智能体。例如,三到五年前存在的Siri和Cortana就是很好的例子。它们现在可能主要不是由大语言模型驱动。其他例子还包括致电航空公司时使用的客户服务系统,它们基本基于决策树和分类器。这些系统仍在被使用,但目前新开发的智能体大多使用LLMs。

智能体之所以有趣,是因为它们在多个方面提出了独特的推理挑战。

  1. 推理与规划:因此,需要对它们进行额外的推理步骤。
  2. 环境表示:如何表示从工具调用中获得的所有输出。
  3. 长上下文建模:除了摘要或问答等任务,智能体可能是当前最大的长上下文应用场景之一。
  4. 评估:如果人们想使用并评估智能体,我会稍微讨论这一点。
  5. 评判模型:这是我们一直在研究的内容。评判模型本质上类似于我们稍后会讨论的奖励模型,但我想现在讨论它们,因为这是专门的智能体课程,之后讲到奖励模型时大家可以联系起来。
  6. 多智能体委托:我不会详细讨论这一点,但会简要提及。

智能体的应用场景

首先,我们来看一些智能体的应用场景。有人构建过智能体来做某事吗?

例如,模拟搜索引擎上的用户行为,预测用户点击和下一步操作。这听起来是个不错的应用。

还有其他有趣的应用场景吗?

好的,感谢大家无意中分享了各自智能体的用途。我们有许多不同的应用场景。

  • 软件开发:这是目前最大的应用领域之一,包括代码生成、调试和测试。流行的例子有Anthropic的Claude Code、Codex,以及我将要讨论的一个开源智能体OpenHands。
  • 网络自动化:例如数据提取、表单填写、测试等。闭源产品有OpenAI的Agent Mode,开源产品则有BrowserUse等研究常用工具。
  • 研究与分析:默认情况下,许多聊天机器人界面(如ChatGPT或Claude)都支持信息收集和报告生成。还有Perplexity Pro这样的产品,以及开源版本如OpenDe Research(有多个变体可供选择)。
  • 交互式环境:例如游戏、模拟和机器人技术,像玩《我的世界》或控制机器人的智能体。

OpenHands 示例

我一直在构建一个名为OpenHands的开源智能体。它最初是一个开源工具,现在是我合作的一家初创公司的一部分。因此,我很多最好的例子都来自这方面的经验。基本上,它是一个支持网页浏览和其他多种工具的软件开发智能体。

我想展示一个小例子。

任务:找到卡内基梅隆大学2025年秋季“Inference Algorithms”课程的网站,下载作业,并预估完成作业一所需的时间。

让我们看看它的准确性。(实际操作中,由于GP5推理速度慢,切换为Claude模型进行演示)。

在演示中,智能体执行了以下步骤:

  1. 调用搜索引擎工具来查找课程。
  2. 找到由我和Amanda教授的课程页面。
  3. 访问作业页面并从中提取信息。
  4. 克隆相关代码。

总结

本节课我们一起学习了智能体的基本概念及其核心挑战,包括推理规划、环境表示和长上下文处理等。我们还探讨了智能体在软件开发、网络自动化等多个领域的应用,并通过OpenHands的实例了解了智能体的实际工作流程。

posted @ 2026-03-26 01:39  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报