Transformer 入门:从零理解 AI 大模型的核心原理
第一部分:数学基础铺垫
在开始之前,让我们先了解一些基础概念。不用担心,我会用最简单的方式来解释。
向量、矩阵、张量
什么是向量(Vector)?
向量就是一排数字的集合。
想象你要描述一个人的特征:
- 身高:175cm
- 体重:70kg
- 年龄:25岁
我们可以把这些数字排成一排:[175, 70, 25],这就是一个向量!
arduino
体验AI代码助手
代码解读
复制代码
向量就像一张"特征身份证"
┌─────────────────────┐
│ [175, 70, 25] │
│ ↑ ↑ ↑ │
│ 身高 体重 年龄 │
└─────────────────────┘
向量的维度:向量中有多少个数字,就是多少维。[175, 70, 25] 是 3 维向量。
为什么计算机需要向量? 因为计算机不认识”苹果”、”快乐”这样的文字,但它认识数字!向量就是把文字”翻译”成数字的方式。
什么是矩阵(Matrix)?
矩阵就是把多个向量堆在一起,形成一个数字表格。
比如我们有3个人的信息:
css
体验AI代码助手
代码解读
复制代码
身高 体重 年龄
小明 → [175, 70, 25]
小红 → [165, 55, 23]
小刚 → [180, 85, 30]
这就是一个 3×3 的矩阵(3行3列)!你可以把它想象成 Excel 表格。
矩阵的形状用 (行数, 列数) 表示:
- 上面的例子是
(3, 3)矩阵 - GPT-2 的词汇嵌入矩阵是
(50257, 768)—— 50257 个词,每个词 768 维
什么是张量(Tensor)?
张量是更高维度的数据结构。
- 一个数字(如
5):标量 = 0维张量 - 一排数字(如
[1,2,3]):向量 = 1维张量 - 一个表格:矩阵 = 2维张量
- 一叠表格:3维张量
想象一本书:
体验AI代码助手
代码解读
复制代码
📖 书本类比:
├── 一个字 = 标量(0维)
├── 一行字 = 向量(1维)
├── 一页纸 = 矩阵(2维)
└── 整本书 = 3维张量
🧊 立体类比:
├── 点 = 标量
├── 线 = 向量
├── 面 = 矩阵
└── 体 = 3维张量
矩阵运算
1. 矩阵转置(Transpose)
转置就是把矩阵”翻转”,行变成列,列变成行。
css
体验AI代码助手
代码解读
复制代码
原矩阵 A: 转置后 Aᵀ:
┌─────────┐ ┌─────────┐
│ 1 2 3 │ │ 1 4 │
│ 4 5 6 │ → │ 2 5 │
└─────────┘ │ 3 6 │
(2×3) └─────────┘
(3×2)
记忆技巧:想象沿着对角线折叠纸张。
在 Transformer 中的应用:计算注意力分数时,Query 矩阵需要和 Key 矩阵的转置相乘。
2. 矩阵乘法(Matrix Multiplication)
矩阵乘法是 Transformer 的核心运算!
规则:行 × 列,逐个相乘再相加
css
体验AI代码助手
代码解读
复制代码
A (2×3) B (3×2) C = A × B (2×2)
┌─────────┐ ┌───────┐ ┌─────────┐
│ 1 2 3 │ × │ 7 8 │ = │ 58 64 │
│ 4 5 6 │ │ 9 10 │ │139 154 │
└─────────┘ │11 12 │ └─────────┘
└───────┘
计算过程(以 C[0,0] = 58 为例):
C[0,0] = 1×7 + 2×9 + 3×11 = 7 + 18 + 33 = 58
↑ ↑ ↑
A第1行的每个元素 × B第1列的对应元素,再相加
形状规则
scss
体验AI代码助手
代码解读
复制代码
A 的形状:(m, n) ← n 必须相等
B 的形状:(n, p) ← n 必须相等
结果形状:(m, p)
例如:(2, 3) × (3, 4) = (2, 4) ✅
(2, 3) × (4, 5) = 错误! ❌ (3 ≠ 4)
在 Transformer 中的应用
scss
体验AI代码助手
代码解读
复制代码
Token 嵌入计算:
┌────────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ Token ID │ × │ 嵌入矩阵 │ = │ 嵌入向量 │
│ (1, 50257) │ │ (50257, 768) │ │ (1, 768) │
│ [0,0,...,1,0] │ │ │ │ [0.23, -0.45,...]│
└────────────────┘ └──────────────────┘ └───────────────────┘
One-hot 编码 查表 该词的向量表示
3. 点积(Dot Product)
点积是两个向量的”相似度计算器”。
计算方法
ini
体验AI代码助手
代码解读
复制代码
向量 A = [1, 2, 3]
向量 B = [4, 5, 6]
点积 = 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32
↑ ↑ ↑
对应位置相乘,然后全部相加
几何意义
点积反映两个向量的相似程度:
css
体验AI代码助手
代码解读
复制代码
A·B = |A| × |B| × cos(θ)
其中 θ 是两个向量的夹角:
• θ = 0° → cos(θ) = 1 → 完全同方向,点积最大
• θ = 90° → cos(θ) = 0 → 垂直,点积为 0
• θ = 180° → cos(θ) = -1 → 完全反方向,点积最小
图示:
B ↗
/
/ θ = 45°(相似)
/
A →→→→→→
B ↑
│
│ θ = 90°(不相关)
│
A →→→→→→
在 Transformer 注意力中的应用
vbnet
体验AI代码助手
代码解读
复制代码
Query 向量("她"):[0.5, 0.3, 0.8, ...]
Key 向量("小红"):[0.4, 0.2, 0.9, ...] ← 相似!点积大
Key 向量("苹果"):[-0.2, 0.7, -0.1, ...] ← 不相似,点积小
注意力分数 = Query · Key
"她" 关注 "小红" 的分数 = 0.5×0.4 + 0.3×0.2 + 0.8×0.9 + ... = 较大
"她" 关注 "苹果" 的分数 = 0.5×(-0.2) + 0.3×0.7 + 0.8×(-0.1) + ... = 较小
4. 缩放(Scaling)
为什么注意力分数要除以 √d?
在计算注意力时,公式是:
ini
体验AI代码助手
代码解读
复制代码
Attention = softmax(Q·Kᵀ / √d)
↑
缩放因子
问题:点积可能变得很大
假设向量维度 d = 768:
- 每个元素大约在
[-1, 1]范围 - 点积 = 768 个数相加,可能变成很大的数(如 ±100)
- 很大的数经过 softmax 会变成极端值(接近 0 或 1)
解决:除以 √768 ≈ 27.7
css
体验AI代码助手
代码解读
复制代码
原始点积: [-100, 50, 30, -20]
除以 √768:[-3.6, 1.8, 1.1, -0.7] ← 数值更温和
这样 softmax 输出更平滑,模型更容易学习
直观理解
css
体验AI代码助手
代码解读
复制代码
🌡️ 温度计类比:
原始点积就像测量火山温度,数值太极端
缩放后就像测量室温,数值更合理
📊 考试分数类比:
原始:[0分, 1000分, 500分] ← 差异太大,难以比较
缩放:[0分, 36分, 18分] ← 差异合理,容易比较
归一化(Normalization)
为什么需要归一化?
归一化让数据保持在合理的范围内,帮助模型更稳定地学习。
想象一下:
arduino
体验AI代码助手
代码解读
复制代码
不归一化的数据:
特征1(年龄): 25, 30, 35 范围:25-35
特征2(收入): 50000, 80000 范围:50000-80000
问题:收入的数值比年龄大几千倍!
模型会认为收入"更重要",但这只是单位不同造成的假象
层归一化(Layer Normalization)
Transformer 使用的是层归一化,它在每一层对数据进行标准化:
计算步骤
ini
体验AI代码助手
代码解读
复制代码
输入向量:x = [2, 4, 6, 8]
Step 1: 计算均值 μ
μ = (2 + 4 + 6 + 8) / 4 = 5
Step 2: 计算方差 σ²
σ² = [(2-5)² + (4-5)² + (6-5)² + (8-5)²] / 4
= [9 + 1 + 1 + 9] / 4 = 5
Step 3: 标准化
x_norm = (x - μ) / √(σ² + ε)
= (x - 5) / √5.0001 (ε 是一个很小的数,防止除以0)
= [-1.34, -0.45, 0.45, 1.34]
Step 4: 缩放和偏移(可学习参数)
output = γ × x_norm + β
效果
css
体验AI代码助手
代码解读
复制代码
归一化前:[100, 200, -50, 0, 300] ← 数值范围大,不稳定
归一化后:[-0.2, 0.5, -1.1, -0.4, 1.2] ← 数值在小范围内,稳定
在 Transformer 中的位置
体验AI代码助手
代码解读
复制代码
┌─────────────────────────────┐
│ 输入 x │
│ ↓ │
│ ┌──────────────┐ │
│ │ Layer Norm 1 │ ←────────┤ 第一次归一化
│ └──────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ 注意力层 │ │
│ └──────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ Layer Norm 2 │ ←────────┤ 第二次归一化
│ └──────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ MLP 层 │ │
│ └──────────────┘ │
│ ↓ │
│ 输出 │
└─────────────────────────────┘
Softmax 函数
Softmax 把任意数字转换成概率分布。
公式
scss
体验AI代码助手
代码解读
复制代码
softmax(xᵢ) = e^xᵢ / Σⱼ e^xⱼ
其中 e ≈ 2.718(自然常数)
计算示例
ini
体验AI代码助手
代码解读
复制代码
输入分数:[2.0, 1.0, 0.1]
Step 1: 计算 e 的幂次
e^2.0 = 7.39
e^1.0 = 2.72
e^0.1 = 1.11
总和 = 7.39 + 2.72 + 1.11 = 11.22
Step 2: 除以总和得到概率
7.39 / 11.22 = 0.66 = 66%
2.72 / 11.22 = 0.24 = 24%
1.11 / 11.22 = 0.10 = 10%
输出概率:[0.66, 0.24, 0.10]
↑
总和 = 1(这就是概率分布!)
Softmax 的特性
css
体验AI代码助手
代码解读
复制代码
特性 1:所有输出都是正数(因为 e^x > 0)
特性 2:所有输出加起来等于 1(概率分布)
特性 3:保持相对大小关系(输入大的,输出也大)
特性 4:放大差异(大的更大,小的更小)
示例:
输入: [3, 1, 1] 差距:2
输出: [0.82, 0.09, 0.09] 差距被放大!
图形化理解
erlang
体验AI代码助手
代码解读
复制代码
分数 概率
┌──────┐ ┌──────┐
3 │██████│ 82%│██████│████████████████
├──────┤ → ├──────┤
1 │██ │ 9%│██ │██
├──────┤ ├──────┤
1 │██ │ 9%│██ │██
└──────┘ └──────┘
温度对 Softmax 的影响
ini
体验AI代码助手
代码解读
复制代码
带温度的 Softmax:softmax(xᵢ / T)
T = 温度参数
温度 T
效果
输入 [3,1,1] 的输出
T = 0.5
更尖锐(更确定)
[0.95, 0.02, 0.02]
T = 1.0
标准
[0.82, 0.09, 0.09]
T = 2.0
更平滑(更随机)
[0.58, 0.21, 0.21]
ini
体验AI代码助手
代码解读
复制代码
🌡️ 温度越低,分布越"尖",最大值占主导
🌡️ 温度越高,分布越"平",选择更随机
图示(T从低到高):
T=0.5: ████████████████░░ ← 几乎只选最大的
T=1.0: ████████████░░░░░░ ← 偏向最大的
T=2.0: ██████░░░░░░░░░░░░ ← 更均匀的分布
残差连接(Residual Connection)
残差连接让信息可以”跳过”某些层直接传递。
公式
ini
体验AI代码助手
代码解读
复制代码
输出 = 层的输出 + 原始输入
或写成:y = F(x) + x
为什么需要残差?
arduino
体验AI代码助手
代码解读
复制代码
问题:深度网络的"梯度消失"
想象传话游戏:
第1个人 → 第2个人 → ... → 第12个人
每传一次,信息就损失一点
传到第12个人时,原始信息可能完全失真了
残差连接 = 给每个人一条"直通热线"
即使中间传话有损失,原始信息也能直接到达
图示
scss
体验AI代码助手
代码解读
复制代码
没有残差连接: 有残差连接:
┌────────┐ ┌────────┐
│ 输入 x │ │ 输入 x │──────────┐
└───┬────┘ └───┬────┘ │
↓ ↓ │
┌────────┐ ┌────────┐ │
│ F(x) │ │ F(x) │ │
└───┬────┘ └───┬────┘ │
↓ ↓ │
┌────────┐ ┌────────┐ │
│ 输出 │ │ + │←─────────┘ (相加)
└────────┘ └───┬────┘
↓
┌────────┐
│ F(x)+x │ ← 输出包含原始信息!
└────────┘
在 Transformer 中的应用
scss
体验AI代码助手
代码解读
复制代码
每个 Transformer Block 有两处残差连接:
x ─────────────────────────┐
│ │
↓ │
[Layer Norm] → [注意力] ───┼→ (+) → x₁
│
x₁ ────────────────────────┐
│ │
↓ │
[Layer Norm] → [MLP] ──────┼→ (+) → x₂(输出)
激活函数:GELU
激活函数给神经网络带来”非线性”,让它能学习复杂模式。
为什么需要非线性?
ini
体验AI代码助手
代码解读
复制代码
如果只有线性运算:
y = ax + b
y = cy + d = c(ax + b) + d = acx + (bc + d)
无论多少层,最终还是 y = 常数×x + 常数
这就只能学习直线关系!
加入非线性:
y = 激活函数(ax + b)
可以学习任意复杂的曲线!
GELU vs ReLU
scss
体验AI代码助手
代码解读
复制代码
ReLU(传统方法):
│ ╱
│ ╱
│ ╱
────────┼─╱──────
│
公式:max(0, x)
特点:x<0 输出0,x>0 输出x,在0处有"尖角"
GELU(Transformer 使用):
│ ╱
│ ╱
│ ╱
────────┼╱───────
╱│
公式:x × Φ(x),其中 Φ 是标准正态分布的累积分布函数
特点:平滑曲线,没有尖角
GELU 的直观理解
erlang
体验AI代码助手
代码解读
复制代码
GELU 可以理解为"概率性门控":
对于输入 x:
• 当 x 很大(正数)时:几乎 100% 让它通过 → 输出 ≈ x
• 当 x 很小(负数)时:几乎 0% 让它通过 → 输出 ≈ 0
• 当 x 接近 0 时:有一定概率通过 → 输出在 0 和 x 之间
这比 ReLU 的"非0即1"更平滑,有助于模型训练
第二部分:语言模型基础概念
什么是 Token(词元)?
Token 是 AI 处理文本的最小单位,可以是一个词,也可以是词的一部分。
举个例子,当你输入 "I love programming" 时:
css
体验AI代码助手
代码解读
复制代码
原文: "I love programming"
↓ 分词(Tokenization)
Token: ["I", "love", "program", "ming"]
↑ ↑ ↑ ↑
完整词 完整词 词的前半部分 词的后半部分
为什么要这样分?
- 如果每个词都是一个 Token,词汇表会非常庞大
- 把长词拆成小块,可以用更少的”积木”拼出更多的词
- GPT-2 的词汇表有 50,257 个 Token,就像有 50,257 块乐高积木
中文的例子:
arduino
体验AI代码助手
代码解读
复制代码
原文: "数据可视化"
Token: ["数据", "可视", "化"]
什么是嵌入向量(Embedding)?
嵌入向量是把文字变成数字的”魔法”,让意思相近的词在数字空间里也靠得近。
想象一个二维平面:
markdown
体验AI代码助手
代码解读
复制代码
美味程度 ↑
│ 🍎苹果
│ 🍊橙子
│
│ 🍕披萨
│ 🍔汉堡
│
──────┼─────────────────→ 甜度
│
│ 🥦西兰花
│ 🥬白菜
在这个例子中:
- 苹果和橙子都是水果,位置靠近
- 披萨和汉堡都是快餐,位置靠近
- 蔬菜在另一个区域
真正的嵌入向量不是2维,而是 768维(GPT-2)!人类无法想象768维空间,但对计算机来说没问题。
arduino
体验AI代码助手
代码解读
复制代码
"苹果" → [0.23, -0.45, 0.12, 0.67, ..., 0.34] ← 768个数字
"橙子" → [0.21, -0.43, 0.15, 0.65, ..., 0.32] ← 很相似!
"汽车" → [0.89, 0.12, -0.78, 0.03, ..., -0.56] ← 很不同!
第三部分:Transformer 架构详解
现在你已经了解了数学基础和语言模型概念,让我们来看 Transformer 是如何工作的!
Transformer 的目标
Transformer 的核心任务:预测下一个词。
erlang
体验AI代码助手
代码解读
复制代码
输入: "今天天气真"
↓
[Transformer 魔法盒子]
↓
输出: "好" (概率 85%)
"糟" (概率 10%)
"的" (概率 3%)
...
就像你和朋友聊天,对方说了半句话,你可以猜出下一个词是什么。Transformer 就是在做同样的事情!
三大核心组件
Transformer 由三个核心部分组成:
arduino
体验AI代码助手
代码解读
复制代码
┌─────────────────────────────────────────┐
│ 输入:"Data visualization" │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 1️⃣ Embedding(嵌入层) │ │ ← 文字变数字
│ └─────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 2️⃣ Transformer Block(×12) │ │ ← 理解语义关系
│ │ • 注意力机制 │ │
│ │ • MLP 层 │ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 3️⃣ Output(输出层) │ │ ← 预测下一个词
│ └─────────────────────────────────┘ │
│ ↓ │
│ 输出:"empowers" │
└─────────────────────────────────────────┘
嵌入层(Embedding Layer)
嵌入层负责把文字”翻译”成数字,分四步完成:
步骤 1:分词(Tokenization)
less
体验AI代码助手
代码解读
复制代码
"Data visualization empowers users to"
↓ 分词
["Data", "visual", "ization", "emp", "owers", "users", "to"]
#1 #2 #3 #4 #5 #6 #7
注意 "empowers" 被拆成了 "emp" 和 "owers" 两个 Token!
步骤 2:Token 嵌入(矩阵查表)
ini
体验AI代码助手
代码解读
复制代码
这是一个矩阵乘法操作!
One-hot 向量 × 嵌入矩阵 = 嵌入向量
[0,0,0,1,0,...,0] × ┌─────────────────┐ = [0.12, -0.34, ...]
(1×50257) │ (50257 × 768) │ (1×768)
Token ID=3 │ 嵌入矩阵 │ "Data"的向量
└─────────────────┘
步骤 3:位置编码
问题: “我爱你” 和 “你爱我” 意思完全不同,但包含相同的词!
解决: 给每个位置加上一个”位置信息”:
scss
体验AI代码助手
代码解读
复制代码
位置0 → [0.01, 0.02, 0.03, ...] (768维)
位置1 → [0.04, 0.05, 0.06, ...] (768维)
位置2 → [0.07, 0.08, 0.09, ...] (768维)
...
步骤 4:最终嵌入(向量相加)
css
体验AI代码助手
代码解读
复制代码
最终嵌入 = Token嵌入 + 位置编码
[0.12, -0.34, 0.56, ...] + [0.01, 0.02, 0.03, ...] = [0.13, -0.32, 0.59, ...]
Token嵌入 位置编码 最终嵌入
注意力机制详解
这是 Transformer 最核心的创新!
注意力机制 —— “谁跟谁相关?”
注意力机制让每个词都能”看到”其他词,理解它们之间的关系。
举个例子:
arduino
体验AI代码助手
代码解读
复制代码
"小明把苹果给了小红,她很开心"
当 AI 读到 “她” 时,需要知道 “她” 指的是谁。注意力机制会计算:
erlang
体验AI代码助手
代码解读
复制代码
"她" 应该关注谁?
├── "小明" → 相关度 10% (男性名字,不太可能)
├── "苹果" → 相关度 5% (水果,不是人)
├── "小红" → 相关度 80% (女性名字,很可能!)
└── "开心" → 相关度 5% (形容词,不是人)
Q、K、V 是什么?(搜索引擎类比)
注意力机制使用三个矩阵来计算关系:
名称
类比
作用
Query (Q) 查询
你在搜索框输入的内容
“我想找什么?”
Key (K) 键
每个网页的标题
“我是什么?”
Value (V) 值
“我包含什么信息?”
markdown
体验AI代码助手
代码解读
复制代码
搜索过程:
1. 你输入搜索词(Query):"苹果手机"
2. 搜索引擎匹配标题(Key):找到相关网页
3. 返回内容(Value):显示网页内容
注意力过程:
1. "她" 的 Query 问:"我应该指代谁?"
2. 每个词的 Key 回答:"我是 [小明/苹果/小红/...]"
3. 匹配最高的 Value(小红的信息)被重点关注
Step 1: 计算 Q、K、V 矩阵
ini
体验AI代码助手
代码解读
复制代码
Q = X × Wq + bq (查询矩阵)
K = X × Wk + bk (键矩阵)
V = X × Wv + bv (值矩阵)
其中:
• X 是输入嵌入,形状 (序列长度, 768)
• Wq, Wk, Wv 是可学习的权重矩阵,形状 (768, 768)
• bq, bk, bv 是偏置向量
例如 7 个 Token 的输入:
X: (7, 768)
Wq: (768, 768)
Q: (7, 768) ← 每个 Token 有一个 768 维的 Query 向量
Step 2: 计算注意力分数(矩阵乘法 + 缩放)
scss
体验AI代码助手
代码解读
复制代码
注意力分数 = (Q × Kᵀ) / √768
计算过程:
┌──────────────┐ ┌──────────────┐
│ Q (7×768) │ × │ Kᵀ (768×7) │ ÷ √768
└──────────────┘ └──────────────┘
↓
┌─────────────┐
│ Score (7×7) │
└─────────────┘
Score[i][j] = 第 i 个词对第 j 个词的关注程度
Step 3: 应用掩码(Mask)
在生成文本时,AI 不能”偷看”未来的内容:
arduino
体验AI代码助手
代码解读
复制代码
掩码矩阵(下三角为0,上三角为-∞):
┌────────────────────────────┐
│ 0 -∞ -∞ -∞ -∞ │ ← 词1只能看词1
│ 0 0 -∞ -∞ -∞ │ ← 词2能看词1,2
│ 0 0 0 -∞ -∞ │ ← 词3能看词1,2,3
│ 0 0 0 0 -∞ │ ← 词4能看词1,2,3,4
│ 0 0 0 0 0 │ ← 词5能看所有
└────────────────────────────┘
Score + Mask:
-∞ 经过 softmax 后变成 0,相当于"看不见"
Step 4: Softmax 归一化
erlang
体验AI代码助手
代码解读
复制代码
Attention Weights = softmax(Score + Mask)
每一行加起来 = 1(概率分布)
例如第 3 行:[0.2, 0.3, 0.5, 0, 0]
表示第 3 个词:
• 20% 注意力给词1
• 30% 注意力给词2
• 50% 注意力给词3(自己)
• 0% 给词4,5(被掩码挡住了)
Step 5: 加权求和(输出)
scss
体验AI代码助手
代码解读
复制代码
Output = Attention_Weights × V
(7×7) × (7×768) = (7×768)
每个词的输出 = 它关注的所有词的 V 向量的加权平均
完整注意力公式
scss
体验AI代码助手
代码解读
复制代码
Attention(Q, K, V) = softmax(Q × Kᵀ / √dₖ) × V
其中 dₖ = 768(Key 的维度)
多头注意力(Multi-Head Attention)
为什么要”多头”?
erlang
体验AI代码助手
代码解读
复制代码
单头注意力:一个专家看问题
多头注意力:12个专家从不同角度看问题,然后综合意见
就像:
• 头1:关注语法关系(主谓宾)
• 头2:关注语义关系(近义词)
• 头3:关注位置关系(相邻词)
• ...
• 头12:关注其他模式
实现方式:分割维度
less
体验AI代码助手
代码解读
复制代码
原始:Q, K, V 都是 (7, 768)
分成 12 头后:
头1: Q₁(7, 64), K₁(7, 64), V₁(7, 64)
头2: Q₂(7, 64), K₂(7, 64), V₂(7, 64)
...
头12: Q₁₂(7, 64), K₁₂(7, 64), V₁₂(7, 64)
768 ÷ 12 = 64(每个头处理 64 维)
合并输出
scss
体验AI代码助手
代码解读
复制代码
每个头输出:(7, 64)
12 个头拼接:(7, 768)
再经过一个线性变换:(7, 768)
MLP 层
MLP(多层感知机)对每个词进行独立的深度加工:
scss
体验AI代码助手
代码解读
复制代码
输入 (7, 768)
↓
┌─────────────────────────────────────┐
│ 第一层线性变换:(768 → 3072) │ ← 扩展 4 倍
│ 矩阵乘法 + 偏置 │
└─────────────────────────────────────┘
↓ (7, 3072)
┌─────────────────────────────────────┐
│ GELU 激活函数 │ ← 添加非线性
└─────────────────────────────────────┘
↓ (7, 3072)
┌─────────────────────────────────────┐
│ 第二层线性变换:(3072 → 768) │ ← 压缩回来
│ 矩阵乘法 + 偏置 │
└─────────────────────────────────────┘
↓
输出 (7, 768)
为什么要先扩展再压缩?
就像写作文:
- 扩展:先把所有想法都写出来(头脑风暴)
- 压缩:再精简成最重要的观点(提炼精华)
输出层
经过 12 层 Transformer Block 处理后,AI 需要预测下一个词:
scss
体验AI代码助手
代码解读
复制代码
最后一个 Token 的表示 (1, 768)
↓
┌────────────────────────────────────┐
│ 线性变换:(768 → 50257) │
│ 得到每个词的"分数"(logits) │
└────────────────────────────────────┘
↓ (1, 50257)
┌────────────────────────────────────┐
│ Softmax:转换为概率分布 │
│ 所有概率加起来 = 1 │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ 采样:根据 temperature, top-k, top-p│
│ 选择下一个 Token │
└────────────────────────────────────┘
第四部分:采样策略
Temperature(温度)
温度控制输出的”随机性”:
温度
效果
比喻
< 1
更确定、保守
考试时选最稳妥的答案
= 1
正常状态
平常心做选择
> 1
更随机、有创意
大胆尝试新选项
erlang
体验AI代码助手
代码解读
复制代码
温度 = 0.2(低温):
"今天天气真___" → "好"(99%确定选这个)
温度 = 1.5(高温):
"今天天气真___" → "奇怪/热/冷/好"(更多可能性)
Top-k 和 Top-p 采样
Top-k:只考虑概率最高的 k 个词
ini
体验AI代码助手
代码解读
复制代码
k = 3:只从 ["好", "糟", "热"] 中选择
Top-p:选择累计概率达到 p 的词
ini
体验AI代码助手
代码解读
复制代码
p = 0.9:选择累计概率达到 90% 的词
如果 "好"=70%, "糟"=15%, "热"=10%,累计已达 95%
就只从这三个词中选
第五部分:总结
信息流总览
scss
体验AI代码助手
代码解读
复制代码
"今天天气"
↓ 分词
[Token IDs]
↓ 嵌入查表 + 位置编码
[嵌入矩阵 7×768]
↓
┌─────────────────── × 12 层 ───────────────────┐
│ ↓ Layer Norm │
│ ↓ 多头注意力 (Q×Kᵀ/√d → softmax → ×V) │
│ ↓ + 残差连接 │
│ ↓ Layer Norm │
│ ↓ MLP (扩展→GELU→压缩) │
│ ↓ + 残差连接 │
└───────────────────────────────────────────────┘
↓
[输出表示 7×768]
↓ 取最后一个 Token
[1×768]
↓ 线性变换
[1×50257] 每个词的分数
↓ Softmax
[概率分布]
↓ 采样
"真" (下一个词)
数学操作总结表
操作
数学公式
在 Transformer 中的作用
矩阵乘法
C = A × B
线性变换、Q/K/V 计算
转置
Aᵀ
注意力分数计算需要 Kᵀ
点积
a·b = Σaᵢbᵢ
计算两个向量的相似度
缩放
x / √d
防止点积过大
Softmax
eˣⁱ / Σeˣʲ
将分数转换为概率
Layer Norm
(x-μ)/σ
稳定训练过程
残差连接
y = F(x) + x
帮助梯度流动
GELU
x × Φ(x)
添加非线性
关键概念回顾
概念
一句话解释
Token
AI 处理文本的最小单位,可以是词或词的一部分
嵌入向量
把文字变成数字的”翻译”方式
注意力机制
让词能”看到”其他词,理解关系
Q/K/V
查询/键/值,用搜索引擎来理解
多头注意力
多个专家从不同角度分析
MLP
对每个词进行深度加工
温度
控制输出的随机程度
GPT-2 参数量计算
以 GPT-2 (small) 为例:
ini
体验AI代码助手
代码解读
复制代码
嵌入层:
• Token 嵌入:50,257 × 768 = 38,597,376
• 位置嵌入:1,024 × 768 = 786,432
每个 Transformer Block(×12):
• 注意力 Wq, Wk, Wv, Wo:4 × 768 × 768 = 2,359,296
• MLP 层:768 × 3072 + 3072 × 768 = 4,718,592
• Layer Norm:2 × 768 × 2 = 3,072
输出层:
• 768 × 50,257 = 38,597,376
总计约 124M(1.24 亿)参数
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu