← 返回首页
04_gpt_machine_viz.html

整机俯瞰:一个字符,如何流过整台 GPT

第 1–3 章把零件都拆懂了。这一页把它们拼成整机,只画我们这个 tiny 模型(≈ 0.8M 参数,不是 GPT-2)。 分两段:① 装机 —— 训练把所有权重矩阵调到位;② 跑起来 —— 一个预设字符串走完前向传播,再采样出下一个字符。全程自动播放

PART 1
🔧 装机 · 训练把权重调到位
PART 2
▶ 跑起来 · 前向 + 采样
这台机器 vocab 65 字符表 n_embd 128 主干维度 n_head 4 头数 n_layer 4 层数 ≈ 0.8M 参数 · 配套 03_transformer.py

① 装机:训练 = 把整机所有权重,从随机雪花调成有结构

整台 GPT 就是下面这一堆权重矩阵(参数真身)。训练做的唯一一件事:用梯度下降把它们从随机雪花,慢慢调出结构。 推理时它们全部冻结、不再变。点下面看一次"训练动画"。

从随机 → 有结构,扫一遍(示意)
输入层 · 把字符变成向量
4 × Block · 每层 = 多头注意力 + FFN(各带残差/LayerNorm)
收尾 · 把向量映射回字符打分
这些矩阵和"中间产物"别混(谁是真身?)
真身 = 被训练、会存盘的权重:上面这些 token_emb / pos_emb / Wq,Wk,Wv / proj / FFN / ln / lm_head。 而 q / k / v / wei / 注意力输出 / 每层的 x 都是推理时现算的中间产物,不存储、训练也不直接拧它们。 训练拧的永远是矩阵;中间产物只是"矩阵 × 输入"算出来的临时结果。
0.8M 是怎么来的?
粗算:嵌入 65×128 + 64×128 ≈ 16.5k;每个 Block 里 注意力投影(Wq/Wk/Wv/proj)≈ 66k、FFN(128×512 + 512×128)≈ 131k, 合 ≈ 197k,×4 层 ≈ 0.79M;lm_head 128×65 ≈ 8k。总计 ≈ 0.8M。GPT-2 是 124M,大了 150 倍,但结构一模一样

② 跑起来:一个字符流过整机 → 采样出下一个 → 接着生

选一个预设开头(不用你打字),机器自动逐字生成。每生成一个字符,都要把当前序列完整过一遍下面四站, 最后 softmax 出概率、采样一个字符接到末尾 —— 这叫自回归生成。

序列当前文本(灰=开头,橙=机器生成)
流水线每个字符都走完这四站(高亮 = 正在算)
STAGE 1
嵌入
末位字符 → 128n_embd 向量
(tok_emb + pos_emb)
STAGE 2
4 × Block
1234
每层:多头注意力(看前文)
+ FFN(逐位置思考)
👆 点开看内部
STAGE 3
ln_f + lm_head
末位向量 → 65vocab 个打分
(下一个字符的 logits)
STAGE 4
softmax → 采样
forward 前向 · STAGE 1–3 算出 logits(推理只有前向,没有反向)
decode · 挑 token
选一个开头,机器开始自动生成 ↑
掀开 Block 看内部 一个 Block = 在残差流上挂两个模块,各自「读 → 算 → 加回」 收起 ✕
残差流 x LN 注意力跨 token → LN → FFN跨维度 x'
看第几层:
左:注意力矩阵(行=谁在看 query,列=被看 key,越深权重越大)。右:选中行的连线。移到某一行,看这个 token 在读哪些前文。
128 维 → 放大 512 维 → ReLU(砍负为0) → 压回 128 维 → 加回残差流
点「▶」看值流过:亮起的线 = 正在走的路径;灰色隐藏节点 = 被 ReLU 砍掉。全程只有 1 个 token。
为什么生成的是"莎士比亚味儿"的乱码,不是真句子?
这台只有 0.8M 参数 + 字符级,又只训练几千步,能学到的是"字母怎么搭、单词长啥样、谁跟谁常一起出现", 所以采样出来像英文、有词形和对话结构,但不成句意。把模型放大(GPT-2 124M)、上更多数据,同一套机器就会越说越像人话。 (本页生成文本是预设示意,为了动画稳定;真跑 03_transformer.py 每次结果不同。)
采样为什么不直接取最高分?
若每次都取最高分(argmax),模型会陷入重复死循环(一直输出同几个字)。按概率随机采样能保持多样性, 所以同一个开头每次生成都不一样。代码里就是 torch.multinomial(probs, 1)