🎯 课程主题
Transformer 架构总结——整合所有基础模块,构建完整的 Encoder-Decoder 模型。
📝 核心知识点
1. 完整架构概览
- 概念说明:Transformer 由编码器和解码器各 $N=6$ 层堆叠构成。
- 关键细节:
- 编码器:6 层,每层包含自注意力 + FFN
- 解码器:6 层,每层包含因果掩码注意力 + 交叉注意力 + FFN
- 编码器输出作为每层解码器的输入
2. 编码器单层结构
- 概念说明:编码器每层包含两个子层。
- 关键细节:
- 多头自注意力(可带填充掩码)
- 输入 → QKV 映射 → 注意力得分 → 信息融合
- Add & Norm(残差连接 + 层归一化)
- 前馈神经网络(FFN)
- Add & Norm
- 输出作为下一层编码器的输入
- 多头自注意力(可带填充掩码)
3. 解码器单层结构
- 概念说明:解码器每层包含三个子层。
- 关键细节:
- 因果掩码多头注意力
- 输入起始符/已生成词 → QKV 映射 → 因果掩码注意力
- Add & Norm
- 交叉注意力
- Q 来自解码器,K/V 来自编码器输出
- Add & Norm
- 前馈神经网络(FFN)
- Add & Norm
- 因果掩码多头注意力
4. 输出层
- 概念说明:解码器输出经线性层和 Softmax 得到词概率。
- 关键细节:
- 线性全连接层:映射到词表大小 $V$
- Softmax:输出每个词的概率
- 取概率最大的词作为输出
5. 推理与训练流程
- 概念说明:推理是串行生成,训练是并行计算。
- 关键细节:
- 推理:
- 编码器处理源语言 → 编码器信息
- 解码器输入
<S>→ 输出第一个词 - 将输出加入输入 → 生成下一个词
- 重复直到结束符
- 训练:
- 输入源语言和正确标签
- 并行计算所有位置的 Loss
- 反向传播更新所有参数
- 推理:
6. 编码器到解码器的连接方式
- 概念说明:编码器输出传入每层解码器的交叉注意力。
- 关键细节:
- 论文方式:编码器最终输出作为每层解码器的输入
- 其他可能:不同层编码器输出对应不同层解码器
- 论文采用统一方式
🧮 核心公式与推导
编码器单层:
$$\text{Enc}(X) = \text{FFN}(\text{LN}(\text{MHA}(X) + X)) + \text{LN}(\text{MHA}(X) + X)$$
解码器单层:
$$H_1 = \text{LN}(\text{MaskedMHA}(X_{dec}) + X_{dec})$$
$$H_2 = \text{LN}(\text{CrossAttn}(H_1, \text{Enc}_{out}) + H_1)$$
$$\text{Dec}(X_{dec}) = \text{LN}(\text{FFN}(H_2) + H_2)$$
输出层:
$$P(y_t) = \text{Softmax}(\text{Linear}(\text{Dec}_t))$$
🏗️ 模型架构与数据流向
完整数据流向:
源语言 → 词嵌入+位置编码 → [编码器×6] → 编码器输出
↓
起始符 → 词嵌入+位置编码 → [解码器×6] → 线性层 → Softmax → 输出词
↑
编码器输出传入每层解码器
💻 代码实战
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class Transformer(nn.Module):
"""完整的Transformer模型"""
def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512,
n_heads=8, n_layers=6, d_ff=2048, dropout=0.1):
super().__init__()
# 输入嵌入
self.src_embedding = nn.Embedding(src_vocab_size, d_model)
self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model)
self.positional_encoding = PositionalEncoding(d_model)
# 编码器和解码器
self.encoder = nn.ModuleList([
EncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers)
])
self.decoder = nn.ModuleList([
DecoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers)
])
# 输出层
self.generator = nn.Linear(d_model, tgt_vocab_size)
def encode(self, src, src_mask):
"""编码器前向传播"""
x = self.positional_encoding(self.src_embedding(src))
for layer in self.encoder:
x = layer(x, src_mask)
return x
def decode(self, memory, tgt, src_mask, tgt_mask):
"""解码器前向传播"""
x = self.positional_encoding(self.tgt_embedding(tgt))
for layer in self.decoder:
x = layer(x, memory, src_mask, tgt_mask)
return x
def forward(self, src, tgt, src_mask, tgt_mask):
"""完整前向传播"""
memory = self.encode(src, src_mask)
output = self.decode(memory, tgt, src_mask, tgt_mask)
return self.generator(output)
# 使用示例
model = Transformer(
src_vocab_size=10000, # 源语言词表
tgt_vocab_size=8000, # 目标语言词表
d_model=512,
n_heads=8,
n_layers=6
)
# src: [batch, src_len], tgt: [batch, tgt_len]
src = torch.randint(0, 10000, (2, 5)) # "我是一条狗"
tgt = torch.randint(0, 8000, (2, 4)) # "<S> I am a"
output = model(src, tgt, src_mask=None, tgt_mask=None)
print(output.shape) # torch.Size([2, 4, 8000]) → 每个位置输出词表概率
⚠️ 常见问题与避坑指南
- 编码器 $N=6$ 层,解码器 $N=6$ 层(论文设置)
- 编码器输出传入每层解码器的交叉注意力
- 推理时编码器只需计算一次,解码器逐步生成
- 训练时并行计算所有位置 Loss
- 三种注意力机制本质相同,区别在输入来源和掩码类型
💡 个人总结与延伸
Transformer 通过模块化设计(注意力 + FFN + 残差 + 层归一化)实现了强大的序列建模能力。Encoder-Decoder 架构适用于翻译等 seq2seq 任务,而 Decoder-only 架构(GPT 系列)已成为当前大语言模型的主流。理解完整架构是后续代码实战的基础,现代大模型在此基础上增加了更多优化(如 Pre-Norm、RoPE、SwiGLU 等)。