[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 个中文词
逐词生成 → 概率坍缩 → 输出翻译