Transformer 入门:从零理解 AI 大模型的核心原理

第一部分:数学基础铺垫

在开始之前,让我们先了解一些基础概念。不用担心,我会用最简单的方式来解释。

向量、矩阵、张量

什么是向量(Vector)?

向量就是一排数字的集合。

想象你要描述一个人的特征:

  • 身高:175cm
  • 体重:70kg
  • 年龄:25岁

我们可以把这些数字排成一排:[175, 70, 25],这就是一个向量!

arduino 复制代码
向量就像一张"特征身份证"
┌─────────────────────┐
│  [175, 70, 25]      │
│   ↑    ↑    ↑       │
│  身高 体重  年龄     │
└─────────────────────┘

向量的维度:向量中有多少个数字,就是多少维。[175, 70, 25] 是 3 维向量。

为什么计算机需要向量? 因为计算机不认识"苹果"、"快乐"这样的文字,但它认识数字!向量就是把文字"翻译"成数字的方式。

什么是矩阵(Matrix)?

矩阵就是把多个向量堆在一起,形成一个数字表格。

比如我们有3个人的信息:

css 复制代码
         身高  体重  年龄
小明 →   [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维张量

想象一本书:

复制代码
📖 书本类比:
├── 一个字 = 标量(0维)
├── 一行字 = 向量(1维)
├── 一页纸 = 矩阵(2维)
└── 整本书 = 3维张量

🧊 立体类比:
├── 点 = 标量
├── 线 = 向量
├── 面 = 矩阵
└── 体 = 3维张量

矩阵运算

1. 矩阵转置(Transpose)

转置就是把矩阵"翻转",行变成列,列变成行。

css 复制代码
原矩阵 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 复制代码
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 复制代码
A 的形状:(m, n)  ← n 必须相等
B 的形状:(n, p)  ← n 必须相等
结果形状:(m, p)

例如:(2, 3) × (3, 4) = (2, 4)  ✅
      (2, 3) × (4, 5) = 错误!  ❌ (3 ≠ 4)
在 Transformer 中的应用
scss 复制代码
Token 嵌入计算:
┌────────────────┐     ┌──────────────────┐     ┌───────────────────┐
│  Token ID      │  ×  │   嵌入矩阵        │  =  │   嵌入向量        │
│  (1, 50257)    │     │ (50257, 768)     │     │   (1, 768)        │
│  [0,0,...,1,0] │     │                  │     │  [0.23, -0.45,...]│
└────────────────┘     └──────────────────┘     └───────────────────┘
   One-hot 编码            查表                    该词的向量表示

3. 点积(Dot Product)

点积是两个向量的"相似度计算器"。

计算方法
ini 复制代码
向量 A = [1, 2, 3]
向量 B = [4, 5, 6]

点积 = 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32
       ↑     ↑     ↑
     对应位置相乘,然后全部相加
几何意义

点积反映两个向量的相似程度:

css 复制代码
A·B = |A| × |B| × cos(θ)

其中 θ 是两个向量的夹角:
• θ = 0°   → cos(θ) = 1  → 完全同方向,点积最大
• θ = 90°  → cos(θ) = 0  → 垂直,点积为 0
• θ = 180° → cos(θ) = -1 → 完全反方向,点积最小

图示:
        B ↗
         /
        /  θ = 45°(相似)
       /
  A →→→→→→

        B ↑
        │
        │  θ = 90°(不相关)
        │
  A →→→→→→
在 Transformer 注意力中的应用
vbnet 复制代码
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 复制代码
Attention = softmax(Q·Kᵀ / √d)
                      ↑
                   缩放因子
问题:点积可能变得很大

假设向量维度 d = 768

  • 每个元素大约在 [-1, 1] 范围
  • 点积 = 768 个数相加,可能变成很大的数(如 ±100)
  • 很大的数经过 softmax 会变成极端值(接近 0 或 1)
解决:除以 √768 ≈ 27.7
css 复制代码
原始点积:  [-100, 50, 30, -20]
除以 √768:[-3.6, 1.8, 1.1, -0.7]  ← 数值更温和

这样 softmax 输出更平滑,模型更容易学习
直观理解
css 复制代码
🌡️ 温度计类比:
原始点积就像测量火山温度,数值太极端
缩放后就像测量室温,数值更合理

📊 考试分数类比:
原始:[0分, 1000分, 500分]  ← 差异太大,难以比较
缩放:[0分, 36分, 18分]     ← 差异合理,容易比较

归一化(Normalization)

为什么需要归一化?

归一化让数据保持在合理的范围内,帮助模型更稳定地学习。

想象一下:

arduino 复制代码
不归一化的数据:
特征1(年龄):   25, 30, 35      范围:25-35
特征2(收入):   50000, 80000    范围:50000-80000

问题:收入的数值比年龄大几千倍!
      模型会认为收入"更重要",但这只是单位不同造成的假象

层归一化(Layer Normalization)

Transformer 使用的是层归一化,它在每一层对数据进行标准化:

计算步骤
ini 复制代码
输入向量: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 复制代码
归一化前:[100, 200, -50, 0, 300]  ← 数值范围大,不稳定
归一化后:[-0.2, 0.5, -1.1, -0.4, 1.2]  ← 数值在小范围内,稳定
在 Transformer 中的位置
复制代码
┌─────────────────────────────┐
│       输入 x                │
│          ↓                  │
│   ┌──────────────┐          │
│   │ Layer Norm 1 │ ←────────┤ 第一次归一化
│   └──────────────┘          │
│          ↓                  │
│   ┌──────────────┐          │
│   │  注意力层     │          │
│   └──────────────┘          │
│          ↓                  │
│   ┌──────────────┐          │
│   │ Layer Norm 2 │ ←────────┤ 第二次归一化
│   └──────────────┘          │
│          ↓                  │
│   ┌──────────────┐          │
│   │   MLP 层     │          │
│   └──────────────┘          │
│          ↓                  │
│       输出                   │
└─────────────────────────────┘

Softmax 函数

Softmax 把任意数字转换成概率分布。

公式

scss 复制代码
softmax(xᵢ) = e^xᵢ / Σⱼ e^xⱼ

其中 e ≈ 2.718(自然常数)

计算示例

ini 复制代码
输入分数:[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 复制代码
特性 1:所有输出都是正数(因为 e^x > 0)
特性 2:所有输出加起来等于 1(概率分布)
特性 3:保持相对大小关系(输入大的,输出也大)
特性 4:放大差异(大的更大,小的更小)

示例:
输入:  [3, 1, 1]     差距:2
输出:  [0.82, 0.09, 0.09]  差距被放大!

图形化理解

erlang 复制代码
     分数                    概率
   ┌──────┐               ┌──────┐
 3 │██████│             82%│██████│████████████████
   ├──────┤     →         ├──────┤
 1 │██    │              9%│██    │██
   ├──────┤               ├──────┤
 1 │██    │              9%│██    │██
   └──────┘               └──────┘

温度对 Softmax 的影响

ini 复制代码
带温度的 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 复制代码
🌡️ 温度越低,分布越"尖",最大值占主导
🌡️ 温度越高,分布越"平",选择更随机

图示(T从低到高):
T=0.5:  ████████████████░░    ← 几乎只选最大的
T=1.0:  ████████████░░░░░░    ← 偏向最大的
T=2.0:  ██████░░░░░░░░░░░░    ← 更均匀的分布

残差连接(Residual Connection)

残差连接让信息可以"跳过"某些层直接传递。

公式

ini 复制代码
输出 = 层的输出 + 原始输入

或写成:y = F(x) + x

为什么需要残差?

arduino 复制代码
问题:深度网络的"梯度消失"

想象传话游戏:
第1个人 → 第2个人 → ... → 第12个人
每传一次,信息就损失一点
传到第12个人时,原始信息可能完全失真了

残差连接 = 给每个人一条"直通热线"
即使中间传话有损失,原始信息也能直接到达

图示

scss 复制代码
没有残差连接:                有残差连接:
┌────────┐                   ┌────────┐
│ 输入 x │                   │ 输入 x │──────────┐
└───┬────┘                   └───┬────┘          │
    ↓                            ↓               │
┌────────┐                   ┌────────┐          │
│  F(x)  │                   │  F(x)  │          │
└───┬────┘                   └───┬────┘          │
    ↓                            ↓               │
┌────────┐                   ┌────────┐          │
│ 输出   │                   │   +    │←─────────┘ (相加)
└────────┘                   └───┬────┘
                                 ↓
                             ┌────────┐
                             │ F(x)+x │ ← 输出包含原始信息!
                             └────────┘

在 Transformer 中的应用

scss 复制代码
每个 Transformer Block 有两处残差连接:

x ─────────────────────────┐
│                          │
↓                          │
[Layer Norm] → [注意力] ───┼→ (+) → x₁
                           │
x₁ ────────────────────────┐
│                          │
↓                          │
[Layer Norm] → [MLP] ──────┼→ (+) → x₂(输出)

激活函数:GELU

激活函数给神经网络带来"非线性",让它能学习复杂模式。

为什么需要非线性?

ini 复制代码
如果只有线性运算:
y = ax + b
y = cy + d = c(ax + b) + d = acx + (bc + d)

无论多少层,最终还是 y = 常数×x + 常数
这就只能学习直线关系!

加入非线性:
y = 激活函数(ax + b)

可以学习任意复杂的曲线!

GELU vs ReLU

scss 复制代码
ReLU(传统方法):
        │    ╱
        │   ╱
        │  ╱
────────┼─╱──────
        │
公式:max(0, x)
特点:x<0 输出0,x>0 输出x,在0处有"尖角"

GELU(Transformer 使用):
        │    ╱
        │   ╱
        │  ╱
────────┼╱───────
       ╱│
公式:x × Φ(x),其中 Φ 是标准正态分布的累积分布函数
特点:平滑曲线,没有尖角

GELU 的直观理解

erlang 复制代码
GELU 可以理解为"概率性门控":

对于输入 x:
• 当 x 很大(正数)时:几乎 100% 让它通过 → 输出 ≈ x
• 当 x 很小(负数)时:几乎 0% 让它通过 → 输出 ≈ 0
• 当 x 接近 0 时:有一定概率通过 → 输出在 0 和 x 之间

这比 ReLU 的"非0即1"更平滑,有助于模型训练

第二部分:语言模型基础概念

什么是 Token(词元)?

Token 是 AI 处理文本的最小单位,可以是一个词,也可以是词的一部分。

举个例子,当你输入 "I love programming" 时:

css 复制代码
原文:      "I love programming"
           ↓ 分词(Tokenization)
Token:    ["I", "love", "program", "ming"]
            ↑    ↑       ↑          ↑
           完整词 完整词   词的前半部分  词的后半部分

为什么要这样分?

  • 如果每个词都是一个 Token,词汇表会非常庞大
  • 把长词拆成小块,可以用更少的"积木"拼出更多的词
  • GPT-2 的词汇表有 50,257 个 Token,就像有 50,257 块乐高积木

中文的例子:

arduino 复制代码
原文:     "数据可视化"
Token:   ["数据", "可视", "化"]

什么是嵌入向量(Embedding)?

嵌入向量是把文字变成数字的"魔法",让意思相近的词在数字空间里也靠得近。

想象一个二维平面:

markdown 复制代码
        美味程度 ↑
              │    🍎苹果
              │         🍊橙子
              │    
              │              🍕披萨
              │         🍔汉堡
              │
        ──────┼─────────────────→ 甜度
              │
              │    🥦西兰花
              │         🥬白菜

在这个例子中:

  • 苹果和橙子都是水果,位置靠近
  • 披萨和汉堡都是快餐,位置靠近
  • 蔬菜在另一个区域

真正的嵌入向量不是2维,而是 768维(GPT-2)!人类无法想象768维空间,但对计算机来说没问题。

arduino 复制代码
"苹果" → [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 复制代码
输入:  "今天天气真"
          ↓
    [Transformer 魔法盒子]
          ↓
输出:  "好" (概率 85%)
        "糟" (概率 10%)
        "的" (概率 3%)
        ...

就像你和朋友聊天,对方说了半句话,你可以猜出下一个词是什么。Transformer 就是在做同样的事情!

三大核心组件

Transformer 由三个核心部分组成:

arduino 复制代码
┌─────────────────────────────────────────┐
│           输入:"Data visualization"     │
│                    ↓                    │
│  ┌─────────────────────────────────┐   │
│  │  1️⃣ Embedding(嵌入层)          │   │  ← 文字变数字
│  └─────────────────────────────────┘   │
│                    ↓                    │
│  ┌─────────────────────────────────┐   │
│  │  2️⃣ Transformer Block(×12)    │   │  ← 理解语义关系
│  │     • 注意力机制                  │   │
│  │     • MLP 层                     │   │
│  └─────────────────────────────────┘   │
│                    ↓                    │
│  ┌─────────────────────────────────┐   │
│  │  3️⃣ Output(输出层)             │   │  ← 预测下一个词
│  └─────────────────────────────────┘   │
│                    ↓                    │
│           输出:"empowers"               │
└─────────────────────────────────────────┘

嵌入层(Embedding Layer)

嵌入层负责把文字"翻译"成数字,分四步完成:

步骤 1:分词(Tokenization)

less 复制代码
"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 复制代码
这是一个矩阵乘法操作!

One-hot 向量 × 嵌入矩阵 = 嵌入向量

[0,0,0,1,0,...,0]  ×  ┌─────────────────┐   =  [0.12, -0.34, ...]
    (1×50257)         │ (50257 × 768)   │        (1×768)
   Token ID=3         │  嵌入矩阵        │      "Data"的向量
                      └─────────────────┘

步骤 3:位置编码

问题: "我爱你" 和 "你爱我" 意思完全不同,但包含相同的词!

解决: 给每个位置加上一个"位置信息":

scss 复制代码
位置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 复制代码
最终嵌入 = Token嵌入 + 位置编码

[0.12, -0.34, 0.56, ...] + [0.01, 0.02, 0.03, ...] = [0.13, -0.32, 0.59, ...]
     Token嵌入                  位置编码                  最终嵌入

注意力机制详解

这是 Transformer 最核心的创新!

注意力机制 ------ "谁跟谁相关?"

注意力机制让每个词都能"看到"其他词,理解它们之间的关系。

举个例子:

arduino 复制代码
"小明把苹果给了小红,她很开心"

当 AI 读到 "她" 时,需要知道 "她" 指的是谁。注意力机制会计算:

erlang 复制代码
"她" 应该关注谁?
  ├── "小明" → 相关度 10%  (男性名字,不太可能)
  ├── "苹果" → 相关度 5%   (水果,不是人)
  ├── "小红" → 相关度 80%  (女性名字,很可能!)
  └── "开心" → 相关度 5%   (形容词,不是人)

Q、K、V 是什么?(搜索引擎类比)

注意力机制使用三个矩阵来计算关系:

名称

类比

作用

Query (Q) 查询

你在搜索框输入的内容

"我想找什么?"

Key (K) 键

每个网页的标题

"我是什么?"

Value (V) 值

网页的实际内容

"我包含什么信息?"

markdown 复制代码
搜索过程:
1. 你输入搜索词(Query):"苹果手机"
2. 搜索引擎匹配标题(Key):找到相关网页
3. 返回内容(Value):显示网页内容

注意力过程:
1. "她" 的 Query 问:"我应该指代谁?"
2. 每个词的 Key 回答:"我是 [小明/苹果/小红/...]"
3. 匹配最高的 Value(小红的信息)被重点关注

Step 1: 计算 Q、K、V 矩阵

ini 复制代码
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 复制代码
注意力分数 = (Q × Kᵀ) / √768

计算过程:
┌──────────────┐     ┌──────────────┐
│   Q (7×768)  │  ×  │  Kᵀ (768×7)  │  ÷  √768
└──────────────┘     └──────────────┘
                           ↓
                    ┌─────────────┐
                    │ Score (7×7) │
                    └─────────────┘

Score[i][j] = 第 i 个词对第 j 个词的关注程度

Step 3: 应用掩码(Mask)

在生成文本时,AI 不能"偷看"未来的内容:

arduino 复制代码
掩码矩阵(下三角为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 复制代码
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 复制代码
Output = Attention_Weights × V

(7×7) × (7×768) = (7×768)

每个词的输出 = 它关注的所有词的 V 向量的加权平均

完整注意力公式

scss 复制代码
Attention(Q, K, V) = softmax(Q × Kᵀ / √dₖ) × V

其中 dₖ = 768(Key 的维度)

多头注意力(Multi-Head Attention)

为什么要"多头"?

erlang 复制代码
单头注意力:一个专家看问题
多头注意力:12个专家从不同角度看问题,然后综合意见

就像:
• 头1:关注语法关系(主谓宾)
• 头2:关注语义关系(近义词)
• 头3:关注位置关系(相邻词)
• ...
• 头12:关注其他模式

实现方式:分割维度

less 复制代码
原始: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 复制代码
每个头输出:(7, 64)
12 个头拼接:(7, 768)
再经过一个线性变换:(7, 768)

MLP 层

MLP(多层感知机)对每个词进行独立的深度加工:

scss 复制代码
输入 (7, 768)
      ↓
┌─────────────────────────────────────┐
│ 第一层线性变换:(768 → 3072)         │  ← 扩展 4 倍
│ 矩阵乘法 + 偏置                      │
└─────────────────────────────────────┘
      ↓ (7, 3072)
┌─────────────────────────────────────┐
│ GELU 激活函数                        │  ← 添加非线性
└─────────────────────────────────────┘
      ↓ (7, 3072)
┌─────────────────────────────────────┐
│ 第二层线性变换:(3072 → 768)         │  ← 压缩回来
│ 矩阵乘法 + 偏置                      │
└─────────────────────────────────────┘
      ↓
输出 (7, 768)

为什么要先扩展再压缩?

就像写作文:

  1. 扩展:先把所有想法都写出来(头脑风暴)
  2. 压缩:再精简成最重要的观点(提炼精华)

输出层

经过 12 层 Transformer Block 处理后,AI 需要预测下一个词:

scss 复制代码
最后一个 Token 的表示 (1, 768)
           ↓
┌────────────────────────────────────┐
│ 线性变换:(768 → 50257)             │
│ 得到每个词的"分数"(logits)         │
└────────────────────────────────────┘
           ↓ (1, 50257)
┌────────────────────────────────────┐
│ Softmax:转换为概率分布             │
│ 所有概率加起来 = 1                  │
└────────────────────────────────────┘
           ↓
┌────────────────────────────────────┐
│ 采样:根据 temperature, top-k, top-p│
│ 选择下一个 Token                    │
└────────────────────────────────────┘

第四部分:采样策略

Temperature(温度)

温度控制输出的"随机性":

温度

效果

比喻

< 1

更确定、保守

考试时选最稳妥的答案

= 1

正常状态

平常心做选择

> 1

更随机、有创意

大胆尝试新选项

erlang 复制代码
温度 = 0.2(低温):
  "今天天气真___"  →  "好"(99%确定选这个)

温度 = 1.5(高温):
  "今天天气真___"  →  "奇怪/热/冷/好"(更多可能性)

Top-k 和 Top-p 采样

Top-k:只考虑概率最高的 k 个词

ini 复制代码
k = 3:只从 ["好", "糟", "热"] 中选择

Top-p:选择累计概率达到 p 的词

ini 复制代码
p = 0.9:选择累计概率达到 90% 的词
如果 "好"=70%, "糟"=15%, "热"=10%,累计已达 95%
就只从这三个词中选

第五部分:总结

信息流总览

scss 复制代码
"今天天气" 
    ↓ 分词
[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 复制代码
嵌入层:
• 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 亿)参数
相关推荐
九.九9 小时前
ops-transformer:AI 处理器上的高性能 Transformer 算子库
人工智能·深度学习·transformer
春日见9 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
恋猫de小郭9 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
寻寻觅觅☆9 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
deephub9 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
偷吃的耗子9 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
大模型RAG和Agent技术实践9 小时前
从零构建本地AI合同审查系统:架构设计与流式交互实战(完整源代码)
人工智能·交互·智能合同审核
老邋遢9 小时前
第三章-AI知识扫盲看这一篇就够了
人工智能
互联网江湖9 小时前
Seedance2.0炸场:长短视频们“修坝”十年,不如AI放水一天?
人工智能
PythonPioneer10 小时前
在AI技术迅猛发展的今天,传统职业该如何“踏浪前行”?
人工智能