大模型应用开发--6--Transformer架构介绍

Transformer 架构详解

2017 年 Google 论文 Attention Is All You Need 提出,是 GPT、BERT 等大模型的基石。


目录

  • [1. 核心概念](#1. 核心概念)
  • [2. Token:文本的最小单位](#2. Token:文本的最小单位)
  • [3. 整体架构](#3. 整体架构)
  • [4. 词嵌入 + 位置编码](#4. 词嵌入 + 位置编码)
  • [5. 自注意力机制(Self-Attention)](#5. 自注意力机制(Self-Attention))
  • [6. 多头注意力(Multi-Head Attention)](#6. 多头注意力(Multi-Head Attention))
  • [7. 前馈网络 + 残差连接 + 层归一化](#7. 前馈网络 + 残差连接 + 层归一化)
  • [8. 掩码机制(Masking)](#8. 掩码机制(Masking))
  • [9. 交叉注意力(Cross-Attention)](#9. 交叉注意力(Cross-Attention))
  • [10. 自回归生成过程](#10. 自回归生成过程)
  • [11. 输出层:Logits → Softmax → 选词](#11. 输出层:Logits → Softmax → 选词)
  • [12. 训练 vs 推理](#12. 训练 vs 推理)
  • [13. Transformer 家族](#13. Transformer 家族)
  • [14. 面试高频问题速答](#14. 面试高频问题速答)
  • [15. 总结](#15. 总结)

1. 核心概念

一句话概括

Transformer 是一种不依赖循环(RNN),完全靠**注意力机制(Attention)**来理解上下文关系的深度学习架构。

与 RNN 的对比

对比项 RNN/LSTM Transformer
处理方式 逐词阅读,顺序处理 所有词同时处理,并行计算
长距离依赖 信息会衰减 注意力直接连接任意位置,无衰减
训练速度 慢(无法并行) 快(GPU 友好,可并行)
可扩展性 堆层效果有限 参数越多能力越强(Scaling Law)

2. Token:文本的最小单位

Token ≠ 词,Token ≠ 词 ID

概念 本质 例子
文本 原始字符串 "unbelievable"
Token 切分后的最小单元(字符串) ["un", "##believ", "##able"]
Token ID Token 在词表中的编号(整数) [489, 23871, 3450]

为什么要用子词(Subword)级别

方案 词表大小 问题
词级别 50万+ 词表太大,罕见词无法覆盖
字符级别 ~100 序列太长,语义信息弱
子词级别 3万~10万 平衡词表大小和序列长度 ✅

主流分词算法

算法 使用模型 原理
BPE GPT 系列 从字符开始,反复合并最高频的相邻对
WordPiece BERT 类似 BPE,但合并标准是最大化语言模型似然
SentencePiece T5/LLaMA 不依赖空格,直接把整句话当字节流处理

在 Transformer 中的流程

复制代码
"我爱NLP"
  → 分词器: ["我", "爱", "N", "LP"]        ← Token(字符串)
  → 查词表: [5, 12, 308, 456]              ← Token ID(整数)
  → 查嵌入表: [[0.2,0.5,...], ...]         ← 嵌入向量
  → 送入 Transformer

3. 整体架构

Transformer 由 Encoder(编码器)Decoder(解码器) 两大部分组成,原始论文中各堆叠 6 层,每层结构相同但参数不同。

3.1 完整架构图

复制代码
    输入: "我爱NLP"                              输出: "I love NLP"
         │                                              ▲
         ▼                                              │
┌────────────────┐                            ┌────────────────┐
│  词嵌入         │                            │  线性层+Softmax  │
│ (Input Embedding)│                          │  (输出层,不属于  │
└───────┬────────┘                            │   6层之内)      │
        │                                     └────────▲───────┘
        ▼                                             │
┌────────────────┐                                    │
│  位置编码        │                                    │
│ (Positional    │                                    │
│   Encoding)    │                                    │
└───────┬────────┘                                    │
        │                                             │
        ▼  相加(⊕)                                     │
   ┌──────────┐                                       │
   │  X (5×4) │    Encoder最终输出 H (5×4)              │
   └────┬─────┘    传给Decoder每一层的                   │
        │          Cross-Attention的 K 和 V            │
        ▼          (6层都用同一个H)                     │
┌──────────────────────────────┐         ┌───────────▼───────────────────
│     Encoder Layer 1          │         │     Decoder Layer 1          │
│  ┌────────────────────────┐  │         │  ┌────────────────────────┐  │
│  │ ① 多头自注意力(无掩码)     │  │        │  │ ① 掩码多头自注意力         │  │
│  │   残差 + LayerNorm      │  │         │  │   残差 + LayerNorm      │  │
│  ├────────────────────────┤  │         │  ├────────────────────────┤  │
│  │ ② 前馈网络 FFN           │  │        │  │ ② 交叉注意力             │  │
│  │   残差 + LayerNorm      │  │         │  │   Q←Decoder K,V←Encoder│  │
│  └────────────────────────┘  │         │  │   残差 + LayerNorm      │  │
│                              │         │  ├────────────────────────┤  │
│  (2个子层)                    │         │  │ ③ 前馈网络 FFN           │  │
│                              │         │  │   残差 + LayerNorm      │  │
│                              │         │  └────────────────────────┘  │
│                              │         │                              │
│                              │         │  (3个子层)                    │
└──────────────┬───────────────┘         └──────────────▲───────────────┘
               │                                        │
        ┌──────┴──────┐                          ┌──────┴──────┐
        │             │                          │             │
        ▼             │                          │             │
┌──────────────────────┐                  ┌──────┴──────────────┐
│  Encoder Layer 2~5   │                  │  Decoder Layer 2~5   │
│  (结构同Layer 1)      │                  │  (结构同Layer 1)      │
│  每层参数不同         │                  │  每层参数不同         │
└──────────┬───────────┘                  └──────▲──────────────┘
           │                                     │
           ▼                                     │
┌──────────────────────┐                  ┌──────┴──────────────┐
│  Encoder Layer 6     │                  │  Decoder Layer 6     │
│  (结构同Layer 1)      │                  │  (结构同Layer 1)      │
└──────────┬───────────┘                  └──────▲──────────────┘
           │                                     │
           └─────────────────────────────────────┘

Encoder最终输出 H (5×4) 传给Decoder每一层的Cross-Attention的 K 和 V (6层都用同一个H)

在Input Embedding词嵌入前还有一步数据预处理: 分词使用tokenizer分词获取token 和 查词表从词表中查找token id。数据预处理不属于transformer架构中的所以transformer架构图从Input Embedding开始,Input Embedding根据token id从嵌入表中查看相应token的向量。

3.2 Encoder 每层结构(2个子层)

复制代码
Encoder Layer:
  ① 多头自注意力(无掩码)+ 残差 + LayerNorm
  ② 前馈网络 FFN + 残差 + LayerNorm

数据流:

复制代码
"我爱NLP"
  → 分词 + 嵌入 + 位置编码 → X (5×4)
  → Layer 1: 自注意力 → FFN → 输出
  → Layer 2: 自注意力 → FFN → 输出
  → ...
  → Layer 6: 自注意力 → FFN → 最终输出 H (5×4)

H = 源语言的上下文表示,传给 Decoder 每一层的 Cross-Attention

3.3 Decoder 每层结构(3个子层)

复制代码
Decoder Layer:
  ① 掩码自注意力(Masked Self-Attention)+ 残差 + LayerNorm
  ② 交叉注意力(Cross-Attention)+ 残差 + LayerNorm
  ③ 前馈网络 FFN + 残差 + LayerNorm

输出层(不属于6层之内):
  线性层 + Softmax → 选词

三个子层各自的作用:

子层 Q/K/V 来源 作用
① 掩码自注意力 Q,K,V 都来自 Decoder 输入 理解已生成词之间的关系,掩码防止看到未来
② 交叉注意力 Q 来自 Decoder,K,V 来自 Encoder 从源语言中提取相关信息,翻译的依据
③ FFN 上一步的输出 非线性变换,增强表达能力

3.4 Encoder vs Decoder 对比

对比项 Encoder 每层 Decoder 每层
自注意力 ✅(无掩码) ✅(有掩码)
交叉注意力
前馈网络 FFN
子层数 2 个 3 个
计算量 较小 较大(多两次注意力)

3.5 关键设计要点

  1. Encoder 只跑一次:输入源语言后,6层处理完输出 H,结果固定不变
  2. Decoder 循环跑:每生成一个词跑一次,自回归逐词生成
  3. 6层共用同一个 H:Decoder 每一层的 Cross-Attention 都直接从 Encoder 的同一个输出 H 取 K/V,不是层间传递
  4. 输出层在6层之外:线性层 + Softmax 是独立的"输出头",不属于 Decoder 的任何一层

4. 词嵌入 + 位置编码

4.1 词嵌入

每个 Token ID 查嵌入表,得到一个 d_model 维向量(原始论文 d_model=512)。

4.2 为什么需要位置编码

Transformer 同时处理所有词,没有顺序概念 。"我打你"和"你打我"如果没有位置编码,模型看到的是完全相同的向量集合。所以需要给每个位置一个独特的指纹

4.3 位置编码公式

复制代码
PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
  • pos = 词在句子中的位置(1, 2, 3, ...)
  • i = 维度对的索引(0, 1, 2, ...)
  • d_model = 嵌入维度总数

4.4 为什么需要两个公式(sin + cos)

sin 和 cos 配对 = 单位圆上的一个点 (x, y) = 唯一确定一个角度状态。

单独用 sin 会有歧义:sin(30°) = sin(150°) = 0.5,加上 cos 就能区分:cos(30°) = 0.866 ≠ cos(150°) = -0.866

4.5 多频率的意义

不同维度对使用不同频率,就像时钟的秒针、分针、时针:

复制代码
维度对0 (i=0):  频率 = 1/10000^(0/d)     ← 高频(秒针),区分相邻位置
维度对1 (i=1):  频率 = 1/10000^(2/d)     ← 中频(分针)
...
维度对N (i=N):  频率 = 1/10000^(2N/d)    ← 低频(时针),区分远距离位置
  • 高频:能精确区分相邻位置,但周期短,远距离会重复
  • 低频:无法区分相邻位置,但周期长,能区分远距离位置
  • 组合:近看靠高频,远看靠低频

4.6 相对位置的表达

复制代码
sin((pos+k)·ω) = sin(pos·ω)·cos(k·ω) + cos(pos·ω)·sin(k·ω)
cos((pos+k)·ω) = cos(pos·ω)·cos(k·ω) - sin(pos·ω)·sin(k·ω)

→ 偏移量 k 对应一个固定的旋转矩阵,不依赖绝对位置 pos
→ 位置3→5 和 位置100→102 的变换完全相同
→ 模型能学到"相隔k个位置"的一致语义关系

4.7 计算示例(d_model=4,5个位置)

复制代码
维度对0: ω₀ = 1/10000^(0/4) = 1.0
维度对1: ω₁ = 1/10000^(2/4) = 0.01

         sin(ω₀)  cos(ω₀)  sin(ω₁)  cos(ω₁)
pos 1  [  0.84,   0.54,   0.01,   1.00 ]
pos 2  [  0.91,  -0.42,   0.02,   1.00 ]
pos 3  [  0.14,  -0.99,   0.03,   1.00 ]
pos 4  [ -0.76,  -0.65,   0.04,   1.00 ]
pos 5  [ -0.96,   0.28,   0.05,   1.00 ]

前两列(高频)变化剧烈 → 区分相邻位置
后两列(低频)变化缓慢 → 区分远距离位置

4.8 最终输入 = 词嵌入 + 位置编码

复制代码
最终输入 = Embedding(token_id) + PE(pos)

5. 自注意力机制(Self-Attention)

5.1 三个角色

角色 含义 类比
Query (Q) "我在找什么信息?" 图书馆搜索的关键词
Key (K) "我能提供什么信息?" 每本书的标签/标题
Value (V) "我的实际内容是什么" 书的正文内容

5.2 计算步骤

复制代码
① 生成 Q, K, V:  X × W_Q → Q,  X × W_K → K,  X × W_V → V
  (三个权重矩阵 W_Q, W_K, W_V 各不相同,训练学习)

② 计算注意力分数:  Score = Q × Kᵀ / √d_k
  (除以 √d_k 防止分数过大)

③ Softmax 归一化:  把分数变成概率分布(0~1,和为1)

④ 加权求和 V:  Output = Attention权重 × V

5.3 为什么除以 √d_k

当 d_k 较大时,Q·K 的点积值会很大,导致 softmax 梯度变得很小(进入饱和区)。除以 √d_k 把分数缩回到合理范围,保证梯度稳定。

5.4 Softmax 归一化

复制代码
softmax(x_i) = e^(x_i) / Σ(e^(x_j))

为什么需要 Softmax 而不是直接用原始分数:

原因 说明
变成权重 分数可负、无界,Softmax 后全为正数,和为1,天然是权重
尺度稳定 不归一化时序列越长输出越大,Softmax 后无论多长都稳定
放大差异 e^x 指数放大,让强信号更突出(差异大时"赢家通吃")

5.5 加权求和 V 的含义

每个词不再只代表自己,而是变成了"我 + 我关注的那些词的信息"的融合体。

复制代码
"自然"的输出 = 24%×V(自然) + 22%×V(语言) + 21%×V(我) + 18%×V(处理) + 16%×V(爱)
             = "自然语言处理"这个整体概念的上下文表示

→ 同一个词在不同句子中,因为上下文不同,会得到不同的输出向量
→ 模型因此能理解多义词、指代关系、长距离依赖

5.6 为什么用 V 而不是原始输入 X

复制代码
X = 词嵌入 + 位置编码     → "我是谁,我在哪"(原始身份)
V = X × W_V(线性变换)  → "我能提供什么信息"(投影后的内容)

类比: X = 一个人的身份证, V = 这个人的专业技能

5.7 计算示例("我"的注意力)

复制代码
分数:     [0.25, 0.33, 0.66, 0.42, 0.03]
e^x:      [1.28, 1.39, 1.93, 1.52, 1.03]    Sum=7.16
softmax:  [0.18, 0.19, 0.27, 0.21, 0.14]    ← "自然"权重最高

加权求和: Out(我) = 0.18×V(我) + 0.19×V(爱) + 0.27×V(自然) + 0.21×V(语言) + 0.14×V(处理)

6. 多头注意力(Multi-Head Attention)

核心思想

一个注意力头只关注一种关系。多个头可以关注不同维度的关系

复制代码
Head 1: 关注语法关系(主谓宾)
Head 2: 关注语义关系(同义/反义)
Head 3: 关注指代关系("他"指代谁)
...
Head 8: 关注长距离依赖

→ 拼接所有头的结果 → 线性变换 → 最终输出

计算流程

复制代码
① 把 d_model 维向量分成 h 个头,每个头 d_k = d_model / h 维
② 每个头独立计算 Q·Kᵀ/√d_k → softmax → 加权V
③ 拼接所有头的输出: Concat(head₁, head₂, ..., headₕ) → (seq_len × d_model)
④ 乘以输出权重矩阵 W_O → 最终输出

7. 前馈网络 + 残差连接 + 层归一化

7.1 前馈网络(FFN)

复制代码
FFN(x) = max(0, x × W₁ + b₁) × W₂ + b₂

第一层: d_model → 4×d_model(扩展4倍)+ ReLU 激活
第二层: 4×d_model → d_model(压缩回来)

→ 对每个位置独立做非线性变换,模型能学到复杂的特征变换,增强表达能力

7.2 残差连接

复制代码
输出 = LayerNorm(x + SubLayer(x))

→ 把输入直接加回来,保证"原始信息不丢失"
→ 解决深层网络梯度消失问题
→ 注意力/FFN 只是"额外添加上下文"

7.3 层归一化(LayerNorm)

层归一化的含义: 残差连接后,训练模型时不同位置的向量数值范围差异很大(有的均值 1.15,有的均值 0.12),直接传入下一层会导致训练不稳定。LayerNorm 把每个位置的向量独立地归一化到"均值≈0、方差≈1"的统一尺度,让所有位置在同一数值范围内被下一层处理,从而稳定训练、加速收敛;推理时维持数值稳定性(和训练时一致的归一化操作)

对每一行(每个词的向量)独立计算:

复制代码
① 均值:   μ = (x₁ + x₂ + ... + x_d) / d
② 方差:   σ² = Σ(x_i - μ)² / d
③ 归一化: x̂_i = (x_i - μ) / √(σ² + ε)
④ 缩放偏移: y_i = γ × x̂_i + β    (γ, β 是可学习参数)

计算示例("我"的向量 1.28, 1.18, 0.09, 2.05):

复制代码
μ = (1.28 + 1.18 + 0.09 + 2.05) / 4 = 1.15
σ² = [(0.13)² + (0.03)² + (-1.06)² + (0.90)²] / 4 = 0.488
σ = √0.488 = 0.699

归一化: [(1.28-1.15)/0.699, (1.18-1.15)/0.699, (0.09-1.15)/0.699, (2.05-1.15)/0.699]
       = [0.19, 0.04, -1.52, 1.29]

→ 均值≈0,方差≈1,所有行统一到相同尺度

归一化前后对比:

复制代码
归一化前(数值范围不统一):
  "我":  [1.28, 1.18, 0.09, 2.05]   → 均值1.15,范围 [0.09, 2.05]
  "爱":  [1.92, -0.75, 0.55, 1.34]  → 均值0.77,范围 [-0.75, 1.92]
  "处理":[-0.61,-1.13, 0.38, 1.83]  → 均值0.12,范围 [-1.13, 1.83]
  → 每行数值范围差异大,下一层处理时不稳定

归一化后(统一到相同尺度):
  "我":  [0.19, 0.04, -1.52, 1.29]  → 均值≈0,方差≈1
  "爱":  [1.15, -1.51, -0.21, 0.57] → 均值≈0,方差≈1
  "处理":[0.27, -0.89, 0.51, 0.11]  → 均值≈0,方差≈1
  → 所有行统一尺度,训练稳定

为什么用 LayerNorm 而不是 BatchNorm:

对比 BatchNorm LayerNorm
归一化方向 对同一维度跨样本归一化(列方向) 对同一样本的所有维度归一化(行方向)
适用场景 CNN(图像) Transformer/RNN
batch size 依赖 依赖,变化时不稳定 不依赖,每个样本独立计算

γ 和 β 的作用: 归一化后所有维度被强制拉到均值0、方差1,但并非每个维度都需要相同的分布。γ(缩放)和 β(偏移)让模型自己决定每个维度的最佳数值分布(初始 γ=1, β=0,训练中学习调整)。


8. 掩码机制(Masking)

8.1 为什么需要掩码

复制代码
训练时: Decoder 一次性处理整个目标序列,"love" 能看到 "NLP"
推理时: 逐词生成,生成 "love" 时 "NLP" 还不存在

→ 如果训练时不加掩码,"love" 会依赖 "NLP" 的信息
→ 推理时没有 "NLP",模型就"懵了"
→ 必须用掩码强制切断未来信息

8.2 掩码矩阵

复制代码
输入: [<START>, I, love, NLP, <END>]

掩码矩阵 M (5×5):
              START   I   love  NLP   END
START    [    0,    -∞,  -∞,   -∞,  -∞  ]
I        [    0,     0,  -∞,   -∞,  -∞  ]
love     [    0,     0,   0,   -∞,  -∞  ]
NLP      [    0,     0,   0,    0,  -∞  ]
END      [    0,     0,   0,    0,   0  ]

0 = 允许看(不遮挡)
-∞ = 遮挡(不允许看未来的词)

规律: 下三角矩阵,当前位置只能看自己和之前的位置

8.3 掩码如何参与计算

复制代码
第1步: 算注意力分数 Q·Kᵀ/√d_k
         START    I     love
  I    [   0.48,   0.82,   0.65]

第2步: 加掩码(未来位置变 -∞)
         START    I     love
  I    [   0.48,   0.82,  -∞  ]

第3步: Softmax(-∞ → 0,未来词权重为0)
         START    I     love
  I    [   0.42,   0.58,  0.00]

第4步: 加权求和 V(未来词的 V 贡献为0)
  Out(I) = 0.42×V(START) + 0.58×V(I) + 0.00×V(love)

8.4 推理时掩码的作用

推理时虽然没有"未来词",掩码仍然必须加,目的是保持和训练时一致。

复制代码
不加掩码: <START> 会看到 I 和 love → 它的表示和训练时不一致 → 预测出错
加掩码:  每个位置只看该看的 → 和训练时一致 → 预测正确

本质: 掩码 = 训练和推理之间的"一致性保证"

8.5 只有一个 <START> 时的掩码

复制代码
序列长度=1,掩码矩阵 M (1×1) = [0]
→ 没有未来词可遮,掩码不起作用
→ 注意力权重 = [1.0](100%关注自己)
→ 输出 = V 本身

9. 交叉注意力(Cross-Attention)

9.1 Q/K/V 来源

复制代码
Q 来自 Decoder 当前输出: "我需要什么信息"
K 来自 Encoder 最终输出 H: "源文本各词的标签"
V 来自 Encoder 最终输出 H: "源文本各词的内容"

9.2 一个 Encoder 输出如何变成 K 和 V

复制代码
Encoder 输出只有一个矩阵 H (5×4)

K = H × W_K^cross  ← 乘以一个权重矩阵,提取"可被匹配的标签"
V = H × W_V^cross  ← 乘以另一个权重矩阵,提取"可被提取的内容"

类比: 同一份档案,用"关键词提取器"得到标签(K),用"内容提取器"得到详情(V)

9.3 6层的 K/V 都来自同一个 Encoder 输出

复制代码
✅ 正确理解:
  Layer 1~6 的 Cross-Attention 中,K 和 V 每层都直接来自 Encoder 的同一个输出 H
  不同的是每层有自己的 W_K 和 W_V,从同一个 H 中提取不同维度的信息

❌ 错误理解:
  Layer 2 的 K,V 来自 Layer 1(不是!)

9.4 计算示例

复制代码
Decoder的Q: [0.45, 0.30, 1.35, 0.50]

Encoder的K (5×4):
  我=[0.72,0.48,0.15,1.20], 爱=[1.55,-0.35,0.62,0.88], ...

注意力分数:
  Score(<START>, 我)   = Q·K(我)/2   = 0.635
  Score(<START>, 爱)   = Q·K(爱)/2   = 0.935
  Score(<START>, 自然) = Q·K(自然)/2 = 1.012  ← 最高
  Score(<START>, 语言) = Q·K(语言)/2 = 0.346
  Score(<START>, 处理) = Q·K(处理)/2 = 0.498

softmax → [0.184, 0.249, 0.269, 0.138, 0.161]
→ <START> 最关注 "自然"(0.269) 和 "爱"(0.249)

9.5 三种注意力对比

注意力类型 Q 来源 K 来源 V 来源
Encoder 自注意力 Encoder 输入 X Encoder 输入 X Encoder 输入 X
Decoder 掩码自注意力 Decoder 输入 Y Decoder 输入 Y Decoder 输入 Y
交叉注意力 Decoder 输出 Encoder 输出 H Encoder 输出 H

10. 自回归生成过程

10.1 核心机制

复制代码
把上一步的输出,当作下一步的输入:

  自己生成的结果 → 回头喂给自己 → 生成下一个结果 → 再喂回去 → ...

就像接龙:
  看到 <START>           → 接 "I"
  看到 <START> I         → 接 "love"
  看到 <START> I love    → 接 "NLP"
  看到 <START> I love NLP → 接 <END>(停止)

10.2 完整流程("我爱NLP" → "I love NLP")

复制代码
Encoder(只跑1次): "我爱NLP" → H (5×4)

Decoder(循环跑):
  步骤1: 输入 [<START>]                → 预测 "I"
  步骤2: 输入 [<START>, I]             → 预测 "love"
  步骤3: 输入 [<START>, I, love]       → 预测 "NLP"
  步骤4: 输入 [<START>, I, love, NLP]  → 预测 <END> → 停止

最终输出: "I love NLP"

10.3 每步输入的变化

复制代码
时间步    Decoder输入              Encoder参考     预测输出
───────────────────────────────────────────────────────────
  1      [<START>]                 "我爱NLP"       → "I"
  2      [<START>, I]              "我爱NLP"       → "love"
  3      [<START>, I, love]        "我爱NLP"       → "NLP"
  4      [<START>, I, love, NLP]   "我爱NLP"       → "<END>"

每次都把上一步的输出加到输入末尾

10.4 为什么每步只取最后一个位置的输出

复制代码
输入: [<START>, I, love]

Decoder 输出3个向量:
  位置0 (<START>) → 向量A  ← 预测"I"时用的,现在不需要了
  位置1 (I)       → 向量B  ← 预测"love"时用的,现在不需要了
  位置2 (love)    → 向量C  ← 用这个预测下一个词 "NLP"

→ 每次只需要最后一个位置的输出
→ 因为它包含了"看完所有已生成词后,下一个词应该是什么"的信息

10.5 停止条件

  • 模型预测出 <END> 标记 → 停止
  • 如果一直不输出 <END>,设置最大长度强制停止

11. 输出层:Logits → Softmax → 选词

11.1 线性层

复制代码
Decoder 6层最终输出: [1.85, 0.32, 0.15, 0.92]  (4维)
       │
       ▼  乘以权重矩阵 W (4×10000[嵌入表有10000各token]),W 是训练学习的
       │
Logits (1×10000): [8.5, 2.1, 1.8, 0.3, -0.5, ...]
                    I   love  the  NLP <END>  ...

→ 每个词的分数 = Decoder输出向量 和 该词权重向量的点积
→ 点积越大,说明 Decoder 输出和该词越"匹配"

Logits 的维度(训练 vs 推理):

复制代码
训练时: n×10000,每个位置都有 logits,并行预测所有词
推理时: n×10000,但只用最后一个位置的 logits 预测下一个词

11.2 Softmax → 概率分布

复制代码
P(I)    = e^8.5  / Σ = 99.67%  ← 最高
P(love) = e^2.1  / Σ = 0.17%
P(the)  = e^1.8  / Σ = 0.12%
...

11.3 选词策略

策略 方式 适用场景
Greedy(贪心) 选概率最高的 简单快速,翻译任务
Beam Search(束搜索) 保留 top-k 条路径,综合选最优 翻译任务常用
Top-k / Top-p Sampling 从 top-k 中随机采样 ChatGPT 等生成任务,增加多样性

12. 训练 vs 推理

12.1 核心区别

对比项 训练 推理
Decoder 输入 完整目标序列(一次性输入) 逐词输入(自回归)
计算方式 并行计算所有位置 串行,逐词生成
掩码作用 防止看到未来词(模拟自回归) 保持和训练一致
输出 所有位置的 logits 都用 只用最后一个位置的 logits
Encoder 每次重新算 只算一次,结果固定
速度 快(并行) 慢(串行,越长越慢)

12.2 训练时的 Teacher Forcing

复制代码
训练时不用模型自己生成的词作为输入,而是用真实答案(Ground Truth):

  输入: [<START>, I, love, NLP]
  目标: [I, love, NLP, <END>]

  位置1: <START> → 预测 I      (和目标 I 比较,算 loss)
  位置2: I       → 预测 love   (和目标 love 比较,算 loss)
  ...

好处: 不依赖前一步的预测结果,避免错误累积,训练效率高

12.3 推理效率优化:KV Cache

朴素推理的问题:

复制代码
步骤1: 输入 [<START>]                → Decoder 处理 1 个词 → 预测 "I"
步骤2: 输入 [<START>, I]             → Decoder 处理 2 个词 → 预测 "love"
步骤3: 输入 [<START>, I, love]       → Decoder 处理 3 个词 → 预测 "NLP"
步骤4: 输入 [<START>, I, love, NLP]  → Decoder 处理 4 个词 → 预测 <END>

问题: 步骤2 重新计算了 <START> 的 K,V;步骤3 又重新计算了 <START>, I 的 K,V...
      大量重复计算!序列越长浪费越严重

KV Cache 的核心思想:

复制代码
缓存已计算过的 K, V,每步只计算新词的 K, V:

步骤1: 输入 [<START>]
  → 计算 K₁,V₁ → 存入缓存
  → 缓存: K=[K₁], V=[V₁]
  → 用完整 K,V 做 attention → 预测 "I"

步骤2: 输入 [I](只输入新词!)
  → 计算 K₂,V₂ → 追加到缓存
  → 缓存: K=[K₁,K₂], V=[V₁,V₂]
  → 用缓存中完整的 K,V 做 attention → 预测 "love"

步骤3: 输入 [love](只输入新词!)
  → 计算 K₃,V₃ → 追加到缓存
  → 缓存: K=[K₁,K₂,K₃], V=[V₁,V₂,V₃]
  → 用缓存中完整的 K,V 做 attention → 预测 "NLP"

→ 每步只处理 1 个新词,不重复计算之前的词

朴素推理 vs KV Cache 对比:

对比项 朴素推理 KV Cache
每步输入 完整序列(n 个词) 只输入新词(1 个词)
每步计算量 O(n) O(1)(只算新词的 K,V)
重复计算 大量重复 无重复
额外开销 需要存储 K,V 缓存(显存换速度)
总计算量(n 步) O(n²) O(n)

输出层也只算最后一个位置:

复制代码
Decoder 6层最终输出: n × d_model 矩阵
线性层: 输出 × W → n × vocab_size 的 logits

朴素做法: 计算所有 n 行的 logits → 只用最后一行做 softmax
优化做法: 只取最后一行 → 只计算 1 × d_model × W → 1 × vocab_size

→ 和 KV Cache 配合,每步只处理 1 个词,从输入到输出全程无浪费

13. Transformer 家族

模型 类型 特点 适用任务
BERT 仅 Encoder 双向理解 分类、问答、NER
GPT 系列 仅 Decoder 自回归生成 对话、写作、代码生成
T5 / BART Encoder + Decoder 通用 seq2seq 翻译、摘要
ViT Encoder 变体 图像分 patch 当"词" 图像分类

14. 面试高频问题速答

Q1: Transformer 为什么比 RNN 好?

并行计算(所有位置同时处理)、长距离依赖无衰减、可扩展性强(参数越多能力越强)。

Q2: 自注意力的 Q/K/V 分别是什么?

Q(Query)= "我在找什么信息",K(Key)= "我能提供什么信息",V(Value)= "我的实际内容"。通过 Q·Kᵀ 算关联度,softmax 归一化后加权求和 V,得到上下文感知的表示。

Q3: 为什么除以 √d_k?

防止点积值过大导致 softmax 进入饱和区,梯度消失。√d_k 是点积方差的标准化因子。

Q4: 位置编码为什么用 sin/cos?

三个原因:①有界(值在 -1,1);②每个位置有唯一编码(sin+cos 配对确定单位圆上的点);③能表达相对位置(偏移 k 对应固定旋转矩阵,不依赖绝对位置)。多频率设计让高频区分近距离、低频区分远距离。

Q5: Decoder 的掩码是什么?为什么需要?

上三角矩阵,把未来位置的注意力分数设为 -∞,softmax 后变成 0。训练时防止"偷看"未来词,推理时保持和训练一致。本质是训练和推理之间的"一致性保证"。

Q6: 交叉注意力的 K/V 从哪来?

来自 Encoder 的最终输出 H。H 乘以 W_K 得到 K,乘以 W_V 得到 V。6 层 Decoder 的每一层都直接从同一个 H 取 K/V(不是层间传递),但每层的 W_K/W_V 不同,提取不同维度的信息。

Q7: Decoder 是怎么逐词生成的?

自回归:把上一步的输出加到输入末尾,作为下一步的输入。每次只用最后一个位置的输出预测下一个词。遇到 <END> 停止。

Q8: 训练和推理有什么区别?

训练时并行处理完整序列(Teacher Forcing),所有位置的 logits 都用;推理时串行逐词生成,只用最后一个位置的 logits。训练快(并行),推理慢(串行)。

Q9: LayerNorm 和 BatchNorm 的区别?

BatchNorm 对同一维度跨样本归一化(列方向),依赖 batch size;LayerNorm 对同一样本的所有维度归一化(行方向),不依赖 batch size。Transformer 用 LayerNorm 因为序列长度可变且 batch size 不稳定。

Q10: 多头注意力为什么有用?

不同的头可以关注不同维度的关系(语法、语义、指代、长距离依赖等),拼接后得到更丰富的表示。

Q11: Logits 是什么?

线性层输出的每个词的"原始分数"(未归一化)。Decoder 输出 d_model 维向量,乘以权重矩阵 W (d_model × vocab_size) 得到 vocab_size 维的 logits,每个维度对应词表中一个词的得分。

Q12: Transformer 的参数量主要在哪?

注意力层的 Q/K/V 权重矩阵、FFN 的两层全连接(占参数量最大,因为扩展 4 倍)、嵌入矩阵(vocab_size × d_model)。层越深、d_model 越大、词表越大,参数越多。


附:关键数字对照

参数 教学示例 GPT-2 Small GPT-3
d_model 4 768 12,288
注意力头数 1 12 96
层数 6 12 96
词表大小 ~100 50,257 50,257
参数量 ~几千 1.17亿 1,750亿

核心机制完全一样,只是规模不同。这就是 Scaling Law 的魅力------同一架构,参数越多能力越强。


15 总结

text 复制代码
- 数据预处理用户输入的文本
	- 分词: Tokenizer 将原始文本切分为子词级别的 Token,如 "unbelievable" → "un", "##believ", "##able"
	- 查词表: 根据 Token 字符串在词表中查找对应的整数编号(Token ID),如 "我","爱" → 5, 12
- 编码器
	- Input Embedding:用 Token ID 查嵌入表,把整数 ID 映射为 d_model 维稠密向量,让模型能在连续空间中处理文本
	- +位置编码
		- 位置编码意义:Transformer 并行处理所有词没有顺序概念,位置编码给每个位置一个独特的"指纹",让模型区分"我打你"和"你打我"
	- 6层
		- 多头自注意力
			- 计算QKV
				- Q: 输入 X 乘以权重矩阵 W_Q,表示"我在找什么信息"
				- K: 输入 X 乘以权重矩阵 W_K,表示"我能提供什么信息"
				- V: 输入 X 乘以权重矩阵 W_V,表示"我的实际内容是什么"
			- 计算 softmax(QK^T/√d_k)V
				- QK^T的意义:计算每个词的 Query 和所有词的 Key 的点积,得到词与词之间的关联度分数矩阵,点积越大说明两个词越相关
				- /√d_k的意义:当 d_k 较大时点积值会很大,导致 softmax 进入饱和区梯度消失;除以 √d_k 把分数缩回合理范围,保持梯度稳定
				- softmax的意义:QK^T 算出的注意力分数有三个问题------可正可负(负数无法作为权重)、无上界(某个词分数特别大时会完全压制其他词)、总和不确定(序列越长总分越大,导致加权求和后的输出幅度不稳定)。Softmax 通过 e^x 指数运算一次性解决这三个问题:所有值变成正数、归一化到和为1(无论序列多长输出幅度都稳定)、指数放大差异让强信号更突出,从而得到一组公平、可比较的注意力权重
				- softmax()V的意义:用归一化后的注意力权重对 V 加权求和,让每个词的输出变成"自己 + 关注的那些词的信息"的融合体,实现上下文感知
		- 残差连接+层归一化
			- 残差连接的操作:把子层输入直接加到子层输出上,即 Output = x + SubLayer(x)
			- 残差连接意义:保证原始信息不丢失,解决深层网络梯度消失问题,让注意力/FFN 只是"额外添加上下文"
			- 层归一化操作:对每个词的向量独立计算均值和方差,归一化到均值≈0、方差≈1,再用 γ 缩放、β 偏移
			- 层归一化意义:统一各位置向量的数值范围,稳定训练、加速收敛;推理时维持和训练一致的计算流程
		- 前馈网络
			- 操作:两层全连接,先 d_model→4×d_model 扩展,经 ReLU 激活,再 4×d_model→d_model 压缩回来
			- 意义:对每个位置独立做非线性变换;没有 ReLU 多层线性变换会坍缩成一层,模型丧失非线性表达能力。自注意力本质是加权求和(线性操作),只能决定"看哪些词",无法改变特征本身的表达。FFN 通过 ReLU 引入非线性,让模型能学到"如果特征A强且特征B弱,则激活特征C"这类复杂的条件逻辑。如果全程都是线性变换,无论堆多少层都等价于一层 y = Wx + b,只能画直线、切平面,无法拟合语言中大量的非线性规律(如多义词在不同上下文含义不同、语法规则的条件触发等)。一句话:注意力负责"信息路由",FFN 的非线性负责"特征加工",缺一不可
		- 残差连接+层归一化

- 数据预处理已经生成的文本
	- 分词: Tokenizer 将已生成的文本切分为 Token,与编码器使用同一个分词器
	- 查词表: 查找每个 Token 对应的 Token ID,与编码器共用同一个词表
- 解码器
	- Output Embedding:用 Token ID 查嵌入表得到向量,与编码器的 Input Embedding 共用同一张嵌入表
	- +位置编码
	- 6层
		- 掩码多头自注意力
			- 操作:和普通自注意力一样算 QK^T/√d_k,但加上三角掩码矩阵把未来位置的分数设为 -∞,softmax 后权重变 0
			- 意义:防止当前位置"偷看"未来的词,保证训练时的计算方式和推理时逐词生成一致
		- 残差连接+层归一化
		- 交叉多头自注意力:
			- KV 来自于编码器输出 H×W_K、H×W_V,Q 来自于解码器掩码多头自注意力的输出×W_Q
		- 残差连接+层归一化
		- 前馈网络
		- 残差连接+层归一化
	- Linear
		- 操作:Decoder 6层最终输出 × 权重矩阵 W (d_model × vocab_size) 得到 logits,每个维度对应词表中一个词的得分
			实际工程中的做法:
			- 训练时
			  不用 KV Cache
			  所有位置并行计算(本来就需要所有位置的 logits 算 loss)
			  线性层 n×vocab_size 全算,没有浪费
			- 推理时
			  用 KV Cache
			  每次只输入1个新词
			  Decoder 6层只处理1个位置
			  线性层只算1行
			  → 几乎零浪费
		- 意义:把 Decoder 的抽象向量映射到词表空间,给每个词打一个"匹配分数",充当"抽象向量→选词"的桥梁
	- Softmax:将 logits 转成概率分布(0~1、和为1),分数越高的词概率越大
	- 选词策略
		- temperature:控制概率分布的"陡峭程度",值越低分布越尖锐(更确定),值越高分布越平坦(更随机)
		- top_k:只从概率最高的 k 个词中采样,直接截断低概率词,k 越小越保守
		- top_p:从概率最高的词开始累加,直到累计概率达到 p(如 0.9)为止,只在这个范围内采样,比 top_k 更灵活
相关推荐
147API1 小时前
蒸馏模型版本升级怎么做,权重、评测器和服务配置一起管
人工智能·深度学习·蒸馏·模型蒸馏
cfm_29142 小时前
基于Binlog实现不停机数据库平滑迁移技术
运维·数据库·架构
迪康Defender2 小时前
AI 重构终端安全运营:智能分析中枢 AI Insight 模块架构与落地场景深度解析
运维·网络·人工智能·其他·安全·重构·架构
XLYcmy2 小时前
京东 算法实习一面 上
rnn·神经网络·llm·微调·transformer·训练·对齐
这个DBA有点耶4 小时前
从DBA到数据架构师(五):数据架构演进中的技术债务管理
数据库·程序人生·云原生·架构·dba·数据库管理员
沪上企服通4 小时前
高端财税的技术切面:从“可审计级旧账重建“看企业税务合规中台的架构演进
架构
dogstarhuang5 小时前
大模型 API 停服怎么办:用 API 网关实现多模型统一接入与可切换架构
人工智能·后端·架构·大模型·api·数字化转型·ai应用
过江龙8477 小时前
Java架构师的AI转型之路(中):Agent与编排体系实战
架构
ziyun6668887 小时前
Docker 容器化 Web 服务架构搭建与自动化运维项目
运维·docker·架构