到这里,你已经拆过不少零件:token 怎么切(第 2·3 课)、意思怎么变成几何(第 4·5 课)、顺序怎么写进向量(第 6 课)、注意力怎么打分怎么配比(第 7·8 课)。但零件拆得再熟,也有一个问题悬着——整台机器到底怎么转?这张全景图回答它:「猫 吃 鱼」从进入模型到吐出下一个词的完整流水线,一站不漏。
三种用法:① 每站可点,下方面板给「人话 + 形状 + 对应课程链接」——你走过的站标 ✓,前方的站标课号,当预告片看;② 按 ▶ 播放,看数据流动画——下半屏里,「猫吃鱼」先变成编号,编号查表摞成矩阵 (3×8),盖上位置戳,被打分、被掩码切掉右上三角、被 softmax 摊成配比,一路变形到最后变成 8 根概率条;抽中的词拼回句尾,同一台机器再转一圈,连转 5 圈。每个数字都是真算的(下面那块代码、同种子同权重预计算),不是手绘示意;③ 底部维度账本全程对账,矩阵走到哪站长成什么形状,一格一格看得见。课程后面的课学完回来看,这张图会一站一站变亮。
先打个预防针:播放到最后,模型写出的续句会是一串不通的胡话——这是故意的,也是本页最重要的一课。这台玩具机器的参数全是随机数:随机参数写不出通顺的话,写得出才是假的(那说明数字是手造的)。动画里每个矩阵、每张概率表都真算自下方代码——胡话,正是"数字没造假"的证明。第 15 课训练之后,同一条流水线一个零件都不换,胡话才会变人话。
交互 · Transformer 全景:12 站地图 + 数据流动画(每一步都是真算的数字)
这张图最重要的一条规律:形状不变量
走完全程你会发现,L(词数)和 d_model(向量维度)从头到尾没变过——Block 进去 (L, d_model)、出来还是 (L, d_model),它不改形状,只精炼内容:每过一站,每个词的向量里就多写进一层上下文。形状只在两处改变:入口(文本 → ids → 查表变成 (L, d_model))和出口(投影回词表,变成 (L, V) 的 logits,生成时只取最后一行)。玩具版 L=3、d_model=8、V=8;真实 GPT-2:L≤1024、d_model=768、Block×12、V≈5 万——形状链一模一样,只是每个数都大了三个数量级。
一句辨清:图上这台是哪种 Transformer?
这张图是 GPT 式(decoder-only,只解码器):一路从左到右、因果掩罩住未来、出口是「下一个词」。它有两个表亲:BERT 式(只编码器)——去掉因果掩码,双向都看,出口不是生成而是每个位置的表示(拿来分类、填空);原版翻译 Transformer(编码器+解码器)——两截网络,中间靠交叉注意力连接(第 7 课讲对齐热图时见过:解码器每一步回头看编码器的全部状态)。今天的大语言模型(GPT/Claude/Qwen 文本模型)几乎都是图上这种 decoder-only。
真算一遍:全景图的 numpy 版(玩具规模)
上面动画里的每个矩阵、每张概率表,是不是真能算出来?——跑一遍就知道。下面这块代码和动画同种子、同权重:动画里的分数表 S、配比表 A、每一圈的概率条,就是这段代码算出的数字。零件全是你学过的(查表、正弦位置编码、四步注意力),只有三处是预习版黑盒:因果掩码、残差、FFN(分别是第 9、11、12 课的主角,此处先用最简版,学完回来看会秒懂):
# ============================================================
# 【演示目标】把上方全景动画真算一遍(玩具规模),并连跑 5 圈自回归:
# 「猫 吃 鱼」→ 嵌入 → +位置编码 → 2 层 Block(因果注意力 + FFN)
# → 投影到词表 → softmax → 下一个词分布 → 拼回句尾再来一圈 ×5
# ——动画里每个矩阵、每张概率表,就是这段代码(同种子、同权重)算出的数字。
# 【思路】
# 1. 玩具词表 8 词、d_model=8;权重一次抽齐后【冻结】——
# 生成 5 圈用的是同一台机器(和动画的 5 圈同一组数字)
# 2. 三处"预习版黑盒":因果掩码(第 9 课)、残差(第 11 课,
# LayerNorm 简化省略)、FFN+GELU(第 12 课)
# 3. 出口投影复用嵌入表(= weight tying;真实模型常用独立矩阵,第 14 课)
# 读法: 5 圈后生成「猫吃鱼。追追追猫」——高置信,却在复读:
# 这就是"没训练"。把 RandomState(0) 改成 1、2、3 再跑,"文风"立刻变;
# 第 15 课训练后,这条流水线一个零件都不换——换的只是里面所有的数字。
# ============================================================
import numpy as np
rng = np.random.RandomState(0) # 换成 1、2、3 再跑,"文风"立刻变
vocab = ["猫", "吃", "鱼", "。", "它", "追", "饱", "了"]
V, d = len(vocab), 8
E = rng.randn(V, d) * 0.5 # 嵌入表(第 4 课)
Ws = [] # 权重一次抽齐,之后【冻结】——
for layer in range(2): # 生成 5 圈用的是同一台机器
Ws += [rng.randn(d, d) * 0.4, # W_q(第 7 课)
rng.randn(d, d) * 0.4, # W_k
rng.randn(d, d) * 0.4, # W_v
rng.randn(d, 4 * d) * 0.3, # W_1(FFN 升维,第 12 课预习)
rng.randn(4 * d, d) * 0.3] # W_2(FFN 降维)
def softmax(x, axis=-1):
e = np.exp(x - x.max(axis=axis, keepdims=True))
return e / e.sum(axis=axis, keepdims=True)
def gelu(x): # FFN 的激活(第 12 课预习)
return 0.5 * x * (1 + np.tanh(0.7978845608 * (x + 0.044715 * x ** 3)))
def PE(L): # 正弦位置编码(第 6 课)
pos = np.arange(L)[:, None]
freq = 10000 ** (np.arange(0, d, 2) / d)
P = np.zeros((L, d))
P[:, 0::2] = np.sin(pos / freq); P[:, 1::2] = np.cos(pos / freq)
return P
def forward(ids): # 一趟完整前向 = 上方全景动画
L = len(ids)
H = E[ids] + PE(L) # 查表 + 位置(形状 (L, 8))
for layer in range(2): # Block × 2(真实 GPT-2 ×12)
Wq, Wk, Wv, W1, W2 = Ws[5 * layer: 5 * layer + 5]
Q, K, Vv = H @ Wq, H @ Wk, H @ Wv # 三副眼镜(第 7 课)
S = Q @ K.T / np.sqrt(d) # 打分 + ÷√d_k(第 8 课)
S[np.triu(np.ones((L, L), dtype=bool), 1)] = -1e9 # 因果掩码(第 9 课预习)
H = H + softmax(S) @ Vv # 加权 V + 残差(第 11 课预习)
H = H + gelu(H @ W1) @ W2 # FFN + 残差(LayerNorm 简化省略)
return H # 出来仍是 (L, 8)——形状不变量
sent = [0, 1, 2] # 分词(第 2 课):猫 吃 鱼
for circle in range(5): # 自回归:一圈出一个词 ×5
H = forward(sent) # (权重全程冻结——同一台机器)
P = softmax(H[-1] @ E.T) # 只看最后一行:下一个词分布
if circle == 0:
print("[形状] ids(3,) → X(3,8) → +PE(3,8) → Block×2(3,8) → 末行(8,) → P(8,)")
print("第 %d 圈 输入「%s」→ 下一个词:" % (circle + 1, "".join(vocab[i] for i in sent)),
" ".join("%s%.0f%%" % (w, p * 100) for w, p in zip(vocab, P)))
sent.append(int(P.argmax())) # 贪心取最大,拼回句尾
print("\n没训练的模型接着写:" + "".join(vocab[i] for i in sent))
print("高置信,却在复读乱跳——零件齐了,参数还是随机数;第 15 课训练后才说人话。")
这一块代码值得存着:第 9–12 课每学一个零件,回来把对应的"预习版黑盒"换成真货(真掩码、真 LayerNorm、多头切分);到第 14 课,它会长成完整的迷你 Transformer——你现在看到的是它的骨架。
检索练习
复习入口:每一站回哪课
延伸
我是你的老师,别客气
指着图问最好使:"Block 为什么不改形状""FFN 为什么偏偏升维 4 倍""logits 为什么只看最后一行"——任何一站都能往下挖一整课。哪个站看着不顺眼(太笼统/太跳),也直接说,我改图。