Transformer
Transformer 架构
Transformer 是现代自然语言处理(NLP)和多模态大模型的基石。它摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),完全依赖 Attention 机制 来捕捉序列中的长距离依赖。
前置知识: 关于 Attention 机制的详细原理(Scaled Dot-Product, Multi-Head, KV Cache),请查阅 Attention 机制 笔记。
1. 宏观概览 (Overview)
Transformer 本质上是一个 Sequence-to-Sequence (Seq2Seq) 的映射机器。
🏗️ 整体架构:Encoder-Decoder
原始的 Transformer 包含两个主要部分:
- Encoder (编码器):负责 “理解”。它读取输入序列,通过层层抽象,将其转化为一个富含语义的连续表示(Context Vector)。
- Decoder (解码器):负责 “生成”。它利用 Encoder 提供的语义信息,并结合已经生成的历史词,逐个预测下一个词。
数据流向:
Input (X) -> [Encoder] -> Context (K, V) -> [Decoder] -> Output (Y)
2. 输入层 (Input Layer)
在进入 Transformer 模块之前,原始文本需要经过预处理。
2.1 Embedding (词嵌入)
将离散的 Token ID 转换为稠密的向量(Dense Vector)。
$$ x*{embedding} \in \mathbb{R}^{d*{model}} $$2.2 Positional Encoding (位置编码)
由于 Transformer 的 Attention 机制是并行处理的,本身不具备位置感(即打乱句子顺序,Attention 结果不变)。为了让模型理解词序(“Tom hits Jerry” vs “Jerry hits Tom”),必须显式注入位置信息。
- 绝对位置编码 (Sinusoidal): 原始论文使用正弦/余弦函数叠加直接相加。 $$ PE(pos, 2i) = \sin(pos / 10000^{2i/d\_{model}}) $$
- 相对位置编码 (RoPE/ALiBi): 现代 LLM(如 LLaMA)大多使用旋转位置编码(RoPE),效果更好,且具有外推性。
3. 编码器 (Encoder): 理解引擎
Encoder 由 $N$ 个相同的层(Block)堆叠而成。每一层包含两个子层:
- Multi-Head Self-Attention:
- 机制: 详见 Attention.md。
- 作用: 让每个词都能"看"到句子中的其他词,建立上下文联系。
- Feed-Forward Network (FFN):
- 也称为 MLP。对每个位置的向量独立进行非线性变换,增加模型的表达能力。
- 现代变体常用 SwiGLU 激活函数。
Add & Norm: 每个子层后都有残差连接(Residual Connection)和层归一化(LayerNorm)。
$$ \text{Output} = \text{LayerNorm}(x + \text{SubLayer}(x)) $$(注: 现代 LLM 多采用 Pre-Norm 结构,即先 Norm 再进子层,训练更稳定)
4. 解码器 (Decoder): 生成引擎
Decoder 同样由 $N$ 个层堆叠而成,但结构略有不同,包含三个子层:
- Masked Self-Attention:
- 关键点: Mask (掩码)。在预测第 $t$ 个词时,绝不能看到 $t$ 之后的词。因此 Attention 矩阵的上三角部分被置为 $-\infty$。
- Cross-Attention (交叉注意力):
- 机制: 详见 交叉注意力。
- 作用: 连接 Encoder 和 Decoder。Query 来自 Decoder(当前生成的搜索意图),Key/Value 来自 Encoder(源句子的语义信息)。
- Feed-Forward Network (FFN): 同 Encoder。
5. 训练 vs 推理 (Training vs Inference)
| 特性 | 训练阶段 (Training) | 推理阶段 (Inference) |
|---|---|---|
| 模式 | Teacher Forcing | Autoregressive (自回归) |
| 输入 | 一次性输入完整的正确目标序列 (Ground Truth)。 | 初始通过 BOS,之后每步输入上一步生成的词。 |
| 并行性 | 高度并行。所有 Token 的 Loss 可以同时计算。 | 串行。必须等 $t$ 生成完才能生成 $t+1$。 |
| 速度 | 快 (得益于矩阵运算)。 | 慢 (受限于解码步数)。需使用 KV Cache 加速。 |
6. 架构演变 (Architecture Variants)
随着发展,Transformer 分化出了三条主要路线:
- Encoder-Only (双向):
- 代表: BERT, RoBERTa。
- 特点: 能同时看到上下文,擅长理解任务(分类、NER)。
- Decoder-Only (自回归/GPT):
- 代表: GPT 系列, LLaMA, Qwen。
- 特点: 只能看到历史,擅长生成任务。这是目前 LLM 的绝对主流。
- Encoder-Decoder (序列到序列):
- 代表: T5, BART, Original Transformer。
- 特点: 经典的翻译/摘要架构。
7. PyTorch 实现
以下是一个简化的 Decoder-Only Block 实现(GPT 风格),展示了最核心的组件。
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class CausalSelfAttention(nn.Module):
def __init__(self, d_model, n_head, max_len=1024):
super().__init__()
self.d_k = d_model // n_head
self.n_head = n_head
# Q, K, V 映射层
self.c_attn = nn.Linear(d_model, 3 * d_model)
# 输出投影层
self.c_proj = nn.Linear(d_model, d_model)
# Causal Mask: 下三角矩阵,保证不能看到未来
self.register_buffer("bias", torch.tril(torch.ones(max_len, max_len))
.view(1, 1, max_len, max_len))
def forward(self, x):
B, T, C = x.size() # Batch, SeqLen, Dim
# 1. 计算 Q, K, V
qkv = self.c_attn(x)
q, k, v = qkv.split(C, dim=2)
# 2. Reshape 为多头: (B, n_head, T, d_k)
q = q.view(B, T, self.n_head, self.d_k).transpose(1, 2)
k = k.view(B, T, self.n_head, self.d_k).transpose(1, 2)
v = v.view(B, T, self.n_head, self.d_k).transpose(1, 2)
# 3. Scaled Dot-Product Attention
att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(self.d_k))
# 4. Apply Mask (关键步:将未来的位置设为负无穷)
att = att.masked_fill(self.bias[:,:,:T,:T] == 0, float('-inf'))
att = F.softmax(att, dim=-1)
# 5. 加权求和
y = att @ v
y = y.transpose(1, 2).contiguous().view(B, T, C)
return self.c_proj(y)
class MLP(nn.Module):
def __init__(self, d_model):
super().__init__()
self.c_fc = nn.Linear(d_model, 4 * d_model)
self.act = nn.GELU() # 现代模型常用 GeLU 或 SwiGLU
self.c_proj = nn.Linear(4 * d_model, d_model)
def forward(self, x):
return self.c_proj(self.act(self.c_fc(x)))
class TransformerBlock(nn.Module):
def __init__(self, d_model, n_head):
super().__init__()
self.ln1 = nn.LayerNorm(d_model)
self.attn = CausalSelfAttention(d_model, n_head)
self.ln2 = nn.LayerNorm(d_model)
self.mlp = MLP(d_model)
def forward(self, x):
# Pre-Norm 结构:Norm -> Layer -> Add
x = x + self.attn(self.ln1(x))
x = x + self.mlp(self.ln2(x))
return x