【AI专栏】图解Transformer - 第04章:LLM生成
博主介绍:程序喵大人
- 35 - 资深C/C++/Rust/Android/iOS客户端开发
- 10年大厂工作经验
- 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
- 《C++20高级编程》《C++23高级编程》等多本书籍著译者
- 更多原创精品文章,首发gzh,见文末
- 👇👇记得订阅专栏,以防走丢👇👇
😉C++基础系列专栏
😃C语言基础系列专栏
🤣C++大佬养成攻略专栏
🤓C++训练营
👉🏻个人网站
【AI专栏】图解Transformer - 第04章:LLM生成
1. 图解 LLM 生成
前三章已经把 Transformer 里最重要的内部动作讲完了:token 变成向量,向量带上位置,self-attention 读取上下文,multi-head 从多个角度看,FFN 继续加工,Residual 和 Norm 让很多层 Block 可以稳定堆起来。
第四章开始回答一个更贴近使用体验的问题:大模型到底怎么把一句话给写出来的?
其实答案很简单:现代 decoder-only LLM 生成文本时,通常是一个 token 一个 token 往后接。模型先读已有上下文,预测下一个 token;选出这个 token 后,把它追加到上下文末尾;再用新的上下文继续预测下一步。
所以生成并不是“模型一次性把整段答案吐出来”。从逻辑上看,它更像一个循环:
已有上下文 → 前向传播 → logits → 概率分布 → 选出 next token → 追加回上下文这个循环反复发生,直到生成结束符、达到长度限制,或被外部规则停止。
LLM 生成的核心节奏是:读完已有上下文,预测下一个 token,再把新 token 接回上下文。
2. 从 Encoder-Decoder 到 Decoder-only
原始 Transformer 论文讲的是 Encoder-Decoder 架构。Encoder 负责读输入序列,Decoder 负责根据已生成部分和 Encoder 输出继续生成目标序列。这个结构很适合机器翻译:左边读源语言,右边生成目标语言。
但GPT 类大语言模型走的是另一条常见路线:decoder-only。整个模型主要围绕“给定前文,预测下一个 token”这个目标来工作。
这并不表示 Encoder-Decoder 结构没有价值。它仍然是 Transformer 家族里的重要结构,很多翻译、语音、视觉语言任务都可能用到类似思想。本章只是把焦点放在现代聊天式、续写式 LLM 常见的 decoder-only 生成路径上。
decoder-only 的输入和输出在同一条文本轨道上。prompt 先作为已知上下文进入模型,模型生成一个新 token,这个 token 再成为下一轮上下文的一部分。
本章聚焦 GPT 类 decoder-only 生成:用已有 token 预测下一个 token。
3. Decoder-only:预测下一个 token
decoder-only 模型的核心训练目标,可以理解成“看前面的 token,猜下一个 token”。例如给定:
我 喜欢模型要预测下一个位置可能是什么。它可能给“CUDA”“编程”“猫”等候选 token 打出不同分数。训练时,真实下一个 token 会作为答案,模型根据预测误差更新参数。
推理时没有标准答案。模型会根据当前上下文算出一组候选分数,再通过解码策略选出一个 token。这个 token 就是生成结果的一小步。
这里要注意“一次预测一个位置”的边界。用户看到的答案是一整段文字,但模型逻辑上是在反复做 next-token prediction。每一步只决定下一个 token,很多步连起来,才变成一句话、一段话或一篇文章。
decoder-only LLM 的基本动作是:给定前文,预测下一个位置。
4. Prompt 先变成上下文
用户输入的 prompt 不会直接以字符串形式进入 Transformer。它先经过 tokenizer,变成一串 token;token 再变成 Token ID;Token ID 查出 embedding;embedding 带上位置信息,进入多层 Transformer Block。
这部分第一章已经讲过。第四章需要把它接到生成流程上:prompt token 是生成第一个新 token 时的已知上下文。
比如用户输入:
解释一下 CUDA模型要生成回答的第一个 token 时,已经能看见 prompt 里的这些 token。它会基于这些已知上下文,预测回答开头最合适的下一个 token。
之后每生成一个新 token,这个 token 也会加入上下文队伍。上下文会随着生成逐步变长,模型每一步都基于“prompt + 已生成内容”继续预测。
生成从 prompt 开始;prompt token 是第一轮预测的已知上下文。
5. Causal Mask:只能看左边
生成式 LLM 有一个关键约束:当前位置不能提前看未来 token。未来 token 还没有生成,模型只能依赖已经出现的上下文。
这就是 causal mask 的作用。它会在 self-attention 里挡住未来位置,让第 i 个位置只能 attend 到自己和左边已经出现的位置。右边位置在当前步骤不可见。
原始 Transformer 的 Decoder self-attention 也使用这种 mask,用来保持自回归属性。自回归的意思就是:当前位置的预测只能依赖已经知道的历史输出。
如果没有 causal mask,训练时模型可能偷看右边真实答案,学到一种推理时无法使用的作弊方式。因为真实生成时,右边答案还不存在。
causal mask 保证生成按从左到右进行:未来 token 还没出现,不能提前看。
6. 最后位置预测下一步
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在 decoder-only 生成里,当前上下文经过模型后,每个位置都会得到一个输出表示。要预测下一个 token,通常使用最后一个位置的输出表示。
为什么是最后一个位置?因为最后位置处在当前上下文的末尾。通过 causal self-attention,它可以看到前面所有已出现 token,也可以看到自己。它的最终表示最适合用来预测“接下来该出现什么”。
这并不表示前面的 token 没有作用。前面的 token 通过 attention 影响最后位置的表示。最后位置像一个站在队伍末尾的同学,它回头看完整个已知上下文,然后代表当前上下文去预测下一步。
训练时,模型可以并行计算很多位置的 next-token prediction。推理生成时,我们关心的是当前序列末尾的那个 next token。
生成下一步时,最后位置的输出表示承担预测下一个 token 的任务。
7. 穿过多层 Block
prompt token 进入模型后,会穿过很多层 Transformer Block。每层都包含 attention、FFN / MLP、Residual、Norm 等结构。第三章已经讲过一层 Block 的内部主线。
在生成场景里,可以把多层 Block 看成一个逐层更新表示的过程。浅层可能更多处理局部模式和较直接的线索;越往后,表示里可能融合更大的上下文范围和更抽象的任务信息。
每一层的输出都会成为下一层的输入。token 的位置没有消失(位置编码信息还在),序列长度也没有因为穿过 Block 变短。真正变化的是每个位置上的向量表示。
最后一个位置一路穿过所有 Block 后,会得到一个最终 hidden state。这个 hidden state 已经吸收了 prompt 和已生成内容里的上下文线索。
多层 Block 不断更新表示;最后位置的最终表示会进入输出层。
8. 最后的 hidden state
hidden state 可以理解成某个位置在模型内部的当前向量表示。它不是自然语言答案,也不是 token 本身,而是一串浮点数。
对于 next-token generation,我们通常取最后位置在最后一层得到的 hidden state。这个向量接下来会送入输出层,也就是常说的 LM Head。
LM Head 的工作是把这个 hidden state 映射到整个词表空间。词表里每个候选 token 都会得到一个分数。这个分数还不是概率,先叫 logits。
这一步是从“上下文表示”走向“具体候选 token”的桥。前面所有 Block 都在更新表示,LM Head 开始把表示转成可选 token 的分数。
最后位置的 hidden state 是预测下一个 token 的直接输入。
9. LM Head:映射到全词表
LM Head 通常是一个线性映射,它把最后位置的 hidden state 投影到词表大小的维度上。
如果词表有 100000 个 token,那么 LM Head 的输出就是 100000 个分数。每个分数对应一个候选 token。模型不是只在几个词里挑答案,而是在整个词表上打分。
用简化公式写,就是:
logits = hidden_state · W_vocab + b很多模型会把输入 embedding 矩阵和输出层权重做共享或绑定,但这属于具体实现细节。对理解生成主线来说,只要抓住一点:LM Head 把模型内部向量转换成全词表候选分数。
LM Head 的作用是:把最后位置的 hidden state 映射成全词表分数。
10. Logits:原始分数
logits 是 softmax 之前的原始分数,它们可以是正数,也可以是负数;不需要加起来等于 1,也不表示概率。
比如模型可能给几个候选 token 打出这样的分数:
CUDA: 8.2 是: 3.1 的: 1.5 猫: -0.7这些分数只表达相对倾向。分数越高,这个 token 在当前上下文下越有可能被选中。但真正用于采样的概率,还要经过 softmax。
logits 很重要,因为很多解码策略会直接修改或过滤 logits。例如 temperature 会缩放 logits,top-k / top-p 会限制候选集合,重复惩罚也常作用在 logits 层面。
logits 是全词表上的原始分数,softmax 之后才是概率。
11. Softmax:分数变概率
softmax 会把 logits 转成概率分布。输出的每个值都大于等于 0,并且所有候选 token 的概率加起来等于 1。
如果某个 token 的 logit 远高于其他 token,softmax 后它的概率会很大。其他 token 仍然可能有概率,但概率会低很多。
这里的 softmax 和第二章 attention 里的 softmax 位置不同。第二章的 softmax 是在 attention 内部,把 QK 分数变成“看谁更多”的权重。第四章这里的 softmax 是在输出层,把全词表 logits 变成“下一个 token 选谁”的概率。
两者数学形式相似,语义不同。attention softmax 分配上下文读取权重;output softmax 分配候选 token 概率。
输出层 softmax 把全词表 logits 变成下一个 token 的概率分布。
12. Temperature:调节随机性
temperature 是生成时常见的解码参数。它会改变概率分布的尖锐程度。
常见做法是把 logits 除以 temperature,再进入 softmax:
probabilities = softmax(logits / temperature)temperature 低于 1 时,分布通常更尖锐。高分候选会更突出,模型更倾向于选择最有把握的 token。temperature 高于 1 时,分布通常更平滑,更多候选 token 会获得机会。
这只是在解码阶段调节选择方式,并不是改变模型学到的知识。温度高不等于答案更聪明,温度低也不等于答案一定更正确。它主要影响生成的确定性和多样性。
temperature 改变概率分布的形状:低温更集中,高温更发散。
13. Top-k / Top-p:筛候选
直接从整个词表采样,有时会让低概率 token 也有机会被抽到。为了控制质量,生成系统常在采样前先缩小候选池。
top-k 的思路很直接:只保留概率最高的 k 个 token,其他候选先过滤掉。例如 top-k = 50,就只从最高的 50 个候选里采样。
top-p 也叫 nucleus sampling。它不是固定保留多少个 token,而是从高到低累加概率,保留累计概率达到 p 的候选集合。比如 top-p = 0.9,就保留能覆盖 90% 概率质量的候选。
top-k 和 top-p 都是解码策略,不是 Transformer Block 结构变化。它们发生在输出概率分布之后,帮助采样过程集中在更合理的候选范围内。
top-k 固定保留前 k 个;top-p 保留累计概率达到阈值的一组候选。
14. Sampling:选下一个 token
模型得到概率分布后,还需要把分布变成一个具体 token。这一步就是解码选择。
最简单的做法是 greedy decoding:每次都选择概率最高的 token。它很确定,但有时会让输出变得重复或缺少变化。
sampling 的做法是按概率抽取。概率高的 token 更容易被选中,概率低的 token 也有较小机会出现。temperature、top-k、top-p 这些参数通常就是为 sampling 服务的。
所以 sampling 并不是随便乱选。它仍然受模型给出的概率分布约束。区别在于,它允许模型在多个合理候选里保留一定随机性。
生成必须把概率分布落成一个 token;greedy 直接取最高,sampling 按概率抽取。
15. Append:接回上下文
选出 next token 后,生成还没有结束。这个新 token 会被追加到当前序列末尾,成为下一轮预测的上下文一部分。
比如当前上下文是:
解释 一下 CUDA模型选出下一个 token:
是下一轮上下文就变成:
解释 一下 CUDA 是然后模型继续预测“是”后面该接什么。这个过程一轮一轮重复,最终形成完整回答。
这也是为什么生成看起来像“逐字往外冒”。实际单位是 token,token 可能是一个字、一个词,也可能是词的一部分。
生成出来的 token 会接回上下文,成为下一轮预测的输入。
16. 自回归生成循环
把整章串起来,decoder-only LLM 的生成循环就是:
上下文 → Transformer 前向传播 → 最后位置 hidden state → LM Head → logits → softmax / 解码策略 → new token → 追加回上下文这条链路会重复很多次。每重复一次,序列就多一个 token。模型每一步都只预测下一个 token,很多个 next token 连起来,才变成用户看到的回答。
这一章聚焦“逻辑生成”层面。真实推理系统还会为了效率做很多优化:第一轮一次性读完整个 prompt,后面逐 token 生成;过去的 K/V 会被缓存;多个请求会被 batch 调度;attention 也会用更高效的实现。这些属于第五章的推理工程内容。
现在先记住一件事:Transformer Block 负责把上下文变成表示,LM Head 把表示变成词表分数,解码策略把概率分布变成一个 token,自回归循环把一个个 token 接成完整文本。
LLM 生成不是一次写完整段答案,而是反复预测并追加下一个 token。
码字不易,欢迎大家点赞,关注,评论,谢谢!
