🎯 课程主题
Transformer 模型的最终实现——将所有模块组装为完整模型。
📝 核心知识点
1. 模型组装流程
- 概念说明:将编码器、解码器、生成器等模块组合为完整 Transformer。
- 关键细节:
- 深度拷贝(deepcopy)复用模块,保证各层参数独立
- 编码器:传入 EncoderLayer + MultiHeadAttention + FFN
- 解码器:传入 DecoderLayer + 3 个注意力模块 + FFN
- 输入层:词嵌入 + 位置编码(源语言和目标语言各一套)
- 输出层:生成器
2. 前向传播流程
- 概念说明:完整模型的数据流向。
- 关键细节:
- 源语言输入 → 词嵌入 + 位置编码 → 编码器 → 编码器输出
- 目标语言输入 → 词嵌入 + 位置编码 → 解码器(接收编码器输出)→ 解码器输出
- 解码器输出 → 生成器 → 词概率分布
3. 参数初始化
- 概念说明:模型参数的初始化方式。
- 关键细节:
- 使用 Xavier 初始化(Glorot 初始化)
- 保证训练初期的梯度稳定
- 初始化后放入设备(GPU)
4. 模块复用与参数独立
- 概念说明:通过 deepcopy 实现模块复用但参数独立。
- 关键细节:
- 多头注意力模块定义一次,通过 deepcopy 复制
- 编码器的自注意力与解码器的注意力参数不同
- 解码器的因果注意力与交叉注意力参数不同
- 每层的 FFN 参数也不同
🧮 核心公式与推导
完整 Transformer 前向传播:
$$X_{enc} = \text{PositionalEncoding}(\text{Embedding}(src) \times \sqrt{d_{model}})$$
$$M = \text{Encoder}(X_{enc}, src_mask)$$
$$X_{dec} = \text{PositionalEncoding}(\text{Embedding}(tgt) \times \sqrt{d_{model}})$$
$$H = \text{Decoder}(X_{dec}, M, src_mask, tgt_mask)$$
$$P(y) = \text{Generator}(H) = \text{Softmax}(\text{Linear}(H))$$
参数初始化(Xavier):
$$W_{ij} \sim U\left[-\sqrt{\frac{6}{n_{in} + n_{out}}}, \sqrt{\frac{6}{n_{in} + n_{out}}}\right]$$
🏗️ 模型架构与数据流向
💻 代码实战
import torch
import torch.nn as nn
import copy
def clones(module, N):
"""深度拷贝模块N次, 保证参数独立"""
return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])
class EncoderDecoder(nn.Module):
"""完整的Transformer模型"""
def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
"""
encoder: 编码器模块
decoder: 解码器模块
src_embed: 源语言嵌入(词嵌入+位置编码)
tgt_embed: 目标语言嵌入(词嵌入+位置编码)
generator: 生成器
"""
super().__init__()
self.encoder = encoder
self.decoder = decoder
self.src_embed = src_embed
self.tgt_embed = tgt_embed
self.generator = generator
def forward(self, src, tgt, src_mask, tgt_mask):
"""
src: [batch, src_len] 源语言token IDs
tgt: [batch, tgt_len] 目标语言token IDs
src_mask: 源语言填充掩码
tgt_mask: 目标语言因果掩码
return: [batch, tgt_len, vocab_size] 词概率分布
"""
# 1. 编码器: 源语言 → 编码器输出
memory = self.encode(src, src_mask)
# 2. 解码器: 目标语言 + 编码器输出 → 解码器输出
output = self.decode(memory, src_mask, tgt, tgt_mask)
# 3. 生成器: 解码器输出 → 词概率分布
return self.generator(output)
def encode(self, src, src_mask):
"""编码器前向传播"""
return self.encoder(self.src_embed(src), src_mask)
def decode(self, memory, src_mask, tgt, tgt_mask):
"""解码器前向传播"""
return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)
def make_model(src_vocab, tgt_vocab, N=6, d_model=512, d_ff=2048,
h=8, dropout=0.1):
"""
构建完整Transformer模型
src_vocab: 源语言词表大小
tgt_vocab: 目标语言词表大小
N: 编码器/解码器层数 (默认6)
d_model: 模型维度 (默认512)
d_ff: FFN隐藏层维度 (默认2048)
h: 注意力头数 (默认8)
dropout: dropout比例 (默认0.1)
"""
c = copy.deepcopy
# 1. 创建多头注意力模块 (用于复用)
attn = MultiHeadedAttention(h, d_model)
# 2. 创建前馈神经网络模块
ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# 3. 创建位置编码模块
position = PositionalEncoding(d_model, dropout)
# 4. 构建完整模型
model = EncoderDecoder(
# 编码器: 6层EncoderLayer
encoder=Encoder(
EncoderLayer(d_model, c(attn), c(ff), dropout), N
),
# 解码器: 6层DecoderLayer
decoder=Decoder(
DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N
),
# 源语言嵌入: 词嵌入 + 位置编码
src_embed=nn.Sequential(
TokenEmbedding(src_vocab, d_model), c(position)
),
# 目标语言嵌入: 词嵌入 + 位置编码
tgt_embed=nn.Sequential(
TokenEmbedding(tgt_vocab, d_model), c(position)
),
# 生成器
generator=Generator(d_model, tgt_vocab)
)
# 5. 参数初始化 (Xavier初始化)
for p in model.parameters():
if p.dim() > 1:
nn.init.xavier_uniform_(p)
return model
# 使用示例
if __name__ == "__main__":
src_vocab = 32000 # 英文词表
tgt_vocab = 32000 # 中文词表
# 构建模型
model = make_model(src_vocab, tgt_vocab, N=6, d_model=512, h=8)
# 移动到GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
# 模拟输入
src = torch.randint(0, src_vocab, (2, 10)).to(device) # 英文
tgt = torch.randint(0, tgt_vocab, (2, 8)).to(device) # 中文
# 前向传播
output = model(src, tgt, src_mask=None, tgt_mask=None)
print(f"源语言输入: {src.shape}")
print(f"目标语言输入: {tgt.shape}")
print(f"输出概率分布: {output.shape}") # [2, 8, 32000]
# 查看模型参数量
num_params = sum(p.numel() for p in model.parameters())
print(f"模型参数量: {num_params:,}") # 约40M-60M
⚠️ 常见问题与避坑指南
- 使用
deepcopy复制模块,保证各层参数独立 - 编码器和解码器的注意力模块参数不同(通过 deepcopy 实现)
- 词表大小必须与分词模型一致
- Xavier 初始化对训练稳定性很重要
- 模型参数量约 40M-60M(取决于词表大小)
💡 个人总结与延伸
完整 Transformer 模型的组装体现了模块化设计的优势。通过组合基础模块(注意力、FFN、残差、归一化),构建出强大的 seq2seq 模型。现代大模型在此基础上增加了更多优化(如 Pre-Norm、RoPE、SwiGLU、MQA 等),但基本架构思想不变。