《第一篇:以“小明在喝水”为例32步学习Transformer》(总览)

19 阅读5分钟

[LLM]关键词 全局固定配置

模型总维度:d_model = 512

注意力头数:h = 8

单头维度:d_k = d_v = 512 / 8 = 64

缩放系数:√d_k = 8

中文序列长度:N = 3(小明、在、喝水)

英文序列长度:M = 5(Xiao、Ming、is、drinking、water)

 

一、输入 → 内存:数据从哪来、存在哪、长啥样

1.1 文本输入(最原始数据)

Encoder 输入(中文):

Plain Text 小明 在 喝水

token 序列:[token_小明, token_在, token_喝水]

长度:3

Decoder 输入(英文):

Plain Text Xiao Ming is drinking water

token 序列:[token_Xiao, token_Ming, token_is, token_drinking, token_water]

长度:5

 

1.2 Embedding 层:文本 → 向量 → 写入内存

1.2.1 Encoder 侧 Embedding

每个 token 映射成 512 维向量

写入内存后的矩阵 X:

形状:3 × 512

含义:3 个词,每个词 512 维

内存布局:连续浮点数数组,3×512 个 float32

Plain Text X = [   [x₁₁, x₁₂, ..., x₁₅₁₂],   # 小明   [x₂₁, x₂₂, ..., x₂₅₁₂],   # 在   [x₃₁, x₃₂, ..., x₃₅₁₂]    # 喝水 ]

1.2.2 Decoder 侧 Embedding

英文每个 token 也映射成 512 维向量

写入内存后的矩阵 Y:

形状:5 × 512

内存:5×512 个 float32

 

1.3 位置编码 Positional Encoding(直接加在内存里)

直接与 Embedding 相加,结果仍存在原内存位置:

X ← X + PE

Y ← Y + PE

形状不变:

X:3 × 512

Y:5 × 512

 

二、从内存 → QKV 计算:数据怎么算、中间结果放哪

2.1 生成 Q、K、V(从内存 X 读出 → 计算 → 写回内存)

2.1.1 权重矩阵 Wq, Wk, Wv(模型参数,常驻内存)

形状均为:512 × 512

内存中连续存储

2.1.2 矩阵乘法:Q = X × Wq

X:3 × 512(读内存)

Wq:512 × 512(读内存)

输出 Q:3 × 512(写回新内存)

同理:

K = X × Wk → 3 × 512

V = X × Wv → 3 × 512

此时内存里新增 3 个矩阵:Q、K、V,每个 3×512。

 

三、多头切分:内存里的大向量 → 拆成 8 份

3.1 多头切分逻辑(只在维度上切,不改变序列长度)

总维度 512 → 8 头 × 64 维

对 Q、K、V 都做同样切分:

| Plain Text Q = [ Q₁ | Q₂ | ... | Q₈ ]  # 每段 64 维 | | ---------------------------------------------------- |

单头数据形状:

Qᵢ:3 × 64

Kᵢ:3 × 64

Vᵢ:3 × 64

内存里变成 8 组小 QKV,每组 3×64。

 

四、单头注意力核心计算:逐步骤拆解(内存→计算→内存)

我们只看 第 i 个头,所有 8 头完全一样。

4.1 步骤1:计算 QKᵀ(词与词的关系矩阵)

4.1.1 从内存读入

Qᵢ:3 × 64

Kᵢ:3 × 64

4.1.2 转置 Kᵀ

Kᵀ 形状:64 × 3

(只是内存访问顺序变了,不复制数据)

4.1.3 矩阵乘:Qᵢ × Kᵢᵀ

输入:3×64 × 64×3

输出:3×3 矩阵(写入内存)

9 个分值

Plain Text [   [小明→小明, 小明→在, 小明→喝水],   [在→小明,   在→在,   在→喝水],   [喝水→小明, 喝水→在, 喝水→喝水] ]

 

4.2 步骤2:除以 8(数值缩放)

3×3 矩阵里 每一个数都 ÷8

结果仍写回 3×3 内存。

 

4.3 步骤3:Softmax 归一化

每一行分别 做 Softmax:

输入:一行 3 个数

输出:一行 3 个概率,和为 1

得到 注意力权重矩阵(仍为 3×3,存入内存)。

 

4.4 步骤4:权重 × V(信息聚合)

权重矩阵:3 × 3

Vᵢ:3 × 64

输出:3 × 64

第 i 个头的注意力输出

 

五、多头拼接:8 个头结果 → 合并回内存

8 个头各输出 3 × 64

在最后一维拼接:

| Plain Text head1 || head2 || ... || head8 | | ----------------------------------------------- |

最终形状:

3 × (8×64) = 3 × 512

写入内存,作为 多头注意力总输出

 

六、Encoder 剩余计算(全在内存中完成)

残差连接:注意力输出 + 原始 X

层归一化 LayerNorm

前馈网络 FFN(两个线性层 + 激活)

再一次残差 + 归一化

最终 Encoder 输出:Memory 矩阵

形状:3 × 512

含义:「小明在喝水」的完整语义

位置:常驻内存,供 Decoder 使用

 

七、Decoder 计算:从内存读取 Encoder 信息

7.1 Decoder 第一层:Masked 自注意力

QKV 来自英文 Y:5 × 512

带掩码:看不到未来词

输出形状:5 × 512

全程内存读写

7.2 Decoder 第二层:交叉注意力(核心!)

7.2.1 数据来源(全部从内存读)

Q:来自 Decoder 上一层 5 × 512

K、V:来自 Encoder 输出 Memory 3 × 512

7.2.2 计算逻辑

Plain Text Attention(Q, K, V) = Softmax( QKᵀ / 8 ) × V

形状:

Q:5 × 512 → 分头后 5 × 64

K:3 × 512 → 分头后 3 × 64

QKᵀ:5 × 3

权重:5 × 3

V:3 × 64

输出:5 × 64

物理意义:

5 个英文词,每个都去匹配 3 个中文字,

自动学习对齐关系,不需要人工词数对应

 

八、最终输出:从计算结果 → 词概率

Decoder 最终输出:5 × 512

线性层映射到词表维度:5 × vocab_size

Softmax → 每一行 = 下一个词的概率

取概率最大的词输出

每确定一个词,概率空间坍缩一大坨。

 

九、极简总结(从输入到内存到计算)

文本 → Embedding → 内存:3×512 / 5×512

内存读 X → 算 QKV → 内存写 3 个矩阵

切成 8 头 → 每组 3×64 进入计算

QKᵀ → 3×3 关系矩阵(9 个分值)

÷8 → Softmax → 加权 V → 单头输出

8 头拼接 → 回到 3×512 内存

Encoder 输出 Memory:3×512 语义

Decoder 交叉注意力:5 个英文词匹配 3 个中文词

逐词生成 → 概率坍缩 → 输出翻译