📚 本文是《Transformer 由浅入深》系列第 9 篇 · 阶段四「走向现代 LLM」

前面我们把架构讲透了。这一篇换个视角:同一个模型,在训练生成这两种状态下,行为其实很不一样。搞懂这层差异,你就会明白大模型为什么"训练能并行、生成却很慢",以及那些 temperaturetop_p 参数到底在调什么。

打个比方:同一个学生,考前刷题考场答题完全是两种状态。刷题时手边有标准答案,可以对照着、整页整页地核对;考场上却只能凭自己一步步往下写,写错一步还可能连累后面。模型的"训练"与"生成",差不多就是这两种状态。本篇就把这两种状态彻底拆开讲清楚。

我们以主流的 Decoder-only(GPT 类) 为主线。

本篇目标

读完你应该能:

  • 写出语言模型的训练目标,理解为什么训练能并行(teacher forcing);
  • 说清训练和推理的根本差异;
  • 解释 temperature / top-k / top-p 如何影响生成结果。

阅读前提

第 7、8 篇(因果掩码、Decoder-only、自回归)。

1. 预训练目标:下一个词预测

GPT 类模型的预训练目标简单得惊人:给定前文,预测下一个词。 用概率的语言说,模型为一句话 \( x_1, x_2, \dots, x_T \) 估计联合概率,并把它拆成一连串"下一个词"的条件概率:

$$ P(x_1, \dots, x_T) = \prod_{t=1}^{T} P(x_t \mid x_1, \dots, x_{t-1}) $$

训练就是让模型对真实出现的下一个词给出尽可能高的概率。对应的损失是交叉熵(等价于最大化对数似然):

$$ \mathcal{L} = -\sum_{t=1}^{T} \log P(x_t \mid x_{直觉:每一步,模型对整个词表输出一个概率分布,我们看它给"正确的那个下一个词“打了多少分——打得越高,损失越小。仅凭这一个目标,在足够多的文本上训练,模型就被迫学会语法、事实、推理乃至风格。

这个目标为什么这么"万能”? 可以把它想成一个永远做不完的"完形填空大赛"。要想准确预测"中华人民共和国成立于 1949 年"里的"1949",模型得懂历史事实;要预测"他打开冰箱,拿出一瓶"后面接"水"还是"牛奶",模型得懂常识;要在一段推理后接出正确结论,模型得会逻辑。换句话说,“预测下一个词"逼着模型把语言背后的知识、常识、推理统统学进去——因为不学,这道完形填空就填不准,损失就降不下来。这就是为什么一个看似简单到不可思议的目标,能撑起整个大模型时代。

交叉熵到底在测什么? 一句话:它在测"模型对正确答案有多惊讶”。如果模型对真实的下一个词给了 0.9 的高概率,说明它"早有预料",\( -\log 0.9 \) 很小,几乎不罚;如果只给了 0.01,说明它"大跌眼镜",\( -\log 0.01 \) 很大,重重一罚。训练的过程,就是不断调整参数,让模型对真实文本越来越"不惊讶"。所以交叉熵也常被通俗地称作模型的"困惑程度"(困惑度 perplexity 就是由它换算来的)——数值越低,模型对语言越"了然于胸"。

2. Teacher Forcing:训练时为什么能并行

这里有个关键问题:既然是"预测下一个词",训练时是不是也得像生成那样,一个一个词地来?

不用。 训练时我们手里已经有完整的正确句子了。于是可以用一个叫 teacher forcing 的技巧:无论模型自己会预测出什么,都直接把"真实的前文"喂给每个位置,让所有位置同时预测各自的下一个词。

打个比方,teacher forcing 就像照着标准答案学解题:做第 3 步时,不管你自己第 2 步算得对不对,老师都直接把"第 2 步的标准答案"递到你面前,让你在正确的基础上往下做。这样每一步都站在"正确前文"的肩膀上,既不会被自己的错误带偏,各步之间也互不拖累——于是可以一口气把所有步骤同时练完,而不必排队等前一步。

这正是第 7 篇那个因果掩码大显身手的地方。靠着掩码保证"位置 \( t \) 只能看到 \( 所有位置的预测和损失并行算完:

输入:  [开始] 今天  天气  真
            ↓     ↓     ↓    ↓        ← 每个位置同时预测,互不等待
预测:   今天   天气   真   好
            (每个预测都只依赖左边的真实词,由因果掩码保证)

这就是 Transformer 相比 RNN 的训练杀手锏:RNN 必须一步步算,而 Transformer 靠"完整答案 + 因果掩码",把整句的训练完全并行化,把 GPU 喂得满满当当。这也呼应了本系列第 1 篇说的——并行,正是 Transformer 诞生的初心。

为什么训练能并行,生成却不能? 关键就一句话:训练时"答案"已经在手,生成时"答案"还没出生。 训练像批改一份已经写满的试卷——所有题的标准答案都在,阅卷老师可以把每道题同时摊开来打分;生成则像现场即兴接龙,下一句还没说出口,你根本无从并行,只能等上一个词落地,才能接着想下一个。这个不对称,是后面理解"生成为什么慢"的总钥匙。

3. 训练的三段式(浅讲)

今天一个能对话的大模型,通常不止"预训练"一步,而是三段式:

  1. 预训练(Pre-training):在海量互联网文本上做"预测下一个词"。模型学到的是广博的语言与世界知识,但它只会"续写",还不太会"听话办事"。
  2. 监督微调(SFT):用人工编写的"指令 → 理想回答"数据继续训练,教模型按指令回答问题,而不是漫无目的地续写。
  3. 对齐(RLHF / DPO 等):用人类偏好数据,让模型的回答更有用、诚实、无害,更符合人的期待。

第 1 步给"知识与能力",第 2、3 步给"听话与品味"。本篇聚焦第 1 步的机制,后两步点到为止。

用一个"培养人才"的比方,这三段分工就很清楚了。预训练像是让一个人博览群书、读遍天下文章:他因此见多识广、出口成章,但你问他问题,他可能不答反而顺着你的话往下编——因为他学的就是"续写",不是"应答"。SFT 像是岗前培训:拿大量"这样问、该那样答"的范例手把手教他,把一身知识扭转成"听懂指令、给出回答"的职业习惯。对齐则像长期的言传身教与价值观打磨:同样一个问题有很多种答法,要教他挑出那个更礼貌、更诚实、更不容易闯祸的答法。值得强调的是,这三步用的几乎是同一套"预测下一个词 + 交叉熵"机制,变的只是喂什么数据、用什么信号去引导——底层引擎一以贯之,这也是大模型训练优雅的地方。

4. 推理:自回归生成

训练能并行,生成却天生是串行的——因为生成时,你没有未来的词可喂,只能一个一个地造,这叫自回归(autoregressive):

  1. 喂入提示(prompt);
  2. 模型输出"下一个词"的概率分布,从中选一个词;
  3. 把这个新词拼回输入末尾;
  4. 拿着变长了的序列,回到第 2 步,再产下一个……直到生成"结束符"。
今天          → 天气
今天 天气      → 真
今天 天气 真   → 好
今天 天气 真 好 → [结束]

自回归最贴切的比方就是文字接龙游戏:每个人只能在前面所有人说完的基础上接一个词,接完再轮到下一个,谁也不能抢跑。模型生成也是如此——它把刚吐出的词重新当作输入的一部分,再预测下一个,如此循环往复,直到自己吐出一个表示"我说完了"的结束符。

这就是为什么大模型生成是一个字一个字往外蹦的:每个新词都依赖前面所有已生成的词,无法跳步并行。更糟的是,每生成一个词,理论上都要把整个序列重新跑一遍 Transformer——大量重复计算。这个效率痛点,正是下一篇 KV Cache 要解决的。

这里值得多想一层:重复计算到底重复在哪? 当你已经生成了"今天 天气 真",要预测下一个词"好"时,模型其实又把"今天"“天气"“真"这几个词从头到尾重新算了一遍注意力——尽管这些词在上一步早就算过、结果一模一样。生成的句子越长,这种"把前文一遍遍重算"的浪费就越大,速度也越拖越慢。聪明的做法当然是把算过的中间结果存起来、下次直接复用,这正是 KV Cache 的核心思路,我们留到下一篇细说。

5. 从概率分布到具体的词:采样策略

第 2 步"从概率分布里选一个词"其实大有学问,直接决定了输出是死板还是有创意。要明白一点:模型每一步只是给出一张"候选词的概率清单”,它并不替你拍板选哪个——真正决定"接哪个字"的,是我们设定的采样策略。同一个模型、同一段提示,换一套采样策略,产出的文字可能从一板一眼变得天马行空。所以这一步看似只是收尾,实则是把"模型的能力"翻译成"你看到的文字"的最后一道关口。常见策略:

  • 贪心(greedy):每步都选概率最高的词。稳定但单调,容易重复、无聊。
  • 束搜索(beam search):同时保留若干条候选序列,最后挑整体概率最高的。适合翻译这类"有标准答案"的任务,但用于开放生成会显得平淡。

贪心的问题在于”只顾眼前":每一步都贪图当下最稳的那个词,但局部最优拼起来未必是整句最好的。这就像下棋只看下一步、不看全局,容易走进死胡同,生成里典型的表现就是车轱辘话来回打转、反复重复同一句。束搜索则像"多带几条路同时探":它在每一步保留几条最有希望的半成品句子(这个条数叫 beam width,束宽),走到最后再回头挑整体得分最高的一条,因而比贪心更不容易因一步之差而满盘皆输。但它本质上还是在"求最稳妥",放到开放创作上反而会显得保守、套路、缺乏惊喜——这也是为什么聊天和写作类场景更偏爱下面的随机采样。

  • 温度采样(temperature):按概率随机采样,并用一个温度 \( T \) 调节分布的"软硬":
$$ P_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} $$

\( T \) 的作用很直观:

  • \( T \to 0 \):分布变尖锐,逼近贪心,保守、确定;
  • \( T = 1 \):就是模型原始分布;
  • \( T > 1 \):分布变平坦,更随机、更有创意,但也更容易"胡言乱语"。
低温 T=0.5:  好 ████████  不错 ██  还行 ▏        ← 集中,保守
高温 T=1.5:  好 ████  不错 ███  还行 ███  糟 ██   ← 摊平,多样

温度的直觉,可以理解成调节模型的**“理智 vs 放飞"旋钮**。温度低,模型像个严谨的公务员:永远挑那个最稳妥、最可能正确的词,回答四平八稳,但也容易千篇一律;温度高,模型像个喝了点酒的诗人:愿意冒险用些冷门的词,文采和惊喜多了,可一不留神就跑题、说胡话。把温度调到 0,基本就退化成贪心了;调得太高,文字就开始"飘”。

  • top-k 采样:只在概率最高的 \( k \) 个词里采样,砍掉长尾,避免选到离谱的词。
  • top-p 采样(nucleus):只在"累计概率达到 \( p \)(如 0.9)“的最小词集合里采样。比固定 \( k \) 更自适应:分布尖锐时候选自动变少,平坦时自动变多。

top-k 和 top-p 都可以理解成”先圈出靠谱的候选,再在里面抽签",区别在于圈的方式:top-k 是"只在前 k 名里抽"——名额固定,不管这几个词靠不靠谱都凑够 k 个;top-p 则是"只在累计可信度够 p 的那拨里抽"——名额随行就市。比如当模型非常确定下一个词时(分布很尖),top-p 可能只圈进 1~2 个词,几乎不给乱来的机会;而当模型本就拿不准时(分布很平),它又会自动放宽到几十个候选,保留多样性。正因为这种自适应,top-p 在实践中往往比固定的 top-k 更省心。

实践中,常把 temperature + top-p 组合使用,在"连贯"和"多样"之间取得平衡。你在 API 里调的 temperaturetop_p,调的就是这里。

6. 训练 vs 推理:暴露偏差

最后点一个微妙但重要的差异。训练时用 teacher forcing,每一步喂的都是真实前文;但推理时,模型只能基于自己生成的前文继续。一旦中途生成了一个不太好的词,这个"错误"会被带进后续步骤,可能滚雪球式累积——这个训练/推理不一致的现象叫暴露偏差(exposure bias)

打个形象的比方:这就像一个只在标准答案下练过琴的学生,第一次登台才发现自己不会从错音里找补。平时练习,老师每个音都把对的递给他,他从没体验过"弹错一个音之后该怎么接回去";真到了舞台上(推理),一旦手抖弹错,后面的旋律全建立在这个错音上,只会越跑越偏。模型也一样:训练时它从没见过"自己写出的烂开头",一旦推理时不慎开了个坏头,就容易顺着这个坏头一路歪下去。

它是开放生成里"越写越跑偏"的根源之一。缓解的思路包括更好的采样策略、训练中适当引入模型自己的输出,以及后续的对齐阶段——但要彻底消除并不容易,属于仍在研究的问题。

理解了暴露偏差,你也就能体会到为什么采样策略不只是"调风格"那么简单:一个稳健的采样策略(比如适中的温度配合 top-p)能在每一步都尽量避免选到那个"开坏头"的词,从源头上减少滚雪球的机会。这也是为什么实际产品里几乎不会把温度无脑拉满——多一分随机,就多一分把自己带沟里去的风险。训练与推理这道天然的裂缝,贯穿了大模型"好不好用"的方方面面。

常见疑问与易错点

问:temperature 到底是调大好还是调小好? 答:看任务。要确定、可复现、少出错的场景(写代码、解数学题、抽取信息、事实问答),把温度调低(甚至 0),让模型走最稳的路;要有创意、花样多的场景(写故事、头脑风暴、起名),把温度调高一些。一句话口诀:要对就调低,要野就调高。 但别盲目拉满,温度太高模型容易语无伦次。

问:top-k 和 top-p 到底有什么区别? 答:top-k 是"名额固定"——永远只在前 k 个候选里抽,不管这一步该不该多给候选;top-p 是"名额浮动"——按累计概率动态决定候选有几个。模型越确定的步骤,top-p 圈的候选越少(更稳),模型越含糊的步骤,候选越多(更活)。所以 top-p 更"懂得看场合",实践中用得更多。

问:为什么大模型生成回答这么慢,有时还一个字一个字往外蹦? 答:因为生成是自回归的,本质上没法并行:必须先有上一个词,才能算下一个词,像接龙一样只能挨个来。而且每蹦一个字,模型(在没有优化时)都要把整段已有内容重新算一遍,计算量随长度不断累积。这正是下一篇 KV Cache 要专门解决的痛点。

问:既然训练就是"预测下一个词",为什么 SFT 还要单独再训一遍,和预训练有啥不同? 答:机制几乎一样(还是预测下一个词 + 交叉熵),不同的是喂的数据和目的。预训练喂的是海量、杂乱的互联网文本,目的是让模型"博学";SFT 喂的是精心编写的"指令→理想回答"配对,目的是把这身学问扭转成"听指令、好好答"的习惯。打个比方:预训练是博览群书,SFT 是岗前培训。

问:为什么同样的问题,模型每次回答都不一样? 答:因为只要温度大于 0、用了随机采样,模型每一步就是在概率分布里抽签,而不是死板地选最高分。抽签自然每次结果不同。想要每次都一致,可以把温度设为 0(或用贪心),让它每步都选概率最高的词——但代价是回答会比较单调。

问:为什么模型有时会一本正经地胡说八道(幻觉)? 答:别忘了它的训练目标只是"让文字接得通顺、像样",而不是"保证内容属实"。当它对某个事实没把握时,它依然会挑一个"读起来最顺、最像那么回事"的词接下去,于是就编出了听着流畅、其实查无此事的内容。这也是为什么对齐阶段(第 3 步)要专门往"诚实"上拉,以及为什么关键信息仍需人工核实。

问:贪心和温度设为 0 是一回事吗? 答:效果上基本等价。温度趋近 0 时,softmax 会把几乎全部概率压到那个最高分的词上,于是"随机抽签"也就退化成"每次都抽中第一名"——这正是贪心在做的事。所以你想要完全确定、可复现的输出时,把温度设成 0 就行,它实际上就是在跑贪心。

问:为什么不直接用束搜索来生成聊天回复,反而要用带随机性的采样? 答:因为束搜索是在"求一条整体概率最高的路",而概率最高往往等于最普通、最安全、最没新意。用它写对话或故事,常会得到正确但乏味、甚至反复重复的句子。聊天和创作要的是自然、多样、有点人味儿,所以更适合用 temperature + top-p 这类带可控随机性的采样,让回答既靠谱又不千篇一律。

问:temperature 和 top-p 一起用会不会冲突,该怎么搭配? 答:不冲突,它俩管的是不同环节,通常先后接力:top-p 先把候选范围"圈定"在一拨靠谱的词里(挡掉长尾的离谱选项),temperature 再在这个范围内调节抽签时的"软硬"。一个常见的稳妥搭配是温度适中、top-p 取 0.9 左右——既保证不跑题,又留出足够的多样性。真正要避免的是"两个旋钮同时拉到极端",那样输出要么僵死要么发散。

小结 & 下一篇预告

这一篇我们打通了"学"与"用"两个状态:

  • 训练目标 = 预测下一个词,损失是交叉熵;
  • teacher forcing + 因果掩码让训练完全并行(Transformer 相对 RNN 的杀手锏);
  • 现代大模型走预训练 → SFT → 对齐三段式;
  • 推理是自回归的、串行的、且有大量重复计算(伏笔 KV Cache);
  • temperature / top-k / top-p 调节生成的保守与多样;
  • 训练用真值、推理用自身输出,带来暴露偏差

我们已经反复提到生成"慢"和"重复计算"。最后一篇,我们就来收束全系列:看现代 LLM 用 KV Cache、FlashAttention、GQA/MQA、MoE、长上下文 等一系列利器,如何把 Transformer 变得更快、更省、更长、更大


📖 系列目录

第 1 篇 文末完整目录。