Transformer 手算全流程
「我爱水课」→「I love easy courses」· 每字一个 token · 所有数字可手算复现 · dmodel=4, h=2, dk=dv=2, Nenc=Ndec=2
0 全局设定
| 参数 | 符号 | 取值 | 说明 |
| 源语言 | — | 我 爱 水 课 | 4 个 token(每个汉字一个 token) |
| 目标语言 | — | I love easy courses | 4 个 token(每个单词一个 token) |
| 词表大小 | V | 8 | 我=0 爱=1 水=2 课=3 I=4 love=5 easy=6 courses=7 |
| 嵌入维度 | dmodel | 4 | 每个 token 用 4 维向量 |
| 注意力头数 | h | 2 | 每头 dk=dv=dmodel/h=2 |
| 缩放因子 | √dk | √2 ≈ 1.4142 | score = Q·KT/√dk |
| FFN 隐藏层 | dff | 8 | X·W1T → ReLU → ·W2T |
| Encoder 层数 | Nenc | 2 | 每层 = Self-Attn + FFN + 残差 + LayerNorm |
| Decoder 层数 | Ndec | 2 | 每层 = Masked Self-Attn + Cross-Attn + FFN |
所有权重由固定随机种子生成(seed=42),取值范围 [-1, 1],取 1 位小数 → 任何一步乘法都能心算验证。完整权重在下方各节逐块给出。
1 对照整体架构图
┌───────────── ENCODER (×2层) ─────────────┐ ┌───────────── DECODER (×2层) ─────────────────────┐
│ 输入: 我爱水课 (4 tokens) │ │ 输入: I love easy courses (4 tokens, 带掩码) │
│ ① Input Embedding + Positional Encoding │ │ ① Input Embedding + Positional Encoding │
│ ↓ (X_in 4×4) │ │ ↓ │
│ ② Multi-Head Self-Attention │ │ ② Masked Multi-Head Self-Attention ◄─ 只看左边 │
│ Q=K=V=X_in, h=2头, d_k=2 │ │ (上三角掩码 -∞, 防偷看未来) │
│ score=QK^T/√2 → Softmax → A·V │ │ ↓ │
│ ↓ + 残差 → LayerNorm │ │ ③ Cross-Attention ◄─ Q=Decoder, K/V=Encoder输出│
│ ③ Feed-Forward (4→8→4, ReLU) │ │ (从源语言「我爱水课」里提取信息) │
│ ↓ + 残差 → LayerNorm │ │ ↓ + 残差 → LayerNorm │
│ 输出: 记忆了整句的 4 个向量 │ │ ④ Feed-Forward (4→8→4, ReLU) │
└──────────────┬───────────────────────────┘ │ ↓ + 残差 → LayerNorm │
│ K/V └────────────────────┬───────────────────────────┘
│ Cross-Attention 从这里取 K/V ↓
└─────────────────────────────────────────────────► ⑤ Linear (4→8 logits)
↓
⑥ Softmax → 概率, 每步预测下一个词
整体流程一句话:Encoder 把「我爱水课」读成 4 个富含上下文的向量(自注意力让每个词看到全句);Decoder 逐个预测「I」「love」「easy」「courses」,每预测一个词时,既通过掩码自注意力看自己已生成的词(防止偷看未来),又通过交叉注意力从 Encoder 的记忆里提取源句信息。
2 Tokenization:每个字一个 token
| ID | 源 token | ID | 目标 token |
| 0 | 我 | 4 | I |
| 1 | 爱 | 5 | love |
| 2 | 水 | 6 | easy |
| 3 | 课 | 7 | courses |
真实 Transformer 用 BPE/子词分词器;本实例按要求「每字一个 token」简化:中文按字、英文按单词。原理完全一致,仅计算量不同。
3 Embedding:从离散 ID 到连续向量
3.1 词嵌入矩阵 E ∈ ℝ8×4(8 词 × 4 维)
dim0 dim1 dim2 dim3
我(0) 0.5 -0.3 0.8 0.1
爱(1) -0.2 0.6 -0.1 0.4
水(2) 0.3 0.2 0.5 -0.4
课(3) -0.6 0.1 0.3 0.7
I(4) 0.4 0.5 -0.2 0.3
love(5) 0.7 -0.1 0.6 -0.3
easy(6) -0.5 0.4 0.2 0.6
courses(7) 0.1 -0.6 0.4 0.2
嵌入 = 查表:token 的 ID 对应 E 的哪一行,就取出哪一行。例如「我」= ID 0 → 取 E 第 0 行 [0.5, -0.3, 0.8, 0.1]。
4 Positional Encoding:注入位置信息
PE(pos, 2i) = sin(pos / 10000^(2i/d)) PE(pos, 2i+1) = cos(pos / 10000^(2i/d))
d=4,所以 i 只取 0、1:
i=0 → 分母 10000^0 = 1; i=1 → 分母 10000^(0.5) = 100
| 位置 pos | dim0 = sin(pos/1) | dim1 = cos(pos/1) | dim2 = sin(pos/100) | dim3 = cos(pos/100) |
| 0 | sin0 = 0 | cos0 = 1 | sin0 = 0 | cos0 = 1 |
| 1 | sin1 = 0.8415 | cos1 = 0.5403 | sin0.01 = 0.0100 | cos0.01 = 1.0000 |
| 2 | sin2 = 0.9093 | cos2 = -0.4161 | sin0.02 = 0.0200 | cos0.02 = 0.9998 |
| 3 | sin3 = 0.1411 | cos3 = -0.9900 | sin0.03 = 0.0300 | cos0.03 = 0.9996 |
✍ 手算验证(弧度制):
sin(0)=0, cos(0)=1;sin(1)≈0.84147, cos(1)≈0.54030;sin(2)≈0.90930, cos(2)≈-0.41615;sin(3)≈0.14112, cos(3)≈-0.98999。全部与表一致 ✓
5 输入矩阵 X_in = E + PE(逐元素相加)
5.1 Encoder 输入(源语言「我爱水课」)
嵌入 E + 位置 PE = X_in (Encoder)
我(0): [0.5, -0.3, 0.8, 0.1] + [0, 1, 0, 1] = [0.5000, 0.7000, 0.8000, 1.1000]
爱(1): [-0.2, 0.6, -0.1, 0.4] + [0.8415, 0.5403, 0.0100, 1.0000] = [0.6415, 1.1403, -0.0900, 1.4000]
水(2): [0.3, 0.2, 0.5, -0.4] + [0.9093, -0.4161, 0.0200, 0.9998] = [1.2093, -0.2161, 0.5200, 0.5998]
课(3): [-0.6, 0.1, 0.3, 0.7] + [0.1411, -0.9900, 0.0300, 0.9996] = [-0.4589, -0.8900, 0.3300, 1.6996]
5.2 Decoder 输入(目标语言「I love easy courses」,训练时 teacher forcing 整句送入)
I(4): [0.4, 0.5, -0.2, 0.3] + [0, 1, 0, 1] = [0.4000, 1.5000, -0.2000, 1.3000]
love(5): [0.7, -0.1, 0.6, -0.3] + [0.8415, 0.5403, 0.0100, 1.0000] = [1.5415, 0.4403, 0.6100, 0.7000]
easy(6): [-0.5, 0.4, 0.2, 0.6] + [0.9093, -0.4161, 0.0200, 0.9998] = [0.4093, -0.0161, 0.2200, 1.5998]
courses(7):[0.1, -0.6, 0.4, 0.2] + [0.1411, -0.9900, 0.0300, 0.9996] = [0.2411, -1.5900, 0.4300, 1.1996]
为什么加位置编码?自注意力本身对位置不敏感(打乱顺序 QK 结果不变)。加上正弦/余弦编码后,每个 token 的向量携带「我在第几个位置」的信息,模型才能区分「水课」和「课水」。
6 Encoder Layer 1 — Self-Attention 核心
6.1 投影权重(可复现,1 位小数)
每头一组 W^Q、W^K、W^V ∈ ℝ2×4,输出 2 维。Head1 用 WQ1a/WK1a/WV1a,Head2 用 WQ1b/WK1b/WV1b:
WQ1a (Head1 Q): WK1a (Head1 K): WV1a (Head1 V):
[-0.3 0.9 0.5 0.2] [ 0.2 0.4 -1.0 0.9] [-0.4 0.0 -0.1 -0.4]
[-0.7 -0.7 -0.9 0.7] [ 0.7 -0.6 -0.6 -0.6] [ 0.2 -0.7 -0.4 -0.3]
WQ1b (Head2 Q): WK1b (Head2 K): WV1b (Head2 V):
[-0.1 0.6 -0.6 0.0] [-0.9 0.9 0.9 0.6] [-0.8 0.0 -0.9 0.8]
[ 0.2 -0.9 0.2 -0.7] [-0.4 -0.8 0.4 -0.1] [-0.5 0.3 -0.4 0.0]
WO1 (输出投影 4×4):
[ 0.1 -0.6 0.9 0.6]
[ 0.9 0.8 0.2 0.8]
[-0.8 -0.6 -0.9 -0.3]
[-0.2 -0.5 0.7 -0.3]
6.2 计算 Q、K、V(对每个 token)
Q = X_in · W^Qᵀ K = X_in · W^Kᵀ V = X_in · W^Vᵀ (逐行做向量·矩阵乘法)
✍ 手算第 1 行(token「我」,X = [0.5, 0.7, 0.8, 1.1],Head1):
Q[0] = 0.5×(-0.3) + 0.7×0.9 + 0.8×0.5 + 1.1×0.2 = -0.15+0.63+0.40+0.22 = 1.10
Q[1] = 0.5×(-0.7) + 0.7×(-0.7) + 0.8×(-0.9) + 1.1×0.7 = -0.35-0.49-0.72+0.77 = -0.79
K[0] = 0.5×0.2 + 0.7×0.4 + 0.8×(-1.0) + 1.1×0.9 = 0.10+0.28-0.80+0.99 = 0.57
K[1] = 0.5×0.7 + 0.7×(-0.6) + 0.8×(-0.6) + 1.1×(-0.6) = 0.35-0.42-0.48-0.66 = -1.21
V[0] = 0.5×(-0.4) + 0.7×0 + 0.8×(-0.1) + 1.1×(-0.4) = -0.20+0-0.08-0.44 = -0.72
V[1] = 0.5×0.2 + 0.7×(-0.7) + 0.8×(-0.4) + 1.1×(-0.3) = 0.10-0.49-0.32-0.33 = -1.04
其余各行同理(逐 token 计算),得到完整矩阵 ↓
Head 1 的 Q、K、V(4×2)
Q1 (来自 WQ1a): K1 (来自 WK1a): V1 (来自 WV1a):
我 [ 1.1000, -0.7900] [ 0.5700, -1.2100] [-0.7200, -1.0400]
爱 [ 1.0688, -0.1863] [ 1.9344, -1.0211] [-0.8076, -1.0539]
水 [-0.1774, -0.7433] [ 0.1752, 0.3043] [-0.7756, 0.0052]
课 [-0.1584, 1.8369] [ 0.7518, -1.0049] [-0.5293, -0.1106]
Head 2 的 Q、K、V(4×2)
Q2 (来自 WQ1b): K2 (来自 WK1b): V2 (来自 WV1b):
我 [-0.1100, -1.1400] [ 1.5600, -0.5500] [-0.2400, -0.3600]
爱 [ 0.6740, -1.8959] [ 1.2079, -1.3448] [ 0.6878, 0.0574]
水 [-0.5626, 0.1205] [-0.4550, -0.1628] [-0.9556, -0.8775]
课 [-0.6861, -0.4145] [ 0.9287, 0.8576] [ 1.4297, -0.1696]
语义解读:Q 是「我在找什么」,K 是「我有什么标签」,V 是「我的内容」。自注意力让每个 token 用自己的 Q 去匹配所有 token 的 K,得到权重后加权求和所有 V。
6.3 注意力分数 score = Q·KT/√dk
score[i][j] = (Q[i]·K[j]) / √2 (√2 ≈ 1.4142,防止点积过大把 softmax 推向极端)
✍ 手算 score 第 1 行(Q我=[1.10,-0.79],Head1):
对 K我=[0.57,-1.21]: (1.10×0.57 + (-0.79)×(-1.21))/1.4142 = (0.627+0.956)/1.4142 = 1.583/1.4142 = 1.1193
对 K爱=[1.9344,-1.0211]: (1.10×1.9344 + (-0.79)×(-1.0211))/1.4142 = (2.1278+0.8067)/1.4142 = 2.9345/1.4142 = 2.0750
对 K水=[0.1752,0.3043]: (1.10×0.1752 + (-0.79)×0.3043)/1.4142 = (0.1927-0.2404)/1.4142 = -0.0477/1.4142 = -0.0337
对 K课=[0.7518,-1.0049]: (1.10×0.7518 + (-0.79)×(-1.0049))/1.4142 = (0.8270+0.7939)/1.4142 = 1.6209/1.4142 = 1.1462
score1 (Head1) = Q1·K1ᵀ/√2:
我 [ 1.1193, 2.0750, -0.0337, 1.1462]
爱 [ 0.5902, 1.5964, 0.0923, 0.7006]
水 [ 0.5645, 0.2941, -0.1819, 0.4339]
课 [-1.6355, -1.5430, 0.3756, -1.3895]
score2 (Head2) = Q2·K2ᵀ/√2:
我 [ 0.3220, 0.9901, 0.1666, -0.7635]
爱 [ 1.4809, 2.3786, 0.0014, -0.7071]
水 [-0.6675, -0.5952, 0.1671, -0.2964]
课 [-0.5956, -0.1919, 0.2685, -0.7019]
读数:score[i][j] 越大 = token i 越「关注」token j。看 Head1 第一行:「我」对「爱」的分最高(2.08),对「水」最低(-0.03),说明这个头学到「我→爱」的强关联(主语后接动词)。
6.4 Softmax:把分数变成权重(每行和为 1)
A[i][j] = e^score[i][j] / Σk e^score[i][k]
✍ 手算第 1 行(Head1,score=[1.1193, 2.0750, -0.0337, 1.1462]):
e^1.1193=3.0627, e^2.0750=7.9666, e^-0.0337=0.9669, e^1.1462=3.1463;总和 = 15.1425
A[0][0]=3.0627/15.1425=0.2023 A[0][1]=7.9666/15.1425=0.5261 A[0][2]=0.9669/15.1425=0.0639 A[0][3]=3.1463/15.1425=0.2078 (和=1.0001≈1 ✓)
A1 (Head1 softmax): A2 (Head2 softmax):
我 [0.2023, 0.5261, 0.0639, 0.2078] [0.2413, 0.4707, 0.2066, 0.0815]
爱 [0.1832, 0.5010, 0.1113, 0.2045] [0.2636, 0.6468, 0.0600, 0.0296]
水 [0.3211, 0.2450, 0.1522, 0.2818] [0.1716, 0.1844, 0.3953, 0.2487]
课 [0.0922, 0.1011, 0.6888, 0.1179] [0.1733, 0.2595, 0.4113, 0.1559]
「我」的注意力分配(Head1):52.6% 看「爱」,20.8% 看「课」,20.2% 看自己,6.4% 看「水」——这就是自注意力的本质:每个 token 按相关性重新分配对全句的注意力。
6.5 加权求和:输出 = A·V
Out[i] = Σj A[i][j] · V[j] (每行 = 各 token V 的加权混合)
✍ 手算 Head1 输出第 1 行(「我」):
dim0 = 0.2023×(-0.72) + 0.5261×(-0.8076) + 0.0639×(-0.7756) + 0.2078×(-0.5293)
= -0.1457 - 0.4249 - 0.0496 - 0.1100 = -0.7300
dim1 = 0.2023×(-1.04) + 0.5261×(-1.0539) + 0.0639×0.0052 + 0.2078×(-0.1106)
= -0.2104 - 0.5545 + 0.0003 - 0.0230 = -0.7874
Head1 输出 A1·V1: Head2 输出 A2·V2:
我 [-0.7300, -0.7874] [ 0.1849, -0.2549]
爱 [-0.7311, -0.7405] [ 0.3665, -0.1155]
水 [-0.6962, -0.6225] [ 0.0635, -0.4402]
课 [-0.7447, -0.2119] [-0.0333, -0.4348]
6.6 拼接 + 输出投影 WO
Concat[i] = [Head1[i] | Head2[i]] ∈ ℝ⁴ → Out = Concat · WOᵀ
拼接 [H1|H2]:
我 [-0.7300, -0.7874, 0.1849, -0.2549]
爱 [-0.7311, -0.7405, 0.3665, -0.1155]
水 [-0.6962, -0.6225, 0.0635, -0.4402]
课 [-0.7447, -0.2119, -0.0333, -0.4348]
Out = Concat·WOᵀ(多头注意力的最终输出,喂给残差):
我 [ 0.4129, -1.4539, 0.9665, 0.7456]
爱 [ 0.6318, -1.2694, 0.7339, 0.8077]
水 [ 0.0968, -1.4640, 1.0054, 0.6270]
课 [-0.2382, -1.1943, 0.8833, 0.3620]
✍ 手算「我」这一行 Out[0](用 WO 的第 j 行做点积):
Out[0][0] = -0.73×0.1 + (-0.7874)×(-0.6) + 0.1849×0.9 + (-0.2549)×0.6 = -0.073+0.4724+0.1664-0.1529 = 0.4129 ✓
Out[0][1] = -0.73×0.9 + (-0.7874)×0.8 + 0.1849×0.2 + (-0.2549)×0.8 = -0.657-0.6299+0.037-0.2039 = -1.4539 ✓
6.7 残差连接 + LayerNorm
残差: X1 = X_in + AttnOut → LayerNorm: (x - μ)/√(σ² + ε)
残差 X_in + AttnOut:
我 [ 0.9129, -0.7539, 1.7665, 1.8456]
爱 [ 1.2733, -0.1291, 0.6439, 2.2076]
水 [ 1.3061, -1.6802, 1.5254, 1.2268]
课 [-0.6971, -2.0842, 1.2133, 2.0616]
LayerNorm 后(每行均值≈0、方差≈1):
我 [-0.0286, -1.6227, 0.7878, 0.8634]
爱 [ 0.3203, -1.3170, -0.4144, 1.4111]
水 [ 0.5400, -1.7261, 0.7063, 0.4797]
课 [-0.5066, -1.3631, 0.6730, 1.1968]
为什么残差+归一化?残差让梯度能直接「抄近道」流回输入,避免深层网络梯度消失;LayerNorm 把每行拉回标准分布,训练更稳。
6.8 Feed-Forward Network(4 → 8 → 4,ReLU)
H = X·W1ᵀ (4→8) → ReLU(H) = max(0, H) → Out = ReLU(H)·W2ᵀ (8→4)
W1_1 (8×4, 第一层): W1_2 (4×8, 第二层):
[-0.4 0.1 -0.7 0.6] [ 0.0 -0.1 -0.9 -0.8 -0.9 0.3 -0.4 0.0]
[-0.9 1.0 0.5 -0.6] [ 0.8 -0.5 -0.2 0.5 -0.5 -0.8 -0.4 -0.7]
[-1.0 0.6 0.4 0.5] [ 0.9 0.6 0.3 0.7 0.6 -0.6 0.8 0.1]
[ 0.5 -0.9 -0.3 -0.8] [ 0.6 0.8 -0.4 -0.8 -0.5 -0.1 0.6 0.7]
[ 0.7 0.2 -0.3 -0.9]
[-0.4 -0.3 0.5 0.3]
[ 0.8 -0.1 -0.8 0.4]
[ 0.5 0.1 0.5 0.0]
中间 H = LN输出·W1ᵀ (4×8):
我 [-0.1842, -1.7211, -0.1982, 0.5190, -1.3580, 1.1511, -0.1454, 0.2173]
爱 [ 0.8770, -2.6591, -0.5707, 0.3408, -1.1849, 0.4831, 1.2839, -0.1788]
水 [-0.5952, -2.1467, -1.0532, 1.2278, -0.6109, 0.7989, 0.2314, 0.4506]
课 [ 0.3133, -1.2888, 0.5563, -0.1858, -1.9062, 1.3071, -0.3286, -0.0531]
ReLU 后(负数清零):
我 [ 0.0000, 0.0000, 0.0000, 0.5190, 0.0000, 1.1511, 0.0000, 0.2173]
爱 [ 0.8770, 0.0000, 0.0000, 0.3408, 0.0000, 0.4831, 1.2839, 0.0000]
水 [ 0.0000, 0.0000, 0.0000, 1.2278, 0.0000, 0.7989, 0.2314, 0.4506]
课 [ 0.3133, 0.0000, 0.5563, 0.0000, 0.0000, 1.3071, 0.0000, 0.0000]
FFN 输出 = ReLU·W2ᵀ (4×4):
我 [-0.0699, -0.8135, -0.3056, -0.3782]
爱 [-0.6413, -0.0280, 1.7651, 0.9755]
水 [-0.8351, -0.4332, 0.6103, -0.6079]
课 [-0.1085, -0.9063, -0.3354, -0.1652]
再次残差 + LayerNorm → Encoder Layer 1 最终输出:
我 [ 0.2437, -1.6980, 0.7259, 0.7284]
爱 [-0.5803, -1.2885, 0.5761, 1.2927]
水 [ 0.0173, -1.4924, 1.3225, 0.1526]
课 [-0.1908, -1.5269, 0.5786, 1.1391]
FFN 的作用:注意力只是「把信息搬来搬去」,FFN 才是「思考加工」——对每个 token 独立做非线性变换,把注意力混合出来的表示再精炼一遍。8 维中间层放大了表达能力,ReLU 引入非线性。
7 Encoder Layer 2(结构相同,权重不同)
7.1 Layer 2 权重
WQ2a: WK2a: WV2a:
[-1.0 0.0 -0.2 -0.6] [ 0.0 0.4 -0.3 0.9] [-0.4 -0.9 0.2 0.0]
[-0.8 -0.3 0.9 -0.4] [ 0.9 -0.5 0.0 -0.4] [-0.9 -0.4 0.8 -0.5]
WQ2b: WK2b: WV2b:
[-0.7 0.0 1.0 -0.5] [-0.3 0.3 0.3 0.1] [-0.9 0.2 0.4 -1.0]
[ 0.3 0.5 -0.5 0.5] [-0.8 0.7 -0.4 -0.6] [ 0.0 -0.5 0.3 -0.7]
WO2: W2_1 (FFN第一层): W2_2 (FFN第二层):
[ 0.4 -0.2 0.9 -0.7] [ 0.8 0.3 -0.3 -0.3] [-0.5 -0.5 0.9 -0.2 0.8 0.3 0.6 0.0]
[-0.3 -0.8 0.8 0.8] [ 0.5 0.8 0.8 0.6] [ 0.2 0.0 -0.6 0.4 -0.4 -1.0 0.3 -0.6]
[-0.5 0.3 0.6 0.1] [ 0.3 -0.8 -0.7 0.8] [ 0.9 0.9 0.8 -0.3 -1.0 0.9 -0.1 0.9]
[ 0.1 -0.5 -0.8 0.8] [ 0.2 -1.0 -0.8 0.3] [ 0.9 0.7 -0.4 -0.2 0.7 -0.4 -0.7 0.1]
[-1.0 -0.7 0.1 0.4]
[ 0.3 -0.6 0.4 -0.5]
[-0.3 0.5 0.3 0.7]
[ 0.3 0.1 -0.8 -0.3]
计算流程与 Layer 1 完全相同:输入 = Layer 1 输出 → Self-Attn → 残差 → LN → FFN → 残差 → LN。这里直接给关键中间量,任一步骤可按 6.2–6.8 的方法复算。
7.2 Layer 2 Self-Attention 分数与权重
Head1 score = Q2a·K2aᵀ/√2: Head1 softmax:
我 [ 0.5125, -0.4664, 0.8353, -0.0756] [0.3019, 0.1134, 0.4170, 0.1677]
爱 [ 0.5212, -0.3424, 0.6103, 0.0291] [0.3199, 0.1349, 0.3497, 0.1956]
水 [ 0.9224, -0.5621, 1.0006, 0.0868] [0.3648, 0.0827, 0.3944, 0.1582]
课 [ 0.4751, -0.3932, 0.7033, -0.0386] [0.3054, 0.1282, 0.3837, 0.1827]
Head2 score = Q2b·K2bᵀ/√2: Head2 softmax:
我 [ 1.1168, 0.8029, 0.9141, 0.9876] [0.2919, 0.2133, 0.2383, 0.2565]
爱 [ 0.6174, 0.4910, 0.5365, 0.5687] [0.2662, 0.2346, 0.2456, 0.2536]
水 [ 1.7245, 1.4320, 1.5387, 1.6179] [0.2877, 0.2147, 0.2389, 0.2586]
课 [ 0.7773, 0.5608, 0.6375, 0.6883] [0.2786, 0.2243, 0.2422, 0.2549]
观察:Layer 2 的注意力分布比 Layer 1「更均匀」(Head2 几乎平摊),因为高层学到的是更全局、更抽象的关系——这正是 Transformer 层数加深的意义:低层抓局部语法,高层抓全局语义。
7.3 Layer 2 输出与最终 Encoder 表示
Attn 输出 = Concat·WO2ᵀ: 残差 X1+Attn: LayerNorm:
我 [-0.5231, -1.5552, -0.8540, 0.5367] [-0.2794, -3.2532, -0.1281, 1.2651] [ 0.1940, -1.6121, 0.2859, 1.1321]
爱 [-0.4961, -1.5071, -0.8627, 0.5474] [-1.0764, -2.7957, -0.2866, 1.8401] [-0.2982, -1.3304, 0.1759, 1.4527]
水 [-0.5155, -1.5360, -0.8621, 0.5476] [-0.4982, -3.0284, 0.4605, 0.7002] [ 0.0632, -1.6503, 0.7124, 0.8747]
课 [-0.5095, -1.5316, -0.8577, 0.5411] [-0.7004, -3.0585, -0.2790, 1.6802] [-0.0658, -1.4654, 0.1842, 1.3470]
FFN 输出: 残差: ⭐ ENCODER 最终输出(喂给 Cross-Attn 的 K/V):
我 [ 2.8090, -1.6633, 0.2133, -0.4219] [ 3.0030, -3.2754, 0.4993, 0.7102] [ 1.2296, -1.5587, 0.1177, 0.2114]
爱 [ 3.2740, -1.2179, -0.6887, -0.2059] [ 2.9758, -2.5482, -0.5127, 1.2468] [ 1.3092, -1.3839, -0.3915, 0.4663]
水 [ 2.5827, -1.8440, 0.0835, -0.1707] [ 2.6458, -3.4942, 0.7959, 0.7041] [ 1.1040, -1.6261, 0.2814, 0.2406]
课 [ 3.1174, -1.4108, -0.2754, -0.3316] [ 3.0515, -2.8761, -0.0912, 1.0154] [ 1.2974, -1.4723, -0.1710, 0.3460]
Encoder 完成 ✅:「我爱水课」被编码成 4 个 4 维向量。这 4 个向量就是源句的「完整记忆」——每个向量都通过自注意力吸收了全句信息。接下来 Decoder 的 Cross-Attention 将把 Q(来自解码端)与这 4 个 K/V 匹配,逐词翻译。
8 Decoder Layer 1 — Masked Self-Attention
8.1 掩码(Mask)—— 关键区别
scoremasked[i][j] = score[i][j] + M[i][j], M 为上三角矩阵:j > i 时 M = -1e9(≈ -∞),否则 0
掩码 M (4×4):
我 [ 0, -∞, -∞, -∞]
爱 [ 0, 0, -∞, -∞]
水 [ 0, 0, 0, -∞]
课 [ 0, 0, 0, 0]
为什么必须掩码?翻译是自回归的:生成「I」时绝不能看到「love/easy/courses」。e^(-1e9)≈0,softmax 后被掩码位置的权重直接归零——模型第 i 个位置只能看到自己及之前的词。这就是「只能看左边」的因果约束。
8.2 Decoder Layer 1 权重
WQd1a: WKd1a: WVd1a:
[ 0.9 0.4 0.1 -0.8] [ 0.8 0.5 0.4 0.4] [ 0.7 0.8 0.0 0.0]
[ 0.2 1.0 -0.7 0.0] [-0.3 -0.4 0.6 0.6] [ 0.6 0.3 0.4 0.6]
WQd1b: WKd1b: WVd1b:
[ 0.8 -0.3 -0.2 -0.8] [-0.4 0.2 -0.9 -0.9] [ 0.5 -0.6 0.2 -0.8]
[ 0.2 -0.9 -0.1 0.1] [ 0.6 -0.3 -0.7 0.0] [-0.9 0.1 0.1 0.3]
WOd1:
[ 0.5 1.0 0.0 -0.4]
[ 0.6 -0.5 -0.1 -0.8]
[-0.9 0.9 0.7 0.4]
[-0.2 -0.7 -0.7 -0.5]
8.3 Masked Self-Attention 计算(输入 = Decoder 的 X_in)
Head1 Q (WQd1a): Head1 K (WKd1a): Head1 V (WVd1a):
I [ -0.1000, 1.7200] [ 1.5100, -0.0600] [ 1.4800, 1.3900]
love [ 1.0645, 0.3216] [ 1.9773, 0.1474] [ 1.4313, 1.7209]
easy [ -0.8959, -0.0883] [ 1.0473, 0.9755] [ 0.2736, 1.2886]
courses[-1.3356, -1.8428] [ 0.0497, 1.5414] [-1.1032, 0.5594]
Head2 Q (WQd1b): Head2 K (WKd1b): Head2 V (WVd1b):
I [-1.1300, -1.1200] [-0.8500, -0.0700] [-1.7800, 0.1600]
love [ 0.4191, -0.0790] [-1.7075, 0.3658] [ 0.0686, -1.0723]
easy [-0.9916, 0.2344] [-1.8048, 0.0964] [-1.0215, 0.1320]
courses[-0.3757, 1.5562] [-1.8810, 0.3207] [ 0.2009, 0.0269]
Head1 score (加掩码后): Head1 softmax:
I [ -0.1797, -1e9, -1e9, -1e9] [1.0000, 0, 0, 0]
love [ 1.1229, 1.5218, -1e9, -1e9] [0.4016, 0.5984, 0, 0]
easy [ -0.9529, -1.2619, -0.7244, -1e9] [0.3343, 0.2455, 0.4202, 0]
courses[-1.3479, -2.0595, -2.2603, -2.0554] [0.4192, 0.2058, 0.1684, 0.2066]
Head2 score (加掩码后): Head2 softmax:
I [ 0.7346, -1e9, -1e9, -1e9] [1.0000, 0, 0, 0]
love [-0.2480, -0.5265, -1e9, -1e9] [0.5692, 0.4308, 0, 0]
easy [ 0.5844, 1.2578, 1.2814, -1e9] [0.2013, 0.3947, 0.4041, 0]
courses[ 0.1488, 0.8562, 0.5856, 0.8526] [0.1516, 0.3075, 0.2346, 0.3064]
✍ 验证掩码效果:
第 1 行(I):被掩码后 softmax = [1, 0, 0, 0],输出 = VI(只看到自己)✓
第 2 行(love):softmax = [0.4016, 0.5984, 0, 0]——只能看 I 和 love,权重和 = 1 ✓
第 4 行(courses):没有掩码限制,正常分配 4 个权重(0.4192+0.2058+0.1684+0.2066≈1)✓
下三角元素(j≤i)完全不受影响,只惩罚「偷看未来」。
8.4 Head 输出、拼接、WOd1、残差、LN、FFN
Head1 输出: Head2 输出: 拼接 [H1|H2]:
I [1.4800, 1.3900] [-1.7800, 0.1600] [ 1.4800, 1.3900, -1.7800, 0.1600]
love [1.4508, 1.5880] [-0.9836, -0.3709] [ 1.4508, 1.5880, -0.9836, -0.3709]
easy [0.9611, 1.4286] [-0.7439, -0.3377] [ 0.9611, 1.4286, -0.7439, -0.3377]
courses[0.7331, 1.2694] [-0.4268, -0.2663] [ 0.7331, 1.2694, -0.4268, -0.2663]
Attn 输出 = 拼接·WOd1ᵀ: 残差 X_in+Attn: LayerNorm:
I [ 2.0660, 0.2430, -1.2630, -0.1030] [ 2.4660, 1.7430, -1.4630, 1.1970] [ 0.9977, 0.5104, -1.6504, 0.1424]
love [ 2.4618, 0.4716, -0.7134, -0.5278] [ 4.0033, 0.9119, -0.1034, 0.1721] [ 1.6868, -0.2044, -0.8255, -0.6569]
easy [ 2.0443, 0.2069, -0.2351, -0.5027] [ 2.4536, 0.1908, -0.0151, 1.0971] [ 1.5638, -0.7612, -0.9727, 0.1701]
courses[ 1.7425, 0.0609, 0.0774, -0.6033] [ 1.9836, -1.5291, 0.5074, 0.5962] [ 1.2723, -1.5314, 0.0941, 0.1650]
FFN 输出 (ReLU·Wd1_2ᵀ): 残差: ⭐ Decoder Layer 1 输出:
I [-1.4523, 0.9341, -1.3311, -1.2765] [-0.4546, 1.4445, -2.9815, -1.1341] [ 0.2064, 1.4060, -1.3896, -0.2228]
love [-2.2144, 1.7438, -2.2431, -2.2306] [-0.5276, 1.5394, -3.0686, -2.8876] [ 0.3748, 1.4684, -0.9695, -0.8737]
easy [-1.6030, 0.7729, -1.5281, -1.0019] [-0.0392, 0.0117, -2.5008, -0.8318] [ 0.7886, 0.8387, -1.6354, 0.0081]
courses[-0.9130, 0.1788, -0.9130, -0.1635] [ 0.3593, -1.3525, -0.8189, 0.0014] [ 1.2072, -1.3379, -0.5445, 0.6752]
Decoder Layer 1 学到的:目标语言内部的语法结构(I 后面跟 love 的可能性、love 后面 easy 的可能性)——只靠掩码自注意力,不看源句。
9 Decoder Layer 2 — Masked Self-Attn + Cross-Attention
9.1 Decoder Layer 2 权重
WQd2a: WKd2a: WVd2a:
[ 0.5 0.6 -0.4 -0.6] [-0.3 0.6 -0.3 0.9] [-0.8 0.8 0.0 0.7]
[ 0.5 0.6 1.0 -0.2] [ 0.7 -0.1 0.5 0.5] [-0.4 0.8 -0.2 -1.0]
WQd2b: WKd2b: WVd2b:
[ 0.8 -0.8 -0.4 0.9] [-0.4 -0.3 0.3 0.5] [-0.9 0.1 -0.1 0.8]
[ 0.9 0.1 0.3 -0.1] [ 0.6 0.6 -0.8 0.0] [-0.3 -0.8 -0.7 0.5]
WOd2:
[ 0.2 -0.8 -0.8 0.4]
[-0.9 0.6 0.4 -0.8]
[-0.8 1.0 -0.3 -0.3]
[ 0.6 0.9 1.0 0.5]
(FFN 权重 Wd2_1/Wd2_2 与 Cross-Attention 权重见 9.4/9.5)
9.2 第一段:Masked Self-Attention(输入 = Dec-L1 输出)
Head1 Q: Head1 K: Head1 V:
I [ 1.6363, -0.3983] [ 0.9980, -0.8023] [ 0.8037, 1.5429]
love [ 1.9805, 0.2737] [ 0.2731, -0.8061] [ 0.2632, 2.0924]
easy [ 1.5468, -0.7395] [ 0.7645, -0.3455] [ 0.0457, 0.6745]
courses[-0.3864, -0.8787] [-0.3939, 1.0442] [-1.5635, -2.1195]
Head2 Q: Head2 K: Head2 V:
I [-0.6042, -0.0682] [-1.0326, 2.0791] [-0.0844, -0.3253]
love [-1.2734, 0.2807] [-1.3182, 1.8815] [-0.7926, -1.0454]
easy [ 0.6214, 0.3022] [-1.0536, 2.2847] [-0.4559, 0.2413]
courses[ 2.8616, 0.7219] [ 0.0927, 0.3572] [-0.6257, 1.4269]
Masked score1: Masked softmax1:
I [1.3807, -1e9, -1e9, -1e9] [1.0000, 0, 0, 0]
love [1.2424, 0.2264, -1e9, -1e9] [0.7342, 0.2658, 0, 0]
easy [1.5112, 0.7202, 1.0169, -1e9] [0.4846, 0.2197, 0.2956, 0]
courses[0.2258, 0.4262, 0.0058, -0.5412] [0.2866, 0.3502, 0.2300, 0.1331]
Masked score2: Masked softmax2:
I [0.3409, -1e9, -1e9, -1e9] [1.0000, 0, 0, 0]
love [1.3425, 1.5604, -1e9, -1e9] [0.4457, 0.5543, 0, 0]
easy [-0.0094, -0.1771, 0.0253, -1e9] [0.3471, 0.2935, 0.3594, 0]
courses[-1.0283, -1.7069, -0.9658, 0.3700] [0.1511, 0.0766, 0.1608, 0.6115]
Head1 输出: Head2 输出: 拼接:
I [0.8037, 1.5429] [-0.0844, -0.3253] [ 0.8037, 1.5429, -0.0844, -0.3253]
love [0.6600, 1.6889] [-0.4769, -0.7244] [ 0.6600, 1.6889, -0.4769, -0.7244]
easy [0.4608, 1.4069] [-0.4258, -0.3331] [ 0.4608, 1.4069, -0.4258, -0.3331]
courses[0.1249, 1.0481] [-0.5294, 0.7821] [ 0.1249, 1.0481, -0.5294, 0.7821]
Attn 输出=拼接·WOd2ᵀ: 残差: LayerNorm:
I [-1.1361, 0.4289, 1.0228, 1.6237] [-0.9297, 1.8348, -0.3668, 1.4009] [-1.2188, 1.1632, -0.7338, 0.7893]
love [-1.1274, 0.8081, 1.5213, 1.0769] [-0.7525, 2.2765, 0.5518, 0.2032] [-1.2076, 1.5587, -0.0164, -0.3347]
easy [-0.8260, 0.5255, 1.2659, 0.9504] [-0.0373, 1.3642, -0.3695, 0.9585] [-0.7301, 1.2519, -1.1999, 0.6782]
courses[-0.0771, -0.3211, 0.8723, 0.8799] [ 1.1302, -1.6590, 0.3278, 1.5551] [ 0.6412, -1.6179, -0.0087, 0.9854]
9.3 ⭐ Cross-Attention:翻译的关键一步
Q = Decoder LayerNorm 输出(目标语言侧,4×2/头)
K = V = Encoder 最终输出(源语言侧,4×2/头)
score = Q·Kᵀ/√2 → Softmax → A·V → 拼接 → WOc2ᵀ
与 Self-Attention 的本质区别:这里没有掩码!Decoder 可以自由查看 Encoder 的全部 4 个源 token——因为它要「从整句源文里找对应信息」。这 4 个源 token 正是 §7.3 的 ENCODER 最终输出。
9.4 Cross-Attention 权重
WQc2a: WKc2a: WVc2a:
[ 0.9 0.7 -0.9 -0.9] [ 0.2 -0.2 0.9 0.7] [-0.9 0.7 0.6 1.0]
[-0.2 0.6 1.0 -0.7] [ 0.7 -0.1 -0.2 -0.5] [ 1.0 0.1 0.5 0.9]
WQc2b: WKc2b: WVc2b:
[ 0.7 -0.5 -0.1 -0.7] [ 0.2 -0.3 -0.8 0.3] [ 0.1 0.1 0.8 -0.2]
[ 0.9 0.2 -0.5 0.3] [ 0.0 0.5 0.0 0.7] [-0.7 -0.9 0.5 0.2]
WOc2:
[ 0.4 -0.6 -0.7 -1.0]
[-0.3 0.2 -0.2 -0.1]
[ 0.8 -0.3 0.0 0.6]
[-0.2 0.2 0.7 0.9]
9.5 Cross-Attention 计算
Head1 Q (来自 Decoder): Head1 K (来自 Encoder): Head1 V (来自 Encoder):
I [-0.3327, -0.3446] [0.8115, 0.8874] [-1.9158, 1.3228]
love [ 0.3203, 1.3947] [0.5126, 0.9000] [-1.9157, 1.3947]
easy [ 0.6888, -0.7775] [0.9677, 0.7588] [-1.7224, 1.2987]
courses[-1.4345, -1.7975] [0.6422, 0.9166] [-1.9549, 1.3760]
Head2 Q (Decoder): Head2 K (Encoder): Head2 V (Encoder):
I [-1.9139, -0.2606] [0.6828, -0.6314] [ 0.0190, 0.6432]
love [-1.3887, -0.8673] [1.1301, -0.3656] [-0.4140, 0.2266]
easy [-1.4917, 0.3967] [0.5557, -0.6446] [ 0.1248, 0.8795]
courses[ 0.5689, 0.5535] [0.9418, -0.4940] [-0.2235, 0.4006]
Cross score1 (无掩码!): Cross softmax1 (权重分布):
I [-0.4072, -0.3399, -0.4126, -0.3744] [0.2441, 0.2610, 0.2427, 0.2522]
love [ 1.0589, 1.0037, 0.9675, 1.0494] [0.2598, 0.2458, 0.2371, 0.2573]
easy [-0.0926, -0.2451, 0.0542, -0.1912] [0.2549, 0.2189, 0.2952, 0.2310]
courses[-1.9510, -1.6639, -1.9461, -1.8164] [0.2231, 0.2973, 0.2242, 0.2553]
Cross score2: Cross softmax2:
I [-0.8077, -1.4621, -0.6332, -1.1836] [0.2944, 0.1530, 0.3505, 0.2021]
love [-0.2832, -0.8855, -0.1503, -0.6219] [0.2939, 0.1609, 0.3357, 0.2095]
easy [-0.8973, -1.2946, -0.7669, -1.1320] [0.2776, 0.1866, 0.3163, 0.2195]
courses[ 0.0276, 0.3116, -0.0287, 0.1855] [0.2250, 0.2989, 0.2127, 0.2635]
✍ 手算第 1 行(I 的 Cross-Attention,Head1):
score[I][我] = (-0.3327×0.8115 + (-0.3446)×0.8874)/1.4142 = (-0.2700-0.3058)/1.4142 = -0.5758/1.4142 = -0.4072
(其余 3 个源 token 同理)→ softmax 得到对「我/爱/水/课」的权重 ≈ [0.24, 0.26, 0.24, 0.25]
输出 = 0.2441×V我 + 0.2610×V爱 + 0.2427×V水 + 0.2522×V课 = [-1.8787, 1.3491]
含义:生成「I」时,Decoder 平均参考了 4 个源词(本例权重较均匀,因为随机权重未训练;训练后权重会锐化到对应词上)。
Head1 输出: Head2 输出: 拼接:
I [-1.8787, 1.3491] [-0.0592, 0.6132] [-1.8787, 1.3491, -0.0592, 0.6132]
love [-1.8800, 1.3485] [-0.0660, 0.6047] [-1.8800, 1.3485, -0.0660, 0.6047]
easy [-1.8677, 1.3437] [-0.0816, 0.5869] [-1.8677, 1.3437, -0.0816, 0.5869]
courses[-1.8824, 1.3524] [-0.1518, 0.5050] [-1.8824, 1.3524, -0.1518, 0.5050]
Attn 输出=拼接·WOc2ᵀ: 残差: LayerNorm:
I [-2.1328, 0.7839, -1.5397, 1.1560] [-3.3515, 1.9471, -2.2735, 1.9454] [-1.2111, 0.9878, -0.7637, 0.9870]
love [-2.1195, 0.7864, -1.5457, 1.1437] [-3.3271, 2.3451, -1.5621, 0.8090] [-1.3307, 1.2780, -0.5189, 0.5716]
easy [-2.0831, 0.7867, -1.5451, 1.1134] [-2.8132, 2.0385, -2.7450, 1.7916] [-1.0138, 1.0518, -0.9847, 0.9467]
courses[-1.9631, 0.8150, -1.6086, 0.9952] [-1.3219, -0.8029, -1.6173, 1.9806] [-0.6174, -0.2539, -0.8243, 1.6956]
9.6 Decoder Layer 2 FFN + 最终输出
Wd2_1 (FFN第一层 8×4): Wd2_2 (FFN第二层 4×8):
[-0.7 0.9 0.0 -0.5] [ 0.0 0.4 -0.9 0.6 0.3 -0.8 0.7 0.8]
[-0.1 1.0 0.0 -0.3] [-0.9 -0.4 0.6 0.5 -0.6 -0.6 -0.3 0.0]
[ 0.3 -0.5 -0.8 -0.7] [ 0.2 -0.3 -0.1 0.5 -0.9 -0.5 0.4 0.8]
[-0.7 -0.7 -0.7 0.3] [ 0.0 0.1 -0.8 -0.1 0.1 -0.5 -0.5 -0.2]
[-0.6 -0.3 0.8 -0.1]
[ 0.3 -0.7 -0.6 -0.9]
[-0.7 -0.4 -0.6 -0.8]
[-0.8 -0.1 -0.6 -0.3]
FFN 中间 (4×8): ReLU 后:
I [1.2432, 0.8128, -0.9372, 0.9870, -0.2794, -1.4849, 0.1213, 1.0322] [1.2432, 0.8128, 0, 0.9870, 0, 0, 0.1213, 1.0322]
love [1.7959, 1.2396, -1.0232, 0.5716, -0.0573, -1.4969, 0.2744, 1.0766] [1.7959, 1.2396, 0, 0.5716, 0, 0, 0.2744, 1.0766]
easy [1.1829, 0.8692, -0.7049, 0.9467, -0.5897, -1.3016, 0.1224, 1.0127] [1.1829, 0.8692, 0, 0.9467, 0, 0, 0.1224, 1.0127]
courses[-0.6441, -0.7008, -0.5857, 1.6956, -0.3824, -1.0389, -0.3282, 0.5052] [0, 0, 0, 1.6956, 0, 0, 0, 0.5052]
FFN 输出 (4×4): 残差: ⭐ DECODER 最终输出 (LayerNorm):
I [1.8280, -0.9869, 1.3726, -0.2845] [0.6169, 0.0009, 0.6089, 0.7025] [0.4800, -1.7170, 0.4515, 0.7855]
love [1.8921, -1.9087, 1.2441, -0.2857] [0.5615, -0.6307, 0.7252, 0.2859] [0.6220, -1.6525, 0.9344, 0.0962]
easy [1.8115, -0.9757, 1.3083, -0.2715] [0.7977, 0.0761, 0.3235, 0.6752] [1.1543, -1.3729, -0.5065, 0.7251]
courses[1.4215, 0.8478, 1.2519, -0.2706] [0.8041, 0.5939, 0.4277, 1.4250] [-0.0226, -0.5790, -1.0189, 1.6205]
10 输出层:Linear + Softmax → 预测概率
10.1 权重 W_out ∈ ℝ8×4(词表 8 个词)
我 爱 水 课 I love easy courses
dim0 [-1.0 -0.8 0.6 0.2 -0.4 -0.3 -0.8 -0.6]
dim1 [-0.4 0.8 0.0 0.5 -0.3 1.0 -0.7 -0.4]
dim2 [-0.6 0.2 -0.8 -0.1 0.3 0.2 -0.5 -0.7]
dim3 [-0.3 0.4 0.1 -0.7 0.1 -0.5 -0.7 0.8]
Logits = DEC_OUT · W_outᵀ → P(token) = softmax(Logits)
10.2 Logits(4 个位置 × 8 个词)
我 爱 水 课 I love easy courses
位置0 -0.2998 -1.3531 0.0054 -1.3575 0.5371 -2.1635 0.0423 0.7112 ← 预测第1个词
位置1 -0.5504 -1.5943 -0.3647 -0.8626 0.5369 -1.7003 0.1247 -0.2893
位置2 -0.5187 -1.8330 1.1702 -0.9125 -0.1292 -2.1830 -0.2167 0.7912
位置3 0.3794 -0.0007 0.9636 -1.3264 0.0391 -1.5862 -0.2015 2.2547
10.3 Softmax 概率与预测结果
我 爱 水 课 I love easy courses argmax
位置0 0.1034 0.0361 0.1403 0.0359 0.2387 0.0160 0.1455 0.2841 → courses (0.2841)
位置1 0.1017 0.0358 0.1225 0.0744 0.3016 0.0322 0.1997 0.1320 → I (0.3016)
位置2 0.0710 0.0191 0.3845 0.0479 0.1048 0.0134 0.0961 0.2632 → 水 (0.3845)
位置3 0.0863 0.0590 0.1547 0.0157 0.0614 0.0121 0.0483 0.5627 → courses (0.5627) ✓
✍ 手算位置 0 的 softmax(logits=[-0.2998,-1.3531,0.0054,-1.3575,0.5371,-2.1635,0.0423,0.7112]):
e^-0.2998=0.7408, e^-1.3531=0.2585, e^0.0054=1.0054, e^-1.3575=0.2572, e^0.5371=1.7110, e^-2.1635=0.1149, e^0.0423=1.0432, e^0.7112=2.0365
总和 = 7.1675 → 各项 ÷ 7.1675 = [0.1034, 0.0361, 0.1403, 0.0359, 0.2387, 0.0160, 0.1455, 0.2841]
读:模型给「courses」最高概率 0.2841,给「I」0.2387——随机权重下模型还没学会,但这正是训练要优化的:交叉熵损失会推高目标词(I)的概率、压低其他词。
训练时的目标(这 4 个位置的目标词):位置0→I、位置1→love、位置2→easy、位置3→courses。
损失 = 交叉熵:位置0 的 -log(0.2387)=1.43,位置1 的 -log(0.0322)=3.44,位置2 的 -log(0.0961)=2.34,位置3 的 -log(0.5627)=0.57。
反向传播更新全部权重后,下一轮预测会向「I love easy courses」收敛。位置 3 已经「猜对」了(courses, 0.5627),说明信息流已经部分工作。
11 全流程回顾:数据在哪一步变成了什么
| 阶段 | 输入 → 输出 | 形状 | 意义 |
| ① Tokenization | 「我爱水课」→ [0,1,2,3] | (4,) | 每字一个 token |
| ② Embedding | ID → E 查表 | (4,4) | 离散词 → 连续向量 |
| ③ Positional | X + PE | (4,4) | 注入位置信息 |
| ④ Encoder L1/L2 | Self-Attn→残差→LN→FFN→残差→LN | (4,4) | 源句全上下文表示 |
| ⑤ Decoder L1 | Masked Self-Attn→FFN | (4,4) | 目标句因果表示 |
| ⑥ Decoder L2 | Masked Self-Attn→Cross-Attn→FFN | (4,4) | 融合源句信息 |
| ⑦ Linear | DEC_OUT · W_outᵀ | (4,8) | 映射回词表 logits |
| ⑧ Softmax | 概率分布 | (4,8) | 逐位预测下一个词 |
三个注意力的分工(Transformer 的精髓):
Encoder Self-Attn 让源词互相看(我爱水课内部)→ 理解源句
Decoder Masked Self-Attn 让目标词只看左边(防止偷看未来)→ 保证自回归
Cross-Attn 让目标词看全部源词(Q=解码端,K/V=编码端)→ 实现翻译对齐
掩码只存在于 Decoder 的 Self-Attention;Cross-Attention 永远不掩码。
复现指南(数据可 100% 复现)
1. 权重:seed=42 的均匀随机数 [-1,1] 取 1 位小数;嵌入矩阵手设。
2. 所有矩阵乘法:行 × 列点积;softmax 每行归一化;LayerNorm 每行 (x-μ)/√(σ²+ε)。
3. 掩码:Decoder Self-Attn 的 score 上三角加 -1e9(≈-∞)。
4. 生成脚本:transformer_calc.py(同目录),运行即输出全部中间量,与本文档逐位一致。
5. 四舍五入差异:文档展示 4 位小数,手算时用更多位会得到 ±0.0001 以内的偏差,属正常。