《大模型训练之路》

目录

正文

  1. 第 1 章:序章:大模型不是魔法,是训练目标的升级
  2. 第 2 章:Token、参数与下一个词
  3. 第 3 章:Transformer 够用版:模型如何读懂上下文
  4. 第 4 章:预训练:互联网文本如何变成训练数据
  5. 第 5 章:预训练公式:下一个 token 的概率
  6. 第 6 章:SFT:把基础模型教成助手
  7. 第 7 章:SFT 公式:为什么它仍然是模仿学习
  8. 第 8 章:从标准答案到偏好对
  9. 第 9 章:RLHF:人类偏好如何变成奖励信号
  10. 第 10 章:DPO:把“哪个回答更好”写进损失函数
  11. 第 11 章:数学题和代码题为什么特殊
  12. 第 12 章:外部验证器:奖励从哪里来
  13. 第 13 章:思维链:奖励最终答案,还是奖励推理过程
  14. 第 14 章:自生成样本:模型如何自己练习
  15. 第 15 章:公式对比:从 token 级损失到回答级奖励
  16. 第 16 章:结语:模型能力来自优化目标的层层改变
  17. 第 17 章:后记:我为什么写这本书

附录

全书出现的核心术语统一收录在书末术语表中,方便读者在阅读公式和训练流程时随时回查。

  • 术语表

第 1 章:序章:大模型不是魔法,是训练目标的升级

本章问题

我们应该怎样理解大模型的能力来源:它到底是某种神秘智能,还是一系列训练目标塑造出来的结果?

章节摘要

这一章先把全书的核心视角立起来:大模型不是魔法,也不是一句“预测下一个词”就能完全解释的玩具。我们要从训练目标的变化来理解它:预训练让模型学会预测文本,SFT 让模型学会模仿高质量回答,RLHF/DPO 让模型学会偏向更被认可的回答,可验证强化学习则让模型开始追求能被外部系统验证的结果。读者不需要一开始就记住所有算法名,只需要先抓住一个问题:每个阶段,模型到底在优化什么?

读者收获

读者应理解:本书不是讲 AI 神话,而是讲训练机制;大模型能力的演化,可以从“训练信号越来越明确、优化目标越来越接近人类想要的能力”来理解。

配图

四层训练目标阶梯

图 1-1|四层训练目标阶梯:大模型能力来自训练目标一层层变化,而不是突然出现的魔法。

读图:训练目标如何一层层改变?

读图时先看每一层在回答什么问题:预训练问真实下一个 token 的概率够不够高,SFT 问回答是否像高质量助手答案,RLHF / DPO 问两个回答中哪个更值得被偏好,可验证强化学习则问完整回答能否通过验证、获得奖励。

这张图是全书地图。后面每一章都会沿着这条阶梯展开:训练信号越来越明确,优化目标也越来越接近我们真正希望模型具备的能力。

正文

“大模型只是预测下一个词”,这句话到底哪里对?

很多人第一次听到大语言模型的原理,都会听到一句很流行的话:

大模型本质上就是预测下一个词。

这句话对不对?

对。至少在预训练阶段,它抓住了大语言模型最核心的训练任务:给定前面的文本,让模型预测接下来最可能出现的 token。严格说,这里说的“词”并不总是中文或英文里自然意义上的词,而是 token。一个 token 可能是一个字、一个词的一部分、一个完整单词,也可能是标点、空格或某种特殊符号。这个细节我们下一章会展开,现在先把它暂时理解成模型处理文本的基本单位。

如果前文是:

今天的天气很

模型要做的事情,就是给所有可能的下一个 token 分配概率。它可能认为“好”的概率很高,“差”的概率也有一定可能,“苹果”“量子”“火车”这类 token 的概率则应该很低。模型不是从一个固定答案表里查“正确答案”,而是根据它的参数和上下文,计算出一个概率分布。

从这个角度看,“预测下一个词”确实不是比喻,而是训练目标。预训练时,模型会看到海量文本。训练系统把文本切成一串 token,然后不断制造这样的任务:给你前面的 token,请你预测真实文本中下一个 token 是什么。模型预测得越接近真实文本,损失越低;预测得越离谱,损失越高。训练过程就是反复调整参数,让模型在越来越多上下文中,给真实下一个 token 更高的概率。

但这句话也很容易误导。

它容易让人以为,大模型只是一个高级版的输入法。输入法也会根据前文推荐下一个词,为什么输入法没有表现出写代码、解数学题、总结论文、翻译文章、规划任务的能力?如果大模型也只是预测下一个词,那这些能力从哪里来?

问题就在于,“预测下一个词”这句话太短了。它说出了训练任务的形式,却没有说出训练任务背后的规模、数据、模型结构和优化过程。

一个小输入法可能只是在常见短语里做局部补全。大语言模型面对的却是互联网上规模巨大的文本:教材、论文、代码、新闻、百科、小说、论坛讨论、问答记录、技术文档、数学推导、法律文本、产品说明、聊天对话。要在这些文本中预测下一个 token,模型不能只记住几个常见搭配。它必须从上下文中捕捉大量模式:语法结构、事实关联、写作风格、代码语法、推理步骤、问答格式、论证方式。

当然,我们要小心这里的措辞。说模型“必须学习这些模式”,并不等于说它像人一样理解世界。更稳妥的说法是:为了降低预测错误,模型的参数会被训练成能够利用这些模式。它并不是把知识以一条条数据库记录的方式存进去,而是在巨大的参数空间里形成一种对文本分布的压缩和拟合。

这也是为什么“只是预测下一个词”既对,又不够。

它对,是因为预训练的公式确实围绕下一个 token 的概率展开。
它不够,是因为这个任务在足够大的数据、足够大的模型、足够长的训练中,会迫使模型学习许多隐藏在文本里的结构。

但即使这样,预训练也还只是第一步。

一个只经过预训练的模型,学到的是“在互联网上,类似文本接下来会怎么写”。它可能会续写一篇文章,模仿一个论坛回复,补全一段代码,延续一段小说,也可能在面对问题时继续生成看起来像答案的文本。可用户真正想要的,不只是“像互联网文本一样继续写下去”,而是希望模型能清楚、有帮助、诚实、符合指令地回答问题。

这就是本书要讲的核心:大模型的能力不是靠一个训练目标一次性完成的。它是被一层层训练目标塑造出来的。

预训练阶段,模型追求的是:下一个 token 像不像原始文本?
监督微调阶段,模型追求的是:这个回答像不像高质量助手答案?
偏好优化阶段,模型追求的是:两个回答里,哪个更值得被偏好?
可验证强化学习阶段,模型追求的是:整个回答能不能获得奖励,能不能通过验证?

如果只记住“大模型是预测下一个词”,我们会低估后面这些训练阶段的作用。
如果只说“大模型有智能”,我们又会跳过真正该解释的机制。

这本书选择第三条路:不神化,也不贬低。我们不把大模型讲成魔法,也不把它压扁成一句口号。我们从训练数据、反馈信号、优化目标和参数更新出发,看看一个模型是怎样从“会续写文本”,一步步变成“会回答问题”,再进一步变成“能在数学、代码等可验证任务里继续改进”的系统。

这条路会比宣传语慢一点,但更可靠。因为只要我们看清每个阶段到底在优化什么,大模型的许多现象就会变得不那么神秘。

为什么“文字接龙”能学到这么多东西?

说到这里,一个自然的问题会冒出来:

如果预训练只是“文字接龙”,为什么模型最后会表现出这么多能力?

这个问题很重要。因为如果回答不好,我们就会落入两个极端。一个极端是把大模型神秘化,好像只要参数足够多,智能就自动从机器里冒出来;另一个极端是把它过度贬低,认为它不过是在背诵网页,没有任何真正能力。两种说法都太快了。它们都跳过了中间最关键的机制:为了把下一个 token 猜对,模型必须学会利用上下文中的大量结构。

我们可以从一个很小的例子开始。

如果前文是:

牛顿提出了

下一个 token 可能是什么?

它可能是“万有引力”,也可能是“运动定律”,但大概率不会是“西红柿炒鸡蛋”。要给“万有引力”更高概率,模型不一定要像人一样在脑子里回忆物理课,但它必须在参数中形成某种关联:牛顿、物理学、力学、引力、运动定律这些词和语境经常一起出现。大量类似样本反复出现,模型就会被训练成在相似上下文中给相似内容更高概率。

这只是事实关联。

如果前文是:

def fibonacci(n):
    if n <= 1:
        return n
    return

要预测后面的 token,模型就不能只靠普通自然语言搭配了。它得学会一些代码结构:缩进、函数调用、递归、变量名、返回值。它不一定像程序员一样“理解”斐波那契数列,但为了在大量代码文本中降低预测错误,它必须捕捉代码里的模式。否则它就无法给 fibonacci(n-1) + fibonacci(n-2) 这类续写更高概率。

如果前文是一道数学推导:

因为 a > b,且 b > c,所以

模型要预测后续内容,就需要利用一种关系结构:如果 a > bb > c,那么通常可以推出 a > c。这并不意味着模型已经拥有了和人完全一样的逻辑理解,但训练目标会推动它在参数中保存许多这样的推理痕迹。文本里不只有事实,还有论证、证明、计算步骤、因果关系、反例、归纳、类比和格式。

这就是预训练有趣的地方。

表面上,它只是让模型预测下一个 token。
实际上,要在足够丰富的文本中做好这个任务,模型会被迫学习很多层次的模式。

第一层是语言表面模式。比如词语搭配、语法结构、标点习惯、句子节奏。模型要知道“今天的天气很”后面常常接“好”“冷”“热”,而不是随便接一个不相关的词。

第二层是事实和知识关联。比如“巴黎是法国的首都”“水在标准大气压下 100 摄氏度沸腾”“Python 里可以用 def 定义函数”。这些内容在训练文本中反复出现,模型为了预测文本,会学到它们之间的统计关系。

第三层是风格和体裁。论文、新闻、小说、论坛、教材、法律文书、代码注释,它们的写法不同。模型要预测下文,就必须区分这些文本类型。它会学到论文里常见的严谨表达,也会学到论坛里常见的随意表达,还会学到教程里一步步解释问题的方式。

第四层是任务和格式。问答文本里,问题后面通常跟答案;代码题里,题目后面可能跟解法;数学题里,条件后面可能跟推导;对话里,一个角色说完话,另一个角色会回应。模型要续写这些文本,就会学到某些任务格式。

第五层是推理过程的痕迹。很多文本不是直接给结论,而是一步步展开:先列条件,再分析,再推导,再得出答案。模型如果见过足够多这种文本,就可能学会模仿这种推理形式。后面我们讲思维链时会看到,这一点非常关键:模型不只是输出答案,也可以输出中间步骤,而这些中间步骤本身也能成为训练和奖励的对象。

但我们还要保持克制。

说模型“学到了知识”,容易让人误会成它脑子里有一本百科全书。其实更准确的说法是:模型参数被调整到能够在许多上下文中产生符合训练数据分布的输出。它没有一个可以直接打开查看的“知识条目表”。你不能指着某个参数说:“这个参数就是巴黎是法国首都。”知识并不是这样存的。

大模型的知识更像一种分布式的能力。它分散在大量参数之间,体现在模型对不同 token 的概率判断上。当前文提到“巴黎”“法国”“首都”时,某些输出会变得更可能;当前文提到“Python”“列表”“排序”时,另一批输出会变得更可能。这种能力不是手工写进去的,而是在海量样本上反复优化出来的。

这也是为什么大模型会有两种看起来矛盾的表现。

一方面,它可以回答很多问题,写出像样的代码,概括复杂文章,甚至给出有条理的推理步骤。因为这些能力的痕迹确实存在于训练数据中,也确实可以通过预测任务被模型吸收。

另一方面,它也会犯一些很奇怪的错误。它可能编造不存在的论文,误解一个简单问题,给出看似自信但实际错误的答案。因为预训练目标并不是“保证事实正确”,而是“让生成文本像训练文本”。如果训练文本里有错误、有矛盾、有流言、有低质量内容,模型也可能学到这些模式。即使训练文本本身正确,模型在生成时也可能把不同语境里的模式拼错。

所以我们不能只用“会预测下一个 token”来贬低它,也不能只用“它学会了知识”来神化它。

更合适的说法是:

预训练让模型在海量文本中学习语言、知识、风格、格式和推理痕迹的统计结构。

这句话听起来没有“机器拥有智能”那么激动人心,但它更接近我们要讲的机制。预训练的确能产生强大的基础能力,但这种能力一开始并不等于可靠的助手能力。

这就把我们带到下一个问题:如果模型已经学到了这么多,为什么还需要后面的训练?

为什么只靠预训练还不够?

预训练完成后,模型已经很厉害了吗?

是的。一个大规模预训练模型,已经可以生成流畅文本,补全代码,模仿不同文体,回答不少事实问题。它的参数里已经积累了大量来自训练数据的模式。没有预训练,后面很多能力都无从谈起。

但如果我们把一个只经过预训练的基础模型直接拿来当助手,问题很快就会出现。

因为预训练模型学到的目标,并不是“帮助用户完成任务”。它学到的是“类似文本接下来会怎么写”。

这两个目标差别很大。

假设用户问:

请用简单的话解释什么是梯度下降。

一个理想助手应该直接解释梯度下降,最好根据读者水平调整语言,必要时给一个例子。但一个只经过预训练的模型,可能会把这个问题当成某种网页片段、论坛对话、教材开头或问答语料的一部分来续写。它可能回答问题,也可能继续编出下一个用户问题,也可能模仿某种并不合适的文本格式。

这不是因为它没有能力生成解释,而是因为它还没有被稳定地训练成“面对指令时应该给出有帮助的回答”。

换句话说,预训练解决的是能力基础问题,后训练解决的是行为塑造问题。

这两个词很重要:能力行为

能力指的是模型有没有足够的语言模式、知识关联、代码结构、推理痕迹。
行为指的是模型在具体交互中会不会按我们期待的方式使用这些能力。

一个人可能懂很多知识,但如果他说话总是答非所问,我们不会说他是好老师。一个模型也类似。预训练让模型拥有大量潜在能力,但这些能力需要被组织成可用的回答方式。用户问问题时,模型应该理解这是一个指令,而不是一段需要继续模仿的互联网文本;应该尽量回答问题,而不是续写无关内容;应该在不知道时承认不确定,而不是编造一个看起来顺滑的答案。

这就是监督微调,也就是 SFT 要做的事情。

SFT 会给模型看大量“用户问题 + 高质量回答”的样本,让它模仿助手式回答。这个阶段并不是从零开始教模型知识。它更像是在告诉模型:你已经从预训练中学到了很多东西,现在请用这种格式、这种语气、这种结构来回答用户。

但 SFT 也不是终点。

因为现实中的好回答往往不是唯一的。同一个问题可能有很多种回答方式:有的更简洁,有的更完整;有的语气更好,有的更准确;有的表面上顺滑,但其实回避了关键问题。只给模型一个标准答案,让它模仿,仍然不够。我们还希望模型学会在多个回答之间判断:哪个更有帮助,哪个更安全,哪个更诚实,哪个更符合人的偏好。

这就引出了 RLHF 和 DPO。

再往后,如果任务是数学题或代码题,我们甚至不一定只依赖人类偏好。因为这类任务有一个特殊优势:答案可以被外部系统验证。代码能不能通过测试,数学答案能不能被检查,工具调用结果是否满足要求,这些都可以变成奖励信号。于是训练目标又进一步变化:模型不只是模仿文本,不只是迎合偏好,还可以朝着“通过验证、获得奖励”的方向更新。

到这里,全书的路径就清楚了。

我们不是在讲一个模型一夜之间变聪明的故事,而是在讲一系列训练目标如何逐步塑造模型行为:

像互联网文本一样续写
        ↓
像高质量助手一样回答
        ↓
生成更被偏好的回答
        ↓
生成能获得奖励、通过验证的回答

这个过程里,每一步都没有魔法。每一步都可以回到数据、反馈信号、优化目标和参数更新。

预训练之后为什么还不够?
因为会续写,不等于会回答。
会回答,也不等于会回答得更好。
会生成看起来好的答案,也不等于能通过外部验证。

本书后面的每一章,其实都在沿着这条线往前走。

四层训练目标:这本书的主线是什么?

现在,我们可以把全书的主线正式摆出来。

这本书不是按“大模型有哪些功能”来组织的。我们不会一章讲聊天,一章讲画图,一章讲写代码,一章讲智能体。那样当然也能写成一本书,但很容易变成应用介绍。应用会不断变化,今天流行的产品形态,明天可能就被新的模型和新的界面替代。

这本书也不是按“有哪些算法名”来组织的。我们不会把 Transformer、SFT、RLHF、PPO、DPO、RAG、Agent、MoE 这些词排成一串,然后逐个解释。那样看起来很技术,但读者很可能记住了一堆名词,却没有真正抓住它们之间的关系。

我们选择另一条线:训练目标的变化

也就是问一个朴素但非常有力的问题:

每个阶段,模型到底在追求什么?

只要抓住这个问题,很多复杂概念就会变得有秩序。

第一层,预训练。

预训练问的是:

下一个 token 像不像原始文本?

这个阶段的数据主要来自大规模文本。互联网上的文章、代码、问答、书籍、教程、论文,会被清洗、切分、整理成 token 序列。训练时,模型看到前文,预测真实出现的下一个 token。这个目标看起来简单,但它是大模型能力的地基。因为文本里包含了语言、知识、代码、推理、格式和风格的痕迹。

这一层的关键词是:文本分布、下一个 token、交叉熵、最大似然、梯度下降。

第二层,监督微调,也就是 SFT。

SFT 问的是:

这个回答像不像高质量助手答案?

预训练模型会续写文本,但不一定会好好回答用户。SFT 使用“用户问题 + 高质量回答”的样本,把模型从基础模型拉向助手模型。它仍然很像监督学习,仍然是逐 token 地提高目标回答的概率。区别在于,目标文本不再是普通互联网文本,而是经过挑选或构造的助手回答。

这一层的关键词是:指令数据、prompt-answer、助手风格、模仿学习。

第三层,偏好优化,包括 RLHF 和 DPO。

这一层问的是:

两个回答里,哪个更值得被偏好?

现实中,好回答往往不是唯一的。一个回答可能更准确,另一个回答可能更简洁;一个回答可能表面客气但没有解决问题,另一个回答可能更直接有效。只模仿一个标准答案,很难教会模型这种“相对好坏”。所以后训练引入偏好数据:同一个 prompt 下,哪个回答更好,哪个回答更差。

传统 RLHF 会把这种偏好训练成奖励模型,再用奖励信号更新语言模型。DPO 则更直接:它把 chosen answer 和 rejected answer 的偏好关系写进损失函数,让模型提高好回答相对坏回答的概率优势。

这一层的关键词是:偏好对、chosen、rejected、奖励模型、DPO、相对概率。

第四层,可验证强化学习。

这一层问的是:

整个回答能不能获得奖励,能不能通过验证?

数学题、代码题、工具调用这类任务有一个特殊之处:很多时候,答案好坏可以被外部机制检查。代码可以运行测试,数学题可以检查最终答案,工具调用可以看结果是否满足要求。于是,训练信号不再只是人类偏好,而可以来自验证器、编译器、测试器、判题系统。

这一层非常关键。因为它让模型的训练目标从“像不像文本”“像不像好回答”“是否被偏好”,进一步推进到“是否能通过验证”。这也是为什么近年来大模型在编程和奥数类任务上进步特别快:这类任务更容易生成候选样本,也更容易用外部系统筛选质量。

这一层的关键词是:reward、验证器、测试器、编译器、奖励函数、KL 约束、自生成样本。

如果把四层合在一起,我们就得到这本书的基本地图:

预训练:下一个 token 像不像文本?
SFT:回答像不像高质量助手答案?
RLHF / DPO:两个回答里哪个更值得偏好?
可验证强化学习:整个回答能不能获得奖励?

这四句话不是口号,而是全书的导航。

每当我们遇到一个新概念,都可以把它放回这张地图里。比如 DPO,不要先把它看成一个复杂算法名,而要先问:它改变了什么训练信号?它优化的是什么目标?它和 SFT 有什么不同?再比如思维链,不要先把它说成“模型会思考了”,而要问:它让中间推理过程变成了什么?这些过程能不能被模仿、比较或奖励?

这样读,大模型训练就不再是一堆散乱术语,而是一条清楚的演化路径。

当然,真实的大模型训练系统比这张四层图复杂得多。不同公司和研究团队会使用不同的数据配方、训练顺序、奖励设计和工程细节。有些方法会交叉出现,有些阶段会反复迭代。我们这里不是要把所有工程实现穷尽,而是先建立一个足够稳定的理解框架。

框架的价值,不在于把复杂世界压扁,而在于让我们知道该从哪里看。

这本书要反复强调的,就是这一点:

大模型训练的演化,不只是数据越来越多、参数越来越大,而是训练信号越来越明确,优化目标越来越接近我们真正想要的能力。

为什么本书不从“AI 会不会有意识”讲起?

讲大模型,很容易被一些更大的问题吸走。

比如:AI 有没有意识?它会不会超过人类?它是不是已经理解了世界?它会不会取代所有工作?这些问题当然重要,也值得讨论。但如果一开始就从这些问题讲起,我们很容易陷入另一种混乱:概念很大,证据很少,观点很多,机制很少。

这本书选择暂时把这些问题放在一边,不是因为它们不重要,而是因为在理解大模型训练之前,直接讨论这些问题,常常会变成态度表达。

有人会非常兴奋,觉得大模型已经接近通用智能。
有人会非常怀疑,觉得它只是拼接训练语料。
有人会非常焦虑,觉得所有职业都会被替代。
有人会非常轻视,觉得它不过是一个更大的输入法。

这些态度背后都有一些现实经验,但如果我们不先弄清训练机制,就很难判断哪些说法有依据,哪些说法只是情绪。

所以本书的策略是:先回到地面。

什么叫回到地面?

就是先问那些可以被清楚回答的问题:

  • 数据从哪里来?
  • 文本怎样变成训练样本?
  • 模型到底预测什么?
  • 损失函数怎样定义?
  • 参数怎样通过梯度下降更新?
  • 为什么 SFT 之后模型更像助手?
  • 为什么偏好对能改变模型回答风格?
  • 为什么代码和数学题可以提供更明确的奖励?
  • 为什么思维链可能让推理过程也进入训练目标?

这些问题没有“AI 是否有意识”那么宏大,但它们更适合作为理解的起点。因为它们能把大模型从云端拉回训练过程。

这并不意味着我们低估大模型。

恰恰相反,只有把机制讲清楚,我们才更能看出它真正厉害的地方。一个系统能够通过预测下一个 token,从海量文本中学习语言、代码、事实、风格和推理痕迹;再通过指令微调学会助手式回答;再通过偏好优化调整行为;再通过可验证奖励在数学和编程任务上继续进步。这件事本身已经足够惊人,不需要再加一层神秘包装。

同时,机制视角也能帮助我们看清局限。

如果模型的基础来自训练数据,那么数据污染、偏见、错误和缺口就会影响它。
如果模型通过人类偏好优化,那么它可能学会迎合偏好,而不一定追求真相。
如果模型通过奖励函数训练,那么它可能找到奖励漏洞,而不是真的解决问题。
如果思维链可以被奖励,那么我们也要问:这些推理步骤是否真的可靠,还是只是看起来合理?

这些问题都不是为了唱衰 AI,而是为了让理解更稳。

我希望这本书的语气始终保持一种克制的乐观:承认大模型能力的真实增长,也承认它的能力来自具体训练机制;承认它已经改变许多任务,也承认它不是自动通向真理的机器。

所以,本书不从意识、灵魂、取代人类这些话题开始。

我们从更小也更硬的地方开始:一个 token,一个概率,一个损失函数,一次参数更新。

这些东西看起来没有宏大叙事那么迷人,但它们才是大模型真正站立的地方。

本章小结

这一章我们先建立了全书的基本视角。

“大模型只是预测下一个词”这句话是对的,但它只说出了预训练任务的形式,没有说出这个任务在海量数据、大模型结构和长期优化中会带来什么。为了预测下一个 token,模型会被迫学习文本中的语言结构、事实关联、代码模式、写作风格和推理痕迹。但预训练仍然只是第一步,它让模型拥有能力基础,却不保证模型会以助手的方式回答问题。

因此,本书会沿着训练目标的变化展开:从预训练的“像文本”,到 SFT 的“像高质量助手答案”,到 RLHF/DPO 的“更被偏好”,再到可验证强化学习的“获得奖励、通过验证”。我们不把大模型讲成魔法,也不把它压扁成口号,而是始终回到数据、反馈信号、优化目标和参数更新。

下一章预告

接下来,我们要进入最基础的单位:token。

很多人说大模型预测“下一个词”,但严格说,模型处理的不是我们日常理解的词,而是 token。token 怎样从文本中切出来?参数又是什么?模型为什么不是一次性写出整篇文章,而是一步步生成?下一章,我们从 token、参数和下一个 token 的概率开始。

延伸阅读

推荐阅读方向:

  • 大语言模型训练流程的综述性文章。
  • 预训练、监督微调、偏好优化和强化学习的入门资料。
  • 关于“模型能力来自训练目标”的技术解释文章。

第 2 章:Token、参数与下一个词

本章问题

大模型说的“预测下一个词”到底是什么意思?模型处理的是词、字,还是别的东西?

章节摘要

这一章解释大语言模型最基础的三个概念:token、参数和概率分布。模型并不是直接理解一整句话,而是把文本切成 token,再根据前面的 token 预测下一个 token 的概率。参数则是模型内部大量可调整的数字,它们决定了模型在不同上下文下会给哪些 token 更高概率。本章不急着讲复杂架构,而是先让读者看懂:所谓生成文本,其实是模型一次次选择下一个 token,再把这些选择串成完整回答。

读者收获

读者应理解:大模型的基本输出不是一次性写出整篇文章,而是按 token 自回归生成;参数不是知识库条目,而是影响概率分布的可训练数字。

配图

从文本到 token,再到下一个 token 概率

图 2-1|从文本到 token:模型生成文本的最小动作,是根据前文预测下一个 token 的概率分布。

读图:从文本到 token,再到概率分布

这张图把模型生成文本的最小动作拆开:人类看到的是一句话,模型看到的是 token 序列;在每一个位置上,模型根据前文计算下一个 token 的概率分布。

读这张图时要抓住一点:大模型不是一次写完整段回答,而是不断重复“看见前文、计算概率、选出下一个 token、再把它放回上下文”的过程。

正文

“下一个词”为什么不是一个准确说法?

上一章我们一直在说:大模型预测下一个 token。

但在日常讨论里,人们更常说“大模型预测下一个词”。这句话方便、直观,也没有完全错。只是如果我们要真正理解大语言模型的训练,就不能一直停留在“词”这个说法上。

因为模型处理的单位,严格说不是词,而是 token

这个区别看起来像技术细节,其实很重要。很多误解都是从这里开始的。我们以为模型像人一样读一个个词,理解一句话,再想出下一句话。但模型看到的并不是我们眼中的自然语言单位。它看到的是一串被切分过的 token 编号。这些编号进入模型,经过一层层计算,最后变成下一个 token 的概率分布。

所以,“下一个词”只是一个方便的口语表达。更准确的说法是:

给定前面的 token 序列,模型预测下一个 token 的概率分布。

什么是 token?

你可以暂时把它理解成模型处理文本的基本颗粒。它有时像一个字,有时像一个词,有时像一个词的一部分,有时又是标点、空格、换行、代码里的括号,甚至某种特殊控制符号。

比如中文句子:

今天的天气很好。

它可能被切成:

今天 / 的 / 天气 / 很 / 好 / 。

也可能在另一个 tokenizer 里被切成别的方式。中文里没有天然空格,所以切分方式会依赖具体的 tokenizer。

英文也一样。我们以为英文天然按单词切分,但模型并不一定这样处理。比如:

unbelievable

它可能不是一个完整 token,而可能被切成:

un / believable

或者:

un / believe / able

这取决于词表里有哪些常见片段。越常见的片段,越可能作为一个 token 出现;不常见的词,可能被拆成多个更小的片段。这样做的好处是,模型可以处理没见过的词,也可以用有限词表覆盖非常多的文本。

代码也会被切成 token。比如:

def add(a, b):
    return a + b

在模型眼里,这不是一段“程序意义”先被理解,再被整体处理。它仍然是一串 token:def、空格、函数名、括号、逗号、冒号、换行、缩进、return、变量名、加号。模型要学习代码能力,第一步仍然是学习这些 token 在代码上下文中的出现规律。

这个事实会让人有点不适应。因为我们习惯认为语言先有意义,再有文字;而模型训练时,首先面对的是 token 序列。意义并不是预先交给模型的东西。模型通过大量 token 序列的预测任务,逐渐形成对语言、知识、代码和推理模式的统计把握。

这就是为什么本章要从 token 讲起。

如果不理解 token,我们就很难理解预训练样本是什么。所谓“互联网文本变成训练数据”,不是把网页内容整理成一个问答知识库,而是把文本切成 token 序列,再构造成“给定前文,预测下一个 token”的样本。

如果不理解 token,我们也很难理解公式。第 5 章我们会看到,预训练公式里的 $x_t$ 就是某个位置上的真实 token,$x_{<t}$ 就是它之前的所有 token。公式不是在抽象地说“理解一句话”,而是在非常具体地说:在这个位置,真实出现的 token 是什么?模型给它的概率有多高?

所以,本章先做一件基础但关键的事:把“下一个词”拆开,换成“下一个 token”。

这一步看似小,后面所有训练目标都要站在它上面。

文本怎样被切成 token?

现在我们知道,模型处理的是 token。接下来的问题是:文本怎样变成 token?

先看一个简单中文句子:

我喜欢机器学习。

人读这句话,很自然地会看到意思:一个人喜欢机器学习。可是模型不能直接接收“意思”。训练系统需要先把这句话转成 token 序列。一个可能的切分方式是:

我 / 喜欢 / 机器 / 学习 / 。

也可能是:

我 / 喜欢 / 机器学习 / 。

哪一种对?这取决于 tokenizer 的词表和规则。对我们现在的理解来说,不必纠结具体切法。关键是:自然文本会先被离散化,变成模型可以处理的一串单位。

英文文本也类似:

Large language models predict the next token.

它可能被切成:

Large / language / models / predict / the / next / token / .

但这只是理想化展示。实际 tokenizer 可能会把前面的空格和词绑定在一起,也可能把低频词拆成更小片段。比如 tokenization 这样的词,可能会被拆成 tokenization,也可能拆成更多部分。

为什么要这样设计?

因为模型需要一个有限的词表。如果我们把每个完整单词都当成基本单位,词表会非常大,而且遇到新词、拼写变化、专业术语、代码变量名时会很麻烦。把文本切成子词片段,可以在覆盖能力和词表大小之间取得平衡。

中文、英文、代码、数学符号、标点、换行,都可以进入同一套 token 序列里。这一点对大模型很重要。因为大模型不是只处理自然语言,它还会处理代码、表格、公式、配置文件、网页片段和各种混合文本。

比如一段代码:

for i in range(10):
    print(i)

在模型看来,foriinrange、括号、数字、冒号、换行、缩进、print,都要以 token 的形式进入上下文。代码中的缩进和换行不是装饰,它们会影响语法结构,也会影响模型对下一个 token 的判断。

再比如数学表达:

如果 x > 0,则 x^2 > 0。

这里既有中文,又有变量、符号、数字、上标形式。模型并不是先把它转换成某种纯数学对象,而是先看到 token 序列。它通过大量类似文本,学习这些符号和表达在上下文中的关系。

这会带来一个重要结论:

大模型的输入世界,首先是 token 序列的世界。

我们看到的是文字、句子、段落、代码、公式。模型看到的是 token 编号序列。人类觉得“机器学习”是一个概念,模型看到的可能是一个 token,也可能是两个 token。人类觉得代码缩进是格式,模型看到的是影响后续 token 概率的上下文线索。

当然,这并不意味着模型只能停留在机械符号层面。恰恰因为 token 序列中承载着大量语言和知识结构,模型通过训练可以学到丰富模式。但我们要始终记住,训练起点不是“概念”,而是 token。

这也解释了为什么模型有时会在很奇怪的地方出错。

有些错误不是因为模型完全不知道某个概念,而是因为 token 切分、上下文长度、概率选择、训练数据分布共同作用,导致它在某一步生成了不合适的 token。一旦这个 token 进入上下文,后面的生成又会在新的上下文基础上继续展开。长回答就是这样一点点滚出来的。

所以,token 化不是一个无聊的预处理步骤。它决定了文本怎样进入模型,也决定了模型预测任务的基本单位。

到这里,我们已经完成了第一个转换:自然文本变成 token 序列。

接下来还要看第二个转换:token 序列进入模型以后,模型输出的到底是什么?

它不是直接输出“正确答案”。它输出的是一张概率表。

模型预测的不是一个答案,而是一张概率表

我们平时使用聊天机器人时,看到的是一句一句的回答。它好像在直接“说出答案”。但从模型内部看,它每一步做的事情更具体:

给定当前上下文,对词表里的每一个可能 token 计算一个分数,再把这些分数变成概率。

假设前文是:

今天的天气很

模型不会只输出一个 token。它会对大量候选 token 给出概率。我们可以用一个简化版来想象:

好:0.42
冷:0.18
热:0.15
糟:0.08
不错:0.05
其他:0.12

真实模型的词表可能有几万到几十万个 token。每一步,它都要在整个词表上形成一个概率分布。概率最高的 token 往往最符合上下文,但生成时不一定永远选择概率最高的 token。系统可以选择最可能的,也可以在一定范围内采样,让回答更自然、更有变化。

这件事看起来简单,但它是理解大模型的关键。

大模型不是在一个数据库里搜索“今天的天气很”后面应该接什么。它是在计算:在当前上下文下,每个候选 token 的概率是多少。这个概率来自模型参数和上下文共同作用的结果。

如果前文换成:

这段代码的时间复杂度是

概率表就会完全不同。模型可能给 OO(线性平方 这类 token 更高概率。

如果前文是:

牛顿提出了

模型可能给“万有引力”“运动定律”相关 token 更高概率。

如果前文是:

def add(a, b):
    return

模型可能给 aa + b、空格、变量名、换行等 token 更高概率。

这说明概率分布不是固定的。它随着上下文变化而变化。一个 token 在某个上下文里概率很高,在另一个上下文里可能几乎不可能出现。

这也是为什么上下文很重要。模型不是孤立地预测下一个 token,而是根据前面已经出现的所有 token 来预测。上下文越清楚,模型越容易把概率集中到合理候选上;上下文越模糊,模型的不确定性就越大。

举个例子:

苹果

如果只有这一个词,后面可能接很多东西。它可能是水果,也可能是公司,也可能是一个句子的开头。模型的概率分布会比较分散。

但如果前文是:

苹果公司发布了新款

后面的 token 大概率会和产品、设备、系统有关。

如果前文是:

桌子上放着一个红色的苹果,

后面的 token 又会更像日常叙述。

这就是上下文对概率分布的塑造。

到了第 5 章,我们会正式看到预训练公式。那里有一个核心项:

$$P_\theta(x_t \mid x_{<t})$$
现在不需要急着读懂这个公式,只要先有一个直觉:它表示模型在看到前文 $x_{<t}$ 后,给真实下一个 token $x_t$ 的概率。预训练要做的事情,就是让这个概率尽可能高。

如果真实下一个 token 是“好”,而模型给“好”的概率很低,训练就会惩罚它。
如果模型给“好”的概率很高,损失就会比较小。

所以,概率表不是附属细节。它就是模型训练和生成的核心界面。

生成时,我们从概率表里选出下一个 token。
训练时,我们检查真实 token 在概率表里的概率高不高。
更新参数时,我们让模型以后在类似上下文中给真实 token 更高概率。

这三件事连在一起,构成了大语言模型最基本的循环。

但还有一个问题没有回答:概率表是怎么来的?

为什么模型在“今天的天气很”后面给“好”更高概率,而不是给“牛顿”更高概率?为什么它在代码上下文中更可能输出代码 token?为什么它能根据不同语境调整概率分布?

答案就在参数里。

参数到底是什么?

我们经常听到一个说法:某个模型有多少多少“参数”。

比如几十亿参数、几百亿参数、上千亿参数。这个数字听起来很震撼,也经常被拿来衡量模型规模。但参数到底是什么?

最简单地说,参数就是模型内部大量可以被训练调整的数字。

这些数字参与模型的计算。输入 token 进入模型后,会被转成向量,经过一层层矩阵运算、非线性变换、注意力计算,最后输出下一个 token 的概率分布。参数就藏在这些计算里,决定信息怎样流动、哪些特征被放大、哪些关联被削弱、哪些候选 token 得到更高分数。

这里我们不急着进入矩阵和向量的细节。那是下一章和后续公式章节会慢慢铺开的内容。现在只需要抓住一个核心:

参数不是一条条知识,而是影响模型计算结果的可训练数字。

这个区别非常重要。

很多人会想象,大模型像一个巨大的知识库。里面有一条记录写着“巴黎是法国首都”,另一条记录写着“牛顿提出万有引力”,还有一条记录写着“Python 里用 def 定义函数”。但模型参数不是这样组织的。

你不能打开模型参数文件,然后找到某一行说:“这一行存着巴黎是法国首都。”也不能删掉某几个参数,就精确删除一条知识。大模型的知识和能力通常是分布式的,散落在大量参数共同形成的计算模式里。

这听起来有点抽象。我们换一种说法。

当模型看到:

法国的首都是

它输出“巴黎”的概率比较高,不是因为某个参数单独写着“答案=巴黎”,而是因为训练过程中,大量相关文本反复调整了模型内部数字,使得在类似上下文下,“巴黎”相关 token 的分数会被推高。

当模型看到:

def square(x):
    return

它可能输出 x * x 或类似代码,也不是因为它查了一个函数模板库,而是因为参数经过大量代码样本训练后,已经让这类上下文和这类续写之间形成了很强的概率关联。

参数的作用,就是把上下文转化为概率分布。

训练前,参数通常是随机初始化的。随机参数不会产生有意义的语言能力。它可能给各种 token 近乎混乱的概率。训练开始后,模型不断看到样本,不断预测下一个 token,不断计算损失,再通过梯度下降调整参数。这个过程重复很多很多次,参数才逐渐形成结构。

所以,大模型训练不是把互联网知识直接复制进参数,也不是把问答对塞进一个数据库。更准确地说,它是通过海量预测任务,让参数逐步变成一种能够生成合理概率分布的计算结构。

这解释了大模型的一些特性。

第一,它能泛化。

如果模型只是背诵,它只能回答训练中原样出现过的问题。但参数学到的是大量模式和关联,所以它有时能处理没见过的表达方式。比如训练数据里未必出现过某个具体提问,但只要它和许多已有模式相关,模型就可能生成合理回答。

第二,它会出错。

因为参数不是知识库,模型输出的是概率分布,不是事实验证结果。某个答案概率高,不代表它一定是真的。它可能只是“像一个合理答案”。这就是为什么大模型会有幻觉:它可以生成非常顺滑、非常像真的文本,但文本像真不等于事实为真。

第三,它难以精确解释。

我们可以观察模型输出,可以分析注意力、激活和参数变化,但很难把一个复杂回答完全还原成某几条参数规则。大模型的能力来自大量参数的共同作用,而不是简单规则表。

这也是本书一直要坚持的克制表达:不要把模型说成神秘智能,也不要把它说成简单查表。它更像是一个被训练出来的概率生成系统。这个系统通过参数,把上下文映射成下一个 token 的概率分布。

理解了这一点,后面的训练公式就更容易读了。

当我们说“训练模型”,其实就是在说:调整这些参数,让模型在训练样本上给正确 token 更高概率;在后训练阶段,则是调整参数,让模型更倾向于高质量回答、被偏好的回答、能获得奖励的回答。

参数是模型行为被塑造的地方。

数据提供样本,损失函数提供方向,梯度下降提供更新方法,而参数承载更新结果。

为什么说生成是自回归的?

现在我们已经有了三个基本概念:token、概率分布、参数。

接下来要把它们连成一个生成过程。

大语言模型生成文本时,并不是一次性把完整答案写出来。它不是先在内部形成一整篇文章,再整体吐出来。更准确的过程是:

已有上下文 → 预测下一个 token → 把这个 token 接到上下文后面 → 再预测下一个 token

这个过程叫自回归生成。

“自回归”这个词听起来有点硬,但直觉并不复杂。模型每生成一个 token,就把这个 token 当作新上下文的一部分,再继续生成下一个 token。它的输出会反过来成为后续输入的一部分。

比如用户输入:

请解释什么是 token。

模型可能第一步生成:

token

现在上下文变成:

请解释什么是 token。token

下一步模型可能生成:

上下文继续变成:

请解释什么是 token。token 是

再下一步生成:

 模型

这样一步步下去,最后形成一整段回答。

真实系统当然会有更复杂的工程细节,比如批量计算、缓存、采样策略、停止条件等。但从理解原理的角度,这个逐 token 生成的过程已经足够重要。

它解释了很多现象。

第一,长回答是由很多局部选择组成的。

我们看到一篇完整回答,会觉得它是一个整体。但模型生成时,每一步只是在当前上下文下选择下一个 token。长篇回答的连贯性,来自很多次局部概率判断的连续结果。一个好的模型,能让这些局部选择在整体上显得有结构、有方向。

第二,前面的输出会影响后面的输出。

如果模型早期生成了一个错误前提,后面就会在这个错误前提上继续展开。比如它一开始把某个年份说错了,后面的解释可能会围绕这个错误年份继续推理。因为从模型角度看,自己刚刚生成的内容已经变成上下文的一部分。

这也是为什么长回答有时会越走越偏。并不是每一步都大错特错,而是前面一个小偏差被后续生成不断继承和放大。

第三,自回归生成让提示词很重要。

用户给模型的 prompt 是初始上下文。这个上下文会影响第一步 token 概率,而第一步又影响第二步,第二步又影响第三步。一个清楚的提示词,会让模型更容易进入正确的生成轨道;一个含糊、矛盾或误导性的提示词,可能让模型从一开始就走到错误方向。

第四,自回归也解释了为什么思维链会有作用。

当模型先生成中间步骤,再生成最终答案时,这些中间步骤会成为后续上下文的一部分。它不是在一个隐藏空间里一次性跳到答案,而是在文本层面留下推理轨迹。后面我们会专门讲思维链:它让中间推理过程显式化,也让这些过程有可能被模仿、比较和奖励。

但是,我们同样要保持克制。

自回归生成不是人类思考的完整模型。模型一步步生成 token,不等于它像人一样有意识地规划整篇文章。它可能表现出规划能力,也可能只是局部模式拼接得很好。我们现在只需要知道:从机制上看,生成是一个逐 token 展开的过程。

这也和预训练完全对应。

训练时,模型学习的是:

给定前文,预测真实下一个 token。

生成时,模型做的是:

给定当前上下文,选择下一个 token。

训练和生成之间有一种非常直接的对应关系。预训练用海量文本教模型如何预测下一个 token;生成时,模型就用这种能力一步步构造新文本。

这就是为什么 token、概率分布、参数和自回归必须放在前面讲。它们是后面所有训练阶段的共同底座。

SFT 也是逐 token 模仿高质量回答。
DPO 也是在回答概率上做相对调整。
强化学习虽然奖励完整回答,但最终仍然要通过参数更新改变模型生成 token 的概率。

大模型的宏观行为,看起来像对话、写作、编程、推理。
它的微观动作,仍然是一次次预测下一个 token。

概率生成不等于随便乱猜

讲到概率,有些读者可能会产生一个新的误解:

既然模型输出的是概率,那它是不是就在随机乱猜?

不是。

概率不等于没有规律。恰恰相反,概率分布是模型把上下文、训练数据和参数计算综合起来之后,对各种可能性的量化。

如果前文是:

中国的首都是

一个训练良好的模型应该给“北京”非常高的概率,给“巴黎”“香蕉”“量子力学”很低的概率。这里虽然仍然是概率分布,但分布并不是平均摊开的。它有非常强的结构。

如果前文是:

请写一个 Python 函数,判断一个数是否为偶数。

模型应该给 def、函数名、括号、取模运算等相关 token 更高概率。这同样不是乱猜,而是代码语境把概率集中到了合理区域。

训练的作用,就是不断塑造这种概率分布。

在训练早期,模型可能不知道哪些 token 合理。随着训练进行,它在大量样本中反复看到类似上下文和真实后续,参数不断调整,合理 token 的概率被推高,不合理 token 的概率被压低。

但是概率生成也意味着:模型不是事实真理机。

一个 token 概率高,只说明它在当前上下文和模型参数下更可能被生成,不说明它一定正确。很多幻觉问题就来自这里。模型可以生成一个“很像正确答案”的回答,但如果这个回答没有经过事实检查或外部验证,它仍然可能是错的。

所以我们需要同时记住两句话:

概率生成不是随便乱猜。
概率高也不等于事实正确。

这两句话看似矛盾,其实正好说明大语言模型的特点。

它不是简单随机机器,因为它的概率分布经过海量训练,包含丰富结构。
它也不是可靠真理机器,因为训练目标首先是让文本像训练数据,而不是让每句话都通过现实验证。

这个区别会贯穿全书。

后面讲 SFT 时,我们会看到模型如何被训练得更像助手。
讲 DPO 时,我们会看到模型如何提高好回答相对坏回答的概率。
讲可验证强化学习时,我们会看到外部验证器如何把“对不对”变成奖励信号。

但无论训练目标怎样变化,最终都要落回模型的输出概率上。训练改变参数,参数改变概率分布,概率分布改变模型生成的文本。

这就是从 token 到参数再到输出的闭环。

本章小结

这一章我们把“预测下一个词”这句话拆得更细了一点。

首先,模型预测的不是严格意义上的词,而是 token。自然文本、代码、标点、换行、符号都会被切成 token 序列,作为模型处理的基本单位。其次,模型每一步输出的不是一个唯一答案,而是整个词表上的概率分布。再往里看,这个概率分布由模型参数和上下文共同决定。参数不是知识库条目,而是大量可训练数字,它们通过训练逐渐学会把上下文映射成合理的下一个 token 概率。

最后,我们看到生成是自回归的:模型生成一个 token,再把它接回上下文,继续生成下一个 token。长回答不是一次性写出来的,而是由许多次这样的局部选择串起来的。

到这里,我们已经知道了模型生成文本的最小动作:给定前文,预测下一个 token。

下一章预告

但还有一个问题没有解决:模型怎样利用很长的上下文?

如果只是看最后一个 token,模型不可能写出连贯文章,也不可能读懂代码结构、问答关系和推理步骤。模型必须让不同位置的 token 互相影响,才能根据上下文形成更好的预测。下一章,我们用够用版的方式理解 Transformer,看看它怎样把 token 序列变成可以相互作用的上下文表示。

延伸阅读

推荐阅读方向:

  • Tokenization、BPE、SentencePiece 等分词方法的入门资料。
  • 自回归语言模型和 next-token prediction 的基础介绍。
  • 参数、概率分布和 softmax 输出层的机器学习基础资料。

第 3 章:Transformer 够用版:模型如何读懂上下文

本章问题

大模型为什么能根据上下文生成看似连贯的回答?Transformer 在这里起什么作用?

章节摘要

这一章用“够用版”的方式解释 Transformer。我们不把它写成架构教材,而是重点说明:模型怎样把一串 token 变成向量,怎样让不同位置的 token 互相影响,怎样根据上下文形成对下一个 token 的预测。注意力机制可以被理解为一种“在当前任务下,哪些上下文更重要”的计算方式。读者不需要掌握所有工程细节,但要明白:预训练之所以可行,是因为 Transformer 给了模型处理长文本关系的结构基础。

读者收获

读者应理解:Transformer 是大模型处理上下文的核心结构;注意力机制不是神秘理解力,而是一种让 token 之间发生信息交互的计算方法。

配图

Transformer 如何让上下文互相影响

图 3-1|Transformer 上下文交互:注意力机制让不同位置的 token 互相影响,帮助模型预测下一个 token。

读图:token 之间如何互相影响?

这张图展示的是上下文里的 token 如何通过注意力机制彼此建立联系。某个 token 的表示不是孤立生成的,而是在多层计算中不断吸收其他位置的信息。

读图时不要把注意力理解成人类意识里的“专注”。它更像一种可训练的相关性计算:模型根据上下文判断哪些位置的信息更有助于预测后续 token。

正文

如果只看最后一个 token,模型为什么不可能写好回答?

上一章我们把大模型生成文本的最小动作讲清楚了:给定前文,预测下一个 token。模型每生成一个 token,就把它接到上下文里,再继续预测下一个 token。这样一轮轮下去,最后形成完整回答。

但这里还有一个大问题。

模型预测下一个 token 时,到底看到了什么?

如果它只看最后一个 token,那它不可能写出连贯回答。因为语言里的许多信息并不在最后一个词里,而在更早的上下文里。

看一个简单句子:

小王把书放进书包,因为他明天要带去学校。

如果只看“学校”前面的最后一个 token,很难知道整个句子在说什么。要理解“他”指的是谁,要知道前面提到了“小王”。要理解“带去学校”的东西是什么,要知道前面提到了“书”。这类指代关系经常跨越多个词。

代码里更明显:

if user.is_admin:
    allow_access()
else:

如果模型要预测 else: 后面应该接什么,它不能只看 else 这个 token。它要知道前面有一个 if 条件,要知道这是访问权限相关逻辑,要知道缩进结构意味着什么。代码上下文里的括号、冒号、缩进、变量名,都可能影响后面的生成。

数学推导也是一样:

已知 a > b,且 b > c,因此

要预测后面合理内容,模型需要利用前面两个条件。只看“因此”没有意义。“因此”后面可以接无数东西,真正决定概率分布的,是前面的逻辑关系。

所以,大模型需要一种机制,让不同位置的 token 可以互相影响。

这就是 Transformer 要解决的核心问题之一:

一串 token 进入模型以后,模型怎样让每个位置都能利用上下文中的其他位置?

我们先不要急着记“多头注意力”“前馈网络”“残差连接”“层归一化”这些架构名词。那些细节以后有需要可以再展开。本章只讲够用版:Transformer 给了模型一种处理上下文的结构,让它不只是看最后一个 token,而是能在一串 token 中计算哪些信息对当前预测更重要。

也就是说,Transformer 的重要性不在于名字新,而在于它让“下一个 token 预测”这件事真正有了利用长上下文的能力。

上一章我们说,模型输出的是一张概率表。现在可以补上一句:这张概率表不是只由最后一个 token 决定,而是由整个上下文经过模型计算后共同决定。

这就是从第 2 章到第 3 章的过渡。

第 2 章回答的是:模型每一步生成什么?
第 3 章要回答的是:模型怎样利用上下文来生成?

token 怎样变成模型能计算的向量?

在讲注意力之前,我们还要先过一座桥:token 怎样进入模型?

token 本身通常可以看成一个编号。比如词表里可能有几万个 token,每个 token 对应一个整数 ID。可是这个编号本身没有什么可计算的意义。

假设:

北京 → 1024
上海 → 2048
苹果 → 4096

这些数字只是编号,不代表“上海”是“北京”的两倍,也不代表“苹果”和“上海”之间有什么数值关系。如果直接拿编号做计算,模型并不能理解 token 之间的关系。

所以,模型会先把 token 映射成向量。

这个过程通常叫 embedding。你可以把它理解成:每个 token 进入模型前,先被转换成一串数字。这串数字不是随便的,它会在训练中被调整,逐渐变成适合模型使用的表示。

比如“北京”“上海”“广州”这类 token,在很多语境里可能有相似用法:城市、地名、地点、交通、天气、人口、经济。训练之后,它们的向量表示可能在某些方向上比较接近。而“北京”和“苹果”虽然也可能在某些语境中共同出现,但整体关系会不同。

当然,我们不能把向量想得太简单。它不是一个二维地图,也不是一张可以直接读懂的语义表。真实模型里的向量维度很高,而且含义分布在许多维度中。我们这里只需要抓住一个直觉:

embedding 把离散 token 变成模型可以连续计算的向量表示。

有了向量,模型就可以做后续计算:比较、组合、加权、变换。token 不再只是孤立编号,而变成可以参与神经网络计算的对象。

这一步也提醒我们:模型不是直接处理“意义”。它处理的是向量。意义感来自这些向量在训练中逐渐形成的结构,以及后续层对这些结构的使用。

比如一句话:

这只猫坐在垫子上。

token 化之后,每个 token 会变成向量。模型接下来要做的,不是分别处理“这”“只”“猫”“坐”“在”“垫子”“上”,然后简单拼起来,而是让这些向量在上下文中互相影响。因为“猫”是什么、“坐”作用于谁、“垫子”是什么位置,都取决于它们之间的关系。

这就进入注意力机制。

embedding 解决的是:token 怎样变成可计算对象?
注意力要解决的是:这些对象怎样在上下文中互相读取信息?

注意力到底在注意什么?

“注意力”这个词很容易让人误会。

人类说“注意”,往往带有意识色彩。比如我注意到窗外下雨了,我注意到你语气变了,我注意到这句话里有一个漏洞。听起来像某种主观意识在选择关注点。

Transformer 里的注意力不是这个意思。

它是一种计算机制。更准确地说,它让模型在处理某个 token 时,可以根据当前上下文,给其他 token 分配不同权重。权重高的位置,对当前表示影响更大;权重低的位置,影响更小。

我们用一个句子来看:

小明把苹果放进书包,因为他下午要吃。

如果模型正在处理“他”,它可能需要关注“小明”,因为“他”指的是谁,要从前文找。
如果模型正在处理“吃”,它可能需要关注“苹果”,因为吃的对象是什么,要从前文找。
如果模型正在预测后面的 token,它可能同时需要利用“小明”“苹果”“下午”等信息。

注意力机制的直觉就是:不同位置之间可以互相“看见”并加权使用。

再看代码:

for item in items:
    print(item)

当模型处理 print(item) 里的 item 时,它需要知道前面循环变量也叫 item。这不是最后一个 token 单独能决定的,而是上下文里的变量绑定关系影响了后续 token 概率。

再看数学:

因为 x 是正数,所以 x 的平方

如果模型要预测后面可能接“大于 0”,它需要关注“x 是正数”。这说明前面的条件会影响后面的结论。

这些例子都在说明同一件事:上下文不是一条平滑的流水线,而是一张关系网。不同 token 之间有远近不同、强弱不同、类型不同的关系。注意力机制给模型提供了一种计算这些关系的方式。

技术上,注意力会涉及查询、键、值这些概念,也就是常说的 Q、K、V。我们这里不展开公式,只给一个够用直觉:

  • 当前 token 会形成一个“我现在需要什么信息”的查询。
  • 上下文中的 token 会提供“我这里有什么信息”的线索。
  • 模型计算它们之间的匹配程度,再决定从哪些位置取更多信息。

这个过程不是人工写规则。不是程序员告诉模型:“如果看到代词,就去找最近的人名。”模型是在训练中通过大量样本学会哪些上下文关系有助于降低预测错误。

这就是注意力厉害的地方。

它不是直接赋予模型理解力,而是提供一种结构,让模型可以灵活地从上下文中读取信息。代词指代、代码变量、数学条件、段落主题、问题和答案之间的关系,都可能通过这种机制被模型利用。

但我们仍然要克制。

注意力权重不等于人类解释。某个 token 对另一个 token 的注意力高,不一定意味着模型“理解”了它们的关系,也不一定能完整解释模型为什么生成某个答案。注意力是模型计算的一部分,不是模型思维的透明窗口。

所以,本章对注意力的说法要非常稳:

注意力不是神秘理解力,而是一种让 token 之间发生信息交互的计算方法。

有了这个方法,模型预测下一个 token 时,就能利用更丰富的上下文。没有它,预训练很难在长文本、代码、推理和复杂问答中发挥今天这样的效果。

为什么需要多层 Transformer?

如果注意力能让 token 之间互相读取信息,那一层注意力是不是就够了?

通常不够。

语言和代码里的关系是分层的。有些关系很局部,有些关系跨越很远;有些关系是词语搭配,有些关系是句法结构,有些关系是段落主题,有些关系是任务意图。一层计算很难一次性把这些关系都处理好。

所以 Transformer 通常会堆叠很多层。

我们可以用一个不严格但有用的直觉来理解多层结构。

较低层可能更容易处理局部关系。比如哪些 token 经常连在一起,标点和词语怎样组合,代码里的括号和缩进怎样对应。

中间层可能逐步形成更复杂的结构。比如主语和谓语的关系,代词和指代对象的关系,函数定义和函数调用之间的关系。

更高层可能形成更贴近任务的上下文表示。比如当前用户到底在问什么,回答应该保持什么风格,代码片段要完成什么功能,数学推导走到了哪一步。

这里要特别小心:这不是说每一层都有固定的人类标签。我们不能武断地说第几层一定处理语法,第几层一定处理推理。真实模型内部远比这个复杂。这个分层描述只是帮助读者理解:多层 Transformer 让信息可以经过多次加工,从简单模式逐渐组合成复杂表示。

你可以把它想成反复改写上下文表示。

最开始,每个 token 只有自己的 embedding。经过一层注意力,它吸收了一些周围信息。经过第二层,它吸收的是已经混合过上下文的信息。层数越往后,每个位置的表示就越不只是原始 token 本身,而是带着越来越多上下文加工痕迹。

这对预测下一个 token 很重要。

当模型要预测一句话后面该接什么时,它需要的不只是最后几个词,而是整个上下文的压缩表示。这个表示要包含主题、语气、事实、指代、格式、任务目标。多层 Transformer 就是在逐步构造这种表示。

代码生成里尤其明显。

一个函数可能几十行前定义了变量,后面又要调用它。一个类可能前面定义了方法,后面要使用这个方法。一个括号没闭合,后面就要补上。模型要写出像样代码,需要同时处理局部语法和长距离结构。

数学推理也类似。

题目前面给出的条件,可能要在几步之后才用到。模型如果不能保留和重组这些上下文信息,就很难生成连贯推导。

这就是多层 Transformer 的价值:它让 token 表示不再停留在孤立位置,而是在多次信息交换和变换后,形成更丰富的上下文表示。

当然,本书不会把 Transformer 写成完整架构教材。我们暂时不展开残差连接、层归一化、前馈网络、多头注意力的具体公式。那些细节很重要,但不是这本书的主线。我们的主线是训练目标如何改变模型行为。

在这条主线下,Transformer 需要承担的解释任务是:

它给模型提供了处理上下文的结构基础,使得下一个 token 预测不只是局部补全,而可以利用长文本中的复杂关系。

理解到这里,就够我们继续往前走了。

Transformer 为什么适合预训练?

现在我们可以把 Transformer 拉回全书主线:预训练。

预训练要做的是,在海量文本上反复进行下一个 token 预测。这个任务看似简单,但数据非常复杂。文本里有长句、段落、代码、公式、引用、对话、推理链、跨句指代。模型要在这些文本里预测下一个 token,就必须有能力处理不同位置之间的关系。

Transformer 适合预训练,至少有三个原因。

第一,它能动态利用上下文。

同一个 token,在不同上下文里意义完全不同。

比如“苹果”:

我吃了一个苹果。
苹果发布了新款手机。

第一个“苹果”更像水果,第二个“苹果”更像公司。模型不能给“苹果”一个永远固定的解释。它必须根据上下文动态形成表示。注意力机制正好提供了这种能力:当前 token 的表示可以根据周围 token 变化。

第二,它能处理长距离关系。

自然语言和代码里,相关信息不一定挨在一起。一个段落开头提出的问题,可能在结尾才回答;一个代码块前面定义的变量,可能后面才使用;一道数学题的条件,可能隔几步才参与推导。Transformer 让远处 token 也有机会影响当前计算。

第三,它适合大规模训练。

在训练时,很多位置的预测可以并行计算。也就是说,模型不必像生成时那样一个 token 一个 token 慢慢来;训练系统可以在一段文本中同时计算多个位置的预测损失。这对大规模预训练非常重要。没有这种工程上的可扩展性,今天这种规模的大模型训练会困难得多。

当然,这里还有很多工程细节,比如位置编码、注意力掩码、批量训练、分布式训练等。我们现在不展开。我们只要知道:Transformer 不只是一个时髦架构名,它和预训练任务非常匹配。

它让模型可以在海量 token 序列中学习上下文关系,再通过下一个 token 预测不断调整参数。

这也解释了为什么第 4 章要讲数据。

有了 token 和 Transformer,只是有了模型处理文本的基本工具。真正让模型学到东西的,是大规模训练数据和优化目标。互联网文本怎样被收集、清洗、切分?所谓“知识”怎样变成文字接龙样本?这些问题决定了模型到底在什么分布上学习。

注意力不是理解的全部

在结束这一章前,我们还要做一次克制提醒。

注意力机制很重要,但不要把它神化。

有时人们看到注意力可视化图,会觉得自己看到了模型“为什么这么想”。某个 token 对另一个 token 注意力高,于是我们说模型在关注它、理解它、利用它。这样的说法有时有启发,但不能太当真。

注意力是计算过程的一部分,不是完整解释。

模型最终输出由很多因素共同决定:embedding、注意力、前馈网络、多层变换、训练数据、参数分布、采样策略。单看某一层、某一个注意力头、某一次权重,很难完整说明模型为什么生成某个答案。

而且,模型可以利用上下文,也可以误用上下文。

它可能抓住了正确线索,也可能被无关细节带偏。它可能在短文本里表现很好,但在长上下文中遗漏前面信息。它可能看似在推理,其实只是模仿了训练数据中的推理格式。Transformer 提供了处理上下文的结构能力,但它不保证输出一定正确。

这点对全书主线很重要。

架构提供可能性。
数据提供学习材料。
优化目标提供训练方向。
参数更新把这些东西写进模型行为。

Transformer 只是其中一环。没有它,大模型很难利用复杂上下文;但只有它,也不会自动产生可靠助手。后面还需要预训练数据、SFT、偏好优化、奖励函数、验证器。

所以,本章对 Transformer 的结论可以很简洁:

Transformer 让 token 之间发生信息交互,使模型能够根据上下文预测下一个 token;但它不是神秘理解力本身。

这句话足够我们进入下一章。

本章小结

这一章我们用够用版的方式理解了 Transformer。

第 2 章告诉我们,模型每一步都在预测下一个 token。但如果模型只能看最后一个 token,它不可能生成连贯回答。Transformer 的作用,是让 token 之间发生信息交互,让模型能根据更长、更复杂的上下文形成预测。

token 首先被转换成向量,也就是 embedding。向量让模型可以对文本进行连续计算。注意力机制则让不同位置的 token 互相读取信息:某些上下文更重要,影响就更大;某些上下文不太相关,影响就更小。多层 Transformer 进一步让这些信息经过多次加工,形成更丰富的上下文表示。

但注意力不是神秘理解力。它是一种计算方法。真正塑造模型能力的,是架构、数据、优化目标和参数更新共同作用。

下一章预告

现在,我们已经有了训练大模型的基本舞台:文本会被切成 token,token 会变成向量,Transformer 会让它们在上下文中互相影响,最后模型输出下一个 token 的概率。

接下来问题变成:这些训练文本从哪里来?互联网文本又是怎样从网页、书籍、代码、论坛、问答,变成模型可以学习的“文字接龙样本”?下一章,我们进入预训练的数据部分。

延伸阅读

推荐阅读方向:

  • 《Attention Is All You Need》:Transformer 原始论文。
  • Transformer、self-attention、positional encoding 的入门资料。
  • 适合初学者的可视化注意力机制讲解。

第 4 章:预训练:互联网文本如何变成训练数据

本章问题

互联网上的文本那么杂,大模型是怎样把它们变成可以学习的训练数据的?

章节摘要

这一章从数据开始讲预训练。模型并不是直接“阅读互联网”,而是训练者先收集大量文本,再经过过滤、清洗、去重、切分和格式化,把它们变成模型可以处理的 token 序列。这里要讲清一个关键点:互联网上看似丰富的知识,最终送进预训练系统时,并不是以“知识库问答”的形式存在,而是被组织成一段段文字接龙样本。模型看到前文,学习预测真实出现的下一个 token。这里要提醒读者:数据规模很重要,但数据质量同样重要。重复文本、低质量网页、错误信息、版权内容、污染样本都会影响模型学到什么。

读者收获

读者应理解:预训练数据不是天然干净的知识库,而是经过筛选和处理的文本集合;互联网上的知识最终会变成“给定前文,预测下一个 token”的文字接龙样本。

配图

互联网文本变成预训练数据

图 4-1|互联网文本变成预训练数据:互联网知识最终被组织成“给定前文,预测下一个 token”的文字接龙样本。

读图:从原始文本到预训练样本

这张图展示互联网文本进入训练系统前经历的变化:采集、清洗、去重、切分、token 化,最后变成大量“给定前文,预测下一个 token”的训练样本。

它要说明的是:互联网知识并不是以知识库条目的形式直接塞进模型,而是被组织成海量文字接龙样本,通过预测任务逐步改变参数。

正文

模型真的在“阅读互联网”吗?

我们经常听到一种说法:

大模型读了整个互联网。

这句话有一定道理,但也很容易误导。

说它有道理,是因为许多大模型的预训练数据确实来自大规模文本,其中相当一部分与互联网有关。网页、百科、论坛、问答、技术文档、代码仓库、论文页面、新闻文章、书籍片段,这些都可能成为训练数据的来源。大模型表现出来的很多知识和语言能力,也确实和这些文本来源密切相关。

但说它误导,是因为“阅读互联网”这个说法太像人在读书。

人阅读一篇文章,会看到标题、段落、语气、事实、观点,会根据自己的经验判断哪些可信、哪些可疑,还会把读到的东西和已有知识联系起来。模型预训练不是这样。模型不会像人一样打开网页、理解网页、判断网页,再把知识整理进脑子里。

预训练面对的是被整理后的 token 序列。

互联网文本首先是原材料。它要经过一系列处理,才会变成模型可以学习的数据。这个过程通常包括收集、过滤、清洗、去重、切分 token、格式化。最后进入训练系统的,不是“网页意义”,也不是一张张知识卡片,而是一段段 token 序列。

更关键的是,这些 token 序列会被用来构造一种非常具体的训练任务:

给定前文 token,预测真实出现的下一个 token。

这就是我们反复说的文字接龙样本。

所以,如果说“大模型读了互联网”,我们最好在心里把它翻译成更准确的话:

训练者从互联网等来源收集大量文本,把它们清洗和切分成 token 序列,再用这些序列训练模型预测下一个 token。

这句话没有“读了整个互联网”那么有冲击力,但它更接近真实训练过程。

这个区别很重要。

如果我们把模型想象成读了一个巨大知识库,就会以为它应该像数据库一样可靠:问什么事实,它就查什么事实;训练数据里有答案,它就应该答对;训练数据里没有答案,它就应该明确不知道。

但大模型不是数据库。它不会在回答时先检索一条确定记录,再原样返回。预训练让它学到的是文本分布中的模式。它根据上下文生成高概率 token。高概率文本可能是事实,也可能是常见误解;可能是准确总结,也可能是看起来像真的幻觉。

因此,从本章开始,我们要改变一个直觉:

预训练不是把互联网知识复制进模型,而是把互联网文本转化为下一个 token 预测任务。

这句话会贯穿第 4 章和第 5 章。

第 4 章讲数据怎样变成这种任务。
第 5 章讲公式怎样优化这个任务。

原始数据从哪里来?

预训练数据的来源非常复杂,不同模型、不同机构、不同阶段的配方都不一样。很多细节也不会完全公开。我们不需要追某个模型的具体数据清单,而要理解数据来源的大类,以及它们给模型带来什么。

第一类是网页文本。

网页是互联网文本的大头。里面有新闻、博客、教程、产品说明、论坛讨论、百科页面、机构网站、问答页面。网页的优势是规模巨大、覆盖面广,几乎什么主题都有。缺点也明显:质量参差不齐,重复严重,广告、垃圾内容、自动生成内容、过时信息都很多。

网页文本让模型接触到大量日常语言和世界知识。它能学到人们怎样写解释、怎样讨论问题、怎样描述事件、怎样表达观点。可它也会学到网页里的噪声:标题党、错误信息、偏见、重复模板。

第二类是书籍和长文档。

书籍、教材、长篇技术文档通常结构更完整,语言更连贯,主题展开更充分。它们对模型学习长程叙述、概念解释和系统性表达很有帮助。一本好教材和一堆碎片网页给模型提供的信号并不一样。教材往往更像有组织的知识,网页则更像杂乱的信息海洋。

当然,书籍数据也涉及版权和授权问题。本书不展开法律细节,但要提醒读者:预训练数据不是一个纯技术问题,它也牵涉版权、隐私和数据治理。

第三类是代码。

代码对大模型非常重要。代码文本不像普通自然语言那样宽松,它有更强的形式结构。括号要匹配,缩进有意义,变量要前后一致,函数调用要符合语法。大量代码数据可以让模型学习编程语言的语法、常见库的使用方式、函数模式、错误处理和测试写法。

这也是为什么代码能力经常被用来观察大模型能力。代码既是文本,又有可执行性。它既能被语言模型生成,又能被编译器和测试器验证。后面讲可验证强化学习时,我们还会回到代码。

第四类是问答和对话数据。

问答数据有一个特殊价值:它天然接近用户和助手的交互形式。一个问题后面跟一个回答,这种结构会帮助模型学习“有人提问时,后面通常应该出现答案”。不过,普通问答数据仍然不是 SFT 意义上的高质量指令数据。它可能有错误、争吵、跑题、低质量回复,也可能只是论坛闲聊。

第五类是论文、百科和技术资料。

这些文本提供更密集的知识和更正式的表达。论文里有摘要、方法、实验、结论;百科里有定义和结构化事实;技术资料里有步骤、接口、配置、限制条件。它们帮助模型学习专业语域。

这些来源混合在一起,构成了一个巨大的文本分布。

注意这个词:分布

模型不是从每条文本中抽取一个知识点,再放进自己的知识表。它是在整个数据分布上学习:哪些表达常见,哪些词经常一起出现,哪些结构在什么上下文里更可能出现,哪些代码片段接在什么函数定义后面,哪些解释方式常见于教程,哪些推理步骤常见于数学文本。

不同来源的比例,会影响模型。

如果代码数据比例高,模型可能更擅长代码。
如果高质量教材多,模型可能更擅长解释概念。
如果论坛和低质量网页很多,模型也可能学到更多口语、争论、偏见和噪声。
如果某种语言数据少,模型在那种语言上的能力可能弱。

所以,预训练数据不是一个中性的背景。它会深刻塑造模型能力。

为什么不能直接把所有文本丢给模型?

既然数据越多越好,那能不能把互联网上能抓到的文本全都丢给模型?

不能。

至少不能不加处理地这么做。

原因很简单:互联网很大,但也很脏。

里面有高质量教程,也有垃圾页面。
有严肃论文,也有复制粘贴的水文。
有真实问答,也有广告、诈骗、乱码、机器生成内容。
有经典书籍,也有侵权转载。
有正确事实,也有谣言、过时信息和恶意内容。

如果把这些东西不加筛选地喂给模型,模型不会自动只吸收好的部分。预训练目标不是“识别真理”,而是“预测文本”。低质量文本也会提供预测信号,只是这些信号可能把模型带向不好的方向。

所以,数据处理非常重要。

第一步通常是过滤。

过滤要尽量去掉明显低质量内容,比如乱码页面、重复模板、关键词堆砌、广告垃圾、空洞页面、恶意内容。过滤不是完美的,也很难完美。它更像是在大规模数据里先做粗筛,把明显不适合训练的文本剔除。

第二步是清洗。

网页里常常有导航栏、版权声明、按钮文字、广告脚本、无意义重复。清洗要尽量保留正文,去掉无关噪声。否则模型可能会学到很多奇怪模式,比如网页底部模板、菜单项、乱码符号。

第三步是去重。

去重比很多人想象得更重要。互联网上重复内容非常多。同一篇文章可能被转载几十次,同一段说明可能出现在无数页面,同一个代码片段可能反复出现。如果不去重,模型会过度看到某些文本,训练分布会被扭曲。

去重还有另一个作用:减少记忆。

如果一段文本在训练数据里出现很多次,模型更容易把它记住,而不只是学到一般模式。这会带来隐私、版权和评估污染的问题。所谓评估污染,就是测试题或基准答案如果已经出现在训练数据里,模型可能不是“会做”,而是见过类似答案。这样评估结果就会虚高。

第四步是格式化。

不同来源的数据格式不同。网页、PDF、代码、论坛、问答、Markdown、HTML,它们都需要被整理成统一可处理的文本形式。格式化不是小事。保留什么结构、去掉什么标记、如何处理代码块和公式,都会影响模型学习。

第五步是切分。

模型一次能处理的上下文长度有限,所以长文本需要被切成适合训练的片段。切得太碎,长距离关系可能丢失;切得太长,又可能超出训练设置或降低效率。怎样切分文本,也会影响模型看到的上下文结构。

这些步骤加起来,说明一件事:

预训练数据不是自然存在的东西,而是工程处理后的结果。

这不是说数据处理可以完全控制模型学到什么。相反,大规模数据太复杂了,任何处理都只能部分控制。训练者可以提高数据质量、调整数据比例、过滤明显问题,但不可能把每一条文本的影响都精确掌控。

这也是大模型训练的一个现实特点:它既依赖海量数据,又很难完全理解每一部分数据的作用。

所以,当我们说“模型学到了互联网知识”时,要同时记住另一面:

模型也可能学到互联网里的噪声、偏见、重复、错误和过时信息。

数据规模很重要。
数据质量同样重要。
数据分布更重要。

互联网知识怎样变成文字接龙样本?

现在进入本章最关键的地方。

互联网上有很多知识。比如:

法国的首都是巴黎。

人读到这句话,会把它理解成一个事实:法国,首都,巴黎。我们可能会把它想象成一张知识卡片:

问题:法国的首都是哪里?
答案:巴黎。

但预训练通常不是这样组织数据的。

在预训练里,这句话首先会被切成 token 序列。为了说明方便,我们用一种简化切分:

法国 / 的 / 首都 / 是 / 巴黎 / 。

然后训练任务不是直接问模型“法国的首都是哪里”。训练任务是对这串 token 中的每个位置做预测。

比如:

前文:法国
真实下一个 token:的

再比如:

前文:法国 / 的
真实下一个 token:首都

再比如:

前文:法国 / 的 / 首都 / 是
真实下一个 token:巴黎

最后这个样本,才和我们直觉里的“法国首都是巴黎”最接近。但从训练形式上看,它仍然不是一个问答卡片,而是一个下一个 token 预测任务。

这就是文字接龙样本。

更一般地说,一段文本:

x_1, x_2, x_3, ..., x_t

可以产生很多训练位置:

给定 x_1,预测 x_2
给定 x_1, x_2,预测 x_3
给定 x_1, x_2, x_3,预测 x_4
...
给定 x_<t,预测 x_t

第 5 章的公式会把这件事写成:

$$P_\theta(x_t \mid x_{<t})$$
现在我们先不用推公式,只要看懂它背后的样本形态:$x_{<t}$ 是前文,$x_t$ 是真实出现的下一个 token。预训练就是让模型在大量这样的位置上,提高真实下一个 token 的概率。

这会改变我们对“知识”的直觉。

模型不是被直接喂了一个结构化事实表:

法国 -> 首都 -> 巴黎

它看到的是很多自然文本中的片段:

法国的首都是巴黎。
巴黎是法国的首都。
作为法国首都,巴黎是欧洲重要城市。
France's capital is Paris.

这些句子从不同角度、不同语言、不同上下文重复出现。模型为了预测下一个 token,会逐渐把“法国”“首都”“巴黎”之间的关系编码进参数里。

这也解释了为什么表达方式很重要。

如果训练数据里只有一种固定表达:

法国的首都是巴黎。

模型可能只在非常相似的上下文里表现好。可如果数据里有很多变体:

巴黎是法国的首都。
法国首都巴黎以艺术和时尚闻名。
问:法国的首都是哪里?答:巴黎。
Paris is the capital of France.

模型就更容易在不同提问方式下生成正确答案。

这不是因为它有一张明确的知识图谱,而是因为多种文本模式共同塑造了它的概率分布。

代码数据也是这样。

一段代码:

def add(a, b):
    return a + b

会变成 token 序列,再产生很多预测任务。

当上下文是:

def / add / ( / a / , / b / ) / :

模型要预测后面可能是换行和缩进。

当上下文是:

def / add / ( / a / , / b / ) / : / 换行 / 缩进 / return

模型要预测后面可能是 a,再后面可能是 +,再后面可能是 b

模型不是被直接教了一条规则“加法函数应该返回 a+b”。它是在大量代码文本中学习函数定义、参数、返回值、缩进和运算符之间的模式。

数学文本同样如此。

比如:

因为 a > b 且 b > c,所以 a > c。

它也会被转成 token 序列。模型在预测 a > c 相关 token 时,需要利用前面的条件。这类文本越多,模型越可能学到某些推理格式。

但请注意,这仍然是文本预测。

模型不是直接在预训练阶段调用一个逻辑证明器来验证 a > c。它只是看到大量数学文本里的条件、推导和结论,并被训练去预测这些文本。后面可验证强化学习会进一步引入验证器,那是另一层训练目标。

这就是我们要区分的地方:

预训练阶段,数学推理主要以文本模式的形式进入模型。
可验证强化学习阶段,数学答案可以进一步被外部系统评价。

两者都重要,但不是一回事。

现在,我们可以更准确地说:

互联网知识进入预训练,不是以“知识条目”的形式进入,而是以“文本序列中的下一个 token 预测任务”的形式进入。

这句话有点长,但非常关键。

它解释了为什么大模型能回答很多知识问题:因为知识关系反复出现在文本序列中。
它也解释了为什么大模型会幻觉:因为预训练目标奖励的是像文本,而不是验证事实。
它还解释了为什么后面需要 SFT、RLHF、DPO 和可验证强化学习:因为仅仅学会续写文本,还不等于学会可靠回答。

让我们再用一个完整例子收束这一节。

原始网页里可能有一段:

梯度下降是一种常用的优化方法,它通过沿着损失函数下降最快的方向更新参数。

经过清洗和 token 化,它变成 token 序列。训练时,其中某些位置会形成样本:

前文:梯度下降 / 是 / 一种 / 常用 / 的
真实下一个 token:优化

另一个位置:

前文:梯度下降 / 是 / 一种 / 常用 / 的 / 优化 / 方法 / , / 它 / 通过 / 沿着 / 损失 / 函数
真实下一个 token:下降

再往后:

前文:... / 更新
真实下一个 token:参数

模型就是通过数以万亿计的类似位置学习语言和知识模式。

这件事朴素到有点反直觉:宏大的“知识学习”,最终落到一个个位置上的“下一个 token 预测”。

可正是这个朴素机制,在足够大的数据和模型上,变成了大语言模型的基础能力。

数据分布怎样塑造模型?

现在我们知道,预训练数据最终会变成大量文字接龙样本。接下来就要问:不同的数据,会怎样塑造不同的模型?

答案很直接:

模型学到的是训练数据分布中的模式。

如果训练数据里代码很多,模型会更频繁地看到代码结构。函数定义、变量命名、库调用、测试用例、错误信息、注释风格,这些都会在预测任务中反复出现。模型为了降低损失,就会更擅长在代码上下文中给合理 token 更高概率。

如果训练数据里高质量解释很多,模型会更频繁地看到清晰定义、分步骤说明、例子、总结。它就更容易学到解释型文本的结构。

如果训练数据里数学推导很多,模型会更频繁地看到条件、公式、变形、结论之间的文本模式。它可能更擅长生成看起来有步骤的推理。

反过来,如果训练数据里低质量文本很多,模型也会学到低质量模式。

比如空泛套话:

这个问题需要从多个角度综合分析。

这句话本身不是错,但如果模型学到太多这种不解决问题的表达,它可能在回答时变得圆滑却空洞。

如果训练数据里有大量错误事实,模型也可能给错误答案更高概率。
如果训练数据里某些群体被刻板描述,模型也可能学到偏见。
如果训练数据里某种语言或领域样本很少,模型在那里的能力就可能弱。

所以,数据不是越多越自动正确。

规模当然重要。没有足够规模,模型很难覆盖语言和知识的复杂性。可是规模只是条件之一。质量、比例、来源、多样性、去重、过滤,同样重要。

这也是为什么不同模型会有不同气质。

有些模型代码能力更强,有些模型中文表达更自然,有些模型数学更好,有些模型更擅长长文总结。差别来自很多方面:模型结构、参数规模、训练算力、后训练方法。但数据配方一定是其中的重要因素。

我们可以把预训练想成一种大规模“语言环境”。

一个人在什么语言环境中长大,会影响他说话方式。模型在什么文本分布中训练,也会影响它生成什么样的文本。这个类比不完全准确,因为人有身体经验、社会互动和主动理解,模型没有这些。但作为直觉,它能帮助我们理解数据分布的重要性。

不过,类比到这里就要停住。

模型不是孩子。训练数据也不是生活经验。模型不会像人一样在世界里行动、观察、试错。它主要是在文本中学习文本模式。即使文本描述了世界,文本也不等于世界本身。

这句话很重要:

训练数据是世界在文本中的投影,不是世界本身。

投影会丢失很多东西。比如真实物理经验、人的情绪、社会互动、隐含背景、沉默知识。文本里有大量知识,但也有大量偏差。模型从文本中学习,就会继承这种投影的力量和局限。

这就是为什么我们不能把预训练模型当成“读完互联网的真理机器”。

它确实从海量文本中学到了很多。
但它学到的是文本分布中的模式。
文本分布很强大,也很不完美。

数据不是世界真相

在结束本章前,我们要做一个重要提醒:

数据不是世界真相。

这句话听起来像常识,但在大模型时代非常容易被忘记。

因为大模型能回答很多问题,能写出流畅解释,能引用许多概念,人们很容易产生一种感觉:它好像读过一切,所以应该知道一切。可它读到的不是世界本身,而是文本。文本是人写下来的东西,带着人的知识,也带着人的错误、偏见、遗漏、立场和时代限制。

第一,文本可能是错的。

互联网上有错误事实、过时信息、误传内容、低质量总结。预训练目标并不会天然区分“真实文本”和“虚假文本”。只要它出现在训练数据里,就可能参与塑造模型概率。

第二,文本可能有偏见。

不同群体、地区、语言、职业在互联网文本中的呈现并不均衡。有些声音更大,有些声音更少;有些观点被反复转载,有些经验几乎不被记录。模型从文本分布中学习,也会受到这种分布影响。

第三,文本可能被污染。

如果评测题、标准答案、竞赛题解出现在训练数据里,模型在评测时可能表现得很好,但这不一定说明它真正具备泛化能力。它可能只是见过类似内容。这就是所谓数据污染或基准泄漏。

第四,文本可能被重复。

重复内容会放大某些模式。一个错误说法如果被大量转载,可能在数据分布里变得很显眼。模型训练时看到它很多次,就可能给类似说法更高概率。

第五,文本不包含所有经验。

很多知识存在于行动、实验、身体经验和社会互动中,不一定完整写在文本里。一个厨师的手感,一个医生的临床判断,一个工程师调试系统的经验,很多都难以完全变成文字。模型从文本里学习,自然会受到文本表达能力的限制。

这些提醒不是为了否定大模型。

恰恰相反,我们越理解数据的局限,就越能准确判断大模型的能力边界。它确实可以从海量文本中学到惊人的模式,但这些模式不是从真空中来,也不是从纯粹真理中来。它们来自数据分布。

这也解释了为什么后训练很重要。

预训练学到的是文本分布。
SFT 会用高质量问答样本塑造回答方式。
RLHF/DPO 会用偏好信号调整模型行为。
可验证强化学习会用外部验证器提供更明确的奖励。

这些后续阶段,不是锦上添花,而是在弥补单纯文本预测目标的不足。

所以,本章最后要留下的不是“数据越多越好”这句简单口号,而是更完整的判断:

数据规模决定模型能看到多少世界的文本投影,数据质量和数据分布决定这些投影会怎样塑造模型。

本章小结

这一章我们讲清了预训练数据的来路。

大模型不是像人一样直接“阅读互联网”,而是训练者从网页、书籍、代码、问答、论坛、技术资料等来源收集文本,再经过过滤、清洗、去重、切分 token 和格式化,整理成可以训练的 token 序列。

本章最关键的转变是:互联网上的知识最终不是以知识库问答的形式送进模型,而是被组织成“给定前文,预测下一个 token”的文字接龙样本。模型看到的是大量文本位置上的预测任务,而不是一张张事实卡片。

这解释了大模型为什么能学到很多,也解释了它为什么会出错。它学到的是数据分布中的模式,而数据分布不是世界真相。数据规模重要,数据质量同样重要。

下一章预告

现在我们已经有了预训练样本:

前文 token → 真实下一个 token

下一章要问:模型怎样用这些样本训练参数?为什么真实下一个 token 的概率越高,损失越低?最大似然、交叉熵和梯度下降到底在这里扮演什么角色?下一章,我们正式进入预训练公式。

延伸阅读

推荐阅读方向:

  • 大规模预训练数据集构建与清洗资料。
  • 数据去重、低质量过滤、数据污染和基准泄漏相关讨论。
  • 语言模型训练语料治理、版权和数据质量相关资料。

第 5 章:预训练公式:下一个 token 的概率

本章问题

预训练的优化目标到底是什么?为什么说它是在预测下一个 token,而不是在“理解世界”?

章节摘要

这一章正式引入预训练的核心公式。给定前面的 token,模型要提高真实下一个 token 的概率。这个目标通常写成交叉熵损失或最大似然形式。我们会把公式逐项拆开:上下文是什么,真实 token 是什么,模型概率是什么,负号和求和代表什么。本章还要讲清模型参数是怎样被训练出来的:损失函数给出“错得有多远”,反向传播计算每个参数该往哪个方向调整,梯度下降方法则一步步更新参数,让真实 token 的概率逐渐提高。

读者收获

读者应理解:预训练通常优化交叉熵 / 最大似然,而不是最小二乘法;它优化的是 token 级预测,模型参数主要通过反向传播和梯度下降更新。

配图

预训练公式拆解图

图 5-1|预训练公式拆解:这个公式的核心,是提高真实下一个 token 的概率,并通过梯度下降更新参数。

读图:公式、概率和参数更新

这张图把预训练的几个关键环节放在一起:文本变成 token,模型给真实 token 分配概率,交叉熵损失衡量预测错误,梯度下降再根据损失更新参数。

读这张图时要把公式和训练流程连起来。公式不是孤立的数学符号,它描述的是模型怎样在海量位置上反复提高真实下一个 token 的概率。

正文

有了文字接龙样本,训练到底在优化什么?

上一章我们讲清了预训练数据的形态。

互联网文本经过收集、清洗、去重、切分 token 和格式化以后,最终会变成大量这样的训练位置:

前文 token → 真实下一个 token

比如:

前文:法国 / 的 / 首都 / 是
真实下一个 token:巴黎

或者:

前文:def / add / ( / a / , / b / ) / : / 换行 / 缩进 / return
真实下一个 token:a

这一章要问的是:有了这样的样本,训练到底怎样发生?

换句话说,模型看到前文以后,会给所有可能的下一个 token 一个概率。训练系统怎样判断这个概率好不好?又怎样根据这个判断去更新参数?

这就是预训练公式要回答的问题。

先不要急着看公式。我们先用一句自然语言说清楚:

预训练希望模型在每个位置上,都给真实出现的下一个 token 更高概率。

这句话就是本章的核心。

如果前文是:

法国 / 的 / 首都 / 是

真实下一个 token 是:

巴黎

那么模型给“巴黎”的概率越高,说明这一步预测越好;模型给“巴黎”的概率越低,说明预测越差。

如果模型把概率分配成:

巴黎:0.70
伦敦:0.08
柏林:0.05
其他:0.17

这就比下面这种分配好:

巴黎:0.02
伦敦:0.35
柏林:0.20
其他:0.43

因为训练文本里真实出现的是“巴黎”。预训练目标不是在这一刻判断“巴黎是不是事实真理”,而是在训练数据给定的这个位置上,让模型提高真实 token 的概率。

这点非常重要。

预训练公式不是直接问:

这个回答有没有帮助用户?

也不是直接问:

这个结论是否经过外部验证?

它问的是:

在这段前文之后,训练文本里真实出现的下一个 token,模型给了多大概率?

这就是为什么我们说预训练是 token 级别的预测任务。

当然,模型在所有位置上都要做这件事。一段文本不是只产生一个训练样本,而是产生很多训练位置。每个位置都有自己的前文和真实下一个 token。模型要在海量文本、海量位置上不断提高真实 token 的概率。

这时就需要一个统一的优化目标。

它要能回答:

  • 模型当前预测错得有多严重?
  • 哪些参数让真实 token 概率太低?
  • 应该怎样调整参数,让下次类似上下文里真实 token 概率更高?

这就是损失函数和梯度下降登场的地方。

但在进入梯度下降之前,我们先看第一个概念:最大似然。

最大似然:让真实文本更可能出现

“最大似然”这个词听起来有点数学,但直觉很简单。

我们已经有了一批真实训练文本。模型的任务,是给这些真实文本更高概率。也就是说,在模型看来,训练数据里实际出现的 token 序列应该是“更可能发生”的。

这就是最大似然的直觉:

调整模型参数,让真实训练数据在模型下出现的可能性尽可能大。

还是用一个简化例子。

假设训练文本是:

法国 / 的 / 首都 / 是 / 巴黎 / 。

模型要在多个位置上预测:

给定 法国,预测 的
给定 法国 / 的,预测 首都
给定 法国 / 的 / 首都,预测 是
给定 法国 / 的 / 首都 / 是,预测 巴黎
给定 法国 / 的 / 首都 / 是 / 巴黎,预测 。

如果模型在这些位置上给真实 token 的概率分别是:

P(的 | 法国) = 0.80
P(首都 | 法国 的) = 0.60
P(是 | 法国 的 首都) = 0.70
P(巴黎 | 法国 的 首都 是) = 0.50
P(。 | 法国 的 首都 是 巴黎) = 0.90

那么模型给整段训练文本的概率,可以粗略理解为这些概率的乘积:

0.80 × 0.60 × 0.70 × 0.50 × 0.90

如果我们能调整参数,让这些真实 token 的概率都升高,那么整段训练文本在模型下的概率也会升高。

这就是“让真实文本更可能出现”。

当然,真实训练不是只看一句话,也不是只看五个位置。真实训练会看海量文本中的海量 token 位置。最大似然目标就是希望模型参数 $\theta$ 让所有训练文本中真实 token 的联合概率尽可能大。

写成直觉形式,就是:

最大化:所有真实下一个 token 的概率乘积

这句话和上一章的文字接龙样本完全对应。

每一个位置都是:

前文 → 真实下一个 token

最大似然就是:

让每一个真实下一个 token 在模型看来都更可能

这时我们已经可以看出,预训练不是普通意义上的“背书”。它不是把整段文本存下来,而是通过参数调整,让模型在类似上下文下给合理 token 更高概率。

但这里还有一个计算上的问题。

如果一段文本有很多 token,我们要把很多概率相乘。概率都小于 1,乘得越多,结果越小。长文本的概率乘积会变成极小极小的数,不方便计算,也不方便优化。

所以我们通常不直接最大化概率乘积,而是取 log。

这就进入下一步:对数似然和负对数似然。

为什么要取 log,再加负号?

最大似然的直觉是让真实文本的概率尽可能大。

如果所有位置的真实 token 概率相乘,是:

P1 × P2 × P3 × ... × PT

那么最大化这个乘积,就是希望每个真实 token 的概率都高一些。

但直接处理乘积不方便。原因有两个。

第一,很多小概率相乘会变得非常小。

比如 1000 个小于 1 的数连续相乘,结果可能小到计算机表示起来都很麻烦。大模型训练面对的是非常长、非常多的 token 序列,直接乘概率不是好办法。

第二,乘法不如加法方便优化。

取对数以后,有一个非常有用的性质:

$$\log(a \times b) = \log a + \log b$$
所以,概率乘积取 log 后,就变成了 log 概率的求和:

$$\log(P_1 \times P_2 \times \cdots \times P_T) = \log P_1 + \log P_2 + \cdots + \log P_T$$
这就是对数似然。

最大化原来的概率乘积,等价于最大化 log 概率求和。因为 log 函数是单调递增的,哪个概率乘积更大,哪个 log 值也更大。

于是训练目标可以写成:

$$\max_\theta \sum_t \log P_\theta(x_t \mid x_{<t})$$
这句话的意思是:在所有位置上,把真实下一个 token 的 log 概率加起来,让这个总和尽可能大。

但机器学习里更常见的写法是“最小化损失”。

如果我们想最大化:

$$\sum_t \log P_\theta(x_t \mid x_{<t})$$
也可以等价地最小化它的相反数:

$$-\sum_t \log P_\theta(x_t \mid x_{<t})$$
这就是负对数似然。

为什么要加负号?

因为 log 概率通常是小于等于 0 的。概率最大是 1,log(1)=0;概率越小,log 值越负。比如:

$$\log(0.9) \approx -0.105$$
$$\log(0.1) \approx -2.303$$
$$\log(0.01) \approx -4.605$$
如果真实 token 的概率很高,比如 0.9,那么 -log(0.9) 很小。
如果真实 token 的概率很低,比如 0.01,那么 -log(0.01) 很大。

这正好符合“损失”的直觉:

模型给真实 token 的概率越低,损失越大;概率越高,损失越小。

于是,我们从最大似然走到了负对数似然:

最大化真实 token 概率
        ↓
最大化真实 token 的 log 概率
        ↓
最小化负 log 概率

这不是换了一个目标,而是同一个目标的不同写法。

读到这里,读者可以先记住一句话:

负对数似然就是把“让真实 token 更可能”写成一个可以最小化的损失函数。

接下来,我们再把它和交叉熵联系起来。

交叉熵:猜错真实 token 会被惩罚

很多介绍大模型训练的文章会说:预训练使用交叉熵损失。

这和刚才讲的负对数似然是什么关系?

在下一个 token 预测里,它们几乎是在描述同一件事。

我们先用一个具体位置来看。

前文是:

法国 / 的 / 首都 / 是

真实下一个 token 是:

巴黎

模型会对整个词表给出概率分布。简化一下:

巴黎:0.70
伦敦:0.10
柏林:0.08
罗马:0.05
其他:0.07

真实分布是什么?

在这个训练样本里,真实答案就是“巴黎”。所以我们可以把真实分布看成:

巴黎:1
伦敦:0
柏林:0
罗马:0
其他:0

这叫 one-hot 分布。意思是:真实 token 的概率为 1,其他 token 为 0。

交叉熵衡量的是:真实分布和模型预测分布之间有多不匹配。

在 one-hot 情况下,交叉熵会变成:

$$-\log P_\theta(\text{真实 token} \mid \text{前文})$$
也就是:

$$-\log P_\theta(\text{巴黎} \mid \text{法国 的 首都 是})$$
这就和负对数似然一致了。

如果模型给“巴黎”的概率是 0.70,损失比较小。
如果模型给“巴黎”的概率是 0.01,损失就很大。

我们可以直观看几个数:

P(真实 token) = 0.90 → 损失约 0.105
P(真实 token) = 0.50 → 损失约 0.693
P(真实 token) = 0.10 → 损失约 2.303
P(真实 token) = 0.01 → 损失约 4.605

真实 token 的概率越低,惩罚增长得越明显。

这很好地符合训练需求。我们希望模型不要只是“有一点点”给真实 token 概率,而是尽量把概率集中到真实 token 以及合理候选上。

当然,在一个具体位置上,训练数据只告诉模型真实出现了哪个 token。它没有告诉模型其他 token 在语义上有多接近。比如“巴黎”和“Paris”可能都和答案相关,但如果训练文本这个位置真实出现的是中文“巴黎”,那么这个位置的监督信号就是提高“巴黎”的概率。模型对其他表达的泛化能力,来自大量不同上下文、不同表达方式共同训练出来的结果。

所以交叉熵不是在直接评价语义质量,而是在每个训练位置上评价模型对真实 token 的概率分配。

这又回到了本书的主线:

预训练阶段,优化目标是 token 级别的。
它问的是:真实下一个 token 的概率高不高?
它不是直接问:整段回答有没有帮助?推理是否正确?用户是否满意?

这些问题,要到后面的 SFT、RLHF、DPO 和可验证强化学习阶段才逐渐进入训练目标。

预训练核心公式逐项拆解

现在我们可以正式写出预训练损失的简化形式:

$$\mathcal{L}_{\text{pretrain}} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
这个公式看起来不长,但里面装着前面几章的全部铺垫。

我们逐项拆开。

$x_t$ 是第 t 个位置上真实出现的 token。

比如训练文本是:

法国 / 的 / 首都 / 是 / 巴黎 / 。

t 对应“巴黎”这个位置时,$x_t$ 就是“巴黎”。

$x_{<t}$ 是第 t 个位置之前的所有 token,也就是前文。

在这个例子里,当 $x_t$ 是“巴黎”时,$x_{<t}$ 就是:

法国 / 的 / 首都 / 是

所以:

$$P_\theta(x_t \mid x_{<t})$$
表示:

参数为 $\theta$ 的模型,在看到前文 $x_{<t}$ 后,给真实下一个 token $x_t$ 的概率。

这里的 $\theta$ 是模型参数。它代表模型内部所有可训练数字。不同的参数,会产生不同的概率分布。训练的目的,就是找到一组参数,让真实 token 在大量训练位置上获得更高概率。

log 是对概率取对数。

前面讲过,取 log 可以把概率乘积变成求和,也让优化更方便。

前面的负号 - 表示我们把最大化 log 概率,转成最小化损失。

$\sum_t$ 表示对所有位置求和。

也就是说,模型不是只在一个位置上预测,而是在训练文本的许多位置上预测。每个位置都会产生一个损失,最后把这些损失加起来,形成整体训练目标。

所以,这个公式用自然语言翻译,就是:

对训练文本中的每个位置,看看模型给真实下一个 token 的概率有多高;概率越低,惩罚越大;把所有位置的惩罚加起来,得到预训练损失。

这就是预训练公式的本质。

它没有直接出现“知识”“理解”“推理”这些词。它只关心一个非常具体的问题:真实下一个 token 的概率。

但我们已经知道,海量文本里的真实下一个 token 背后,包含语言、事实、代码、格式、推理痕迹。模型为了降低这个损失,就会被训练成更擅长捕捉这些模式。

这也是为什么公式看起来简单,但结果并不简单。

公式本身问的是 token 概率。
数据里包含的是人类写下的复杂文本。
模型结构提供了利用上下文的能力。
梯度下降不断调整参数。

这些东西合在一起,才产生了预训练模型的基础能力。

但到这里,我们只讲了“损失怎么算”。还没有讲“参数怎么变”。

损失函数只是告诉我们模型错得有多远。真正训练模型,还需要知道:怎么根据这个损失调整参数?

这就要进入反向传播和梯度下降。

参数是怎样被训练出来的?

模型参数不是手工写进去的。

没有人逐个告诉模型:

这个参数负责巴黎。
这个参数负责 Python。
这个参数负责数学归纳法。

真实训练不是这样发生的。

训练过程更像是一个反复循环:

输入一批 token 序列
        ↓
模型根据当前参数预测下一个 token 概率
        ↓
根据真实 token 计算损失
        ↓
计算损失对参数的梯度
        ↓
沿着降低损失的方向更新参数
        ↓
继续下一批数据

这里最关键的是梯度。

梯度可以理解成:如果想让损失下降,每个参数应该往哪个方向调整,以及大概调整多少。

我们可以用一个非常简化的比喻。

假设你站在山坡上,目标是往低处走。你看不到整座山的完整地图,但你能感受到脚下坡度:往哪个方向走,地势下降最快。梯度就像这个坡度信息。梯度下降就是沿着让损失下降的方向,走一小步。

写成简化公式,就是:

$$\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}$$
逐项解释:

  • $\theta$:模型当前参数。
  • L:当前损失。
  • $\nabla_\theta \mathcal{L}$:损失函数对参数的梯度。
  • $\eta$:学习率,控制每次更新迈多大步。
  • $\theta \leftarrow \cdots$:用新参数替换旧参数。

这个公式的直觉是:

看看损失往哪个方向变大,然后朝相反方向调整参数,让损失变小。

为什么是减号?

因为梯度指向损失上升最快的方向。如果我们想让损失下降,就要往反方向走。所以是:

$$\theta - \eta \nabla_\theta \mathcal{L}$$
学习率 $\eta$ 很重要。

如果步子太小,训练会非常慢。
如果步子太大,可能越过低点,甚至让训练不稳定。

真实大模型训练使用的优化器通常比最朴素的梯度下降复杂,比如 Adam 或 AdamW 之类的方法。但它们的核心思想仍然是:根据梯度调整参数,让损失下降。

反向传播则是计算梯度的方法。

模型从输入到输出,会经过很多层计算。损失出现在输出端,但参数分布在模型各层。反向传播会从损失出发,沿着计算图反向传播影响,计算每个参数对损失的贡献。哪些参数调整会让真实 token 概率升高?哪些参数调整会让损失下降?这些信息通过梯度传回来。

我们不需要在这里展开反向传播的数学细节。只要抓住它在训练流程中的角色:

反向传播负责算出每个参数该怎么改,梯度下降负责按这个方向更新参数。

这就回答了一个非常关键的问题:

大模型的参数是怎么“学到知识”的?

不是把知识句子直接写进去。
不是把网页压缩成数据库。
不是人工标注每个参数含义。

而是:

大量文本样本
        ↓
预测下一个 token
        ↓
计算交叉熵损失
        ↓
反向传播计算梯度
        ↓
优化器更新参数
        ↓
模型概率分布发生微小改变

这个过程重复极其多次,参数才逐渐形成有用结构。

单次更新很小。它可能只是让某些上下文下某些 token 的概率稍微变高或变低。但当训练样本足够多、更新次数足够多、模型容量足够大,这些微小调整会累积成模型的语言能力、代码能力、事实关联和推理痕迹。

这也是为什么我们不能把参数理解成知识条目。

参数更像是一个巨大概率机器的内部结构。训练不断雕刻这个结构,让它在不同上下文下输出更合理的概率分布。

第 4 章讲的数据,是训练材料。
本章讲的损失函数,是训练目标。
梯度下降,是参数更新机制。

三者合起来,才是预训练。

为什么不是最小二乘法?

这里需要校准一个常见说法。

很多人第一次理解机器学习时,会从最小二乘法开始。最小二乘法很经典,也很直观:模型预测一个数,真实答案也是一个数,两者相减,平方,再最小化误差。

比如预测房价:

真实房价:100 万
模型预测:90 万
误差:10 万
平方误差:100

这种场景是连续数值回归。最小二乘很合适。

但大语言模型预训练不是这种问题。

它不是预测一个连续数值,而是在一个很大的词表里,预测下一个 token 的概率分布。真实目标不是“某个数值”,而是“这个位置真实出现的是哪个 token”。

这更像分类问题。

词表里可能有十万个 token。给定前文,模型要给这十万个 token 分配概率。训练数据告诉它:这个位置真实出现的是其中某一个 token。我们希望真实 token 的概率高,其他 token 的概率相对低。

在这种情况下,交叉熵比最小二乘更自然。

因为交叉熵直接惩罚:

模型给真实 token 的概率太低。

如果模型给真实 token 概率 0.9,损失很小。
如果给真实 token 概率 0.01,损失很大。

这正好符合下一个 token 预测的目标。

所以,如果有人说“大模型训练就是回归”,我们要小心。

从非常宽泛的角度说,训练确实是在优化参数,让模型输出接近目标。这种“用损失函数调整参数”的直觉是对的。但严格说,预训练通常不是最小二乘回归,而是基于最大似然 / 交叉熵的下一个 token 预测。

这个校准很重要。

因为本书后面会反复讲“优化目标变化”。如果一开始把预训练目标说成最小二乘,后面讲 DPO、强化学习、奖励函数时就会混乱。我们需要从一开始就把术语放稳:

预训练优化的是真实下一个 token 的概率,常用交叉熵 / 最大似然目标。

不是最小二乘法。

但你最初抓到的直觉是对的:模型能力来自参数更新,参数更新来自优化目标。只不过,不同阶段的优化目标不同。预训练是交叉熵,SFT 仍然接近交叉熵,DPO 是偏好优化,强化学习是奖励最大化。

这正是全书的主线。

损失下降不等于模型真的理解世界

这一章讲了公式,很容易让人产生一种确定感:既然有损失函数,有梯度下降,有参数更新,那模型是不是就在一步步逼近真实世界?

要小心。

预训练损失下降,首先意味着模型更会拟合训练数据分布。

也就是说,在训练数据类似的上下文中,它更能给真实下一个 token 高概率。这当然很重要,也确实会带来强大的语言能力。但这不等于模型直接获得了世界真相。

原因前面已经埋下了。

第一,训练目标是预测文本,不是验证事实。

如果训练文本里有错误,模型仍然可能学到错误模式。交叉熵不会自动知道某句话是否真实。它只知道训练样本中这个位置出现了哪个 token。

第二,训练数据是世界的文本投影,不是世界本身。

文本里有事实,也有偏见、遗漏、噪声、过时信息。模型拟合文本分布,就会继承文本分布的优点和问题。

第三,预训练优化的是局部 token 概率。

虽然长文本训练会让模型学到很多长程模式,但公式本身仍然是在每个位置上提高真实下一个 token 的概率。它并不直接奖励“回答对用户有帮助”,也不直接奖励“推理过程严谨”,更不直接奖励“代码通过测试”。

这就是为什么预训练之后还需要后训练。

预训练能让模型会续写、会模仿、会生成许多看似合理的文本。
但要让模型更像助手,需要 SFT。
要让模型更符合人类偏好,需要 RLHF 或 DPO。
要让模型在代码、数学等任务中追求可验证正确性,需要奖励函数和验证器。

所以,本章公式不是全书终点,而是起点。

它告诉我们基础模型怎样被训练出来,也告诉我们基础模型的边界在哪里。

如果我们理解了这一点,后面的章节就会顺得多。

SFT 为什么仍然像交叉熵?因为它还是在模仿给定答案。
DPO 为什么是目标变化?因为它不再只是模仿一个答案,而是比较 chosen 和 rejected。
强化学习为什么更进一步?因为它优化完整回答的奖励,而不是单个真实 token 的概率。

预训练公式给了我们第一把尺子。后面的训练阶段,就是在换尺子。

本章小结

这一章我们把预训练的优化目标正式写成了公式。

从第 4 章的文字接龙样本出发,预训练要做的事情是:给定前文 token,让模型提高真实下一个 token 的概率。最大似然从直觉上说,就是让真实训练文本在模型看来更可能出现。为了计算方便,我们取 log,把概率乘积变成求和;再加负号,把最大化 log 概率变成最小化损失。对于 one-hot 的真实 token 目标,这个负对数似然也就是常说的交叉熵损失。

核心公式是:

$$\mathcal{L}_{\text{pretrain}} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
它优化的是 token 级预测,而不是完整回答质量。

模型参数通过反向传播和梯度下降更新:

$$\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}$$
参数不是被直接写入知识,而是在大量样本上反复计算损失、计算梯度、更新参数后,逐渐形成能够输出合理概率分布的结构。

最后,我们也校准了一个重要概念:预训练通常不是最小二乘法,而是最大似然 / 交叉熵目标。最小二乘适合许多连续数值回归问题,而下一个 token 预测更像词表上的概率分类问题。

下一章预告

现在我们已经知道基础模型怎样通过预训练学会续写文本。

但会续写,还不等于会回答。预训练模型学到的是文本分布,它可能续写网页、论坛、小说、代码,也可能生成看似答案的内容。可是用户真正想要的是清楚、有帮助、符合指令的助手回答。

下一章,我们进入监督微调 SFT:把基础模型教成助手。

延伸阅读

推荐阅读方向:

  • 最大似然估计和负对数似然的基础教材。
  • 交叉熵损失在分类和语言模型中的解释。
  • 反向传播、梯度下降、Adam 优化器的入门资料。

第 6 章:SFT:把基础模型教成助手

本章问题

为什么经过预训练的模型还需要监督微调?它已经会生成文本了,为什么还不一定会好好回答问题?

章节摘要

这一章解释从基础模型到助手模型的第一步:监督微调,也就是 SFT。预训练模型学到的是文本分布,它可能会续写网页、模仿论坛、补全文档,但不一定知道面对用户问题时应该给出清楚、有帮助、符合指令的回答。SFT 使用高质量的指令-回答样本,让模型模仿助手式输出。这里要强调一个容易误解的点:SFT 的训练形式仍然接近交叉熵监督学习,只是数据从普通文本变成了 prompt 和高质量 answer。

读者收获

读者应理解:预训练提供能力基础,SFT 塑造回答方式;SFT 不是让模型从零学知识,而是让它学会按照人类期待的问答格式输出。

配图

SFT 把基础模型教成助手

图 6-1|SFT 把基础模型教成助手:SFT 用高质量指令回答样本塑造模型的回答方式。

读图:从基础模型到助手模型

这张图展示 SFT 如何把基础模型推向助手形态:训练样本不再只是普通文本,而是用户指令和高质量助手回答。

读图时要注意,SFT 改变的是模型模仿的对象。它仍然在逐 token 提高目标答案的概率,但目标文本已经从互联网续写变成了更符合人类期待的助手回答。

正文

会续写,为什么还不等于会回答?

前 5 章,我们讲完了预训练的基本逻辑。

互联网文本被整理成 token 序列,模型在这些序列上学习预测下一个 token。它的优化目标是让真实下一个 token 的概率更高。通过最大似然、交叉熵和梯度下降,模型参数被一步步调整,最终形成一个很强的基础模型。

这个基础模型已经很厉害。

它能续写文章,补全代码,模仿不同文体,回答一些事实问题,甚至生成看起来有推理步骤的文本。可是,如果我们把一个只经过预训练的模型直接交给用户,它未必是一个好助手。

为什么?

因为“会续写”和“会回答”不是一回事。

预训练模型学到的是文本分布。它看到一个开头,会继续生成像训练数据中类似文本的后续。可是用户使用大模型时,通常不是想让它随便续写一段互联网文本,而是想让它完成一个任务。

用户可能问:

请用通俗的话解释什么是交叉熵。

一个好助手应该直接解释概念,根据读者水平调整语言,必要时给例子,还要避免不必要的公式堆砌。

但一个只经过预训练的基础模型,可能把这句话当成某种文本片段的开头。它可能继续写一个教材段落,也可能模仿论坛回答,也可能生成另一个问题,也可能写出风格对但不够聚焦的内容。它具备生成相关文本的能力,却不一定稳定地遵循“用户正在向我提问,我应该给出有帮助的回答”这个交互模式。

这就是能力和行为的区别。

预训练主要提供能力基础。
SFT 开始塑造模型行为。

能力基础包括语言、知识、代码、格式、推理痕迹。行为方式则包括:是否听指令,是否直接回答问题,是否组织清楚,是否避免跑题,是否承认不确定,是否保持合适语气。

一个模型可能有能力解释梯度下降,但如果它总是以网页续写方式输出,就不好用。它可能知道很多代码模式,但如果用户问“帮我修这个 bug”,它却继续补全用户的问题,那也不是助手。

所以,预训练之后还需要监督微调,也就是 SFT。

SFT 的任务,可以用一句话概括:

用高质量的指令-回答样本,把基础模型训练成更像助手的模型。

这一步不是从零开始教模型知识。模型在预训练中已经学到大量模式。SFT 更像是告诉模型:当你看到用户这样提问时,应该以这种方式回答。

这一步看起来没有预训练那么宏大,但非常关键。没有它,模型可能有很多潜在能力,却不会稳定地以用户期待的方式释放出来。

什么是基础模型,什么是助手模型?

我们需要先区分两个词:基础模型和助手模型。

基础模型,也常被称为 base model。它主要经过预训练,学到的是大规模文本分布。它擅长续写,也可以完成一些任务,但它的默认行为更像“继续写下去”。

助手模型,则是在基础模型之上经过后训练,让它更适合对话和任务完成。它看到用户问题时,更倾向于给出直接、有帮助、结构清楚的回答。

这两个模型可能拥有相同或相近的底层能力,但表现出来的行为很不一样。

举个例子。

用户输入:

解释一下什么是梯度下降。

基础模型可能续写成:

解释一下什么是梯度下降。这个问题在机器学习中非常常见,下面是一些网友的回答……

它像是在补全一段网页或论坛内容。

助手模型更可能回答:

梯度下降是一种优化方法。你可以把它想象成在山坡上往低处走:损失函数越低,模型预测越好。每次训练时,模型根据梯度判断参数该往哪个方向调整,然后迈一小步。

这就是行为差异。

基础模型不一定“不会”解释梯度下降。它只是没有被充分训练成稳定遵循指令的交互形态。助手模型则被训练成更像一个回答者。

这也解释了为什么我们不能只看预训练能力。

一个大模型是否好用,不只取决于它在预训练中学到多少文本模式,还取决于它后面怎样被对齐、怎样被微调、怎样被偏好优化、怎样被约束。很多用户感受到的“这个模型好不好用”,其实很大一部分来自后训练。

但也要避免另一个误解:SFT 不是魔法。

SFT 不是把一个空白模型训练成聪明助手。它通常是在预训练模型的基础上进行的。没有预训练打底,SFT 的少量高质量问答样本不可能凭空提供全部语言和知识能力。

所以更准确的关系是:

预训练:提供基础能力
SFT:塑造助手行为

基础模型像是一个读过大量文本、会续写很多东西的模型。SFT 则把它推向一种更可用的交互方式:用户问问题,模型给答案;用户给任务,模型按要求完成;用户要求解释,模型组织清楚地解释。

这种转变,就是从 base model 到 assistant model。

SFT 数据长什么样?

SFT 的核心材料,是高质量的指令-回答样本。

形式通常很简单:

prompt:请解释什么是 token。
answer:token 是模型处理文本的基本单位。它不一定等同于一个词,可能是一个字、一个词的一部分、一个完整单词,也可能是标点或特殊符号。

再比如:

prompt:把下面这段话改写得更简洁。
answer:……

或者:

prompt:写一个 Python 函数,判断一个数是否为偶数。
answer:
def is_even(n):
    return n % 2 == 0

还可以是:

prompt:这段代码为什么报错?
answer:错误原因是变量在定义前被使用。你可以先初始化变量,再调用函数。

这些样本和普通互联网文本不一样。

普通预训练文本可能是一篇文章、一段论坛讨论、一页文档、一段代码。它不一定有明确的“用户请求”和“助手回答”结构。

SFT 数据则有意构造成任务交互形式。它告诉模型:当前面出现一个用户指令时,后面应该出现怎样的回答。

这种数据可以覆盖很多任务:

  • 概念解释
  • 摘要总结
  • 翻译
  • 改写
  • 代码生成
  • 错误分析
  • 数学解题
  • 信息抽取
  • 计划制定
  • 多轮对话

关键不是任务种类本身,而是数据质量。

高质量回答应该清楚、准确、符合指令、结构合理。它不应该答非所问,不应该空泛绕圈,不应该在不知道时编造,也不应该用不适合读者的方式表达。

当然,现实中的 SFT 数据来源也有很多种。有些来自人工撰写,有些来自专家标注,有些来自已有数据集,有些可能由模型生成后再筛选。不同来源的数据质量差异很大。

本书暂时不展开数据生产流程,只抓住一个核心:

SFT 用 prompt-answer 样本,把模型训练成更像一个助手。

这里的训练形式,和预训练仍然很像。

模型看到 prompt 和已经生成的 answer 前文,预测 answer 中的下一个 token。也就是说,它仍然是在模仿目标文本。只不过,目标文本从普通互联网续写变成了高质量助手回答。

第 7 章我们会专门拆 SFT 公式。现在先记住结论:

SFT 改变了训练数据的形态,但它仍然主要是模仿学习。

SFT 到底在教模型什么?

把 SFT 说成“把基础模型教成助手”,容易让人以为 SFT 教给模型的是一套显式规则。比如:

规则 1:用户问问题时要回答。
规则 2:回答要分点。
规则 3:不知道时要承认不知道。
规则 4:不要胡编。

这种说法有一点帮助,但并不准确。真实的 SFT 不是把规则一条条写进模型参数里,而是通过大量示范样本,让模型在参数空间里形成一种倾向:遇到类似输入时,更可能生成类似高质量助手回答的输出。

这句话很重要。

SFT 教的不是某一条孤立规则,而是一种回答分布。

在预训练阶段,模型面对的是大规模文本世界。这个世界很复杂:有新闻,有论文,有小说,有论坛争论,有代码,有广告,有错误答案,也有高质量教程。模型从中学到的是“文本一般怎样继续”。到了 SFT 阶段,训练数据被刻意筛选成“用户指令 + 理想回答”的形式。模型看到的世界变窄了,也更明确了:它不再只是学习互联网文本怎样继续,而是在学习一个好助手怎样回应用户。

因此,SFT 至少在教下面几类行为。

第一,按问题回答,而不是继续编写上下文。

用户问“什么是过拟合”,助手应该解释过拟合,而不是把这句话续写成一篇网页标题、论坛帖子或者考试题。这个差别看似简单,却是从基础模型到助手模型的关键一步。

第二,遵守指令中的约束。

用户可能要求“用三句话解释”“用小学生能懂的话解释”“给出 Python 代码”“不要使用公式”“请先给结论再解释原因”。这些要求都不是单纯的知识问题,而是格式、语气、长度和任务边界问题。SFT 数据中如果有大量这种样本,模型就会学到:回答不只是内容正确,还要满足用户指定的形式。

第三,组织结构。

好的助手回答往往不是一整团文字。它会先给结论,再解释原因;会把复杂问题拆成几步;会在必要时列出要点;会在代码前说明思路;会在总结时指出下一步。SFT 让模型模仿这种组织方式。

第四,语气和角色。

基础模型可能模仿任何语气。它可以像新闻报道,也可以像论坛争吵,也可以像学术论文。助手模型需要更稳定:清楚、克制、耐心、直接。这里的“语气”不是装饰,它影响用户能不能理解答案,也影响模型是否容易跑偏。

第五,承认不确定性。

一个好助手不应该在证据不足时硬编。SFT 数据可以示范这样的回答方式:

这个问题我不能仅凭题面确定,因为还缺少……
如果你的意思是 A,那么答案是……
如果你的意思是 B,那么需要另外分析。

这不是让模型变得“谦虚”这么简单,而是在塑造一种回答习惯:当输入不足以支持确定结论时,不要把概率低的猜测包装成事实。

第六,边界感。

有些请求不适合直接满足,有些问题需要提示风险,有些任务需要用户补充条件。SFT 可以让模型学会在这些场景下给出更合适的回应。当然,真正严格的安全边界通常还会依赖后续的偏好优化、安全训练和系统层约束,不能只靠 SFT。

把这些点合在一起,我们可以看到 SFT 的真实作用:

SFT 不是单纯增加知识,
而是把已有能力组织成一种更适合人机对话的输出方式。

这也解释了为什么 SFT 数据量通常远小于预训练数据量,却能显著改变用户体验。因为它不需要重新教模型整个语言世界,而是在已有语言能力之上,调整模型在特定交互场景中的输出偏好。

不过,这里必须保持清醒。

SFT 仍然是模仿。它让模型模仿高质量答案,但模仿不等于理解所有事实,也不等于在所有新问题上都能判断真伪。一个模型可以学会“像专家一样说话”,但如果底层知识不足、推理过程薄弱、检索不到最新信息,或者训练样本本身有错误,它仍然可能给出错误答案。

这就是为什么我们后面还要讲偏好数据、奖励模型、DPO、可验证任务和自生成样本。SFT 是后训练的第一步,但不是最后一步。

SFT 的局限:会说得像,不等于一定说得对

SFT 能让模型更像助手,但“像助手”不是最终目标。最终目标是有帮助、可靠、符合用户意图。这里有一段距离。

最容易出现的误解是:既然 SFT 使用高质量答案训练模型,那么模型是不是就会稳定输出高质量答案?

答案是否定的。

原因之一是覆盖不足。

训练样本再多,也不可能覆盖所有问题。现实用户的问题千奇百怪,可能跨学科,可能有模糊前提,可能包含最新事件,可能要求模型组合多个技能。SFT 只能让模型从见过的示范中泛化到没见过的问题,但泛化不是保证。

原因之二是样本质量不可能完美。

如果 SFT 数据中存在错误、偏见、表达不清、过度自信、格式僵化,模型也可能学到这些问题。高质量数据很贵,尤其是数学、法律、医学、代码、科研等领域。数据标注者需要专业知识,也需要统一标准。只要示范答案本身有问题,模仿学习就可能把问题传给模型。

原因之三是 SFT 优化的是“模仿目标答案”,不是直接优化“用户最终满意”。

这点非常关键。

SFT 的训练信号通常是一条标准回答。模型被鼓励生成这条回答中的 token。可是用户真正关心的不是“模型是否复现了某个标准答案的字面形式”,而是“这个回答是否解决了我的问题”。两者有关,但不是同一个目标。

比如,同一个问题可能有多个好答案。一个答案可以先讲直觉,一个答案可以先给公式,一个答案可以用代码演示,一个答案可以用生活类比。SFT 通常只给模型一个或少数几个示范,模型会学习这些示范,却不一定知道在多个可行回答之间哪一个更符合用户偏好。

原因之四是 SFT 容易让模型学到“好答案的外观”。

有些回答看起来结构清楚、语气自信、术语很多,但事实是错的。人类读者有时也会被这种外观迷惑。模型经过 SFT 后,更会使用助手式语言,这提高了可读性,也带来一个风险:错误答案可能看起来更像正确答案。

这就是所谓“会说得像,不等于一定说得对”。

我们可以用一个小例子说明。

用户问:

某个冷门开源库的最新版本是否支持某个参数?

一个经过 SFT 的模型可能会给出非常像样的回答:先说明结论,再解释参数用途,再给代码示例。但如果它没有最新信息,也没有检索工具,或者训练数据里没有相关内容,它仍然可能猜错。SFT 可以让它回答得更有条理,却不能凭空保证事实更新。

这不是说 SFT 不重要。相反,SFT 是让模型从“会续写”变成“能对话”的关键步骤。只是我们要准确理解它的边界:

SFT 主要解决“怎样回答”的问题,
不能单独彻底解决“回答是否最好、是否最真、是否最符合偏好”的问题。

后面几章要处理的,正是这个边界。

当我们有多个候选回答时,哪个更好?当人类更喜欢 A 而不是 B 时,怎样把这种偏好变成训练信号?当数学题、代码题可以自动验证对错时,模型能不能自己生成答案、自己筛选、再用更强的目标继续训练?当我们希望模型不仅给最终答案,还给出更可靠的推理过程时,奖励函数该怎样设计?

这些问题已经超出了普通 SFT。

所以,本章不是 SFT 的终点,而是后训练故事的起点。它把基础模型推向助手模型,但还没有真正解决“回答质量如何被比较、奖励和优化”的问题。

本章小结

本章讲清楚了 SFT 的位置:它是从基础模型到助手模型的第一步。

预训练让模型学会从海量文本中预测下一个 token,形成语言、知识和模式能力。但只经过预训练的模型,默认更像一个文本续写器,不一定稳定地按用户意图回答问题。SFT 使用高质量的 prompt-answer 样本,让模型模仿助手式回答,从而学会更直接、更清楚、更符合指令地回应用户。

不过,SFT 的本质仍然是模仿学习。它改善的是回答方式、结构、语气和指令遵守能力,但不能单独保证事实永远正确,也不能解决多个回答之间哪个更符合人类偏好的问题。它让模型“更会回答”,但还没有完全解决“什么回答更好”。

下一章预告

下一章我们进入 SFT 的公式层面。重点不是堆公式,而是看清楚一件事:SFT 虽然换成了指令回答数据,但训练目标仍然很接近预训练,仍然是在目标回答上做最大似然估计,也就是让模型更倾向于生成示范答案中的下一个 token。理解这一点,后面再理解 RLHF 和 DPO 的“优化目标变化”就会轻松很多。

延伸阅读

推荐阅读方向:

  • Instruction tuning 和 supervised fine-tuning 相关论文。
  • InstructGPT 技术路线中关于监督微调的部分。
  • 对话格式、prompt-answer 数据构造和高质量标注规范资料。

第 7 章:SFT 公式:为什么它仍然是模仿学习

本章问题

SFT 看起来已经是在训练助手了,为什么说它的优化目标仍然接近预训练?

章节摘要

这一章拆解 SFT 的训练公式。给定 prompt 和一段高质量 answer,模型仍然是在逐个 token 地提高标准回答的概率。换句话说,SFT 并没有直接比较多个回答哪个更好,而是告诉模型:“在这个问题后,请更像这段答案。”这和预训练很像,区别在于上下文从普通文本变成了用户指令,目标文本从互联网续写变成了助手回答。本章要让读者看懂:SFT 是非常重要的一步,但它本质上仍然是模仿学习,还没有真正进入“偏好选择”。

读者收获

读者应理解:SFT 虽然使用指令回答数据,但训练目标仍然接近最大似然模仿学习;它还没有真正比较两个回答哪个更好。

配图

SFT 公式与预训练公式的相似性

图 7-1|SFT 公式与预训练公式:二者都在逐 token 提高目标文本概率,只是数据形态变了。

读图:预训练和 SFT 的公式相似性

这张图把预训练公式和 SFT 公式放在一起看。两者形式相似,都是让模型提高目标 token 的概率;真正变化的是数据形态和上下文结构。

读图时要抓住这个差别:预训练模仿普通文本中的下一个 token,SFT 模仿高质量回答中的下一个 token。所以 SFT 很重要,但它本质上仍然是模仿学习。

正文

先把上一章的问题推进一步

上一章我们讲了 SFT 的作用:把基础模型训练得更像一个助手。

它让模型更愿意按指令回答,更会组织结构,更会使用合适语气,也更容易在用户问题后给出一个完整回答,而不是随意续写成网页、论坛、小说或代码片段。

从用户体验看,这一步变化很明显。一个只经过预训练的基础模型,和一个经过 SFT 的助手模型,使用起来常常像两个不同的产品。

但从训练公式看,事情没有那么神秘。

SFT 并没有突然把模型训练成一个会主动判断“什么回答最好”的系统。它做的核心事情,仍然很接近我们在预训练里看到的那套逻辑:给模型一段上下文,让它预测目标文本中的下一个 token;如果预测得不像示范答案,就通过损失函数和梯度下降调整参数。

只是这一次,上下文不再是普通互联网文本,而是用户的 prompt;目标文本不再是网页里真实出现的后续内容,而是人工整理、专家编写或筛选出来的高质量助手回答。

所以,本章要抓住一个关键判断:

SFT 的行为效果很像“教模型当助手”,
但它的优化目标仍然主要是“模仿示范回答”。

这个判断非常重要。它一方面让我们看见 SFT 为什么有效,另一方面也让我们看见 SFT 为什么不够。

如果模型只是模仿一个标准答案,它还没有真正学会比较多个答案。它还没有被直接问:“同一个问题下,回答 A 和回答 B,哪个更好?”这正是后面偏好优化、RLHF 和 DPO 要处理的问题。

预训练公式回顾:下一个 token 像不像文本

为了看懂 SFT,我们先把第 5 章的预训练公式拿回来。

预训练目标可以简化写成:

$$\mathcal{L}_{\text{pretrain}} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
这个公式的意思是:给定前面的 token,模型要提高真实下一个 token 的概率。

逐个看符号:

  • $x_{<t}$:第 t 个位置之前的所有 token,也就是模型看到的前文。
  • $x_t$:训练文本中真实出现的第 t 个 token。
  • $P_\theta(x_t \mid x_{<t})$:参数为 $\theta$ 的模型,在看到前文后,给真实下一个 token 的概率。
  • log:对概率取对数,让连乘概率变成更容易优化的相加形式。
  • $-\sum_t$:把所有位置的负对数概率加起来,得到总损失。

这个公式问的问题非常朴素:

在这段真实文本的前文之后,
模型有没有把真实下一个 token 的概率给高?

如果模型给真实 token 的概率很高,$\log P_\theta(x_t \mid x_{<t})$ 就比较接近 0,前面加负号以后损失较小。

如果模型给真实 token 的概率很低,$\log P_\theta(x_t \mid x_{<t})$ 就是一个绝对值较大的负数,前面加负号以后损失较大。

损失越大,说明模型当前参数越不适合这个样本。训练系统会通过反向传播计算梯度,再用梯度下降或 Adam 这类优化器更新参数:

$$\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}$$
这里 $\eta$ 是学习率,$\nabla_\theta \mathcal{L}$ 是损失对参数的梯度。直觉上,它告诉模型参数应该往哪个方向动,才能让下次类似上下文中真实 token 的概率更高。

这一套机制不是在直接灌输知识,而是在不断改变模型的概率分布。模型在海量样本上反复经历这个过程,最终形成对语言、事实、风格、代码和推理痕迹的统计掌握。

但预训练数据里没有稳定的“用户问题 -> 助手回答”结构。它只是普通文本。文本中当然可能有问答,也可能有教程,也可能有代码注释,但它们不是被统一整理成助手对话的形式。

这就是 SFT 要接上的地方。

SFT 公式:下一个 token 像不像助手答案

SFT 的核心公式可以写成:

$$\mathcal{L}_{\text{sft}} = -\sum_t \log P_\theta(y_t \mid \text{prompt}, y_{<t})$$
乍一看,它和预训练公式几乎是同一个形状。

预训练是:

$$\mathcal{L}_{\text{pretrain}} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
SFT 是:

$$\mathcal{L}_{\text{sft}} = -\sum_t \log P_\theta(y_t \mid \text{prompt}, y_{<t})$$
相同点是:二者都在逐 token 提高目标文本的概率。

不同点是:SFT 多了一个明确的 prompt,而目标 token $y_t$ 来自一段高质量助手回答。

现在逐项解释 SFT 公式。

prompt 是用户问题或指令。比如:

请用通俗语言解释什么是交叉熵。

y 是这条 prompt 对应的高质量回答。它可以是一段解释、一段代码、一个总结、一份计划,也可以是多轮对话中的助手回复。

$y_t$ 是高质量回答中的第 t 个 token。

$y_{<t}$ 是回答中第 t 个 token 之前的所有 token。

$P_\theta(y_t \mid \text{prompt}, y_{<t})$ 表示:模型在看到用户 prompt,以及已经生成的回答前文以后,给标准回答中下一个 token 的概率。

也就是说,在训练时,模型不是凭空生成完整答案再让人打分,而是在示范回答的每一个位置上,被要求预测“这里正确的下一个 token 是什么”。

实际工程里,SFT 通常会在一个 batch 的许多样本上求平均,也常常只对 assistant answer 部分计算损失,而不把用户 prompt 本身当作要模仿的目标。这里把公式写成求和,是为了让读者看清最核心的动作:在给定 prompt 和回答前文时,提高示范回答下一个 token 的概率。

举一个极简例子。

prompt 是:

解释什么是 token。

示范回答是:

token 是模型处理文本的基本单位。

训练时,模型会经历类似这样的预测:

看到 prompt,预测回答第一个 token:token
看到 prompt + "token",预测下一个 token:是
看到 prompt + "token 是",预测下一个 token:模型
看到 prompt + "token 是模型",预测下一个 token:处理

真实训练当然不会按汉字这么简单切分,实际 token 可能是字、词、子词或符号片段。但直觉是一样的:模型在示范回答的路径上,一步步学习下一个 token。

这也解释了为什么说 SFT 仍然是模仿学习。

模型不是在自己探索许多回答以后再被奖励。它是在一条已经给定的示范回答上学习:这个问题后面,应该更像这样回答。

这个公式到底怎样训练模型?

现在我们把公式里的训练动作说得更具体一点。

对于一条 SFT 样本:

prompt: 请解释梯度下降。
answer: 梯度下降是一种优化方法……

模型会根据当前参数 $\theta$,计算示范回答中每个 token 的概率。

如果在某个位置,示范回答的真实 token 是“优化”,而模型给“优化”的概率很高,那么这个位置的损失就小。

如果模型更倾向于输出别的 token,比如“分类”“模型”“算法”,而给“优化”的概率很低,那么这个位置的损失就大。

所有位置的损失累加起来,就是这条样本对模型的训练压力。训练系统再对大量样本求和或取平均,得到一个 batch 的损失,然后更新参数。

用自然语言说:

SFT 会惩罚那些不像示范回答的 token 预测,
奖励方向则是让模型以后更容易走上示范回答那条路径。

这里的“奖励方向”不是强化学习里的 reward,而是梯度下降意义上的方向:损失函数告诉参数怎样调整,才能让示范答案概率更高。

如果一批 SFT 数据都具有类似风格,例如:

  • 先给结论
  • 再分点解释
  • 遇到不确定信息时说明限制
  • 对代码问题给出可运行片段
  • 对概念问题使用通俗类比

那么模型参数就会被反复推向这些模式。久而久之,它在遇到新 prompt 时,也更可能生成类似结构和语气的回答。

这就是 SFT 塑造行为的方式。

它不是在模型内部放入一份“助手守则”,而是通过概率分布的改变,让某些回答方式变得更容易出现,让另一些回答方式变得不容易出现。

从这个角度看,SFT 非常强大,也非常朴素。

强大在于:只要基础模型已经具备足够语言能力,少量高质量示范就可能明显改变交互体验。

朴素在于:它仍然没有脱离监督学习的基本框架。模型看见输入,模仿标签;模仿得不像,就产生损失;损失通过梯度更新参数。

SFT 为什么仍然是模仿学习?

现在我们可以回答本章标题里的问题:为什么说 SFT 仍然是模仿学习?

因为它的训练信号是一段给定答案,而不是一个比较关系,也不是一个外部奖励。

假设同一个 prompt 是:

请解释什么是交叉熵。

我们可能有三种回答。

回答 A:非常通俗,适合完全没有数学基础的读者。

回答 B:包含公式,适合有机器学习基础的读者。

回答 C:很短,只给一句定义。

这三种回答未必有绝对好坏。哪一个更好,取决于用户是谁、场景是什么、问题要求是什么。

如果 SFT 数据只给了回答 B,那么模型会学习更像回答 B。它不会直接知道:在面对入门读者时,回答 A 可能更好;在面对考试复习时,回答 C 可能更高效;在面对技术读者时,回答 B 更合适。

SFT 的公式只问:

示范答案里的下一个 token,概率够不够高?

它没有问:

这三个回答里,哪一个更受人类偏好?

也没有问:

这个完整回答最终能不能解决任务?

更没有问:

这段推理过程是否可靠?

所以,SFT 的核心仍然是“像不像示范答案”。如果示范答案好,模型就向好的行为靠近;如果示范答案有问题,模型也可能模仿问题。

这也是为什么 SFT 数据质量如此重要。

它不仅影响模型知道什么,还影响模型怎样说话、怎样组织答案、怎样处理不确定性、怎样对待边界问题。一个过度啰嗦的数据集,可能训练出啰嗦的模型;一个过度自信的数据集,可能训练出不爱承认不确定的模型;一个格式单一的数据集,可能让模型回答变得僵硬。

我们可以把 SFT 的边界总结成一句话:

SFT 能把模型推向“像好答案”,
但还没有直接训练模型判断“哪个答案更好”。

这个边界,正是第 8 章和第 9 章要打开的门。

SFT 的局限:标准答案不等于偏好答案

到这里,SFT 的价值和局限就都清楚了。

价值在于,它用高质量示范,把模型从基础文本续写推向助手式回答。

局限在于,它通常仍然围绕标准答案做模仿。

现实对话里,一个问题常常不止一个合理答案。即使答案事实都正确,表达方式、详细程度、语气、结构、风险提示也可能不同。人类偏好有时就在这些差异里。

比如用户问:

我想了解大模型预训练,用三分钟讲清楚。

回答 A 可能非常完整,但需要十分钟才能读完。

回答 B 可能抓住主线,三分钟能读完,但略过了细节。

回答 C 可能公式很多,对专家友好,对普通读者却太重。

如果用户明确说“三分钟讲清楚”,回答 B 可能更好。可是普通 SFT 公式不会天然比较 A、B、C。它只会沿着训练集中给定的那条 answer 学习。

这就是从 SFT 走向偏好优化的必要性。

我们需要一种新的训练信号,不只是告诉模型“这个答案长什么样”,还要告诉模型:

同一个问题下,这个回答比那个回答更好。

一旦训练信号变成这种比较关系,优化目标就会发生变化。

模型不再只是提高某一个标准答案中每个 token 的概率,而是要提高 chosen answer 相对于 rejected answer 的优势。这里就会进入 RLHF、奖励模型和 DPO 的世界。

这也是本书主线中很关键的一次转弯。

预训练问:

下一个 token 像不像原始文本?

SFT 问:

下一个 token 像不像高质量助手答案?

偏好优化会进一步问:

两个完整回答里,哪个更值得被偏好?

注意这里的单位变了。

预训练和 SFT 主要在 token 级别计算损失。偏好优化开始把完整回答放到比较关系里。虽然实际算法仍然会涉及 token 概率,但训练信号已经不再只是一个标准答案,而是一个相对判断。

理解这一点,第 8 章就很好进入了。

第 8 章我们暂时不急着讲 DPO 公式,也不急着讲复杂强化学习。我们先讲一个更基础的问题:偏好对到底是什么?为什么同一个 prompt 下的 chosen / rejected,会成为后训练里如此重要的数据形态?

本章小结

本章把 SFT 放回公式里看了一遍。

SFT 的核心目标可以写成:

$$\mathcal{L}_{\text{sft}} = -\sum_t \log P_\theta(y_t \mid \text{prompt}, y_{<t})$$
它的意思是:给定用户 prompt 和示范回答前文,模型要提高示范回答中真实下一个 token 的概率。这个公式和预训练很像,区别在于预训练模仿普通文本,SFT 模仿高质量助手回答。

因此,SFT 很重要,但它本质上仍然是模仿学习。它让模型更像好助手,却还没有直接比较多个回答哪个更好,也没有直接最大化完整回答的外部奖励。

下一章预告

下一章我们进入“偏好对”。如果同一个问题下有两个回答,一个更好、一个更差,我们怎样把这种比较关系变成训练数据?这一步会把我们从“模仿一个标准答案”带向“优化回答之间的相对偏好”。

延伸阅读

推荐阅读方向:

  • Teacher forcing 与自回归语言模型训练资料。
  • 监督微调中的最大似然目标和交叉熵损失。
  • 指令微调数据集构造和训练实践资料。

第 8 章:从标准答案到偏好对

本章问题

为什么只给模型一个标准答案还不够?为什么后训练需要“哪个回答更好”这样的偏好数据?

章节摘要

这一章完成从 SFT 到偏好优化的关键过渡。现实中的好回答往往不是唯一的:同一个问题可以有多个正确表达,也可能在准确性、礼貌、简洁、完整性、安全性之间存在取舍。如果只用一个标准答案训练,模型学到的是模仿;如果给它两个回答,并告诉它哪个更好,训练信号就变成了偏好关系。本章会用具体例子展示 prompt、chosen answer、rejected answer 的数据形态,并解释为什么这种数据更适合训练模型的回答质量。

读者收获

读者应理解:偏好对把训练信号从“模仿一个标准答案”推进到“比较两个回答哪个更好”;chosen 和 rejected 表达的是相对质量关系。

配图

从标准答案到偏好对

图 8-1|从标准答案到偏好对:训练信号从“模仿一个答案”变成“比较两个回答哪个更好”。

读图:从一个答案到两个答案

这张图展示训练信号从“给一个标准答案”变成“比较两个候选回答”。同一个 prompt 下,chosen 和 rejected 构成一组偏好对。

读图时要注意,偏好对不要求 chosen 完美,也不要求 rejected 完全错误。它只表达一个相对判断:在这两个回答之间,哪一个更值得模型靠近。

正文

标准答案为什么不够?

第 7 章我们看见,SFT 的公式本质上仍然是在模仿一个示范答案。

给定一个 prompt,再给定一段高质量 answer,模型逐 token 提高这段 answer 的概率。这样训练出来的模型,会更像一个助手,也更能遵守指令。

但是,问题到这里并没有结束。

现实中的“好回答”,经常不是唯一的。

同一个问题,可以有多个正确答案;同一个正确答案,也可以用多种方式表达;同一个表达方式,在不同用户面前,效果也可能完全不同。

比如用户问:

请解释什么是大模型预训练。

回答 A 可以这样写:

预训练就是用大量文本训练模型预测下一个 token,让模型学到语言规律和知识模式。

回答 B 可以这样写:

你可以把预训练理解成让模型做海量文字接龙。给它前文,让它猜真实文本里下一个 token 是什么。猜错就调整参数,猜得越来越像训练数据。

回答 C 可以这样写:

预训练通常优化最大似然目标:

$$\mathcal{L} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
它让模型在给定上下文时提高真实下一个 token 的概率。

这三个回答都可能是好回答。

如果读者是完全外行,B 可能最好;如果读者只需要一句话定义,A 更合适;如果读者已经有机器学习基础,C 反而更清楚。

所以,“好回答”不是一个孤立标签,而是和用户目标、背景、表达约束、任务场景有关。

SFT 只给模型一个标准答案时,它能学到“像这个答案”。但它不直接告诉模型:为什么这个答案比另一个答案更好,什么场景下应该更详细,什么场景下应该更简洁,什么时候应该给公式,什么时候应该先讲直觉。

这就是标准答案的局限。

标准答案适合教模型模仿,但不够适合教模型比较。

后训练要继续往前走,就需要一种新的数据形态:偏好对。

一条 SFT 样本长什么样?

先看 SFT 样本。

它通常长这样:

prompt:
请用通俗语言解释什么是交叉熵。

answer:
交叉熵可以理解为模型猜答案时的惩罚。真实答案发生了,但模型如果给它的概率很低,就会受到很大惩罚;如果模型给它的概率很高,惩罚就小。在大模型训练中,交叉熵常用来衡量模型预测下一个 token 的好坏。

这条样本给模型的是一个示范。

它告诉模型:当用户这样问时,一个高质量回答可以长成这样。训练时,模型会沿着这段 answer,逐 token 提高这些目标 token 的概率。

这很有用。

但它没有提供比较信息。

它没有告诉模型下面这个回答为什么差:

交叉熵就是一种数学公式,机器学习里经常用。

这个回答不是完全错,但太短、太空,没有解释清楚“惩罚”和“概率”的关系。对于外行读者,它帮助有限。

SFT 样本如果只保留第一段标准 answer,模型会学习那段 answer 的形式,却不一定明确知道第二段回答差在哪里。

再比如,有时候差回答并不是事实错误,而是不符合指令。

用户要求:

用不超过三句话解释什么是 token。

一个回答可能写得很完整,但写了十段。这种回答知识上未必错,但没有遵守用户约束。SFT 如果只有一个标准答案,可以示范正确格式;但如果想让模型更清楚地知道“太长不好”“没有按要求不好”,偏好对会更直接。

一条偏好样本长什么样?

偏好样本通常不是一个 prompt 对一个 answer,而是一个 prompt 对两个或多个回答,并标出哪个更好。

最基本的形式是:

prompt:
请用通俗语言解释什么是交叉熵。

chosen answer:
交叉熵可以理解为模型猜答案时的惩罚。真实答案发生了,但模型如果给它的概率很低,就会受到很大惩罚;如果给它的概率很高,惩罚就小。在大模型训练中,交叉熵常用来衡量模型预测下一个 token 的好坏。

rejected answer:
交叉熵就是一种数学公式,机器学习里经常用。

这里有三个核心部分:

  • prompt:同一个用户问题。
  • chosen answer:被认为更好的回答。
  • rejected answer:被认为较差的回答。

chosen 不一定完美。

它只是相对于 rejected 更好。

rejected 也不一定完全错误。

它可能只是解释不够清楚,结构不够好,没有遵守指令,遗漏关键条件,语气不合适,或者安全边界处理得不好。

这个细节很重要。

很多人一听到 chosen / rejected,就以为 chosen 是真理,rejected 是错误。其实偏好数据常常表达的是相对质量,而不是绝对真伪。

再看一个例子。

prompt:

请用一句话解释什么是 SFT。

回答 A:

SFT 是用高质量指令-回答样本,让基础模型学会更像助手一样回答问题的监督微调过程。

回答 B:

SFT 是监督微调,它会使用大量数据来训练模型,使模型更好地进行自然语言处理任务,并提升模型的整体性能。

回答 B 不一定错,但它更泛,信息密度更低,也没有点出“指令-回答样本”和“助手式回答”。如果标注者认为 A 更好,那么 A 就是 chosen,B 就是 rejected。

这条偏好样本传达的信息,比一条标准答案更丰富。

它不仅告诉模型“好回答长什么样”,还隐含告诉模型“相比之下,哪些回答方式不够好”。

偏好关系到底提供了什么训练信号?

偏好关系的价值,在于它把抽象的回答质量标准落到了具体比较上。

“回答要好”太抽象。

“回答要准确、简洁、有帮助、符合指令、安全、礼貌”也仍然比较抽象。

但如果给出两个真实回答,让人判断哪个更好,训练信号就具体多了。

比如:

prompt:
给我一个 Python 函数,判断字符串是否是回文。

chosen:
def is_palindrome(s):
    s = s.lower()
    return s == s[::-1]

rejected:
def is_palindrome(s):
    return True

这里 chosen 明显更好,因为 rejected 完全没有实现任务。

再比如:

prompt:
用三点总结 Transformer 的注意力机制。

chosen:
1. 注意力机制会计算当前位置与其他位置的相关性。
2. 相关性越高,对当前位置表示的影响越大。
3. 它让模型能根据上下文动态聚合信息。

rejected:
Transformer 是一种神经网络架构,广泛用于自然语言处理,具有很强性能。

这里 rejected 不是胡说,但没有回答“用三点总结注意力机制”这个具体要求。

再比如:

prompt:
我不懂数学,请解释最大似然。

chosen:
最大似然就是让模型更相信真实发生过的数据。训练时,如果真实答案出现了,模型就应该给它更高概率。

rejected:
最大似然估计是通过最大化参数条件下观测样本联合概率来估计统计模型参数的方法。

这里 rejected 的定义可能更正式,但对“不懂数学”的用户不友好。chosen 更符合用户背景。

这些例子说明,偏好关系不仅能表达事实正确性,还能表达指令遵守、表达清晰度、用户适配、任务完成度和安全性。

它把“质量”变成了相对判断:

在同一个 prompt 下,
回答 A 比回答 B 更值得模型生成。

这正是偏好优化需要的信号。

SFT 的信号是:

请模仿这个 answer。

偏好数据的信号是:

同一个 prompt 下,请更偏向 chosen,而不是 rejected。

这两句话看起来差不多,其实训练含义已经发生变化。

模仿只需要一个答案。偏好需要至少两个答案。

模仿强调“像不像”。偏好强调“比另一个更好”。

模仿更接近 token 级监督。偏好开始走向回答级比较。

偏好不等于真理

讲偏好数据时,必须加一个克制提醒:偏好不等于真理。

人类更喜欢一个回答,不代表这个回答一定事实正确。

有时候,一个回答更流畅、更有条理、更自信,于是更容易被偏好;但它可能在某个细节上是错的。另一个回答虽然表达笨拙,却可能更准确。

这在大模型训练中是一个真实风险。

如果标注者过度偏好“听起来像专家”的回答,模型就可能学会专家口吻,而不是更可靠的事实判断。

如果标注者偏好短答案,模型可能变得过于简略。

如果标注者偏好长答案,模型可能变得啰嗦。

如果标注规范没有明确区分“事实正确”和“表达舒服”,模型可能把这两件事混在一起学。

偏好还会受到文化背景、语言习惯、任务场景和标注者专业水平的影响。

比如医学、法律、金融、科研领域,一个普通标注者可能更喜欢解释得顺畅的回答,但未必能发现专业错误。代码任务中,如果不运行测试,人类也可能错判某段代码是否真的可用。

所以,偏好数据不是万能真理源。

它是一种有用但有噪声的训练信号。

这句话需要记住:

偏好优化能让模型更符合某种评价标准,
但评价标准本身是否可靠,需要另外保证。

这也是为什么后面我们要专门讲数学题、代码题和外部验证器。

在可验证任务中,偏好可以不只来自人的主观判断,还可以来自测试器、编译器、判题系统、形式化验证或答案检查机制。这样的反馈不一定完美,但在某些任务上比纯人工偏好更明确。

也就是说,偏好对解决的是 SFT 的一个问题:只模仿一个答案不够。

但偏好对自身也有问题:偏好标准可能不稳定、不专业、有噪声。

理解这个边界,才能避免把 RLHF 或 DPO 神化。

为什么偏好对会通向 RLHF 和 DPO?

现在我们可以自然进入下一步。

一旦我们有了大量偏好对,就有两条重要路线。

第一条路线是 RLHF。

RLHF 可以粗略理解为三步:

1. 先用 SFT 得到一个会回答的模型。
2. 收集同一 prompt 下多个回答的人类偏好。
3. 用这些偏好训练奖励模型,再用强化学习让模型生成更高奖励的回答。

在这条路线里,偏好对先被用来训练一个 reward model。

这个 reward model 学习判断:给定 prompt 和 answer,这个 answer 大概有多好。然后,语言模型生成回答,reward model 给分,训练系统再根据奖励更新语言模型。

也就是说,RLHF 会把偏好关系转化成奖励信号。

第二条路线是 DPO。

DPO 的思想更直接:既然我们已经有 chosen 和 rejected,能不能不单独训练一个奖励模型,而是直接让语言模型提高 chosen 相对于 rejected 的概率优势?

这就是第 10 章要讲的内容。

现在先不急着写 DPO 公式。我们只需要知道:DPO 之所以可能,是因为偏好对提供了比较关系。

没有 chosen / rejected,就很难谈“提高好回答相对坏回答的优势”。

这两条路线表面不同,但共同点非常清楚:

训练信号从“这个答案应该被模仿”,
变成了“这个回答比另一个回答更值得偏好”。

这正是全书主线里第三层训练的入口。

预训练阶段,模型问的是:

下一个 token 像不像原始文本?

SFT 阶段,模型问的是:

下一个 token 像不像高质量助手答案?

偏好优化阶段,模型开始面对一个新问题:

同一个 prompt 下,哪个完整回答更好?

注意,这里已经不只是 token 级别的问题了。

偏好标签通常作用在完整回答上。一个回答可能前半段好、后半段差;也可能事实正确但不够有帮助;也可能语气很好但没有解决问题。人类偏好经常是对完整回答的综合判断。

这就把训练目标推向了更接近用户真实需求的方向。

当然,它也带来新的复杂性:怎样训练奖励模型?奖励模型会不会偏?模型会不会为了获得高分而迎合奖励模型?偏好数据噪声怎么处理?DPO 为什么可以绕开奖励模型?这些问题后面会逐一展开。

但本章先把桥搭好。

从标准答案到偏好对,变化看似只是多了一个回答。实际上,它改变了训练信号的性质。

本章小结

本章讲的是从 SFT 到偏好优化的关键过渡。

SFT 样本通常是 prompt -> answer,它告诉模型“请模仿这个高质量答案”。偏好样本则是 prompt -> chosen / rejected,它告诉模型“同一个问题下,这个回答比那个回答更好”。这让训练信号从单一示范,变成了相对比较。

不过,偏好不等于真理。人类偏好可能受表达、语气、专业水平和标注标准影响。偏好数据很重要,但它仍然是一种有噪声的反馈信号,需要通过后续方法谨慎使用。

下一章预告

下一章我们进入 RLHF:偏好数据怎样变成奖励模型?模型又怎样根据奖励调整自己的回答?这一步会正式把我们从“模仿答案”带到“追求更高奖励”的训练框架。

延伸阅读

推荐阅读方向:

  • 人类偏好标注流程和 pairwise preference 数据构造资料。
  • Reward model 训练数据质量控制资料。
  • RLHF 数据管线和标注一致性相关讨论。

第 9 章:RLHF:人类偏好如何变成奖励信号

本章问题

人类说“这个回答更好”,模型怎样把这种判断变成可以训练的奖励信号?

章节摘要

这一章解释传统 RLHF 的基本路线:先收集人类对回答的偏好比较,再训练一个奖励模型,让它学会给回答打分;之后再用强化学习方法更新语言模型,使它更倾向于生成高奖励回答。这里要特别强调优化目标的变化:预训练和 SFT 主要看 token 是否像训练文本,而 RLHF 开始评价完整回答是否值得奖励。本章不追求展开 PPO 的全部细节,而是先让读者理解“奖励模型”这个中间环节,以及为什么它让模型训练从模仿文本转向追求更被认可的回答。

读者收获

读者应理解:RLHF 先用人类偏好训练奖励模型,再用奖励模型给完整回答打分;它把训练目标从模仿答案推进到追求更高奖励。

配图

RLHF 奖励模型流程

图 9-1|RLHF 奖励模型流程:人类偏好先变成奖励模型,再用奖励信号更新语言模型。

读图:人类偏好如何变成奖励信号

这张图把 RLHF 的两步关系连起来:先用人类偏好训练奖励模型,再用奖励模型给语言模型的回答打分,并把分数变成后续优化信号。

读图时要区分两个模型:语言模型负责生成回答,奖励模型负责评价回答。RLHF 的关键,是把人类的相对偏好转化成可用于训练的奖励信号。

正文

从“哪个更好”到“给回答打分”

第 8 章我们讲了偏好对。

一条偏好样本大致长这样:

prompt: 用户问题
chosen answer: 更好的回答
rejected answer: 较差的回答

它表达的不是“请模仿这一个标准答案”,而是“同一个问题下,这个回答比那个回答更好”。

这一步已经比 SFT 往前走了一大步。

SFT 的信号是示范。偏好对的信号是比较。

但是,比较关系还不能直接拿来做传统强化学习。

强化学习通常需要一个奖励信号,也就是 reward。模型生成一个回答以后,训练系统要能问:

这个回答得多少分?

偏好对回答的是另一个问题:

这两个回答里,哪个更好?

这两个问题有关,但不是同一个问题。

RLHF 要解决的核心,就是把人类的比较判断转化成可以训练模型的奖励信号。

这里出现了一个关键角色:奖励模型,也就是 reward model。

你可以把奖励模型理解成一个翻译器。它把人类偏好中的“这个比那个好”,翻译成一个模型可以使用的分数:

给定 prompt 和 answer,
奖励模型输出一个分数,
表示这个回答有多值得奖励。

当然,这只是直觉说法。奖励模型并不拥有绝对真理,它只是从人类偏好数据中学到一种打分方式。这个打分方式可能有用,也可能有偏差。我们后面会专门提醒这一点。

现在先抓住 RLHF 的主线:

人类偏好 -> 奖励模型 -> 奖励分数 -> 更新语言模型

这是从 SFT 进入 RLHF 的关键变化。

SFT 让模型更像示范答案。

RLHF 开始让模型追求更高奖励。

RLHF 的三步路线

传统 RLHF 可以拆成三步。为了避免被技术细节淹没,我们先用最简洁的流程看一遍。

第一步,先做 SFT。

也就是用高质量的指令-回答样本,把基础模型训练成一个初步可用的助手模型。没有这一步,模型可能还不稳定,生成的回答不一定像对话助手,也不方便后续收集偏好。

这一步得到的模型,可以叫 SFT 模型。

第二步,收集偏好数据,并训练奖励模型。

给同一个 prompt,让模型生成多个回答,或者让多个模型生成回答。然后请人类标注者比较这些回答:哪个更好,哪个更差。

数据形态类似:

x: prompt
y_w: chosen answer / winner
y_l: rejected answer / loser

接着,用这些偏好对训练一个奖励模型 $r_\phi(x,y)$。这个奖励模型的任务是:看到 prompt 和 answer,输出一个分数。对于人类偏好的回答,分数应该更高;对于被拒绝的回答,分数应该更低。

第三步,用奖励模型更新语言模型。

语言模型针对 prompt 生成回答,奖励模型给这个回答打分。训练算法再根据分数调整语言模型参数,让它以后更倾向于生成高分回答。

用一条线串起来就是:

SFT 模型
  -> 生成候选回答
  -> 人类比较 chosen / rejected
  -> 训练 reward model
  -> reward model 给新回答打分
  -> 强化学习更新语言模型

这里有一个容易误解的地方。

RLHF 不是让人类在每一步训练中实时盯着模型。人类主要参与的是偏好数据标注。奖励模型训练好以后,它会在后续训练中自动给大量回答打分。

也就是说,人类偏好被“蒸馏”进了奖励模型。

这个奖励模型就成了后续强化学习阶段的反馈来源。

奖励模型学的是什么?

奖励模型的目标不是生成回答,而是评价回答。

语言模型回答问题。奖励模型给回答打分。

这两个模型的角色不同。

假设有一条偏好样本:

x: 请用通俗语言解释什么是最大似然。

y_w:
最大似然可以理解为:我们已经看到了某些数据,于是希望找到一组参数,让这些数据在模型看来最“合理”、最容易发生。

y_l:
最大似然估计是一种参数估计方法,常用于统计模型。

这里 $y_w$ 被人类认为更好,因为它更符合“通俗语言”的要求,解释也更完整。$y_l$ 不一定错,但太抽象。

奖励模型要学到的是:

$$r_\phi(x, y_w) > r_\phi(x, y_l)$$
其中:

  • x 是 prompt。
  • $y_w$ 是 winner,也就是 chosen answer。
  • $y_l$ 是 loser,也就是 rejected answer。
  • $r_\phi(x,y)$ 是奖励模型对回答 y 的打分。
  • φ 表示奖励模型自己的参数。

这个不等式是奖励模型的核心直觉。

它不要求我们提前知道 chosen 的绝对分数是多少,也不要求 rejected 的绝对分数是多少。它只要求奖励模型学会:在这个 prompt 下,chosen 的分数应该高于 rejected。

这正好适合偏好对。

因为偏好数据本来就是相对判断。

人类标注者通常更容易回答“两个回答哪个更好”,而不是给每个回答打一个稳定、可校准的绝对分数。

如果让不同人给回答打 1 到 10 分,标准可能差异很大。一个人给 8 分,另一个人可能给 6 分。但如果让他们比较 A 和 B,很多时候一致性会更高。

所以,奖励模型不是直接从绝对分数学起,而是从相对偏好学起。

它学会的不是“这个回答永远值 8 分”,而是“在这类比较里,哪些回答更可能被人类认为好”。

奖励模型的训练目标

为了把这种比较关系变成可优化目标,可以使用一个常见形式:

$$\mathcal{L}{\text{reward}} = -\log \sigma\left(r\phi(x, y_w) - r_\phi(x, y_l)\right)$$
我们按公式解释模板拆开。

x 是用户问题或 prompt。

$y_w$ 是人类更偏好的回答,也就是 chosen / winner。

$y_l$ 是较差的回答,也就是 rejected / loser。

$r_\phi(x,y_w)$ 是奖励模型给 chosen 的分数。

$r_\phi(x,y_l)$ 是奖励模型给 rejected 的分数。

$r_\phi(x,y_w)-r_\phi(x,y_l)$ 是两者的分数差。

$\sigma$ 是 sigmoid 函数。它把分数差压到 0 到 1 之间,可以理解成“奖励模型认为 chosen 胜过 rejected 的概率”。

前面的 - log 则把这个概率变成损失。

直觉非常简单:

如果 chosen 分数明显高于 rejected,损失就小。
如果 chosen 分数没有高于 rejected,损失就大。

奖励模型训练时,参数 φ 会被调整,使它越来越倾向于给 chosen 更高分、给 rejected 更低分。

注意,这里训练的是奖励模型,不是语言模型本身。

这一步还没有让语言模型变强。它只是训练出一个评价器。

这个评价器能不能好用,取决于偏好数据质量、标注标准、模型容量、训练方式以及任务覆盖范围。

如果偏好数据本身偏向某种表面风格,奖励模型也会学到这种偏向。

如果标注者在专业问题上判断不准,奖励模型也可能继承这种不准。

所以,奖励模型不是客观真理机器。它是一个从偏好数据里学出来的打分器。

但有了它,我们就可以进入下一步:用这个打分器来更新语言模型。

用奖励模型更新语言模型:从模仿到追求奖励

SFT 阶段,语言模型的目标是模仿示范答案。

RLHF 的强化学习阶段,目标变成了让模型生成更高奖励的回答。

可以把核心目标简化写成:

$$\max_\theta ; \mathbb{E}_{y \sim P_\theta(\cdot \mid x)} \left[r_\phi(x, y)\right]$$
这个公式需要慢慢读。

x 是 prompt。

$P_\theta(\cdot \mid x)$ 是语言模型在 prompt x 下生成回答的概率分布。这里 $\theta$ 是语言模型的参数。

$y \sim P_\theta(\cdot \mid x)$ 表示回答 y 是从当前语言模型的分布中生成出来的。

$r_\phi(x,y)$ 是奖励模型给这个回答的分数。

E[...] 是期望,表示我们不是只关心某一个回答,而是关心模型整体生成分布下的平均奖励。

这个目标用自然语言说就是:

调整语言模型参数,
让它生成的回答在奖励模型那里获得更高平均分。

这和 SFT 已经很不一样。

SFT 的训练数据里有标准答案。模型沿着标准答案逐 token 学习。

RLHF 强化学习阶段,模型会自己生成回答,然后由奖励模型评价这个完整回答。训练系统再根据评价结果调整语言模型,让高分回答更容易被生成,低分回答不那么容易被生成。

这里的优化单位已经从“标准回答中的下一个 token”推进到了“模型生成的完整回答”。

当然,实际训练时,语言模型仍然是按 token 生成,参数更新也仍然会落到每个 token 的概率上。但训练信号的来源变了:它不再只是示范答案里的真实 token,而是奖励模型对完整回答的评价。

这就是本书反复强调的主线:

训练形式可能仍然落在 token 概率上,
但训练信号已经从文本模仿变成了回答级奖励。

至于具体怎样用强化学习算法更新语言模型,传统 RLHF 常用 PPO 一类方法。PPO 的完整细节比较复杂,涉及策略更新、优势估计、裁剪目标等。本书不在这里展开,因为它会把读者从主线带走。

我们在这里需要理解的是路线,而不是背算法细节:

语言模型生成回答;
奖励模型给回答打分;
强化学习算法调整语言模型,
让高分回答的概率提高。

只要抓住这条链,RLHF 的核心就清楚了。

为什么要加 KL 约束?

如果目标只是最大化奖励模型分数,会出现一个问题:模型可能为了追求高分而偏离太远。

奖励模型毕竟不是完美裁判。它是从有限偏好数据中训练出来的。只要它有漏洞,语言模型就可能学会利用这些漏洞。

比如,奖励模型可能偏爱非常自信、结构工整、语气圆滑的回答。语言模型如果只追求奖励,就可能越来越会写漂亮话,却不一定更准确。

奖励模型可能偏爱长回答。语言模型就可能变得冗长。

奖励模型可能没有覆盖某些专业场景。语言模型在这些场景中追求高分时,可能生成表面上讨好奖励模型、实则不可靠的内容。

这类问题常被称为奖励作弊或奖励黑客。意思不是模型真的有主观恶意,而是优化过程会寻找奖励函数中的漏洞。

所以,RLHF 训练中通常还会加入一个约束,让当前模型不要偏离参考模型太远。这个约束常用 KL 散度表示:

$$\max_\theta ; \mathbb{E}\left[r_\phi(x, y)\right] - \beta,\mathrm{KL}\left(P_\theta(\cdot \mid x) \Vert P_{\text{ref}}(\cdot \mid x)\right)$$
这里:

  • $\mathbb{E}[r_\phi(x,y)]$ 表示希望提高回答的平均奖励。
  • $P_\theta(\cdot \mid x)$ 是当前正在更新的语言模型分布。
  • Pref(. | x) 是参考模型分布,通常可以理解为更新前比较稳定的模型,例如 SFT 模型。
  • $\mathrm{KL}(P_\theta \Vert P_{\text{ref}})$ 衡量当前模型分布和参考模型分布之间的距离。
  • $\beta$ 控制约束强度。

这个公式的意思是:

你可以追求更高奖励,
但不要为了奖励偏离原来的语言模型太远。

KL 约束像一个稳定器。

没有它,模型可能过度优化奖励模型,导致语言质量下降、回答风格变怪、泛化能力变差,甚至学会一些奖励模型喜欢但人类并不真正想要的模式。

有了它,训练目标就变成一种平衡:

一边提高奖励,
一边保持和原模型的距离不要太大。

这也是后训练里很常见的思想:不是把模型推向任意高奖励方向,而是在保留基础能力的前提下调整行为。

克制提醒:被偏好不等于更真实

RLHF 的价值很大。

它让模型不再只是模仿文本,而是开始追求更符合人类偏好的回答。这对改善用户体验非常关键。

但我们必须克制地理解它。

被偏好,不等于更真实。

奖励模型学到的是人类偏好数据中的模式。人类偏好可能包含对准确性的判断,也可能包含对表达方式、语气、格式、完整性、安全性的判断。这些因素混在一起,形成一个综合偏好。

有时候,这种综合偏好很有用。

比如,用户希望回答简洁,模型就不该写得过长。用户要求通俗解释,模型就不该堆公式。用户提出危险请求,模型就应该拒绝或转向安全建议。

但有时候,偏好也会误导。

一个回答写得流畅、自信、结构清楚,可能更容易被标注者喜欢;但它可能在事实细节上是错的。

一个回答语气保守、承认不确定,可能看起来“不够有用”;但在证据不足时,它反而更诚实。

一个医学或法律问题,如果标注者没有专业背景,偏好判断就可能偏向“听起来像正确”的答案,而不是专业上真正可靠的答案。

所以,RLHF 不是通往真理的自动机器。

它优化的是某种由人类反馈塑造出来的奖励信号。

这个奖励信号越可靠,训练越可能有效;奖励信号越有偏差,模型就越可能沿着偏差继续优化。

这也是为什么本书后面要继续讲可验证任务。

在数学题、代码题、工具调用等场景里,我们有机会用外部验证器提供更明确的反馈:答案对不对,代码能不能跑,测试能不能通过,工具调用是否完成任务。

这些反馈不一定完美,但它们让“奖励”不只来自主观偏好,也可以来自更明确的任务结果。

RLHF 把我们带入回答级奖励;可验证强化学习会进一步追问:奖励能不能更客观、更可检验?

本章小结

本章讲清楚了 RLHF 的基本路线。

偏好对本身只是比较关系:同一个 prompt 下,哪个回答更好。RLHF 先用这些偏好对训练奖励模型,让奖励模型学会给完整回答打分;然后语言模型生成回答,奖励模型给分,强化学习算法再根据奖励更新语言模型参数。

这一步的关键变化是:训练信号从 SFT 的“模仿标准答案”,推进到了“让完整回答获得更高奖励”。不过,奖励模型来自人类偏好数据,它不是事实真理本身。RLHF 能改善模型行为,也可能放大偏好数据和奖励模型中的偏差。

下一章预告

下一章我们进入 DPO。RLHF 需要先训练奖励模型,再用强化学习更新语言模型。那么有没有可能绕开奖励模型,直接把 chosen / rejected 这类偏好对写进语言模型的损失函数?DPO 正是在回答这个问题。

延伸阅读

推荐阅读方向:

  • InstructGPT 和 RLHF 训练流程相关论文。
  • Reward model 与 Bradley-Terry 偏好建模资料。
  • PPO、KL 约束和奖励模型过优化相关资料。

第 10 章:DPO:把“哪个回答更好”写进损失函数

本章问题

如果我们已经知道 chosen answer 比 rejected answer 更好,能不能不单独训练奖励模型,而是直接用这个偏好关系训练语言模型?

章节摘要

这一章重点讲 DPO。DPO 的价值不只是少了一个奖励模型,而是非常适合说明“优化目标变化”这条主线。SFT 是让模型模仿一个标准答案;DPO 则是在同一个 prompt 下,提高 chosen answer 相对于 rejected answer 的概率优势。我们会展示 DPO 公式,并加入轻量推导:不做完整数学证明,但要说明它为什么可以绕开奖励模型,以及为什么偏好对可以直接转化为损失函数。

读者收获

读者应理解:DPO 的核心是把 chosen / rejected 偏好关系直接写进损失函数,提高 chosen 相对 rejected 的概率优势,而不是简单模仿一个答案。

配图

DPO 把偏好写进损失函数

图 10-1|DPO 偏好优化:DPO 的核心,是提高 chosen 回答相对 rejected 回答的概率优势。

读图:偏好关系怎样进入损失函数

这张图展示 DPO 和 SFT 的差别。SFT 面对的是一个示范答案,DPO 面对的是 chosen 和 rejected 的相对关系。

读图时要抓住 DPO 的核心:它不是简单提高 chosen 的概率,而是提高 chosen 相对于 rejected 的概率优势。训练信号已经从“模仿答案”变成了“优化偏好关系”。

正文

DPO 要解决的到底是什么问题?

前一章讲 RLHF 时,我们看到了一条比较完整的路线:

偏好对 -> 奖励模型 -> 奖励分数 -> 强化学习更新语言模型

这条路线很自然。

人类先比较回答,训练系统再把这些比较变成奖励模型。奖励模型能给新回答打分,于是语言模型可以通过强化学习追求更高分。

但是,这条路线也比较重。

它需要单独训练一个奖励模型。奖励模型训练好以后,还要用强化学习算法更新语言模型。传统做法里常常会涉及 PPO 这类方法,训练过程复杂、调参敏感,也容易出现奖励模型被过度优化的问题。

于是,一个问题自然出现:

如果我们已经有 chosen / rejected,
能不能不单独训练奖励模型,
直接用这个偏好关系训练语言模型?

DPO,也就是 Direct Preference Optimization,正是在回答这个问题。

它的名字里有两个关键词。

第一个是 Direct,直接。

意思是:偏好对不必先绕到一个显式 reward model,再通过强化学习回到语言模型。偏好关系可以直接写进语言模型的损失函数。

第二个是 Preference Optimization,偏好优化。

意思是:它优化的不是“像不像一个标准答案”,而是“同一个 prompt 下,chosen answer 相对 rejected answer 是否更占优势”。

所以,DPO 的关键不只是省掉奖励模型。

更重要的是,它非常适合说明本书主线中的一次目标变化:

SFT:让模型更像一个给定答案。
DPO:让模型更偏向 chosen,而不是 rejected。

这里的训练信号已经不同了。

SFT 面对的是一条标准答案。DPO 面对的是一对比较答案。

SFT 的核心问题是:

这个 answer 的下一个 token,模型给的概率够不够高?

DPO 的核心问题是:

同一个 prompt 下,模型是否更应该生成 chosen,而不是 rejected?

如果把这一区别抓住,DPO 就不难理解。

如果一上来就盯着公式里的 log、$\sigma$、$\beta$,反而容易迷路。

先把 SFT 和 DPO 的训练信号摆在一起

我们先不用公式,先看数据形态。

SFT 样本是:

prompt:
请解释什么是梯度下降。

answer:
梯度下降是一种优化方法。它根据损失函数的梯度,沿着让损失下降的方向更新参数。

SFT 训练时,模型要提高这段 answer 中每个 token 的概率。

它学到的是:

这个问题后面,应该更像这段示范回答。

DPO 样本是:

prompt:
请解释什么是梯度下降。

chosen:
梯度下降是一种优化方法。你可以把它理解成在山坡上往低处走:损失越低,模型预测越好。每次训练时,模型根据梯度判断参数该往哪个方向调整。

rejected:
梯度下降就是一种机器学习算法,可以提高模型性能。

DPO 训练时,不只是让模型模仿 chosen。

它要学习的是:

在这个 prompt 下,chosen 应该比 rejected 更有优势。

这个差别很微妙,也很关键。

如果我们只把 chosen 当作标准答案来做 SFT,模型会提高 chosen 中每个 token 的概率。这样做当然也有用,但它没有充分利用 rejected 的信息。

rejected 告诉模型:有些回答看起来也像回答,但质量不够。它可能太空、太泛、没有遵守指令、事实不完整,或者没有真正帮助用户。

DPO 把这部分信息也放进训练目标里。

它不是只说:

请更像 chosen。

而是说:

请让 chosen 相对于 rejected 更容易被模型生成。

这就是偏好优化和普通模仿学习的区别。

也正是在这里,我们要纠正一个常见误解。

DPO 不是因为“样本由 AI 自动产生,所以不能用交叉熵”。

决定训练目标的,不是样本是谁生成的,而是训练信号是什么形态。

如果 AI 生成了一条高质量答案,并且我们把它当作标准答案,那么它仍然可以用于 SFT。

如果人类写了两个回答,并标出哪个更好,那么它也可以用于 DPO。

关键是:

SFT 的信号是目标答案。
DPO 的信号是偏好关系。

偏好关系一出现,优化目标就自然变了。

DPO 公式先看整体

DPO 的核心公式可以写成:

$$\mathcal{L}_{\text{dpo}} = -\log \sigma\left(\beta\left[ \log \frac{P_\theta(y_w \mid x)}{P_{\text{ref}}(y_w \mid x)} - \log \frac{P_\theta(y_l \mid x)}{P_{\text{ref}}(y_l \mid x)} \right]\right)$$
先不要被公式吓住。

我们先看它的整体结构。

最外层是:

$$-\log \sigma(\cdots)$$
这和许多二分类损失很像。它希望括号里的值越大越好。括号里的值越大,$\sigma(\cdots)$ 越接近 1,$-\log \sigma(\cdots)$ 越小。

所以,DPO 损失希望中间那一大项变大。

中间那一大项是:

$$\beta\left[\text{chosen 的相对优势} - \text{rejected 的相对优势}\right]$$
其中 chosen 的相对优势是:

$$\log \frac{P_\theta(y_w \mid x)}{P_{\text{ref}}(y_w \mid x)}$$
rejected 的相对优势是:

$$\log \frac{P_\theta(y_l \mid x)}{P_{\text{ref}}(y_l \mid x)}$$
也就是说,DPO 不是只看当前模型 $P_\theta$ 给 chosen 多高概率,也不是只看它给 rejected 多低概率。

它看的是当前模型相对于参考模型的变化。

如果当前模型比参考模型更偏向 chosen,同时没有更偏向 rejected,那么 chosen 的相对优势就会更大。

DPO 希望:

chosen 的相对优势 > rejected 的相对优势

这就是公式的核心。

把公式压缩成一句话:

DPO 让当前模型相对于参考模型,
更增加 chosen 的概率优势,
而不是 rejected 的概率优势。

这句话比公式更重要。

公式只是把它写成可以优化的形式。

逐项解释 DPO 公式

现在我们逐项拆公式。

x 是 prompt,也就是用户问题或任务。

$y_w$ 是 winner,也就是 chosen answer。它是偏好对里被认为更好的回答。

$y_l$ 是 loser,也就是 rejected answer。它是偏好对里被认为较差的回答。

$P_\theta(y \mid x)$ 是当前语言模型在 prompt x 下生成完整回答 y 的概率。

这里要注意,y 是一整段回答,不是单个 token。

不过,语言模型生成一整段回答的概率,仍然可以拆成一连串 token 概率的乘积:

$$P_\theta(y \mid x) = \prod_t P_\theta(y_t \mid x, y_{<t})$$
所以,DPO 看起来在比较完整回答,但底层仍然依赖语言模型对每个 token 的概率。

实际实现里,通常不会直接把很多很小的概率相乘,而是使用 token log probability 的求和形式;不同实现还可能处理回答长度、mask、prompt 部分是否计入等细节。本书不展开这些工程差异,只抓住概念核心:DPO 比较的是当前模型相对于参考模型,对整段 chosen 和 rejected 的倾向变化。

Pref(y | x) 是参考模型在同一 prompt 下生成回答 y 的概率。

参考模型通常可以理解为一个固定模型,例如 SFT 后的模型。它在 DPO 训练过程中不更新,用来提供一个基准。

为什么需要参考模型?

因为我们不只是想让模型无限追求偏好数据,还希望它不要偏离原来稳定的语言能力太远。

如果没有参考模型,模型可能为了提高偏好目标,把概率分布推得很极端。参考模型提供了一种“相对变化”的标尺。

$\log \frac{P_\theta(y \mid x)}{P_{\text{ref}}(y \mid x)}$ 表示当前模型相对参考模型,对回答 y 的倾向增强了多少。

如果这个值很大,说明当前模型比参考模型更倾向于这个回答。

如果这个值很小甚至为负,说明当前模型相对参考模型并没有更偏向这个回答,甚至更不偏向它。

$\beta$ 是一个控制强度的参数。

它会影响偏好优化的力度,也和模型相对参考模型的偏离程度有关。直觉上,$\beta$ 调节“偏好信号说话有多大声”。

$\sigma$ 是 sigmoid 函数。

它把一个实数压到 0 到 1 之间。这里可以理解成:模型认为 chosen 胜过 rejected 的概率。

最后,- log 把这个概率变成损失。

如果模型已经很明显地让 chosen 优于 rejected,那么 $\sigma(\cdots)$ 接近 1,损失接近 0。

如果模型没有让 chosen 优于 rejected,甚至让 rejected 更占优势,那么 $\sigma(\cdots)$ 会变小,损失变大。

于是训练就有了方向:

提高 chosen 相对 rejected 的优势。

这就是 DPO 的公式含义。

轻量推导:为什么能绕开奖励模型?

现在我们解释一个更深的问题:DPO 为什么可以绕开奖励模型?

在 RLHF 里,我们先训练一个显式奖励模型 r(x, y)。然后语言模型追求更高奖励,同时通常会受到 KL 约束:

$$\max_\theta ; \mathbb{E}\left[r(x, y)\right] - \beta,\mathrm{KL}\left(P_\theta(\cdot \mid x) \Vert P_{\text{ref}}(\cdot \mid x)\right)$$
这个目标的意思是:模型既要生成高奖励回答,又不要偏离参考模型太远。

在这样的 KL 约束强化学习问题里,可以推导出一个重要关系:理想情况下,一个回答的奖励,和当前模型相对于参考模型提高它概率的程度有关。

用简化形式表达,就是:

$$r_\theta(x, y) \approx \beta \log \frac{P_\theta(y \mid x)}{P_{\text{ref}}(y \mid x)} + \text{常数}$$
这不是要读者记住严格证明,而是要看懂思想:

如果一个回答真的更值得奖励,
那么优化后的模型应该相对参考模型更倾向于生成它。

也就是说,奖励可以被“隐含”在当前模型和参考模型的概率比里。

RLHF 的做法是:

先学一个显式 reward model,
再用 reward model 更新语言模型。

DPO 的想法是:

既然奖励和概率比之间有对应关系,
那能不能把这个关系直接代入偏好损失,
不用单独训练 reward model?

回到偏好数据。

如果人类认为 $y_w$ 好于 $y_l$,在奖励模型视角下,可以写成:

$$r(x, y_w) > r(x, y_l)$$
如果用概率比来表示隐含奖励,就得到:

$$\beta \log \frac{P_\theta(y_w \mid x)}{P_{\text{ref}}(y_w \mid x)} > \beta \log \frac{P_\theta(y_l \mid x)}{P_{\text{ref}}(y_l \mid x)}$$
把两边相减,就得到 DPO 公式中间那一项:

$$\beta\left[ \log \frac{P_\theta(y_w \mid x)}{P_{\text{ref}}(y_w \mid x)} - \log \frac{P_\theta(y_l \mid x)}{P_{\text{ref}}(y_l \mid x)} \right]$$
再用 sigmoid 把这个差值变成“chosen 胜过 rejected”的概率形式,用 -log 变成损失,就得到 DPO 的训练目标。

这就是轻量推导的核心。

我们不需要展开所有数学细节,只要看见这条桥:

偏好比较
  -> 奖励差
  -> 当前模型相对参考模型的概率优势差
  -> DPO 损失

所以,DPO 不是没有奖励思想。

更准确地说:

DPO 把奖励模型隐式化了。

它不再单独训练一个 reward model,而是把偏好关系直接变成语言模型参数更新的目标。

DPO 到底怎样更新模型?

公式理解以后,我们再看训练方向。

对于一条偏好样本:

x: 用户 prompt
y_w: chosen answer
y_l: rejected answer

DPO 会计算当前模型和参考模型分别对 $y_w$、$y_l$ 的概率。

如果当前模型相对于参考模型,已经明显更偏向 $y_w$ 而不是 $y_l$,那么损失较小。

如果当前模型没有体现这种偏好,甚至更偏向 $y_l$,那么损失较大。

梯度更新会推动模型:

提高 y_w 的相对概率优势;
降低 y_l 的相对概率优势。

注意这里说的是“相对概率优势”。

这不是简单地把 chosen 的所有 token 概率无脑推高,也不是简单把 rejected 的所有 token 概率压到零。

DPO 关心的是相对于参考模型的变化,而且 chosen 和 rejected 是成对比较的。

这带来一个很重要的直觉:

DPO 不是在教模型背答案,
而是在校正模型对好回答和差回答的相对偏好。

这也是为什么 DPO 适合后训练。

预训练已经提供语言能力。SFT 已经提供助手式回答能力。DPO 在这个基础上进一步调整:当模型面对多个可能回答时,它应该更倾向于那些被偏好的回答。

比如,一个模型面对用户问题可以生成两类回答:

回答 A:直接、准确、符合要求。
回答 B:流畅但空泛,没有真正解决问题。

SFT 可能让模型学会生成像 A 这样的答案。

DPO 则可以明确告诉模型:当 A 和 B 都是可能输出时,A 应该比 B 更占优势。

这种训练尤其适合处理那些难以写成单一标准答案的问题。

因为很多开放式任务没有唯一答案,但可以比较优劣。

例如:

  • 哪个总结更抓重点?
  • 哪个解释更适合外行?
  • 哪个拒绝回答更安全也更有帮助?
  • 哪个代码修改更符合用户需求?
  • 哪个数学解法更清楚?

这些问题不一定适合只有一个标准答案的 SFT,但非常适合偏好对。

常见误解:DPO 不是因为样本由 AI 生成,所以不能用交叉熵

这里要专门纠正一个容易出现的误解。

有人会说:

预训练和 SFT 可以用交叉熵,
但到了 DPO,因为样本是 AI 自动生成的,所以不能再用交叉熵,必须换强化学习目标。

这句话不准确。

真正决定训练目标的,不是样本是不是 AI 生成的,而是我们手里有什么训练信号。

如果 AI 生成了一个高质量答案,经过人类审核后被当作标准答案,那么它完全可以进入 SFT,用交叉熵训练。

例如:

prompt -> AI 生成答案 -> 人类确认质量高 -> 作为 answer 做 SFT

这没有问题。

反过来,如果两个回答都是人类写的,但我们标出了一个 chosen、一个 rejected,那么它们也可以用于 DPO。

例如:

prompt -> 人类回答 A / 人类回答 B -> 标注 A 更好 -> 做偏好优化

这也没有问题。

所以,分界线不在于“人类生成”还是“AI 生成”。

分界线在于:

训练信号是一个目标答案,
还是两个回答之间的偏好关系。

如果训练信号是目标答案,适合用 SFT 式最大似然目标:

$$\mathcal{L}_{\text{sft}} = -\sum_t \log P_\theta(y_t \mid \text{prompt}, y_{<t})$$
如果训练信号是偏好关系,适合用 DPO 这类偏好优化目标:

chosen 应该相对 rejected 更占优势

这就是本书想强调的“优化目标变化”。

不是因为数据来源变得神秘,而是因为反馈信号变了。

从这个角度看,你最初提出的直觉里,有一部分非常重要:强化学习和偏好优化的本质,确实要从优化目标变化来理解。

但需要校正的是:

不是“AI 自动产生样本,所以不能用交叉熵”,
而是“训练信号从标准答案变成偏好/奖励,所以优化目标要改变”。

这句话可以作为全书后训练部分的一个关键判断。

DPO 的局限

DPO 让偏好优化变得更直接,但它也不是魔法。

第一,DPO 仍然依赖偏好数据质量。

如果 chosen / rejected 标注本身有噪声,模型就会沿着有噪声的方向更新。

如果标注者偏好流畅但不准确的回答,DPO 可能让模型更会生成流畅但不准确的回答。

如果偏好数据覆盖不足,模型在新场景中的表现仍然可能不稳定。

第二,DPO 优化的是相对偏好,不等于事实真理。

它能让模型更倾向于被偏好的回答,但被偏好不一定等于更正确。这个问题和 RLHF 类似。

尤其在专业领域,如果偏好标注没有专业标准,模型可能学习到“看起来专业”的表达,而不是专业上真正可靠的内容。

第三,参考模型和 $\beta$ 的选择会影响训练稳定性。

参考模型太弱,基准不好;参考模型太强或太保守,也会影响优化空间。$\beta$ 太小或太大,都可能改变偏好信号和稳定约束之间的平衡。

第四,DPO 不自动解决验证问题。

对于数学题、代码题、工具调用等任务,很多时候我们不只是想知道“哪个回答看起来更好”,还想知道“答案能不能验证通过”。

这就需要第 11 章开始讲的内容。

偏好优化是非常重要的一层,但它仍然主要来自比较关系。到了数学和代码这类任务,我们有机会使用更明确的反馈:答案是否正确,代码是否通过测试,程序是否运行,证明是否满足规则。

这会把训练目标继续推向“完整回答能不能获得可验证奖励”。

本章小结

本章讲清楚了 DPO 的核心:它把偏好关系直接写进语言模型的损失函数。

SFT 的训练信号是一个标准答案,目标是让模型更像这段答案。DPO 的训练信号是同一个 prompt 下的 chosen / rejected,目标是让模型提高 chosen 相对于 rejected 的概率优势。它不再单独训练显式奖励模型,而是利用当前模型相对参考模型的概率比,构造一种隐式奖励差。

因此,DPO 不是因为样本由 AI 生成才换损失函数,而是因为训练信号从“目标答案”变成了“偏好关系”。这正是本书主线里的关键变化:优化目标越来越接近“哪个回答更值得被选择”。

下一章预告

下一章我们进入数学题和代码题。偏好对可以告诉模型哪个回答更好,但数学和代码还有一个特殊优势:很多结果可以被外部系统验证。答案对不对、代码能不能跑、测试能不能通过,这些都可能变成更明确的奖励信号。

延伸阅读

推荐阅读方向:

  • Direct Preference Optimization 原始论文。
  • Bradley-Terry 偏好模型和 pairwise ranking loss 资料。
  • RLHF、DPO、IPO、KTO 等偏好优化方法对比资料。

第 11 章:数学题和代码题为什么特殊

本章问题

为什么数学题和代码题经常被用来训练或评估大模型的推理能力?它们和普通问答有什么不同?

章节摘要

这一章进入可验证任务。普通开放问答的好坏往往需要人来判断,而且标准并不总是清楚;但数学题和代码题有一个特殊优势:很多时候答案可以被验证。数学题可以检查最终答案是否正确,代码题可以通过编译器、单元测试或在线判题系统判断是否通过。这让训练信号比普通偏好更明确。本章要说明:可验证任务并不意味着模型真的“懂了”全部推理过程,但它确实提供了更清晰的奖励来源,使模型可以围绕正确性进行更直接的优化。

读者收获

读者应理解:数学题和代码题特殊,是因为很多结果可以由外部系统验证;可验证性让训练信号比普通主观偏好更明确。

配图

数学题和代码题的可验证性

图 11-1|数学题和代码题的可验证性:这类任务的特殊之处,是答案好坏可以由外部机制验证。

读图:为什么数学和代码更容易提供反馈

这张图把数学题和代码题放在一起,是为了说明它们的共同点:答案好坏可以被某种外部机制检查。

读图时要看到这种可验证性的价值。相比开放式问答,数学答案、单元测试、编译结果和判题系统更容易给出明确反馈,因此更适合形成奖励信号。

正文

从“人觉得好”到“系统能验证”

前面几章,我们一直在讲偏好。

SFT 用高质量答案教模型模仿。偏好对告诉模型,同一个 prompt 下哪个回答更好。RLHF 把偏好变成奖励模型。DPO 则把偏好关系直接写进损失函数。

这些方法很重要,但它们大多依赖一个前提:有人或者某种偏好系统判断回答质量。

可是在一些任务里,回答质量不一定只能靠人主观判断。

数学题和代码题就是两类很特殊的任务。

数学题有时可以检查最终答案是否正确。代码题可以运行,可以编译,可以测试,可以看输出是否符合预期。也就是说,它们有机会把“这个回答好不好”变成一个更明确的问题:

答案能不能通过验证?

这和普通开放问答不一样。

如果用户问:

请解释人工智能对教育的影响。

这个回答好不好,很难由一个简单程序判断。它可能涉及观点、结构、事实、例子、价值判断和表达风格。

但如果用户问:

计算 37 × 48。

答案可以直接验算。

如果用户问:

写一个函数,判断数组中是否存在重复元素。

代码可以运行测试。

这就是数学题和代码题的特殊性:它们更容易产生明确反馈。

在训练大模型时,明确反馈非常珍贵。因为一旦反馈明确,训练系统就更容易知道哪些回答应该被强化,哪些回答应该被降低概率。

本书主线到这里又往前推进了一步。

预训练问:

下一个 token 像不像原始文本?

SFT 问:

下一个 token 像不像高质量助手答案?

DPO 问:

两个回答里,哪个更值得被偏好?

可验证任务开始问:

这个完整回答能不能通过验证?

这就是从偏好走向验证的入口。

开放问答为什么难以自动评价?

为了理解数学和代码为什么特殊,我们先看普通开放问答为什么难。

假设用户问:

请介绍一下大模型对未来工作的影响。

一个回答可以从技术角度讲,一个回答可以从管理角度讲,一个回答可以从普通员工角度讲,一个回答可以从教育和培训角度讲。

它们可能都合理。

哪一个更好,取决于用户想要什么。

如果用户是企业管理者,他可能关心组织效率和岗位变化。

如果用户是学生,他可能关心职业规划。

如果用户是政策研究者,他可能关心就业结构和监管。

这类问题没有单一标准答案。

即使答案事实都正确,也可能在表达方式上差异很大。一个回答更简洁,另一个更完整;一个回答更谨慎,另一个更有观点;一个回答适合外行,另一个适合专业读者。

这就是为什么开放问答常常需要人类偏好。

人类可以综合判断:

  • 是否回答了问题
  • 是否事实可靠
  • 是否表达清楚
  • 是否适合用户背景
  • 是否有帮助
  • 是否安全

但这些标准很难写成一个简单自动程序。

如果我们试图写一个函数:

score(answer) = ?

马上会遇到困难。

事实正确性怎么自动判断?表达清晰度怎么量化?用户是否满意怎么预测?不同读者的偏好怎么统一?

所以,在开放问答里,奖励信号通常比较软。我们可以用人类偏好、奖励模型、AI 评审等方式近似,但它们都不是完全明确的验证器。

数学和代码不同。

它们在很多场景下可以把评价问题变得更硬。

不是问:

这个回答是不是让人感觉更好?

而是问:

这个答案是否等于标准结果?
这段代码是否通过测试?
这个输出是否满足规则?

这就为训练提供了更清晰的反馈。

数学题为什么更容易验证?

数学题的一个重要特点是:许多题目有明确答案。

例如:

求 23 + 58。

模型回答 81,可以直接判断正确。

再比如:

解方程 2x + 3 = 11。

模型回答 x = 4,可以代回原方程验证:

2 × 4 + 3 = 11

成立。

对于更复杂的问题,也可以使用数值代入、符号计算、定理证明器或形式化验证工具来检查某些结果。

数学题的验证方式有很多层次。

最简单的是最终答案匹配。

比如选择题、填空题、简单计算题,标准答案明确。模型输出以后,系统可以直接比较。

稍微复杂一点的是等价性检查。

比如标准答案是:

x = 1/2

模型回答:

x = 0.5

字面不同,但数学上等价。这时需要更聪明的检查方式。

再复杂一点,是过程或证明检查。

模型不仅要给最终答案,还要给推导过程。系统可能检查每一步是否符合规则,或者把证明转换成形式化语言交给验证器。

当然,并不是所有数学题都容易验证。

很多开放式证明、竞赛题解、几何构造、复杂推理过程,并不能简单靠字符串匹配判断。即使最终答案正确,过程也可能有漏洞;即使过程看起来合理,也可能隐藏错误。

所以,我们要准确表达:

数学题比普通开放问答更容易验证,
但不代表所有数学推理都容易验证。

这已经足够重要。

因为一旦有一批可以验证的题目,训练系统就能自动产生大量反馈。

模型可以尝试生成答案。验证器判断对错。正确答案得到正奖励,错误答案得到低奖励或负奖励。这个过程就比单纯依赖人类主观偏好更明确。

代码题为什么更容易验证?

代码题的可验证性更直观。

因为代码可以运行。

用户让模型写一个函数:

给定一个整数数组,判断是否存在重复元素。

模型可能生成:

def has_duplicate(nums):
    return len(nums) != len(set(nums))

这个函数能不能工作,可以用测试来检查:

has_duplicate([1, 2, 3]) -> False
has_duplicate([1, 2, 1]) -> True
has_duplicate([]) -> False

如果测试都通过,我们就有理由给它更高奖励。

代码任务的验证器可以有很多种。

第一种是语法检查。

代码是否能被解析?有没有明显语法错误?

第二种是编译或运行。

对于编译型语言,编译器能不能通过?对于解释型语言,运行时是否报错?

第三种是单元测试。

给定输入,看输出是否符合预期。

第四种是隐藏测试。

模型可能通过公开样例,但在边界条件上失败。隐藏测试能检查更多情况。

第五种是性能约束。

有些代码不仅要结果正确,还要在时间和内存限制内完成。在线判题系统常常会检查这一点。

这些验证机制让代码题成为训练大模型的重要场景。

它们提供了一种相对明确的反馈:

代码通过测试 -> 奖励高
代码没有通过 -> 奖励低

当然,这里也要谨慎。

通过测试不等于程序在所有情况下都正确。

测试覆盖可能不足。模型可能写出只适配测试样例的代码。某些代码可能有安全风险。某些实现虽然通过测试,却可读性差、可维护性差,或者在真实工程环境中不合适。

所以,代码验证也不是完美真理。

但相比普通开放问答,它已经提供了更硬的反馈来源。

这就是为什么编程任务在大模型训练和评估里如此重要。

可验证性如何变成奖励?

可验证任务最关键的价值,是可以把结果转换成奖励函数。

最简单的奖励函数是二值奖励:

$$R(x,y) = \begin{cases} 1, & \text{如果答案通过验证} \ 0, & \text{如果答案没有通过验证} \end{cases}$$
这里:

  • x 是题目或任务。
  • y 是模型生成的完整回答。
  • $R(x,y)$ 是奖励函数。

如果是数学题,验证器检查 y 的最终答案是否正确。

如果是代码题,验证器运行测试,看代码是否通过。

通过就给 1,失败就给 0。

这当然是最简化的形式。

现实中,奖励可以更细。

例如代码题可以按通过测试的比例给分:

$$R(x,y) = \frac{\text{通过的测试数}}{\text{总测试数}}$$
数学题也可以有部分奖励。最终答案错了,但中间某些步骤正确,可以给一点过程分。或者模型最终答案对了,但推理过程明显不可靠,也可以降低奖励。

不过,先从二值奖励理解就够了。

它已经体现了和前面阶段的差别。

SFT 的损失问:

下一个 token 是否像示范答案?

DPO 的损失问:

chosen 是否相对 rejected 更占优势?

可验证奖励问:

模型生成的完整回答是否通过验证?

这是一种更接近任务结果的反馈。

如果模型写出一段代码,里面某个 token 和标准答案不同,并不一定是错。代码可以有很多种写法。只要它通过测试,就可能是好答案。

这点非常重要。

代码题和数学题常常不适合只用“模仿标准答案”的方式训练。因为解法可能很多,表达可能不同,但结果可以验证。

可验证奖励绕开了“必须长得像标准答案”的限制。

它更关心:

你最终做成了没有?

这就是强化学习在这些任务里特别有吸引力的原因。

模型可以生成多个候选答案。验证器筛选出成功的答案。训练系统再提高成功答案的概率。

这条路会在第 12 章进一步展开。

可验证不等于完全理解

讲到这里,读者可能会产生一种兴奋感:既然数学和代码可以验证,那是不是只要让模型不断刷题、不断运行测试,它就会真正学会推理?

需要冷静一点。

可验证很重要,但可验证不等于完全理解。

第一,最终答案正确,不代表推理过程可靠。

模型可能猜对答案。也可能中间推理有漏洞,但最后碰巧得到正确结果。数学题尤其如此。如果奖励只看最终答案,模型可能学会一些投机路径。

第二,测试通过不代表程序完全正确。

测试只能覆盖有限情况。如果测试样例不充分,代码可能通过测试,却在未覆盖的输入上失败。

第三,验证器本身可能有漏洞。

如果奖励函数设计不严,模型可能学会利用漏洞获得高分,而不是解决真实任务。

第四,可验证任务覆盖的能力有限。

数学和代码很重要,但大模型还要处理解释、写作、规划、沟通、检索、判断、审美和复杂现实任务。这些任务很多无法简单验证。

所以,我们应该把可验证任务放在准确的位置上。

它不是万能道路,但它提供了一类特别清晰的训练信号。

在这类任务里,模型能力提升往往更容易形成闭环:

生成答案 -> 自动验证 -> 得到奖励 -> 更新模型 -> 再生成更好答案

这也解释了为什么数学和代码常常被放在大模型推理训练的核心位置。

它们不是因为天然更神秘,而是因为反馈更明确。

训练系统最喜欢明确反馈。

反馈越明确,优化目标就越容易设计。优化目标越清楚,模型参数更新就越有方向。

本章小结

本章讲清楚了数学题和代码题为什么特殊。

普通开放问答的质量往往需要人类综合判断,评价标准容易混合事实、表达、语气和用户偏好。数学题和代码题则在很多场景下可以由外部系统验证:数学答案可以检查,代码可以编译、运行和测试。这让训练信号从“人觉得哪个更好”,进一步走向“这个回答能不能通过验证”。

不过,可验证不等于完全理解。最终答案正确不一定说明推理过程可靠,代码通过测试也不代表所有情况都正确。可验证任务的价值在于提供更明确的奖励来源,而不是自动保证模型真正掌握了一切。

下一章预告

下一章我们继续沿着这条线往下走:外部验证器到底怎样产生奖励?测试器、编译器、判题系统、答案检查器分别能给模型什么反馈?当这些反馈进入训练目标时,强化学习的含义会变得更具体。

延伸阅读

推荐阅读方向:

  • 单元测试、在线判题系统和代码执行反馈资料。
  • 数学答案验证、符号计算和形式化验证入门资料。
  • 可验证任务在大模型训练与评估中的应用讨论。

第 12 章:外部验证器:奖励从哪里来

本章问题

在可验证强化学习中,模型生成回答以后,奖励到底是怎样产生的?

章节摘要

这一章解释外部验证器的作用。模型生成完整回答后,系统可以把答案交给不同验证机制:数学任务可以比较答案或检查推导约束,代码任务可以运行测试、检查编译结果,工具调用任务可以判断结果是否满足目标。验证器输出的分数或通过/失败信号,就可以变成训练中的奖励。本章会用流程图说明 prompt、model answer、verifier、reward、update 之间的关系,并提醒读者:验证器越明确,奖励越可靠;但验证器也可能被过拟合或被模型钻空子。

读者收获

读者应理解:外部验证器把模型回答转成奖励信号;代码测试通过率、答案检查和工具调用结果都可以成为 $R(x,y)$ 的来源。

配图

外部验证器产生奖励

图 12-1|外部验证器产生奖励:模型回答经过验证器后,得到奖励信号,再用于更新模型。

读图:验证器在模型外部

这张图展示模型生成回答以后,外部验证器如何检查结果,并把检查结果转成奖励信号。验证器不是模型内部的直觉,而是独立的评价机制。

读图时要抓住闭环:模型生成回答,验证器给出反馈,反馈再影响后续训练。这个闭环让“回答是否真的完成任务”进入优化目标。

正文

奖励不是凭空来的

第 11 章我们讲到,数学题和代码题特殊,是因为很多时候可以被验证。

模型回答以后,系统可以检查答案是否正确,代码是否通过测试,输出是否符合要求。

但这里还有一个更具体的问题:

奖励到底从哪里来?

强化学习经常说 reward。可是 reward 不是一个神秘数字,也不是模型自己觉得“我做得不错”。

在可验证任务里,奖励通常来自外部验证器。

模型生成一个回答。验证器检查这个回答。检查结果再被转换成奖励。

这条关系可以写成:

模型回答 -> 外部验证器 -> 奖励信号

这个顺序很重要。

模型本身并不天然知道自己答对了没有。它可以生成一个看起来很合理的数学推导,但最后答案错了。它也可以写一段看起来很漂亮的代码,但运行时直接报错。

要知道答案是否可靠,需要一个评价机制。

人类偏好是一种评价机制。奖励模型是一种评价机制。测试器、编译器、判题器也是评价机制。

本章要讲的,是最后这一类:外部验证器。

它们的共同特点是:评价过程相对明确,可以自动运行,并且能把结果转成训练中的奖励。

外部验证器是什么?

外部验证器可以理解为模型之外的检查系统。

它接收两个东西:

输入任务 x
模型回答 y

然后输出一个评价结果:

通过 / 失败
分数
错误信息
测试通过数量
格式是否正确
工具调用是否完成目标

不同任务有不同验证器。

数学题里,验证器可以是答案比较器。

例如题目是:

求 125 + 376。

模型回答 501,答案检查器可以直接判断正确。

如果题目是方程,验证器可以把答案代回去检查。

代码题里,验证器可以是编译器、解释器、单元测试或在线判题系统。

模型生成一段代码,系统先检查语法,再运行测试,再看输出是否符合预期。

工具调用任务里,验证器可以检查任务是否完成。

比如模型要调用日历工具创建一个会议,验证器可以检查会议是否真的创建在正确时间、正确参与人、正确标题下。

这些验证器形式不同,但本质相同:

它们把模型回答的质量,转成可计算的反馈。

这就是奖励的来源。

在开放问答中,奖励常常来自人类主观偏好或奖励模型近似判断。

在可验证任务中,奖励可以来自更明确的外部检查。

这会让训练目标更具体。

奖励函数怎样写?

可验证强化学习的核心目标可以写成:

$$\max_\theta ; \mathbb{E}_{y \sim P_\theta(\cdot \mid x)} \left[R(x, y)\right]$$
这个公式在第 11 章已经露过面,现在我们正式拆开。

x 是任务输入,例如一道数学题、一道代码题、一个工具调用任务。

y 是模型生成的完整回答。

$P_\theta(\cdot \mid x)$ 是当前模型在任务 x 下生成各种回答的概率分布。

$y \sim P_\theta(\cdot \mid x)$ 表示回答 y 是从模型当前分布中采样或生成出来的。

$R(x,y)$ 是奖励函数。它根据任务和回答,给出一个奖励值。

E[...] 是期望,表示模型要提高自己生成分布下的平均奖励。

用自然语言说:

让模型更倾向于生成那些能获得高奖励的完整回答。

这和前面几个阶段都不一样。

预训练是:

真实下一个 token 的概率够不够高?

SFT 是:

高质量助手回答中的下一个 token 概率够不够高?

DPO 是:

chosen 相对 rejected 是否更有概率优势?

可验证强化学习是:

完整回答经过验证器后,奖励够不够高?

这个变化非常关键。

模型不再只是像某段文本,也不只是偏向某个被人类选择的回答,而是开始围绕任务结果优化。

当然,实际训练仍然会通过 token 概率和参数梯度来完成。模型生成回答还是逐 token 生成,参数更新也仍然改变 token 概率分布。

但训练信号来自完整回答的验证结果。

这就是“回答级奖励”的含义。

主例子:代码测试通过率如何成为 reward

代码题是理解 reward 的最好例子之一。

假设 prompt 是:

写一个 Python 函数,判断一个字符串是否是回文。

模型生成回答 y

def is_palindrome(s):
    return s == s[::-1]

验证器准备一组测试:

is_palindrome("aba") -> True
is_palindrome("abc") -> False
is_palindrome("") -> True
is_palindrome("a") -> True

如果模型代码全部通过,就给高奖励。

如果只通过一部分,就给部分奖励。

一个简单奖励函数可以写成:

$$R(x,y) = \frac{\text{passed_tests}(y)}{\text{total_tests}}$$
如果 4 个测试全部通过,奖励是 1

如果只通过 2 个,奖励是 0.5

如果一个都没通过,奖励是 0

这个奖励比“人觉得这段代码好不好”更明确。

因为测试器能运行代码,能检查输出,能给出可重复结果。

当然,测试通过率不是唯一奖励。

还可以加入:

  • 是否语法正确
  • 是否运行超时
  • 是否内存超限
  • 是否符合函数签名
  • 是否处理边界条件
  • 是否避免危险操作

不同任务可以设计不同奖励。

但代码测试通过率已经足以说明核心机制:

模型生成代码;
测试器运行代码;
测试结果变成奖励;
训练系统提高高奖励代码的概率。

这比 SFT 更接近任务结果。

SFT 可能让模型模仿一段参考代码。但同一个题目可以有很多正确实现。只要通过测试,它们都可能是好答案。

验证器让训练系统不必只问“像不像参考答案”,而是可以问:

能不能完成任务?

奖励回路:生成、验证、更新

有了验证器以后,训练可以形成一个闭环。

第一步,模型生成候选回答。

对于同一个 prompt,模型可以生成多个不同答案:

y_1, y_2, y_3, ...

第二步,验证器评价这些回答。

例如代码题中,测试器运行每段代码,得到不同通过率:

$$R(x,y_1)=1.0, \quad R(x,y_2)=0.4, \quad R(x,y_3)=0.0$$
第三步,训练系统根据奖励更新模型。

高奖励回答对应的生成路径,会被推高概率。低奖励回答对应的生成路径,会被降低概率,或者作为负样本参与偏好训练。

第四步,更新后的模型再次生成回答。

如果训练有效,它会更倾向于生成通过验证的答案。

这个过程可以概括为:

生成 -> 验证 -> 奖励 -> 更新 -> 再生成

这就是可验证强化学习最直观的循环。

这里要注意,参数更新并不是把某个正确答案直接复制进模型。

模型仍然通过梯度更新参数。训练系统只是利用奖励信号,让那些导致高奖励回答的 token 序列在未来更可能出现。

换句话说:

验证器不直接写参数;
验证器提供反馈;
优化器根据反馈更新参数。

这和本书前面讲的所有训练阶段是一致的。

数据和反馈信号决定损失或奖励。损失或奖励决定梯度方向。梯度更新参数。参数改变以后,模型的生成分布改变。

只是到了这里,反馈信号更接近任务结果。

验证器明确,不等于没有风险

外部验证器让奖励更明确,但它也会带来新的风险。

第一,测试覆盖不足。

代码通过了现有测试,不代表它在所有输入下都正确。如果测试只覆盖简单情况,模型可能生成只适配简单情况的代码。

例如,题目要求判断回文,但测试只有:

"aba"
"abc"

模型可能没有处理大小写、空格、标点或 Unicode 字符。它通过了测试,却不一定满足真实需求。

第二,奖励作弊。

如果模型发现某种方式能拿到高奖励,但并没有真正解决问题,它就可能沿着这个方向优化。

比如在某些评测环境里,如果测试器设计有漏洞,模型可能利用漏洞绕过检查。

第三,过拟合验证器。

训练系统如果长期使用同一套测试或同一类验证规则,模型可能越来越擅长适应这些规则,但泛化到新任务时未必可靠。

第四,验证目标可能太窄。

代码通过测试,但可读性差、安全性差、维护性差。数学答案正确,但解释过程混乱。工具调用完成了表面目标,但没有考虑用户真实意图。

这说明,验证器提供的是某个角度的反馈,不是任务质量的全部。

所以,外部验证器的设计非常关键。

奖励函数写得越清楚,模型越容易朝正确方向优化。

奖励函数有漏洞,模型就可能把漏洞放大。

这和第 9 章讲的奖励模型问题本质相通:

模型会优化你给它的目标,
但这个目标未必完全等于你真正想要的东西。

在可验证强化学习中,这句话尤其重要。

因为验证器越自动化,训练规模越大;训练规模越大,奖励设计中的小漏洞越可能被放大。

只奖励最终答案够不够?

到这里,我们还需要提出一个新问题:

如果奖励只看最终答案,够不够?

对于某些任务,最终答案奖励已经很有用。

代码通过测试,就是好信号。数学答案正确,也是好信号。

但在复杂推理任务中,只看最终答案可能不够。

模型可能最终答对,但中间过程是错的。

模型也可能最终答错,但前面推理有一部分是对的。

如果训练系统只给最终结果奖励,就会丢失很多过程信息。

这就引出思维链和过程奖励。

思维链让模型把中间推理过程写出来。过程一旦变成可见文本,就有可能被评价:

这一步推理是否成立?
这个中间结论是否正确?
模型有没有跳步?
有没有使用错误假设?

于是奖励函数不一定只能看最终答案,也可以尝试评价推理过程。

这不是说所有思维链都应该被展示给用户,也不是说长推理一定更好。本书后面会保持克制地讲这个问题。

但从训练目标角度看,思维链打开了一个新对象:

不只奖励结果,
也可能奖励过程。

这正是第 13 章要讨论的内容。

本章小结

本章讲清楚了外部验证器怎样产生奖励。

模型生成完整回答以后,验证器可以检查答案是否正确、代码是否通过测试、工具调用是否完成目标。验证结果被转成 $R(x,y)$,训练目标就可以写成最大化模型生成回答的期望奖励。和 SFT、DPO 相比,这一步更接近任务结果:不只是像不像答案,也不只是哪个更被偏好,而是回答能不能通过某种外部检查。

但验证器不是完美裁判。测试覆盖不足、奖励作弊、过拟合验证器和目标过窄,都会让模型沿着错误方向优化。奖励越明确,越需要谨慎设计。

下一章预告

下一章我们进入思维链。到目前为止,奖励大多盯着最终答案:通过还是失败,正确还是错误。但复杂推理里,中间过程也很重要。思维链让这些过程变得可见,于是问题变成:我们应该只奖励最终答案,还是也奖励推理过程?

延伸阅读

推荐阅读方向:

  • 奖励函数设计和 reward hacking 相关资料。
  • 代码测试通过率作为训练信号的案例。
  • 外部工具、验证器和强化学习训练闭环相关资料。

第 13 章:思维链:奖励最终答案,还是奖励推理过程

本章问题

思维链为什么能提升大模型解题能力?它到底是提示技巧、训练数据,还是奖励函数的一部分?

章节摘要

这一章解释思维链,也就是让模型显式写出中间推理步骤。我们要避免把它神化成“模型突然会思考了”:更准确地说,思维链让原本隐藏在生成过程中的中间步骤变成可见文本,从而给模型更多计算和自我校正的空间。在训练层面,思维链可以通过 SFT 让模型模仿高质量推理过程,也可以在强化学习中进入奖励设计:奖励可以只看最终答案是否正确,也可以进一步评价推理步骤是否合理,这对应结果奖励和过程奖励的差别。

读者收获

读者应理解:思维链不是神秘思考能力,而是让中间推理过程显式化;它可以作为提示技巧、训练数据,也可以成为过程奖励的评价对象。

配图

思维链与过程奖励

图 13-1|思维链与过程奖励:思维链让中间推理过程变得可见,从而可能被监督、比较和奖励。

读图:从答案奖励到过程奖励

这张图把两种奖励方式放在一起:一种只看最终答案,另一种尝试评价中间推理过程。

读图时要理解思维链的训练意义。思维链不是神秘意识,而是让中间步骤变得可见;一旦过程可见,它就可能被模仿、比较或奖励。

正文

思维链为什么容易被神化?

思维链是大模型里最容易被神化的概念之一。

很多人第一次看到模型写出一长串推理步骤,会有一种感觉:它好像真的在思考。

比如用户问一道数学题,模型不直接给答案,而是写:

第一步,我们先设未知数……
第二步,把条件代入方程……
第三步,化简得到……
所以最终答案是……

这种输出确实比直接冒出一个答案更像人类解题。

但我们需要保持本书一贯的清醒视角。

思维链不是神秘意识,也不是模型突然获得了某种不可解释的内在思想。

更准确地说:

思维链是把中间推理过程显式写成文本。

这件事有几个实际效果。

第一,它给模型更多生成空间。

如果模型必须一步给出最终答案,复杂问题很容易出错。让模型先写中间步骤,相当于把一个大问题拆成多个小问题。每一步的文本又会成为后续 token 的上下文,帮助模型继续生成。

第二,它让模型有机会自我校正。

当中间步骤写出来以后,后面的生成可以参考前面的推导。如果前面列出了条件,后面就更容易避免漏掉条件。

第三,它让训练系统有可能观察过程。

如果模型只输出最终答案,训练系统只能奖励结果。如果模型输出中间过程,训练系统就多了一个可以监督、比较或奖励的对象。

这第三点,是本章最重要的部分。

思维链不只是提示技巧。它也可以是训练数据的一部分,还可以进入奖励函数。

我们要从三个层次理解它:

提示技巧:让模型按步骤回答。
训练数据:让模型模仿高质量推理过程。
奖励对象:让训练系统评价中间推理是否合理。

这三个层次不要混在一起。

同样叫思维链,它在不同训练阶段扮演的角色并不相同。

思维链的第一种角色:提示技巧

最容易理解的一种思维链,是提示技巧。

也就是在 prompt 里要求模型逐步分析。

例如:

请一步一步分析这道题。

或者:

先列出已知条件,再推导答案。

这类提示可能让模型生成更有结构的中间步骤。对于数学题、逻辑题、规划任务和复杂解释任务,它有时会明显改善结果。

为什么?

从自回归生成角度看,模型每生成一个 token,都会把前面生成的内容作为新的上下文。中间步骤一旦写出来,就不只是展示给读者看,也会成为模型后续生成的条件。

这就像给模型搭了一条文本脚手架。

如果没有中间步骤,模型要从 prompt 直接跳到 final answer。这个跨度很大。

如果有中间步骤,模型可以先生成局部结论,再基于局部结论继续推理。

不过,提示层面的思维链没有改变模型参数。

它只是改变了输入指令和输出格式。

模型之所以能响应这种提示,是因为它在预训练、SFT 或后训练中已经见过大量分步骤解释、解题过程、教程文本和推理样式。

所以,提示技巧不是凭空创造能力,而是在调用和组织已有能力。

这一点要说清楚。

思维链的第二种角色:训练数据

思维链还可以作为训练数据。

在 SFT 阶段,我们不一定只给模型最终答案,也可以给它完整解题过程。

数据形态可以写成:

prompt:
一道数学题或推理题

reasoning steps:
第一步……
第二步……
第三步……

final answer:
最终答案……

如果把 reasoning steps 和 final answer 一起作为目标文本,模型训练时就会逐 token 模仿整个推理过程。

这仍然是监督学习。

公式上可以理解为:

$$\mathcal{L}_{\text{sft-cot}} = -\sum_t \log P_\theta(z_t \mid \text{prompt}, z_{<t})$$
这里 z 表示包含推理过程和最终答案的完整目标文本。

它和普通 SFT 的区别,不在于损失函数突然变了,而在于目标文本的内容变了。

普通 SFT 可能是:

prompt -> final answer

带思维链的 SFT 是:

prompt -> reasoning chain -> final answer

这会塑造模型的输出习惯。

模型会学会在某些问题上先分析,再回答;先拆条件,再求解;先解释原因,再给结论。

这对复杂任务很有帮助。

但这里也要注意:模仿推理过程,不等于推理过程一定正确。

如果训练数据中的推理过程质量高,模型会受益。

如果训练数据中充满看似合理但实际有漏洞的推理,模型也可能学到这种坏习惯。

所以,思维链训练数据的关键不是“步骤越长越好”,而是“步骤是否可靠、清楚、能推动问题解决”。

思维链的第三种角色:奖励对象

思维链最值得放进本书主线的地方,是奖励函数。

第 12 章讲外部验证器时,我们主要讨论最终答案奖励。

例如:

数学题答案正确 -> 奖励高
代码测试通过 -> 奖励高

这叫结果奖励。

它只关心最终结果。

但复杂任务里,结果奖励有时不够。

比如一道数学题,模型最终答案错了,但前面有几步推理是对的。如果只给 0 分,训练系统就丢掉了这些有用信息。

反过来,模型最终答案对了,但中间过程乱写,只是碰巧猜对。如果只给 1 分,训练系统可能强化了不可靠的路径。

这时,我们就会问:

能不能评价中间推理过程?

思维链让这个问题成为可能。

因为中间过程被写出来了,训练系统就可以尝试评价:

  • 每一步是否符合题目条件
  • 中间结论是否正确
  • 是否出现逻辑跳跃
  • 是否使用了错误公式
  • 是否遗漏关键约束
  • 过程是否能支持最终答案

这就是过程奖励。

结果奖励问:

最终答案对不对?

过程奖励问:

推理过程是否也合理?

这和本书主线正好对应。

训练目标不再只看最后输出,而是开始关注生成过程中的中间结构。

公式:最终奖励与过程奖励

我们可以用一个简化公式表达最终奖励和过程奖励的组合:

R_total(x, c, y) = α R_final(x, y) + (1 - α) R_process(x, c)

逐项解释。

x 是用户问题或任务。

c 是模型生成的中间推理过程,也就是思维链。

y 是模型最终给出的答案。

$R_{\text{final}}(x,y)$ 是最终答案奖励。

例如数学答案是否正确,代码是否通过测试,工具调用是否完成目标。

$R_{\text{process}}(x,c)$ 是过程奖励。

它评价中间推理步骤是否合理,是否遵守题目条件,是否逐步推进,是否有明显错误。

α 是一个权重,控制最终答案奖励和过程奖励的比例。

如果 α 接近 1,训练更重视最终答案。

如果 α 较小,过程奖励占比更高。

这个公式的直觉是:

一个回答不只看最后结果,
也可以看它是怎样走到这个结果的。

当然,现实中过程奖励不一定真的用这么简单的线性加权。这里的公式是为了帮助读者理解思想。

真正困难的地方在于:

R_process(x, c) 怎么得到?

最终答案有时可以自动验证。代码能不能通过测试,数学最终答案能不能匹配,这些相对容易。

但推理过程是否合理,往往更难判断。

一个步骤看起来像数学推导,未必真的成立。

一个解释看起来清楚,未必逻辑严密。

一个过程很长,未必比短过程更好。

所以,过程奖励有吸引力,也有难点。

过程奖励的难点

过程奖励听起来很美,但它很难。

第一,中间步骤不一定容易评价。

最终答案是 42,我们可以比较是否正确。但一段推理过程可能有很多自然语言描述、公式变形、隐含假设和跳步。要判断每一步是否可靠,比判断最终答案复杂得多。

第二,过程可能看起来合理但实际错误。

大模型很擅长生成流畅文本。它可以写出很像推理的内容,但其中某一步使用了错误前提。人类读者如果不仔细检查,可能会被表面结构迷惑。

第三,过程奖励可能鼓励形式主义。

如果奖励系统偏爱长步骤,模型可能生成冗长推理。如果奖励系统偏爱固定格式,模型可能套模板,而不是更好地解决问题。

第四,有些任务不适合展示完整过程。

在实际产品中,模型可以进行内部计算、草稿推理或工具调用,但最终给用户的回答不一定要暴露全部中间过程。用户需要的是可靠、清楚、合适的答案,而不是永远读一长串推导。

所以,思维链要分清两个层面:

训练中是否利用中间过程;
回答中是否完整展示中间过程。

这两件事不完全相同。

训练可以利用过程监督,最终回答可以只给必要解释。

第五,过程奖励仍然依赖评价器质量。

如果过程评价器不可靠,模型可能学会写出评价器喜欢的推理样式,而不是真正更可靠的推理。

这和前面讲过的奖励模型、验证器风险是一脉相承的。

模型会优化奖励函数。

奖励函数如果没有准确表达我们真正想要的东西,模型就可能优化出偏差。

因此,本章对思维链的结论应该保持平衡:

思维链很重要,
因为它让中间过程变得可见、可模仿、可评价。

但思维链不是魔法,
关键仍然是过程能否被可靠监督和奖励。

这才是把思维链放回训练目标主线的正确位置。

本章小结

本章讲清楚了思维链的三个角色。

作为提示技巧,思维链让模型按步骤生成,给复杂任务更多文本空间。作为训练数据,思维链可以进入 SFT,让模型模仿高质量推理过程。作为奖励对象,思维链让中间过程变得可见,从而可能被过程奖励监督。

核心公式是:

R_total(x, c, y) = α R_final(x, y) + (1 - α) R_process(x, c)

它表达的是:训练不一定只奖励最终答案,也可以尝试奖励推理过程。但真正难点在于过程奖励是否可靠。长推理不一定正确,看似清楚的步骤也可能有漏洞。

下一章预告

下一章我们进入自生成样本。既然模型可以生成答案,也可以生成推理过程;既然数学和代码可以被验证,那么模型能不能自己生成大量练习,再用验证器筛选哪些值得学习?这会把训练材料的来源进一步打开。

延伸阅读

推荐阅读方向:

  • Chain-of-thought prompting 原始论文和后续研究。
  • Outcome supervision 与 process supervision 对比资料。
  • 推理过程数据构造、过程奖励和可验证推理训练资料。

第 14 章:自生成样本:模型如何自己练习

本章问题

模型自己生成题目、答案或推理过程,再用验证器筛选,这算不算真正的“自我学习”?

章节摘要

这一章解释自生成样本和自我练习。模型可以生成大量候选答案、解题过程或代码方案,再通过验证器、测试器、奖励模型或其他筛选机制判断哪些更好。好样本可以用于继续训练,差样本也可以作为偏好优化中的 rejected answer。这里还要解释一个现象:为什么大模型近年在编程和奥数这类领域进步很快?一个重要原因是,这些领域的训练样本更容易由 AI 大量生成,再由测试器、判题器、形式化验证或答案检查机制筛选质量。

读者收获

读者应理解:自生成样本的关键不在模型自己生成,而在生成后能否被可靠验证和筛选;编程和数学任务适合形成生成、验证、反馈、更新闭环。

配图

自生成样本闭环

图 14-1|自生成样本闭环:真正起作用的是生成、筛选、反馈和参数更新,而不是神秘自我进化。

读图:生成、筛选、反馈、更新

这张图展示自生成样本真正起作用的闭环:模型生成候选样本,验证器或筛选器判断质量,训练系统利用高质量反馈更新模型。

读图时要避免一个误解:自生成不等于自动变聪明。真正关键的是筛选和反馈机制,只有好坏能被区分,生成出来的样本才可能转化为有效训练信号。

正文

“模型自己练习”到底是什么意思?

这一章我们讲一个很容易被说得玄乎的话题:模型自己练习。

有些说法会把它描述成“模型开始自我进化”。这个说法听起来很有冲击力,但不够准确。

更清楚的表达应该是:

模型可以生成大量候选样本,
再由外部验证器、奖励模型或筛选机制判断哪些值得学习。

这里真正起作用的,不是模型凭空知道自己变聪明了。

真正起作用的是一个闭环:

生成 -> 筛选 -> 反馈 -> 参数更新

模型可以生成很多东西:

  • 数学题的答案
  • 解题过程
  • 代码方案
  • 测试用例
  • 题目变体
  • 问题改写
  • 候选回答

但生成出来,不代表可靠。

一个模型可以生成十个解法,其中九个是错的。也可以生成一段看似完整的推理,其实中间有漏洞。还可以生成一段代码,语法正确但逻辑错误。

所以,自生成样本的关键不在“生成”,而在“筛选”。

如果没有可靠筛选,模型生成的大量样本可能只是大量噪声。

如果有可靠筛选,情况就不同了。

模型可以大量尝试,验证器从中找出成功样本。训练系统再把成功样本用于继续训练,把失败样本用于对比或惩罚。这样,模型就有机会从自己的尝试中受益。

这就是所谓“自己练习”的技术含义。

不是模型自己给自己颁奖。

而是模型生成候选,外部机制提供反馈,优化器更新参数。

自生成样本的基本闭环

自生成样本可以用一个简单流程表示:

1. 模型生成大量候选
2. 验证器或筛选器评价候选
3. 选出好样本,标记差样本
4. 用这些样本继续训练模型

这个流程可以服务不同训练目标。

如果筛选器找到了高质量答案,可以把它们当作 SFT 数据:

prompt -> high-quality answer

如果同一个 prompt 下有好答案和坏答案,可以构造偏好对:

prompt -> chosen / rejected

如果验证器能直接给分,可以进入强化学习:

prompt -> answer -> reward

所以,自生成样本不是单独的一种训练目标。

它更像一种扩大训练材料来源的方法。

真正决定训练方式的,仍然是反馈信号:

如果反馈是标准答案,就做 SFT。
如果反馈是偏好对,就做 DPO/RLHF。
如果反馈是奖励,就做强化学习。

这和前面纠正 DPO 误解时说的是同一个逻辑。

样本是不是模型生成的,不自动决定损失函数。

关键要看这些样本后来被怎样评价、怎样组织、怎样进入优化目标。

候选答案:一次生成多个可能解

为什么自生成样本常常要一次生成多个候选?

因为单次生成不可靠。

对于一道数学题,模型第一次可能算错,第二次可能走另一条路,第三次可能碰巧得到正确答案,第四次可能写出更清楚的推导。

对于一道代码题,模型可能生成多种实现:

y_1: 使用双重循环
y_2: 使用哈希集合
y_3: 使用排序
y_4: 语法错误

这些候选质量不同。

如果我们只看一个答案,错了就结束,信息很少。

如果生成多个答案,再用验证器筛选,就能得到更多训练材料。

这有点像让模型做多次尝试。

在代码任务中,测试器可以选出通过测试的实现。

在数学任务中,答案检查器可以选出最终答案正确的解法。

在开放问答中,奖励模型或人类偏好可以选出更好的回答。

多候选生成带来探索。验证器带来选择。

没有探索,模型很难发现更好的答案。没有选择,模型不知道哪些尝试值得学习。

所以,自生成闭环里,生成和筛选必须同时存在。

编程任务为什么特别适合自生成?

编程任务非常适合自生成样本,原因很直接:代码可以运行。

模型可以为同一个题目生成许多代码方案。测试器可以自动运行这些代码。通过测试的代码,就是相对可靠的训练材料。

例如题目是:

写一个函数,返回数组中只出现一次的数字。

模型可以生成多个实现:

实现 A:用哈希表计数
实现 B:用异或
实现 C:双重循环
实现 D:错误地返回第一个元素

测试器运行一批测试:

[2, 2, 1] -> 1
[4, 1, 2, 1, 2] -> 4
[1] -> 1

通过测试的实现可以作为好样本。失败实现可以作为差样本,或者用于构造 chosen/rejected。

这使编程训练形成非常清楚的闭环:

生成代码 -> 运行测试 -> 通过/失败 -> 继续训练

编程还有另一个优势:错误信息很有价值。

代码不通过测试时,系统可能得到语法错误、运行时错误、断言失败、超时、内存超限等信息。这些信息可以帮助筛选,也可以用于构造更细的训练信号。

这就是为什么大模型在编程领域进步很快的一个重要原因。

不是因为代码任务更像魔法,而是因为代码任务提供了大量可自动验证的反馈。

模型可以生成大量代码。测试器可以快速筛选。通过测试的样本可以继续训练。失败样本也能告诉模型哪些路径不好。

这种数据闭环非常强。

当然,它仍然有局限。

测试覆盖不足,模型可能过拟合测试。代码通过样例,不代表工程质量好。某些任务需要安全性、可维护性和上下文理解,这些不一定能被简单测试捕捉。

但即便如此,编程任务的反馈密度仍然比普通开放问答高得多。

奥数/数学任务为什么也适合?

数学任务,尤其是有明确答案的数学题,也适合自生成样本。

原因和编程类似:很多结果可以验证。

模型可以生成题目变体,也可以生成多个解法。验证器可以检查最终答案,或者检查某些中间步骤。

例如一个代数题,模型可以生成不同解法:

解法 A:直接化简
解法 B:代入变量
解法 C:错误移项

如果最终答案可以代回原题验证,那么系统就能筛选出正确解法。

对于选择题和填空题,答案检查更直接。

对于证明题,情况复杂一些。证明过程是否正确,常常难以自动判断。但如果能使用形式化证明系统,或者把题目限制在某些可检查范围内,就可以得到更强反馈。

这说明,数学任务内部也有层次。

简单计算和明确答案题更容易验证。

复杂证明和开放式解法更难验证。

但总体上,数学比普通开放问答更容易形成训练闭环:

生成解法 -> 检查答案/过程 -> 筛选正确样本 -> 继续训练

这也是为什么奥数、竞赛数学、形式化数学等方向常常被用来推动模型推理能力。

关键不是“数学天然让模型聪明”,而是数学提供了更清楚的反馈信号。

训练系统最需要的,就是这种清楚反馈。

自生成样本怎样进入不同训练目标?

自生成样本生成以后,可以走不同路径。

第一条路径:进入 SFT。

如果一个模型生成的答案经过验证后质量很高,就可以把它作为示范答案:

prompt -> verified answer

然后用 SFT 目标训练模型模仿。

第二条路径:进入偏好优化。

如果同一个 prompt 下有一个通过验证的答案和一个失败答案,就可以构造偏好对:

chosen: 通过验证的回答
rejected: 没有通过验证的回答

这可以用于 DPO 或 RLHF 的偏好数据。

第三条路径:进入强化学习。

如果验证器能直接给出奖励分数,就可以用:

$$R(x,y)$$
来训练模型追求更高奖励。

比如代码题用测试通过率:

$$R(x,y) = \frac{\text{passed_tests}(y)}{\text{total_tests}}$$
数学题用答案正确性:

$$R(x,y) \in {0,1}$$
这三条路径说明,自生成样本本身不是答案。

它只是原材料。

真正决定训练意义的是后面的评价和组织方式。

这也是本章最重要的判断:

样本可以由模型生成,
但训练信号不能凭空可靠。

可靠性来自验证器、筛选器、偏好标注、奖励模型或其他评价机制。

如果评价机制可靠,自生成样本能放大训练规模。

如果评价机制不可靠,自生成样本也会放大错误。

克制提醒:自生成不等于自证正确

最后必须提醒:自生成不等于自证正确。

模型生成的样本可能有很多问题。

第一,错误会被复制。

如果模型本身有某类系统性错误,它生成的数据也可能带着同样错误。没有外部筛选,这些错误可能进入训练,进一步强化。

第二,样本可能变窄。

模型倾向于生成自己熟悉的模式。自生成数据如果缺少多样性,可能让训练分布越来越窄。

第三,验证器漏洞会被放大。

如果筛选机制有漏洞,模型生成的数据可能越来越适应漏洞,而不是适应真实任务。

第四,高质量筛选仍然昂贵。

代码测试相对容易,但现实任务、复杂推理、开放问答、专业领域判断,仍然需要高质量评价机制。

所以,自生成样本不是一条绕过反馈信号的捷径。

它恰恰更依赖反馈信号。

生成越多,越需要可靠筛选。

否则,规模只会放大噪声。

这也是本书主线的延续:

大模型能力提升,不只是因为数据更多,
而是因为训练信号更清楚,优化目标更有效。

自生成样本可以让数据变多。

验证器和奖励函数决定这些数据能不能变成有效训练。

本章小结

本章讲清楚了自生成样本的真正含义。

模型可以生成大量候选答案、代码、解题过程和题目变体,但生成不等于可靠。真正让自生成样本有价值的,是验证器、测试器、奖励模型或人类筛选把好样本和差样本区分开。好样本可以用于 SFT,成对样本可以用于 DPO/RLHF,奖励分数可以用于强化学习。

编程和数学任务之所以进步快,一个重要原因是它们更容易形成“生成、验证、反馈、更新”的闭环。代码可以跑测试,数学答案可以检查,这让训练信号比普通开放问答更明确。

下一章预告

下一章我们把前面所有公式放在一起对比:预训练、SFT、DPO、可验证强化学习、过程奖励,它们到底分别在优化什么?到那里,全书的训练目标主线会完成一次总收束。

延伸阅读

推荐阅读方向:

  • Self-training、自生成数据和 rejection sampling 相关资料。
  • Best-of-N、verifier-guided generation 和自动筛选样本方法。
  • 代码与数学任务中的生成-验证-训练闭环案例。

第 15 章:公式对比:从 token 级损失到回答级奖励

本章问题

预训练、SFT、DPO、可验证强化学习和过程奖励的公式,究竟体现了哪些不同的训练目标?

章节摘要

这一章是全书的公式收束章。我们把几类目标并排放在一起:预训练优化真实下一个 token 的概率;SFT 优化高质量回答 token 的概率;DPO 优化 chosen answer 相对 rejected answer 的概率优势;可验证强化学习优化完整回答的期望奖励;思维链相关训练则进一步提醒我们,奖励可以只看最终结果,也可以尝试评价中间推理过程。重点不是堆公式,而是让读者真正看见:公式里的训练单位从 token 到回答,再到推理过程,训练信号从文本到偏好再到验证奖励,优化目标也随之改变。

读者收获

读者应理解:预训练、SFT、DPO、奖励最大化和过程奖励不是同一个公式换名字,而是训练信号、优化对象和反馈粒度逐步变化。

配图

训练目标公式对比

图 15-1|训练目标公式对比:不同公式对应不同训练信号、优化对象和反馈粒度。

读图:训练目标的层层变化

这张图把预训练、SFT、DPO、奖励最大化和过程奖励放在一起比较。它展示的不只是公式不同,而是训练信号、优化对象和反馈粒度都在变化。

读图时可以沿着一条线看:从 token 级损失,到回答级偏好,再到完整回答奖励,最后扩展到中间推理过程。全书的公式主线在这里完成收束。

正文

为什么要做公式总对比?

到这一章,我们已经走过了全书最重要的技术路径。

预训练、SFT、偏好对、RLHF、DPO、可验证强化学习、思维链和自生成样本,都已经出现过。

如果只记算法名,读者很容易混乱。

这些词看起来很多,但本书真正想讲的是一条主线:

训练信号变了,优化目标就变了;
优化目标变了,模型行为也会变。

公式的意义就在这里。

公式不是为了显得专业,也不是为了把读者挡在门外。公式最有价值的地方,是它能把一句模糊的话变成一个清楚的问题。

比如:

预训练让模型学习语言。

这句话太泛。

公式会把它变成:

给定前文,模型有没有提高真实下一个 token 的概率?

再比如:

强化学习让模型更会解题。

这句话也太泛。

公式会把它变成:

模型生成的完整回答,能不能获得更高奖励?

所以,这一章不是数学炫技。

它是全书的显微镜。

我们把几个关键公式并排放在一起,看看每个阶段到底在问什么问题。

预训练:真实下一个 token 的概率

预训练公式是全书的起点:

$$\mathcal{L}_{\text{pretrain}} = -\sum_t \log P_\theta(x_t \mid x_{<t})$$
它的训练数据是大规模文本。

这些文本经过清洗、去重、切分和 token 化以后,变成 token 序列:

x_1, x_2, x_3, ..., x_t

模型看到前文 $x_{<t}$,要预测真实下一个 token $x_t$。

反馈信号是什么?

就是训练文本里真实出现的 token。

模型给真实 token 的概率高,损失小。

模型给真实 token 的概率低,损失大。

所以,预训练的优化目标可以用一句话概括:

让模型更像原始文本分布。

这里的“像”,不是模仿某一个答案,而是整体上学习文本世界里的统计规律。

它带来的能力非常基础,也非常重要。

语言能力、事实模式、代码结构、推理痕迹、写作风格,都可能通过这个目标被吸收进参数。

但预训练并没有直接问:

这个回答对用户有没有帮助?

也没有问:

这个完整答案是否正确?

它问的是 token 级问题:

下一个 token 像不像真实文本?

这就是预训练的边界。

SFT:高质量回答中的下一个 token

SFT 公式和预训练很像:

$$\mathcal{L}_{\text{sft}} = -\sum_t \log P_\theta(y_t \mid \text{prompt}, y_{<t})$$
这里的数据不再是普通互联网文本,而是指令-回答样本。

prompt 是用户问题。

y 是高质量助手回答。

$y_t$ 是回答中的第 t 个 token。

$y_{<t}$ 是回答前文。

反馈信号是什么?

是示范回答中的真实 token。

模型在看到 prompt 和已有回答前文后,要提高示范回答下一个 token 的概率。

所以,SFT 的优化目标可以概括为:

让模型更像高质量助手答案。

它和预训练的相同点是:仍然逐 token 做最大似然或交叉熵式训练。

它和预训练的不同点是:目标文本变了。

预训练目标文本是普通文本。

SFT 目标文本是高质量回答。

这一变化会显著改变模型行为。

模型不只是学会续写文本,而是学会按用户指令回答,学会使用助手式结构,学会更稳定地完成任务。

但 SFT 仍然没有直接比较多个回答。

它仍然主要问:

这个示范答案里的下一个 token,概率够不够高?

它还没有问:

两个回答里,哪个更好?

这就是 DPO 和 RLHF 出场的原因。

DPO:chosen 相对 rejected 的优势

DPO 的公式是:

$$\mathcal{L}_{\text{dpo}} = -\log \sigma\left(\beta\left[ \log \frac{P_\theta(y_w \mid x)}{P_{\text{ref}}(y_w \mid x)} - \log \frac{P_\theta(y_l \mid x)}{P_{\text{ref}}(y_l \mid x)} \right]\right)$$
这一章不再重新推导,只抓住对比。

DPO 的数据是偏好对:

x: prompt
y_w: chosen / winner
y_l: rejected / loser

反馈信号是什么?

不是一个标准答案,而是一个偏好关系:

y_w 比 y_l 更好。

优化目标是什么?

提高 chosen 相对于 rejected 的概率优势。

这里要注意,DPO 不只是提高 chosen 的概率。

它看的是当前模型相对于参考模型的变化:

$$\log \frac{P_\theta(y \mid x)}{P_{\text{ref}}(y \mid x)}$$
如果当前模型比参考模型更倾向于某个回答,这个值就更大。

这里的 $P_\theta(y \mid x)$ 表示整段回答的概率。底层仍然可以拆成 token 概率,工程实现中通常用 token log probability 求和来计算。也就是说,DPO 虽然讨论的是完整回答之间的偏好,但参数更新最终仍然通过 token 概率的变化来完成。

DPO 希望 chosen 的这个相对优势,大于 rejected 的相对优势。

所以,DPO 问的问题是:

同一个 prompt 下,模型是否更应该生成 chosen,而不是 rejected?

这和 SFT 的区别非常明显。

SFT 面对一个答案。

DPO 面对两个回答之间的偏好关系。

SFT 是模仿。

DPO 是相对偏好优化。

这也是为什么我们说,DPO 的关键不是“样本由 AI 生成”,而是“训练信号变成了偏好关系”。

奖励最大化:完整回答值不值得奖励

可验证强化学习的核心目标是:

$$\max_\theta ; \mathbb{E}_{y \sim P_\theta(\cdot \mid x)} \left[R(x, y)\right]$$
这里的数据形态又变了。

模型面对任务 x,自己生成完整回答 y。然后外部验证器、奖励函数或评价系统给出 $R(x,y)$。

反馈信号是什么?

是完整回答的奖励。

代码能不能通过测试,数学答案是否正确,工具调用是否完成目标,都可以变成奖励。

优化目标是什么?

让模型生成的回答获得更高平均奖励。

这和预训练、SFT 的 token 级损失不同。

预训练和 SFT 都有一个目标文本,模型逐 token 模仿。

奖励最大化不要求回答长得像某个标准答案。它关心任务结果。

代码可以有很多写法。只要通过测试,都可能获得高奖励。

数学题可以有不同解法。只要答案正确,奖励可以很高。

所以,可验证强化学习问的是:

这个完整回答值不值得奖励?

这就是从 token 级预测到回答级评价的关键变化。

KL 约束:为什么不能只追求奖励

如果只追求奖励,模型可能偏离太远。

所以后训练中常常会加入 KL 约束:

$$\max_\theta ; \mathbb{E}\left[R(x, y)\right] - \beta,\mathrm{KL}\left(P_\theta(\cdot \mid x) \Vert P_{\text{ref}}(\cdot \mid x)\right)$$
这里的第一项 $\mathbb{E}[R(x,y)]$ 表示希望提高平均奖励。

第二项 $\mathrm{KL}\left(P_\theta(\cdot \mid x) \Vert P_{\text{ref}}(\cdot \mid x)\right)$ 表示当前模型分布和参考模型分布之间的距离。

$\beta$ 控制约束强度。

这个公式问的是:

在提高奖励的同时,模型有没有偏离参考模型太远?

为什么要这样做?

因为奖励函数可能有漏洞。

奖励模型可能偏爱某种口吻。测试器可能覆盖不足。验证器可能只看最终答案,不看过程质量。

如果模型只追求奖励,就可能学会钻空子。

KL 约束的作用,是让模型在改变行为时保持稳定,不要为了奖励把原有语言能力和分布破坏掉。

它提醒我们:

后训练不是无限推高某个分数,
而是在奖励和稳定性之间做平衡。

过程奖励:思维链进入公式

思维链和过程奖励可以用这个简化公式表示:

R_total(x, c, y) = α R_final(x, y) + (1 - α) R_process(x, c)

这里的数据包含三个部分:

x 是任务。

c 是中间推理过程。

y 是最终答案。

反馈信号也分成两类:

$R_{\text{final}}(x,y)$ 看最终答案。

$R_{\text{process}}(x,c)$ 看推理过程。

这个公式问的问题是:

我们只奖励最终答案,
还是也奖励中间推理过程?

这是比回答级奖励更细的一步。

结果奖励只看最后。

过程奖励试图看路径。

如果模型最终答案正确,但过程胡乱编造,过程奖励可以降低总分。

如果模型最终答案错误,但前几步推理正确,过程奖励可以保留一部分有价值信号。

当然,过程奖励很难。

因为评价中间推理往往比评价最终答案更复杂。推理过程可能看起来合理但实际有错。长步骤也不一定更好。

所以,这个公式的重点不是说过程奖励已经完美解决了推理问题,而是说明训练目标可以继续细化:

从最终答案,推进到中间过程。

公式背后的统一问题

现在我们可以把全书核心问题压缩成一组对比。

预训练问:

下一个 token 像不像原始文本?

SFT 问:

下一个 token 像不像高质量助手答案?

DPO 问:

同一个 prompt 下,chosen 是否比 rejected 更应该被生成?

RLHF / 可验证强化学习问:

完整回答能不能获得更高奖励?

过程奖励问:

中间推理过程是否也值得奖励?

这几句话,就是本书最重要的公式解释。

我们不是在背算法名。

我们是在看每个阶段到底把什么当作反馈,推动参数往哪里变。

一旦读者理解这一点,就能更清楚地判断各种新方法。

以后再遇到一个新名词,不必先被名字吓住。可以先问四个问题:

数据是什么?
反馈信号是什么?
优化目标是什么?
参数更新想让模型更倾向于什么?

只要能回答这四个问题,大多数训练方法的骨架就会显露出来。

这也是本书想交给读者的理解方式。

本章小结

本章把前面所有关键公式做了一次总对比。

预训练优化真实下一个 token 的概率,SFT 优化高质量回答 token 的概率,DPO 优化 chosen 相对 rejected 的概率优势,可验证强化学习优化完整回答的期望奖励,过程奖励则进一步把中间推理过程纳入评价。

这些公式不是同一个目标换名字,而是训练信号和优化对象在逐步变化。大模型训练的演化,不只是数据越来越多,而是反馈越来越明确,目标越来越接近我们真正想要的行为。

下一章预告

下一章进入结语。技术公式已经收束,接下来我们回到全书总判断:大模型既不是魔法,也不是简单的文字接龙玩具。它是一套被数据、反馈信号、优化目标和参数更新共同塑造出来的系统。

延伸阅读

推荐阅读方向:

  • 最大似然、交叉熵、KL 散度和奖励最大化的基础资料。
  • SFT、RLHF、DPO 和可验证强化学习的对比性介绍。
  • 过程监督、奖励模型和验证器训练相关综述资料。

第 16 章:结语:模型能力来自优化目标的层层改变

本章问题

读完全书以后,我们应该怎样重新理解大模型:它到底是知识库、工具、智能体,还是被训练目标塑造出来的系统?

章节摘要

这一章收束全书。我们回顾四层训练:预训练让模型学会语言和世界模式,SFT 让模型学会助手式回答,RLHF/DPO 让模型学会偏向被认可的回答,可验证强化学习让模型在明确奖励下继续改进。思维链则提醒我们,模型能力不只体现在最终答案,也可能体现在中间推理过程能否被表达、评价和优化。结语要保持克制乐观:大模型的能力是真实增长的,但它不是魔法,也不是自动通向真理的机器。

读者收获

读者应理解:大模型是被训练目标塑造出来的系统;理解训练目标,比背诵算法名更能帮助判断 AI 的能力、局限和未来方向。

配图

训练目标塑造模型行为

图 16-1|训练目标塑造模型行为:模型行为由数据、反馈信号、优化目标和参数更新共同塑造。

读图:数据、反馈、目标、参数更新

这张图把全书收束成一个总闭环:数据提供样本,反馈信号定义好坏,优化目标把好坏写成可训练的方向,参数更新再改变模型未来的输出倾向。

读图时要把它当作理解新技术的检查表。以后遇到新的大模型方法,也可以继续追问:数据是什么,反馈是什么,目标是什么,参数被推向哪里?

正文

大模型不是单一标签能解释的东西

读到这里,我们可以回到一个最初的问题:

大模型到底是什么?

它是知识库吗?

有一点像。它确实在预训练中吸收了大量文本里的事实、模式和表达方式。用户问一个问题,它常常能给出看起来像知识检索的回答。

但它不是传统知识库。

知识库通常存储结构化条目,查询时返回明确记录。大模型的知识分布在参数里,通过概率生成表现出来。它不是打开某个表,取出某条记录,而是在给定上下文时生成最可能的 token 序列。

它是工具吗?

也有一点像。它可以写代码、总结文档、解释概念、生成计划、调用外部工具。

但它也不是普通工具。

普通工具通常有明确输入、明确输出和稳定规则。大模型的行为更像一个概率系统:同一个问题可能生成不同回答,输出质量依赖上下文、训练数据、后训练方式和外部工具。

它是智能体吗?

在某些场景下,它可以表现出类似智能体的能力:理解任务、拆解步骤、调用工具、根据反馈调整行为。

但它也不是脱离训练目标的自主生命。

它的能力和行为,仍然深受数据、反馈信号、优化目标和参数更新方式影响。

所以,本书给出的理解方式是:

大模型是被训练目标层层塑造出来的概率生成系统。

这句话不华丽,但很有用。

它既避免把大模型神化,也避免把它贬低成“只是文字接龙”。

回顾四层训练主线

全书的技术主线可以压缩成四层。

第一层是预训练。

预训练问:

下一个 token 像不像原始文本?

模型在海量文本上学习预测真实下一个 token。这个阶段给模型打下语言、知识、代码和模式能力的基础。

第二层是 SFT。

SFT 问:

下一个 token 像不像高质量助手答案?

模型不再只是模仿普通文本,而是模仿高质量 prompt-answer 样本。它开始更像一个助手:更会听指令,更会组织回答,更会按用户需求输出。

第三层是偏好优化,包括 RLHF 和 DPO。

它问:

同一个 prompt 下,哪个回答更值得被偏好?

训练信号从单一标准答案变成 chosen/rejected。模型不只是模仿一个答案,而是学习回答之间的相对质量。

第四层是可验证强化学习。

它问:

完整回答能不能获得奖励,能不能通过验证?

数学题、代码题、工具调用等任务可以提供更明确的反馈。模型生成答案,验证器检查结果,奖励信号再推动参数更新。

思维链则进一步提醒我们:

奖励不一定只能看最终答案,也可能评价中间推理过程。

这四层不是互相替代,而是层层叠加。

预训练提供基础能力。SFT 塑造助手行为。偏好优化调整回答质量。可验证强化学习在明确反馈下继续推动能力。

为什么训练目标比算法名更重要?

AI 技术更新很快,算法名会不断变化。

今天是 RLHF、DPO,明天可能是新的偏好优化方法、新的过程监督方法、新的可验证训练框架。

如果只背算法名,很快就会跟不上。

但如果抓住训练目标,就会稳很多。

以后遇到一个新方法,可以先问四个问题:

数据是什么?
反馈信号是什么?
优化目标是什么?
参数更新想让模型更倾向于什么?

这四个问题,比算法名更接近本质。

如果一个方法使用互联网文本预测下一个 token,它大概率属于预训练或类似目标。

如果一个方法使用高质量问答样本,让模型模仿答案,它接近 SFT。

如果一个方法使用 chosen/rejected,它属于偏好优化。

如果一个方法让模型生成答案,再由验证器给奖励,它就进入强化学习或可验证训练。

如果一个方法评价中间推理步骤,它就涉及过程监督或过程奖励。

这样理解,技术世界就不再是一堆孤立名词。

它变成一张有脉络的地图。

能力增长是真实的,但不是魔法

大模型能力的增长是真实的。

这点不需要否认。

它们确实越来越会写代码、解题、总结、规划、解释和调用工具。某些任务上的进步非常明显。

但真实增长不等于魔法。

这些能力背后有很多因素:

  • 更大规模的数据
  • 更强的模型架构
  • 更大的计算资源
  • 更高质量的 SFT 数据
  • 更好的偏好优化
  • 更明确的验证器
  • 更有效的自生成样本闭环
  • 更细的过程监督

这些因素共同塑造模型行为。

如果只用一句“涌现智能”概括,就会错过真正关键的机制。

如果只用一句“它只是预测下一个词”否定,也会低估训练目标层层变化带来的能力提升。

更准确的态度是:

不神化,也不轻视。

大模型不是神秘生命,但它确实是人类目前构造出的非常强大的学习系统。

理解它,最好的入口不是宣传词,也不是恐惧叙事,而是训练机制。

局限也是真实的

同样,模型局限也是真实的。

预训练数据不是世界真相。

互联网上有错误、偏见、重复、噪声和过时信息。模型从这些数据中学习,就会继承其中一部分问题。

SFT 让模型更像助手,但不保证事实永远正确。

模型可能学会清楚语气,却仍然在事实上出错。

偏好优化让模型更符合人类偏好,但偏好不等于真理。

人类可能偏好流畅、自信、结构漂亮的回答,却没有发现专业错误。

奖励模型和验证器提供反馈,但它们也可能有漏洞。

模型会优化给定目标。如果目标设计不完善,模型可能学会钻空子。

思维链让过程可见,但过程可见不等于过程可靠。

长推理可能只是长,未必正确。

所以,我们理解大模型时,要始终记住:

模型优化什么,就会更倾向于什么;
但我们写下的目标,不一定完全等于我们真正想要的东西。

这句话是很多后训练问题的根源。

读者可以怎样使用这套框架

这本书不是为了让读者记住一串术语。

更希望留下的是一种判断方法。

以后再看到一个新的大模型训练方法,可以先不急着问它是不是最新、是不是最强、是不是某个公司提出的。先问四个更朴素的问题:

第一,训练数据是什么?
第二,反馈信号是什么?
第三,优化目标是什么?
第四,参数更新以后,模型会更倾向于什么?

如果一个方法说自己能提升推理能力,就看它是否提供了更好的推理数据、更可靠的过程反馈,还是只是让模型输出更长步骤。

如果一个方法说自己能提升代码能力,就看它是否引入了测试器、编译器、运行反馈或更多高质量代码数据。

如果一个方法说自己能对齐人类偏好,就看它使用的是人工偏好、奖励模型、DPO 类损失,还是其他偏好优化方式。

如果一个方法说模型可以自我改进,就更要问清楚:谁在筛选样本?谁在给奖励?验证器是否可靠?坏样本会不会被放大?

这些问题听起来朴素,却能挡住很多含混表达。

因为大模型训练最终还是要落到可操作的东西上:数据进入训练系统,反馈信号形成损失或奖励,优化器更新参数,模型生成分布发生改变。

只要抓住这条线,许多复杂概念就不会漂在空中。

本章小结

本章收束了全书技术主线。

大模型不是单一标签能解释的东西。它有知识库的一面,有工具的一面,也可能具备智能体式行为,但更根本地说,它是被训练目标层层塑造出来的概率生成系统。预训练、SFT、偏好优化、可验证强化学习和过程奖励,分别用不同反馈信号推动模型行为变化。

理解大模型,不必先背所有算法名。更重要的是学会追问:数据是什么,反馈信号是什么,优化目标是什么,参数更新想让模型更倾向于什么。

下一章预告

技术部分到这里结束。最后一章是后记,我会回到写作动机:为什么一个曾经学习 AI、后来转到金融领域的人,仍然想花力气把大模型训练这件事讲清楚。

延伸阅读

推荐阅读方向:

  • Transformer、预训练、SFT、RLHF、DPO 和过程监督的关键论文。
  • 大模型训练管线和后训练技术路线综述。
  • 数据治理、奖励设计和可验证强化学习相关资料。

第 17 章:后记:我为什么写这本书

写这本书,并不是因为我想制造一个新的 AI 神话。恰恰相反,我越来越觉得,我们需要少一点神话,多一点清楚。

大模型出现以后,很多讨论变得很极端。一种说法把它讲成近乎魔法的东西,好像模型突然拥有了某种不可解释的意识;另一种说法又把它轻轻放下,说它不过是文字接龙,没有什么值得认真理解。这两种说法,我都不太满意。

大模型当然不是魔法。它的能力来自数据、模型结构、训练目标、反馈信号和大量参数更新。但它也不只是一个可以随便轻视的文字接龙玩具。预训练、SFT、偏好优化、可验证强化学习、自生成样本和工具调用,这些技术层层叠加以后,确实塑造出了非常强的能力。所以,我想写一本书,用尽量简单、清楚、少宣传腔的方式,把这件事讲出来。不是讲行业口号,不是讲未来想象,而是讲一个朴素问题:大模型到底是怎样被训练出来的?

我曾经学习和研究过 AI。那时候的 AI,还没有今天这样进入普通人的日常生活,很多概念更多停留在论文、实验、课程和专业讨论里。后来,我的职业道路发生了变化,转到了金融领域。金融是另一个对模型、数据、风险和决策都非常敏感的行业。进入这个领域以后,我不再以 AI 研究者的身份工作,但我并没有停止关注 AI。

相反,可能正因为离开了纯技术岗位,我反而更能感受到一个问题:很多非 AI 专业的人,也越来越需要理解 AI。他们未必需要训练模型,未必需要手写 Transformer,未必需要推导所有公式。但他们需要知道,大模型的能力从哪里来,它为什么会出错,它为什么能写代码和解题,它为什么需要人类反馈,它为什么在某些任务上进步特别快,它的局限到底在哪里。如果这些问题都只能靠口号回答,普通读者就很难建立自己的判断。

这些年,我一直在跟踪 AI 的技术进展。尤其是大模型出现以后,我持续关注相关论文、技术路线和产业变化。刚开始,很多概念也并不是一下子就完全清楚。预训练、SFT、RLHF、DPO、思维链、过程奖励、可验证强化学习、自生成样本,这些词如果分开看,很容易变成一堆名词。真正让我觉得脉络逐渐清晰的,是把它们放回一个问题里:每个阶段,模型到底在优化什么?

预训练优化下一个 token 的概率。SFT 优化高质量助手答案的概率。DPO 优化 chosen 相对 rejected 的概率优势。可验证强化学习优化完整回答的奖励。思维链让推理过程可能被监督和奖励。一旦从训练目标看,很多技术路线就不再散乱。它们变成了一条线:训练信号越来越明确,优化目标越来越接近我们真正想要的能力。这也是本书的核心主线。

我写这本书时,心里想的读者不是已经熟悉深度学习论文的人。我更希望它能帮助那些不是 AI 专业、但确实想弄懂大模型的人。他们可能来自金融、教育、管理、产品、法律、媒体、工程、研究或其他行业。他们可能已经在工作中使用 AI,也可能只是意识到这项技术会改变很多事情。但他们面对的问题很类似:我不想只听宣传,也不想被公式劝退,我只是想知道它到底怎么回事。

这本书就是为这种需求写的。所以,我尽量不用堆术语的方式写。但我也不想把所有公式都删掉。有些地方,公式比自然语言更诚实。比如预训练公式能清楚告诉我们,模型不是在直接判断回答好不好,而是在提高真实下一个 token 的概率。DPO 公式能清楚告诉我们,它不是简单模仿标准答案,而是在提高 chosen 相对 rejected 的优势。奖励函数能清楚告诉我们,强化学习关心的是完整回答是否值得奖励。所以,本书的写法是:能用通俗语言讲清的,就用通俗语言;必须用公式才能说准确的,就给出公式,再慢慢拆开。

我希望这本书尽量准确。通俗不等于随便。为了让读者容易理解,可以使用类比,但类比之后必须回到技术本体。我也希望这本书尽量清醒,不把 AI 写成神话,也不把 AI 写成笑话。它真实地强,也真实地有限。我还希望这本书尽量有主线。它不是大模型百科,也不是算法手册,而是围绕一个核心问题展开:训练目标怎样一步步改变模型行为。

更重要的是,我希望给读者一种判断方法。以后遇到新的 AI 概念,不妨先问:数据是什么?反馈信号是什么?优化目标是什么?参数更新想让模型更倾向于什么?如果这几个问题能回答出来,很多技术名词就不会那么吓人。

写到这里,技术部分已经结束。如果这本书能让一个原本觉得大模型很神秘的人,看见它背后的训练机制;能让一个原本只会说“它就是预测下一个词”的人,看见后训练目标的层层变化;能让一个非 AI 专业读者建立起自己的判断框架,那它就完成了我最初的目标。

我不认为每个人都必须成为 AI 专家。但在今天,越来越多人需要和 AI 共处、协作、判断它的输出,甚至把它纳入自己的工作流程。这个时候,完全不理解它的基本机制,就容易在两个极端之间摇摆:要么过度相信它,要么轻率否定它。

理解原理,不是为了消除所有不确定性。恰恰相反,是为了更准确地面对不确定性。当我们知道预训练只是预测文本,就不会把模型每一句话都当成事实;当我们知道 SFT 主要塑造回答方式,就不会把礼貌流畅误认为绝对可靠;当我们知道偏好优化来自人类选择,就会记得偏好不等于真理;当我们知道可验证强化学习依赖验证器,就会追问验证器覆盖了什么、漏掉了什么;当我们知道思维链可以被训练和奖励,也会记得长步骤不等于好推理。

这就是我希望这本书带给读者的东西:不是一个简单结论,而是一套看问题的方式。

大模型还会继续变化。新的架构、新的训练方法、新的产品形态都会出现。今天的术语可能很快过时,但“数据是什么、反馈是什么、目标是什么、参数怎样更新”这几个问题不会过时。如果读者以后面对新的 AI 技术,还能想起这几个问题,这本书就没有白写。


《大模型训练之路》术语表

本术语表按全书主线整理:先是模型生成基础,再是预训练、SFT、偏好优化、可验证强化学习和思维链相关概念。每个术语尽量用“通俗解释 + 技术解释”校准含义,避免把算法名当成解释本身。

token

通俗解释:模型处理文本的基本小块。它不一定等于一个汉字、一个词或一个英文单词,可能是字、词的一部分、完整单词、标点或特殊符号。

技术解释:语言模型输入和输出的离散符号单位。文本经过 tokenizer 转换成 token 序列后,模型在每个位置上预测下一个 token 的概率分布。

tokenizer

通俗解释:把人类文本切成 token 的工具。

技术解释:将字符串映射为 token id 序列的编码器,同时也能把 token id 解码回文本。常见方法包括 BPE、WordPiece、SentencePiece 等。

参数

通俗解释:模型内部大量可调整的数字。它们不是一条条知识记录,而是共同决定模型在某个上下文下更倾向于输出什么。

技术解释:神经网络中的权重和偏置等可训练变量,通常记为 $\theta$。训练通过损失或奖励信号更新参数,从而改变模型的输出概率分布。

自回归生成

通俗解释:模型不是一次写完整篇回答,而是生成一个 token,再把它接到上下文后面,继续生成下一个 token。

技术解释:语言模型按照 $P_\theta(y_t \mid x, y_{<t})$ 逐步生成序列。每一步生成都会依赖输入 prompt 和此前已生成的 token。

Transformer

通俗解释:大模型处理上下文的核心结构,让不同位置的 token 可以互相影响。

技术解释:基于自注意力机制的神经网络架构。它通过多层 attention、前馈网络、残差连接和归一化等结构,把 token 序列转换成上下文相关表示。

注意力机制

通俗解释:模型在处理当前 token 时,可以根据上下文判断哪些位置更相关。

技术解释:self-attention 会计算 token 之间的相关性权重,并据此聚合上下文信息。注意力权重是模型计算的一部分,不等于人类意义上的解释或理解。

预训练

通俗解释:用海量文本训练模型预测下一个 token,让模型学到语言、知识、代码和推理痕迹的基础模式。

技术解释:通常使用最大似然 / 交叉熵目标,在大规模 token 序列上优化 $P_\theta(x_t \mid x_{<t})$,使模型提高真实下一个 token 的概率。

最大似然

通俗解释:让真实出现过的数据,在模型看来更可能出现。

技术解释:通过最大化训练数据在模型参数 $\theta$ 下的概率来估计参数。语言模型预训练中,常写成最大化所有真实 token 条件概率的对数和。

交叉熵损失

通俗解释:模型猜下一个 token,猜得越离谱,惩罚越大;给真实 token 的概率越高,损失越小。

技术解释:衡量真实分布和模型预测分布之间差异的损失函数。在 one-hot 目标下,交叉熵等价于真实 token 的负对数似然。

梯度下降

通俗解释:根据损失函数告诉我们的方向,一小步一小步调整参数,让损失降低。

技术解释:利用损失对参数的梯度更新参数,简化形式为 $\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}$。实际大模型训练常使用 Adam 或其变体。

SFT

通俗解释:监督微调,用高质量“用户指令-助手回答”样本,把基础模型训练得更像助手。

技术解释:Supervised Fine-Tuning。通常仍使用最大似然 / 交叉熵目标,在 answer 部分提高示范回答 token 的概率。它主要是模仿学习,不直接比较多个回答。

prompt

通俗解释:用户给模型的问题、指令或上下文。

技术解释:语言模型生成时的条件输入。在 SFT、DPO、RLHF 和可验证任务中,prompt 通常记为 x

answer / response

通俗解释:模型或标注者对 prompt 给出的回答。

技术解释:在训练数据中常记为 y。在 SFT 中它是示范文本;在偏好优化中它可能是 chosen 或 rejected;在强化学习中它是被奖励函数评价的完整输出。

偏好对

通俗解释:同一个问题下有两个回答,一个更好,一个较差。

技术解释:pairwise preference 数据,通常包含 x、$y_w$、$y_l$,其中 $y_w$ 是 chosen / winner,$y_l$ 是 rejected / loser。

chosen

通俗解释:在一组回答比较中,被认为更好的回答。

技术解释:偏好优化中的正向样本,常记为 $y_w$。chosen 不一定完美,只表示相对于 rejected 更被偏好。

rejected

通俗解释:在一组回答比较中,被认为较差的回答。

技术解释:偏好优化中的反向样本,常记为 $y_l$。rejected 不一定完全错误,可能只是更空泛、更不符合指令或帮助较小。

RLHF

通俗解释:用人类反馈训练模型,让模型更倾向于生成被人类认可的回答。

技术解释:Reinforcement Learning from Human Feedback。常见流程是先做 SFT,再用偏好数据训练奖励模型,最后用强化学习方法更新语言模型。

奖励模型

通俗解释:一个学会给回答打分的模型。

技术解释:reward model,通常记为 $r_\phi(x,y)$。它从 chosen / rejected 偏好数据中学习,使 chosen 的分数高于 rejected。

DPO

通俗解释:不单独训练奖励模型,而是直接把“chosen 比 rejected 更好”写进语言模型损失函数。

技术解释:Direct Preference Optimization。它通过当前模型相对参考模型的 log probability ratio,直接提高 chosen 相对 rejected 的概率优势。

参考模型

通俗解释:一个固定的基准模型,用来约束当前模型不要偏离太远。

技术解释:在 DPO 或带 KL 约束的 RLHF 中,参考模型常记为 Pref。它通常是 SFT 后或训练前的稳定模型。

KL 约束

通俗解释:一根“别跑太远”的绳子。模型可以追求更高奖励或偏好,但不要偏离原来的语言能力太多。

技术解释:使用 KL 散度衡量当前模型分布与参考模型分布之间的距离,常出现在 $\mathbb{E}[R] - \beta,\mathrm{KL}(P_\theta \Vert P_{\text{ref}})$ 这类目标中。

reward

通俗解释:模型回答获得的奖励分数。

技术解释:奖励函数 $R(x,y)$ 对完整回答 y 给出的评价,可以来自奖励模型、人类偏好、测试器、编译器、判题器或其他验证器。

外部验证器

通俗解释:模型之外的检查系统,用来判断回答是否通过验证。

技术解释:可执行、可判定或可评分的评价机制。例如单元测试、编译器、数学答案检查器、在线判题系统、工具调用结果检查器等。

可验证强化学习

通俗解释:让模型生成完整回答,再用验证器给奖励,训练模型更倾向于生成能通过验证的回答。

技术解释:以 $\max_\theta,\mathbb{E}{y \sim P\theta(\cdot \mid x)}[R(x,y)]$ 为核心的训练思路,重点是奖励函数可以由外部系统更明确地产生。

思维链

通俗解释:让模型把中间推理步骤写出来,而不是只给最终答案。

技术解释:Chain-of-thought。它可以是提示技巧、SFT 目标文本的一部分,也可以成为过程奖励的评价对象。思维链不是神秘意识,关键在于中间过程是否可靠。

结果奖励

通俗解释:只看最终答案对不对、代码能不能通过测试。

技术解释:$R_{\text{final}}(x,y)$,评价最终输出 y 的奖励函数。它适合很多可验证任务,但可能忽略推理过程质量。

过程奖励

通俗解释:不仅看最终答案,还看中间步骤是否合理。

技术解释:$R_{\text{process}}(x,c)$,评价思维链或中间推理过程 c 的奖励。它有助于利用过程信息,但评价难度更高。

自生成样本

通俗解释:模型自己生成候选答案、代码、题目或推理过程,再由验证器或筛选器判断哪些值得学习。

技术解释:self-generated data。它本身不是训练目标,关键取决于后续如何筛选、标注和组织:可进入 SFT、偏好优化或强化学习。

奖励作弊

通俗解释:模型学会了拿高分的捷径,但没有真正解决任务。

技术解释:reward hacking。模型优化的是给定奖励函数,如果奖励函数有漏洞,模型可能利用漏洞获得高奖励而偏离真实目标。

数据污染

通俗解释:评测题或答案提前出现在训练数据里,让模型评测表现虚高。

技术解释:benchmark contamination / data leakage。它会削弱评测对泛化能力的衡量,使模型看起来比实际更会解决新问题。