LLM Transformer的解码器内幕
文章目录
一、前文回顾与本文概要
| 博文 | 讲什么 | 本文深入到哪里 |
|---|---|---|
| 第一篇 | 训练流程(预训练→SFT→RM→RL) | — |
| 第二篇 | 神经网络怎么学习 | Feed Forward 里的 WL1/WL2 |
| 第三篇 | Transformer 五大步骤 | Encoder/Decoder 未探究 |
| 第四篇 | 注意力机制解密 | 把Decoder拆开,解剖内部每一层 |
二、注意力机制 —— 从最简例子开始
2.1 场景:预测新同学体重
班里有 N 位同学,登记了身高和体重:
同学①:身高 1.60m → 体重 50kg
同学②:身高 1.65m → 体重 55kg
同学③:身高 1.80m → 体重 70kg
...
新同学小明:身高 1.78m → 体重未知,怎么预测?
注意力机制的做法:
-
计算相关度(相似度):小明身高 1.78m,跟每个同学比较身高差异
- 身高 1.75m 的同学 → 相关度高(差异小)
- 身高 1.60m 的同学 → 相关度低(差异大)
-
Softmax 归一化:把所有相关度转成概率(加起来 = 1)
-
加权求和:
预测体重 = 相关度₁ × 体重₁ + 相关度₂ × 体重₂ + 相关度₃ × 体重₃ + ...
2.2 这就是 Q、K、V 的直觉理解
| 符号 | 含义 | 本例中 |
|---|---|---|
| Q(Query) | 发出查询的"我" | 小明的身高(1.78m) |
| K(Key) | 被查询的"标签" | 每位同学的身高 |
| V(Value) | 真正有价值的内容 | 每位同学的体重 |
| 相关度 | Q 和每个 K 的相似度 | 身高差异的量化 |
| 输出 | V 的加权和 | 预测体重 |
核心洞察:Q 和 K 只是用来算相关度权重的工具人,真正有价值的是 V 的加权求和结果。
2.3 不止一个特征:多维 QKV
只用身高预测体重不够准 —— 再加上胸围、腰围等特征:
Q = [身高, 胸围] → 2 维向量
K = [身高, 胸围]ₙ → N 个 2 维向量
V = [体重]ₙ → N 个体重值
三、自注意力机制(Self-Attention)
3.1 从"不同角色"到"同一个我"
| 普通注意力 | 自注意力 | |
|---|---|---|
| Q 来源 | 外部查询(小明) | 自己的向量 |
| K 来源 | 数据库(同学) | 自己的向量 |
| V 来源 | 数据库(同学) | 自己的向量 |
| 本质 | Q ≠ K ≠ V | Q = K = V(同一批向量复制三份) |
3.2 自注意力在 Transformer 里如何工作
回顾前文:650 个汉字 → 1,300 个 Token → 1,300 个 12,288 维向量。
把这 1,300 个向量复制三份(Q、K、V 完全相同),然后:
第 1 个向量作为"中心主题词":
├── Q₁ 跟 K₁~K₁₃₀₀ 逐一计算相关度
├── 得到 1,300 个相关度系数(Softmax 后加起来 = 1)
├── 最大的系数一定是跟自己(Q₁ 和 K₁ 的相似度最高,如 0.6)
└── 加权求和:0.6×V₁ + 0.01×V₂ + ... → 新向量₁
第 2 个向量作为"中心主题词":
└── 同样操作 → 新向量₂
...(1,300 次)
输入 1,300 个向量 → 输出 1,300 个新向量(每个都聚合了全文信息)
关键特性:每个新向量"大部分"还是自己的信息(因为自相似度最高),但同时也"借"了一些其他 Token 的信息过来。
3.3 为什么要引入神经网络(Wq、Wk、Wv)?
如果 Q = K = V 完全一样,那限制太大了。算法工程师的做法:
┌── Wq 神经网络 ──→ Q(1300 个向量)
1300 个原始向量 ──┼── Wk 神经网络 ──→ K(1300 个向量)
└── Wv 神经网络 ──→ V(1300 个向量)
三个神经网络把同一批向量"翻译"成了三种不同的"语言",从不同角度处理信息。
四、多头自注意力机制(Multi-Head Self-Attention)
4.1 为什么需要多头?
一组 QKV 只能从一个角度去理解文字。但一段文字的含义是多维的:
“无法挽回” —— 单头可能只看到"无法"和"挽回"组成了一个短语。但如果有 96 个头,有的头关注语法结构,有的头关注情感色彩,有的头关注前后逻辑…
4.2 多头的实现方式(以 GPT-3 为例)
核心参数:
- 原始向量维度:12,288
- 头数:96
- 每头维度:128(12,288 ÷ 96 = 128)
每个头的处理:
┌─ 头₁:Wq₁(12288→128) → Q₁有128维 / Wk₁ → K₁ / Wv₁ → V₁ → 加权求和 → 128维输出
│
├─ 头₂:Wq₂(12288→128) → Q₂有128维 / Wk₂ → K₂ / Wv₂ → V₂ → 加权求和 → 128维输出
│
...(共96个头)
│
└─ 头₉₆:Wq₉₆(12288→128) → ... → 128维输出
最后:96 个 128 维拼起来 → 经过 Wo 神经网络 → 1 个 12,288 维向量
4.3 关键洞察
| 问题 | 答案 |
|---|---|
| 每个头从什么角度? | 不知道,人类无法设定也无法事后描述 |
| 怎么保证角度不同? | 随机初始化 Wq/Wk/Wv,随机数只要足够随机就不会相同 |
| 训练结束后能区分吗? | 不能。最终参数人类看不懂,只看到 96 组数字确实不同 |
| 96 和 128 的关系? | 96×128 = 12,288,这是 GPT-3 的巧合,不是必须 |
算法工程师真的是无所不用其极——他们把一段文字分成 96 个"分身",让每个分身从不同角度去理解,最后再汇总。人类无法设计这些角度,但通过机器学习和随机初始化,模型自己"摸索"出了 96 种不同的理解方式。
4.4 为什么自注意力能并行计算?
这是当年抛弃 RNN(循环神经网络)的核心原因:
RNN(串行):第 1 个字算完 → 第 2 个字才能算 → 第 3 个字...
Self-Attention(并行):同一层内所有 1,300 个向量互不依赖
→ 只需要等上一层全部算完,本层就可以全部并行
→ 1,000 张显卡同时开工,效率爆炸
五、前馈神经网络(Feed Forward)
5.1 每一层 Decoder 的两个模块
Decoder 每一层 = 多头自注意力 + Feed Forward
输入(1300 个向量)──▶ 多头自注意力 ──▶ 输出(1300 个向量)
│
▼
Feed Forward ──▶ 输出(1300 个向量)
两个模块性质不同:
| 多头自注意力 | Feed Forward | |
|---|---|---|
| 主导者 | 人类算法工程师设计框架 | 机器/神经网络自主 |
| 参数规模 | 相对小 | 约 2 倍于多头注意力 |
| 向量间交互 | 有(Token 之间互相借信息) | 无(每个向量单独处理) |
| 结构 | 96 个头 + Wo | 两层神经网络 |
5.2 Feed Forward 的结构
输入向量(12,288 维)
│
▼
WL1:放大 4 倍 → 49,152 维
│
▼
激活函数(非线性)
│
▼
WL2:缩回 → 12,288 维
- 参数数量 ≈ 12,288 × 49,152 + 49,152 × 12,288 ≈ 12 亿参数/层
- GPT-3 有 96 层 → Feed Forward 总参数超过 1,000 亿
为什么先放大再缩小? 放大是为了给特征更大的"表现空间",缩小是提取浓缩后的核心特征。就像把一张图放大 4 倍精细处理,再缩回原尺寸,但处理后的图包含的信息已经完全不同了。
六、GPT-3 完整参数盘点
每个 Decoder 层包含的参数
一层 Decoder:
多头自注意力:
Wq × 96 = (12288 × 128) × 96 = ~1.5 亿参数
Wk × 96 = (12288 × 128) × 96 = ~1.5 亿参数
Wv × 96 = (12288 × 128) × 96 = ~1.5 亿参数
Wo = 12288 × 12288 = ~1.5 亿参数
小计:~6 亿参数
Feed Forward:
WL1 = 12288 × 49152 = ~6 亿参数
WL2 = 49152 × 12288 = ~6 亿参数
小计:~12 亿参数
一层总计:~18 亿参数
96 层总计:~1,728 亿参数
+ Embedding:~6 亿参数
+ 位置编码:~0.25 亿参数
+ Linear 层:~6 亿参数(复用 Embedding 或独立)
─────────────────────
GPT-3 总计:~1,750 亿参数
七、注意力矩阵的 Mask(掩码)
为什么需要 Mask?
训练时给模型一条 1,000 字的数据:
当前任务:用前 200 字预测第 201 个字
问题:后面 800 个字不能让模型看到(那是答案!)
解决:Mask 矩阵
Tok₁ Tok₂ Tok₃ Tok₄ ...
Tok₁ ✓ ✗ ✗ ✗
Tok₂ ✓ ✓ ✗ ✗
Tok₃ ✓ ✓ ✓ ✗
Tok₄ ✓ ✓ ✓ ✓
→ 形成下三角矩阵:只能往前看,不能往后看
使用模型时不需要 Mask —— 因为根本没有后面的内容可以偷看。
八、相关度计算:点积 + Softmax
两个向量如何算相似度?
向量 A = [0.1, 3, 0.1]
向量 B = [3, 0.1, 3]
点积 = 0.1×3 + 3×0.1 + 0.1×3 = 0.3 + 0.3 + 0.3 = 0.9
点积越小 → 两个向量越不相似。
对比:
向量 C = [2.2, 0.6, 3.2]
B·C = 3×2.2 + 0.1×0.6 + 3×3.2 = 6.6 + 0.06 + 9.6 = 16.26
→ C 跟 B 的相似度远高于 A 跟 B。
Softmax 的作用
点积结果可能是 0.9、16.26、甚至负数 —— 无法直接判断"大"或"小"的尺度。
Softmax([0.9, 16.26, 1.2]) → [2.13×10E−7, 0.9999995, 2.88×10E−7]
特点:
- 所有值变成 0~1 之间
- 加起来等于 1
- 原来大的依然大,小的依然小
- 负数也能处理(负相关 → 极小的概率)
九、完整训练流程
以模型刚开始训练(所有参数随机初始化)为例,假设一条训练数据有 100 个字:
输入 3 个字"人类简"
│
▼
Tokenization: 3 个字 → 若干 Token
│
▼
Embedding: 每个 Token → 12,288 维向量(随机初始化的 W)
│
▼
Positional Encoding: 位置向量相加(随机初始化的 W)
│
▼
┌─ Decoder 第 1 层 ──────────────────────────────┐
│ 多头自注意力(96 个头): │
│ Wq/Wk/Wv → 信息聚合 → Wo 汇总 │
│ ↓ │
│ Feed Forward: │
│ WL1 放大 4 倍 → 激活 → WL2 缩回 │
│ ↓ │
│ 输出:3 个向量(维度不变,信息质变) │
└─────────────────────────────────────────────────┘
│
▼
...(重复 95 次,共 96 层)...
│
▼
最后一层输出 3 个向量 → 只保留最后一个
│
▼
Linear + Softmax → 50,000 个候选 Token 的概率分布
│
▼
预测"第四个字" → 跟标准答案"史"对比 → 误差很大(因为参数都是随机的)
│
▼
反向传播:从第 96 层 → 第 1 层 → Embedding → Positional Encoding
每一层的每个参数都计算"该变大还是变小"
│
▼
更新全部 1,750 亿参数
│
▼
下一批训练数据...循环往复
十、核心概念速查表
| 概念 | 一句话解释 |
|---|---|
| 注意力机制 | 根据相似度决定每个 V 的权重,加权求和得到输出 |
| Q(Query) | “提问者”,用来跟 K 算相似度 |
| K(Key) | “被查询的标签”,跟 Q 比较得出相关度 |
| V(Value) | “真正有价值的内容”,被加权求和 |
| 自注意力 | Q = K = V(同一批数据),自己能看见自己的注意力 |
| 多头注意力 | 96 组 Wq/Wk/Wv,从 96 个不同角度理解同一段文字 |
| Feed Forward | 两层神经网络,先放大 4 倍再缩回,参数≈多头注意力的 2 倍 |
| 点积 | 两个向量对应位置相乘再求和,衡量相似度 |
| Softmax | 把任意数值变成概率(0~1,和为 1) |
| Mask | 训练时屏蔽后面内容,形成下三角矩阵 |
| KV Cache | DeepSeek 的核心优化,缓存 K 和 V 避免重复计算 |
十一、Q&A
Q: 为什么每个头的维度要减小(12,288 → 128)? A: 减小计算量。12,288 维点积太贵了,分成 96 个 128 维后计算量大幅降低,而且每个头专注不同的特征角度。
Q: 96 个头分别关注什么角度,人类能知道吗? A: 不能。虽然 QKV 都来自同一段文字,但多头随机初始化导致关注角度大概率不同,算法工程师也无法设定和描述,机器学习会自动找到最优的 96 种角度。
Q: 为什么先放大 4 倍再缩回?信息不会丢失吗? A: 会丢失,但这正好是特征提取的目的——神经网络学会了该保留什么、该丢弃什么。就像人脑处理信息,不可能也不需要对所有细节过目不忘。
Q: 自注意力为什么能并行计算? A: 同一层内,所有 Token 之间的计算互不依赖——只要你等上一层的 1,300 个向量全部算完,本层 1,300 个可以同时开工。这让多 GPU 并行成为可能。
Q: 自注意力和注意力机制有什么区别? A: 注意力 = QKV 来自不同来源(小明 vs 同学)。自注意力 = QKV 来自同一来源(自己的一段文字),是自己看自己。
Q: DeepSeek 为什么省算力? A: 核心创新在 KV Cache。注意力计算中有大量重复的 K 和 V 计算,DeepSeek 设计了压缩缓存方式,大幅减少了显存占用和重复计算。