# 模型全景 · 一个 GPT 只有五个零件

> 鸟瞰 model.py——LayerNorm、CausalSelfAttention、MLP、Block、GPT 五个类怎么拼成一台 GPT，数据怎么从 token 编号流到「下一个 token 的分数」，以及 GPTConfig 七个超参各自决定什么。

- 作者：David（道雾轩）
- 专栏：nanoGPT 源码解读（https://daiw.org/manual/nanogpt.md）
- 最后更新：2026-07-13
- 原文：https://daiw.org/manual/nanogpt/model-overview
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 模型全景 · 一个 GPT 只有五个零件

`model.py` 是整个 nanoGPT 的心脏。约 300 行，定义了「一个 GPT」的全部。在扎进最难的注意力之前，这一章先站高一点，把**整台机器的装配图**看清楚：有哪几个零件、它们怎么套在一起、数据从哪进从哪出。

## 五个类，由小到大搭起来

`model.py` 里就五个类（外加一个存配置的 `GPTConfig`）。它们的关系是**层层嵌套、由小拼大**：

| 类 | 是什么 | 由谁组成 |
| --- | --- | --- |
| `LayerNorm` | 归一化层，给数据「校准量纲」 | 最底层零件 |
| `CausalSelfAttention` | **因果自注意力**，让每个位置「回看前文」 | 最底层零件 |
| `MLP` | 前馈网络，逐位置「加工」信息 | 最底层零件 |
| `Block` | 一个 Transformer 层 | = LayerNorm × 2 + Attention + MLP |
| `GPT` | 完整模型 | = 词嵌入 + Block × n_layer + 输出层 |

一句话：**`Block` 是把注意力和 MLP 打包成的「一层」，`GPT` 就是把这样的层叠 `n_layer` 遍，前面加个「查字典」的入口、后面加个「做预测」的出口。** 就这么简单。

## 数据怎么流过整台机器

比起干看类图，顺着**数据流**走一遍更有感觉。一批 token 编号 `idx` 进来，到「下一个 token 的分数」`logits` 出去，中间是这样：

```mermaid
flowchart TB
  idx["idx<br/>(B, T) 一批 token 编号"] --> wte["wte 词嵌入<br/>查表:编号 → 向量"]
  idx --> wpe["wpe 位置嵌入<br/>第几个位置 → 向量"]
  wte --> add(("＋"))
  wpe --> add
  add --> drop["dropout"]
  drop --> blocks["Block × n_layer<br/>(一层层理解上下文)"]
  blocks --> lnf["ln_f 最终归一化"]
  lnf --> head["lm_head<br/>向量 → 每个词的分数"]
  head --> logits["logits<br/>(B, T, vocab_size)"]
```

翻译成大白话：

1. **查字典(`wte`)**：每个 token 编号，查一张表换成一个 `n_embd` 维的向量。编号 `13` 从此变成一串能参与运算的浮点数——这串数字就是模型对这个 token 的「初始理解」。
2. **标位置(`wpe`)**：注意力本身分不清「谁在前谁在后」，所以额外查一张**位置表**，把「你是第 0 个 / 第 1 个 / …… token」也编码成向量，加到词向量上。
3. **叠 Block（重头戏）**：相加后的向量流过 `n_layer` 个 `Block`。每个 `Block` 里，注意力让各位置**互相看**、MLP 再逐位置**加工**。层数越多，模型能捕捉的上下文关系越复杂。
4. **收尾(`ln_f` + `lm_head`)**：最后归一化一下，再用 `lm_head` 把每个位置的向量投影成一个长度为 `vocab_size` 的分数向量——**这就是「下一个 token 是词表里每一个字的可能性打分」**。

第一站我们说「GPT 就是猜下一个字」，现在你看到了：所谓「猜」，就是这条流水线最后吐出的那个 `logits`——65 个（或 50257 个）分数，谁高就更可能是下一个字。

## 一个 `Block` 内部长什么样

`Block` 是叠出深度的那块积木，它的 `forward` 只有两行，却是整个 Transformer 的招牌结构：

```python
def forward(self, x):
    x = x + self.attn(self.ln_1(x))
    x = x + self.mlp(self.ln_2(x))
    return x
```

三个要点：

- **先归一化，再进子层**：`self.ln_1(x)` 先把 `x` 校准，再喂给注意力。这叫 **Pre-LN**（把 LayerNorm 放在子层前面），比原始 Transformer 的 Post-LN 训练更稳，是现代 GPT 的标配。
- **残差连接 `x + ...`**：注意力/MLP 的输出不是替换 `x`，而是**加回**到 `x` 上。好处是梯度有一条「高速公路」能直通到底，深层网络也不容易「学不动」。你可以把它理解成：每个子层只负责**在原有理解上做增量修正**，而不是推倒重来。
- **两个子层，各管一摊**：`attn` 负责「跨位置」交换信息（第 5 个 token 去看第 1、2、3 个），`mlp` 负责「单位置」深加工（把每个位置自己的向量揉得更有表达力）。一个横向、一个纵向，交替叠加。

`LayerNorm`、`MLP` 这两个零件都不难，我们在后面的章节顺带讲清；真正需要单开一章、逐行细啃的，是那个 `self.attn`——**因果自注意力**。

## GPTConfig：七个旋钮

模型的「体格」由一个 `dataclass` 决定，一共七个超参：

```python
@dataclass
class GPTConfig:
    block_size: int = 1024
    vocab_size: int = 50304 # GPT-2 vocab_size of 50257, padded up to nearest multiple of 64 for efficiency
    n_layer: int = 12
    n_head: int = 12
    n_embd: int = 768
    dropout: float = 0.0
    bias: bool = True # True: bias in Linears and LayerNorms, like GPT-2. False: a bit better and faster
```

逐个说清它们各自拧的是什么：

| 超参 | 含义 | 拧大了会怎样 |
| --- | --- | --- |
| `block_size` | **上下文长度**：一次最多看多少个 token（默认 1024） | 能看更长的上文，但注意力开销随长度**平方**增长 |
| `vocab_size` | **词表大小**：输出层要在多少个候选里选（默认 50304） | 输出层更宽、更吃显存 |
| `n_layer` | **层数**：叠多少个 `Block`（默认 12） | 模型更深、更能抽象，也更慢更难训 |
| `n_head` | **注意力头数**：把注意力拆成几路并行（默认 12） | 能同时关注多种关系，详见下一章 |
| `n_embd` | **嵌入维度**：每个 token 向量多长（默认 768） | 表达力更强，参数量随它**平方**涨 |
| `dropout` | **随机失活比例**：训练时随机丢弃的比例（默认 0） | 抗过拟合，但预训练大数据时通常设 0 |
| `bias` | Linear / LayerNorm **要不要偏置项** | `False` 略快、还略好，是 nanoGPT 相对 GPT-2 的小改动 |

<Callout type="info">
  一个有意思的细节：默认 `vocab_size = 50304`，而 GPT-2 的真实词表是 **50257**。多出来的 47 个是「凑整」——把词表大小**补齐到 64 的整数倍**，GPU 上的矩阵运算能对齐得更整齐、跑得更快。这些填充位置永远不会被真正用到，纯粹是为了性能。又一个「逐行读才看得见」的工程小心思。
</Callout>

默认这套(`n_layer=12, n_head=12, n_embd=768`)正好就是 **GPT-2 的最小款（124M 参数）** 的规格。把这几个数字调大，就是 GPT-2 medium / large / XL——**结构一模一样，只是零件尺寸变了**。这也再次说明：读懂 nanoGPT，你就读懂了整个 GPT-2 家族的骨架。

## 下一步

全景图有了，零件清单也齐了。现在，深吸一口气，我们去啃那块最硬的骨头——`CausalSelfAttention`。它是 GPT 里最关键、也最容易被「示意图」糊弄过去的部分。我们一行都不跳。👉 [注意力：CausalSelfAttention 逐行拆解](https://daiw.org/manual/nanogpt/attention)
