Transformer 内部结构与整体流程
约 2403 字大约 8 分钟
2026-06-29
自回归模型
把 LLM 当成一个黑盒,输入称之为提示词(prompt),输出是文本。在使用模型时, 模型并不是一次性生成所有的文本,而是一次生成一个词元。

每生成一个新词元,模型都会把 "当前已有的全部序列" 重新跑一遍 前向传播(forward propagation),算出下一个词,然后把这个新词 追加到序列末尾,作为下一轮的输入,再算下一个,如此循环

这种 "拿自己早先的输出当后续输入"的模型,机器学习里有专门的名字 —— 自回归模型(autoregressive model)。Transformer 就是一个自回归模型。你看到的 "流畅一段话",其实是模型几十上百次 "预测下一个词" 接龙出来的,每一次接龙都是一次完整的前向传播。
向前传播
向前传播就是数据从输入层开始,逐层经过每层神经元的计算,最后产出结果。一次前向传播分四步:
- 输入 嵌入(把词变成向量)
- 多层 神经网络处理(就是堆叠的 Transformer 块)
- 输出一个分布 —— 词表里 所有 词元各自的概率
- 按某种策略从分布里 选一个词 生成出来(怎么选,是下一节 "解码策略" 的事)
LLM 内部结构
LLM 内部从前到后分为四块:
| 顺序 | 模块 | 干的事 | 输入 → 输出 |
|---|---|---|---|
| 1 | 分词器 Tokenizer | 自然语言 → 词元 ID | 字符串 → 整数列表 |
| 2 | 嵌入层 Embedding | 词元 ID → 向量 | 整数 → 高维向量 |
| 3 | 堆叠的 Transformer 块 | 融合上下文语义 | 向量 → 向量(同形状) |
| 4 | 语言建模头 LM Head | 向量 → 下个词的概率 | 向量 → 概率分布 |
分词器 Tokenizer
第一步,把自然语言切成词元,再映射成词表里的 ID
"我今天很开心"
↓ 切分
['我', '今天', '很', '开', '心']
↓ 查词表映射 ID
[1, 354, 2764, 77, 199]切分粒度不一定按 "字" 或 "词",取决于分词器的训练(BPE 之类,见 "词元" 那节),这里的重点是结果:一串整数 ID
嵌入层 Embedding
整数 ID 本身没法直接交给神经网络算 —— 354 和 355 在数值上挨着,但语义上可能毫不相干,直接拿整数运算没有意义
所以要通过 嵌入层,把每个 ID 映射成一个 固定维度的向量:
[1, 354, 2764, 77, 199]
↓ 嵌入层
[
[0.12, -0.87, ..., 0.34], # token 1
[0.02, 0.45, ..., -0.11], # token 354
...
[0.76, -0.01, ..., 0.08], # token 199
]维度通常是 768(小模型)或 4096(大模型)。这些向量是模型可学习的参数,训练时会不断被优化 —— "哪些词该靠近" 是学出来的,不是写死的
堆叠的 Transformer 块
这是 LLM 的 核心模块,模型理解上下文、捕捉语义关系全靠该模块。每个 Transformer 块内部有两个子模块:
提示
下一节是 "Transformer 块" 的内容,这里只建立全局认知
自注意力机制(Self-Attention)
让每个词元能 "看见" 它前面的所有词元,从而理解上下文。比如预测 "开心" 时,模型会去关注 "我" "今天" "很",并给不同词元分配不同 权重
"我 今天 很 开心"
↑ ↑ ↑ ↑
每个词元都能关注到它前面的词元(自回归只看左边)前馈网络(Feed-Forward Network)
对每个位置的向量单独做非线性变换,提升表达能力。为什么叫 "堆叠" ?因为 Transformer 块不是一层,而是很多层串起来:
块1 → 块2 → 块3 → 块4 → ...每层接收上一层的输出再加工。层数越多,越能捕捉高层次、复杂的语言结构,对文本理解越准。
| 模型 | 参数规模 | Transformer 层数 |
|---|---|---|
| GPT-2 (small) | 117M | 12 |
| GPT-3 | 175B | 96 |
| LLaMA 2 (7B) | 7B | 32 |
| LLaMA 2 (70B) | 70B | 80 |
| PaLM 2 | 340B | 120 |
| Mistral (7B) | 7B | 32 |
主流模型 Transformer 层数对比
进块前是 [序列长度 × 768] 的向量数组,出块后 形状不变,还是 [序列长度 × 768] —— Transformer 块不改变形状,只是把每个向量 "染上" 了上下文信息
语言建模头 LM Head
经过多层 Transformer 块后,每个位置都得到一个融合了上下文的高维向量。语言建模头负责最后一步:把向量变成 "下一个词元" 的预测
自回归生成时,只取 最后一个位置 的向量来预测下一个词
[
[0.12, -0.87, ..., 0.34], ← "我"
[0.45, 0.10, ..., -0.77], ← "今天"
[-0.22, 0.63, ..., 1.02], ← "很" ← 只用这个
]重要
因为 Transformer 是上下文感知结构。输入 "我今天很" 经过多层处理后,最后一个 "很" 对应的向量 已经不是孤立的 "很" 了 —— 它通过自注意力 融合了前面 "我""今天""很"的全部信息。所以这一个向量,代表的是 "我今天很" 这整个上下文
公式为:
softmax(logitsi)=∑j=1nelogitsjelogitsi
到了这一步,模型手里只有 一个向量 z("很" 那个位置、长度 768,但已融合整句上下文)。它现在要回答一个问题 —— 词表里 5 万个词,下一个最该是哪个?
模型没法直接 "想出" 一个词,只能做一件事:给每个候选词打分 → 把分数变成概率 → 挑一个。logits 就是 "打分" 的结果,softmax 就是 "把分数变概率" 的工具
算 logits(给每个词打分)
语言建模头本质是一个 线性变换矩阵 W。假设词表大小
V = 50000,向量维度d = 768,那 W 的形状就是[768 × 50000](这是参数量最大的层之一)logits = z · W可以把 W 想成 5 万个 "评委",每个评委专门负责一个词。
z(长度 768)挨个跟每个评委做点积,得出 "这个词有多合适" 的一个分。5 万个评委 → 5 万个分 → 这就是logits,一个长度 50000 的数组,词表里每个词元一个分数但这个分数是 未归一化 的,还不能直接当概率用,原因有两个。例如词表只有 5 个词时:
词元 logits 开心 4.2 累 2.7 忙 1.5 昨天 -1.2 小狗 -3.5 - 可正可负、范围不限:你看到了
-3.5,可概率哪有负的?概率必须在 0~1 之间 - 加起来不等于 1:
4.2 + 2.7 + 1.5 - 1.2 - 3.5 = 3.7,而一个合法的概率分布,所有可能性必须正好加起来等于 1(100%)
重要
所以 logits 只是 "谁分高谁分低" 的相对大小,要拿它来 "按概率挑词",得先转换 —— 这就轮到 softmax
- 可正可负、范围不限:你看到了
softmax(把分数压成概率)
输入一串可正可负的分数,输出一串 0~1 之间、加起来正好等于 1 的概率,而且原来分高的,概率也高。它就是公式里的两步 —— 先对每个值取指数(保证变正数)、再除以总和(保证加起来等于 1,这也是 "归一化 normalize" 这个词的来历)
每个 logits 取指数
e^x**(e ≈ 2.718,不管正负,结果恒为正)e^4.2 ≈ 66.7 e^2.7 ≈ 14.9 e^1.5 ≈ 4.48 e^-1.2 ≈ 0.30 e^-3.5 ≈ 0.03 ← 负的 logits 也变成了正数(很小)这一步解决了 "概率不能为负" 的问题。接下来:每个数除以它们的总和(保证加起来等于 1,即 "归一化")
总和 = 66.7 + 14.9 + 4.48 + 0.30 + 0.03 ≈ 86.4 开心: 66.7 / 86.4 ≈ 0.77 累: 14.9 / 86.4 ≈ 0.17 忙: 4.48 / 86.4 ≈ 0.05 昨天: 0.30 / 86.4 ≈ 0.003 小狗: 0.03 / 86.4 ≈ 0.0003现在这组数就是合法概率分布了:每个都在 0~1 之间、总和为 1、开心最高 —— 模型认为下一个词最可能是 "开心"
提示
为什么先取指数而不是直接除?因为
e^x会 放大差距:开心和累的 logits 只差 1.5,取指数后变成 66.7 对 14.9,概率被拉开到 0.77 对 0.17,高分词更突出。这个特性正是下一节温度系数要调的东西示例代码
import math vocab = ["开心", "累", "忙", "昨天", "小狗"] logits = [4.2, 2.7, 1.5, -1.2, -3.5] # softmax:先取指数,再除以总和 exps = [math.exp(x) for x in logits] total = sum(exps) probs = [e / total for e in exps] for word, p in zip(vocab, probs): print(f"{word}: {p:.4f}") print("总和:", sum(probs)) # 输出: # 开心: 0.7553 # 累: 0.1685 # 忙: 0.0508 # 昨天: 0.0034 # 小狗: 0.0003 # 总和: 1.0
完整流程
把四块连起来走一遍。输入一句没说完的话:
我今天很目标:预测下一个最可能的词元
分词器处理
["我", "今天", "很"] → [1, 354, 2764]嵌入层处理 —— 每个 ID 映射成 768 维向量,形成
[3, 768]的二维数组"我" → [0.12, -0.87, ..., 0.34] "今天" → [-0.11, 0.45, ..., 0.90] "很" → [0.20, 0.14, ..., -0.06]Transformer 块处理 —— 过多层后形状不变,仍是
[3, 768]。只取最后一个z = [0.12, -0.45, ..., 0.33] ← 长度 768这个 z 虽是 "很" 的向量,但已融入前面所有词元的语义
语言建模头计算 logits —— 假设词表 50000 个词元
logits = z × W → 得到 [1 × 50000] 的向量 logits = [0.9, -1.3, 2.1, ..., 5.7] ← 长度 50000softmax 归一化为概率
{ 开心: 0.61, 累: 0.12, 忙: 0.08, 郁闷: 0.04, 美丽: 0.02, ... }
