Transformer:现代 AI 的地基
这一篇是整条路线的枢纽。 懂了它,L4 的大模型原理、L5 的 RAG 与上下文工程都会变得自然。
一句话
Transformer = 自注意力 + 前馈网络 + 残差 + LayerNorm,层层堆叠。
原文标题 Attention Is All You Need(2017)—— 它把 RNN 从序列建模里彻底移除了。
整体结构
输入 token ids
↓ Embedding + 位置编码
┌──────────────────────────────┐
│ × N 层 │
│ ├─ LayerNorm │
│ ├─ 多头自注意力 │
│ ├─ 残差 + │
│ ├─ LayerNorm │
│ ├─ 前馈网络 FFN │
│ └─ 残差 + │
└──────────────────────────────┘
↓ LayerNorm
↓ Linear → vocab 维度
↓ Softmax → 下一个 token 的概率其中「× N 层」里,每一层的内部就是这条链路:
1
LayerNorm先归一化,再进子层
2
多头自注意力token 之间交换信息
3
残差相加x + Attn(LN(x))
4
LayerNorm再归一化一次
5
FFN逐 token 独立变换
6
残差相加回到主干,进下一层
| 编码器(BERT) | 解码器(GPT) |
|---|---|
| 双向注意力(能看左右) | 因果注意力(只能看左边) |
| 适合理解类任务 | 适合生成类任务 |
今天的 LLM 几乎都是 decoder-only。
一、位置编码:没有它,Transformer 是「词袋」
自注意力本身不知道顺序:打乱输入,输出的集合不变。必须额外注入位置信息。
| 方案 | 说明 | 代表 |
|---|---|---|
| 正弦位置编码 | 固定公式,无参数 | 原始 Transformer |
| 可学习位置嵌入 | 每个位置一个向量 | BERT、GPT-2 |
| RoPE(旋转位置编码) | 把位置信息旋转进 Q/K | LLaMA、Qwen、绝大多数现代 LLM |
| ALiBi | 用距离惩罚偏置 | 部分长文本模型 |
python
import math, torch
def sinusoidal_pe(max_len, d_model):
pe = torch.zeros(max_len, d_model)
pos = torch.arange(max_len).unsqueeze(1).float()
div = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(pos * div)
pe[:, 1::2] = torch.cos(pos * div)
return pe # (max_len, d_model)为什么 RoPE 重要
它让「相对位置」自然编码进点积,且便于外推到更长上下文(配合插值方法)。你在 L4 会看到「上下文扩展」的技术都建立在它之上。
二、前馈网络 FFN:参数最多的部分
python
class FFN(nn.Module):
def __init__(self, d_model, d_ff=None, dropout=0.1):
super().__init__()
d_ff = d_ff or d_model * 4
self.net = nn.Sequential(
nn.Linear(d_model, d_ff), nn.GELU(),
nn.Dropout(dropout), nn.Linear(d_ff, d_model))
def forward(self, x): return self.net(x)注意:一个 Transformer 层里,FFN 的参数通常是注意力的 2 倍(4d² vs 4d²,但 FFN 中间层 4d 使之为 8d²)。
在 LLaMA 类模型里,FFN 用的是 SwiGLU(三个矩阵 + 门控),参数量更多: FFN(x) = (SiLU(xW₁) ⊙ xW₃)W₂,中间维度约为 8/3 d。
三、残差与 LayerNorm:能训深的原因
python
class Block(nn.Module):
def __init__(self, d_model, n_heads, dropout=0.1):
super().__init__()
self.norm1 = nn.LayerNorm(d_model)
self.attn = MultiHeadAttention(d_model, n_heads) # 见序列一章
self.norm2 = nn.LayerNorm(d_model)
self.ffn = FFN(d_model)
self.drop = nn.Dropout(dropout)
def forward(self, x, mask=None):
# Pre-LN:先归一化再进子层(现代标准,训练更稳)
x = x + self.drop(self.attn(self.norm1(x), self.norm1(x), self.norm1(x), mask))
x = x + self.drop(self.ffn(self.norm2(x)))
return x| 设计 | Post-LN(原始) | Pre-LN(现代) |
|---|---|---|
| 顺序 | LN(x + F(x)) | x + F(LN(x)) |
| 训练稳定性 | 需精心 warmup | 稳定得多 |
| 采用者 | BERT | GPT-2 之后全部 |
LayerNorm vs BatchNorm:LN 在特征维归一化,与 batch 大小无关,序列长度不一也不受影响 —— 这就是 NLP 不用 BN 的原因。
四、把 Transformer 拼起来
python
class MiniGPT(nn.Module):
def __init__(self, vocab, d_model=256, n_heads=8, n_layers=4, max_len=512):
super().__init__()
self.tok_emb = nn.Embedding(vocab, d_model)
self.pos_emb = nn.Embedding(max_len, d_model)
self.blocks = nn.ModuleList([Block(d_model, n_heads) for _ in range(n_layers)])
self.norm = nn.LayerNorm(d_model)
self.head = nn.Linear(d_model, vocab, bias=False)
def forward(self, ids):
B, T = ids.shape
pos = torch.arange(T, device=ids.device)
x = self.tok_emb(ids) + self.pos_emb(pos)
mask = torch.tril(torch.ones(T, T, device=ids.device)).bool()
for blk in self.blocks:
x = blk(x, mask)
return self.head(self.norm(x)) # (B, T, vocab) logits训练目标极其简单:给定前 t 个 token,预测第 t+1 个。
python
logits = model(ids[:, :-1]) # 输入去掉最后一个
loss = F.cross_entropy(
logits.reshape(-1, vocab),
ids[:, 1:].reshape(-1), # 标签是右移一位
ignore_index=pad_id)GPT 的全部魔法就在这四行。 规模上去了,能力就涌现了 —— 这就是 L4。
五、复杂度与为什么需要优化
| 操作 | 复杂度 | 说明 |
|---|---|---|
| 自注意力 | $O(T^2 d)$ | 序列长度平方,长文本的瓶颈 |
| FFN | $O(T d^2)$ | 与长度线性 |
| 显存(注意力矩阵) | $O(T^2 \times \text{heads})$ | 长上下文的主要显存来源 |
对应的优化(L4 展开):
| 问题 | 技术 |
|---|---|
| 注意力太慢/太占显存 | FlashAttention(分块计算,不落盘完整矩阵) |
| 超长序列 | 稀疏/滑窗注意力、线性注意力、Mamba |
| 推理重复计算 | KV Cache |
| 部署吞吐 | PagedAttention / vLLM / 连续批处理 |
六、常见变体对照
| 模型族 | 结构 | 训练目标 | 擅长 |
|---|---|---|---|
| BERT | Encoder-only | 掩码语言模型(MLM) | 分类、抽取、检索(embedding) |
| T5 | Encoder-Decoder | Span 去噪 | 翻译、摘要 |
| GPT / LLaMA / Qwen | Decoder-only | 下一 token 预测 | 生成、通用对话、Agent |
| ViT | Encoder(图像切 patch) | 分类 / 对比 | 视觉 |
| CLIP | 双塔对比 | 图文对比 | 跨模态检索 |
为什么 decoder-only 赢了
- 训练目标统一(就是预测下一个词),任何文本都能用
- 推理时天然支持「输入即上下文」,无需改结构
- 规模化时 scaling law 表现最好
七、动手验证(强烈建议做)
- 实现上面的 MiniGPT,在一个小语料(如莎士比亚/中文小说)上训练
- 观察 loss 从 ~4.2(随机)降到 ~2.0 左右
- 用温度参数采样生成,感受
temperature与top_p的影响(见 Prompt 工程) - 可视化第 1 层的注意力权重,看它学到了什么
做完这一步,你对大模型的理解会超过 80% 只会调 API 的人。