🎯 课程主题
Transformer 模型的训练——超参数配置、训练流程与权重保存。
📝 核心知识点
1. 模型超参数
- 概念说明:Transformer 模型的核心配置参数。
- 关键细节:
d_model = 512:模型维度n_heads = 8:注意力头数n_layers = 6:编码器/解码器层数d_ff = 2048:FFN 隐藏层维度dropout = 0.1:神经元失活比例vocab_size = 32000:词表大小(必须与分词模型一致)
2. 训练配置
- 概念说明:训练过程的超参数设置。
- 关键细节:
batch_size = 32:批大小(12GB 显存)epochs = 30:训练轮次(约 30 轮收敛)learning_rate:学习率(可不调整)- 5070 显卡:约 1 小时/轮
- 4080 显卡:约 40 分钟/轮(batch=32)
- 显存占用:batch=32 时约 12GB
3. 特殊 Token 索引
- 概念说明:词表中的特殊标记。
- 关键细节:
<pad>:填充符索引(用于对齐)<bos>或<S>:起始符索引<eos>或</s>:结束符索引- 用于因果填充和序列控制
4. 训练流程
- 概念说明:模型训练的完整步骤。
- 关键细节:
- 加载训练集、验证集、测试集
- 转换为 DataLoader
- 创建模型并移至 GPU
- 定义损失函数(交叉熵)
- 定义优化器(Adam)
- 循环训练:前向传播 → 计算损失 → 反向传播 → 更新参数
- 每轮验证,保存最佳权重
5. 权重保存
- 概念说明:训练结果保存。
- 关键细节:
- 保存路径:
runs/目录下 - 保存
best.pt(最佳验证得分) - 保存
last.pt(最后一轮权重) - 可加载预训练权重继续训练
- 保存路径:
6. 训练结果
- 概念说明:训练过程中的指标变化。
- 关键细节:
- 训练 Loss:不断下降
- 验证 Loss:不断下降
- BLEU Score:不断上升
- 约 30 轮收敛,BLEU ≈ 26.3
🧮 核心公式与推导
交叉熵损失:
$$\mathcal{L} = -\sum_{t=1}^{T} \sum_{i=1}^{V} y_{t,i} \log \hat{y}_{t,i}$$
其中 $y_{t,i}$ 是真实标签的 one-hot 编码,$\hat{y}_{t,i}$ 是预测概率。
BLEU 评分:
$$\text{BLEU} = \text{BP} \cdot \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)$$
其中:
- $p_n$:n-gram 精确度
- $w_n = 1/N$:权重(通常 $N=4$)
- BP:长度惩罚因子
BLEU 评分等级:
- 0-20:较差翻译
- 20-40:一般翻译(能理解但有错误)
- 40-60:中等质量翻译
- 60-80:高质量翻译
- 80-100:极好翻译(几乎与参考一致)
🏗️ 模型架构与数据流向
💻 代码实战
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from config import Config
from model import make_model
from dataset import TranslationDataset, collate_fn
def train_model():
"""模型训练主函数"""
config = Config()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 1. 加载数据集
train_dataset = TranslationDataset(config.train_data)
valid_dataset = TranslationDataset(config.valid_data)
train_loader = DataLoader(
train_dataset, batch_size=config.batch_size,
shuffle=True, collate_fn=collate_fn
)
valid_loader = DataLoader(
valid_dataset, batch_size=config.batch_size,
shuffle=False, collate_fn=collate_fn
)
# 2. 创建模型
model = make_model(
src_vocab=config.src_vocab_size,
tgt_vocab=config.tgt_vocab_size,
N=config.n_layers,
d_model=config.d_model,
d_ff=config.d_ff,
h=config.n_heads,
dropout=config.dropout
).to(device)
# 3. 损失函数 (忽略padding位置)
criterion = nn.CrossEntropyLoss(
ignore_index=config.pad_idx,
label_smoothing=0.1
)
# 4. 优化器
optimizer = torch.optim.Adam(
model.parameters(),
lr=config.learning_rate,
betas=(0.9, 0.98),
eps=1e-9
)
# 5. 学习率调度器 (可选: warmup)
scheduler = torch.optim.lr_scheduler.LambdaLR(
optimizer, lr_lambda=lambda step: warmup_lr(step, config)
)
# 6. 训练循环
best_bleu = 0
for epoch in range(config.epochs):
# 训练阶段
model.train()
total_loss = 0
for batch in train_loader:
src = batch.src.to(device)
tgt = batch.tgt.to(device)
src_mask = batch.src_mask.to(device)
tgt_mask = batch.tgt_mask.to(device)
# 前向传播
output = model(src, tgt[:, :-1], src_mask, tgt_mask)
# 计算损失
loss = criterion(
output.reshape(-1, output.size(-1)),
tgt[:, 1:].reshape(-1)
)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()
total_loss += loss.item()
avg_loss = total_loss / len(train_loader)
# 验证阶段
val_loss, bleu_score = evaluate(model, valid_loader, criterion, device)
print(f"Epoch {epoch+1}/{config.epochs}")
print(f" Train Loss: {avg_loss:.4f}")
print(f" Val Loss: {val_loss:.4f}")
print(f" BLEU Score: {bleu_score:.2f}")
# 保存最佳模型
if bleu_score > best_bleu:
best_bleu = bleu_score
torch.save(model.state_dict(), 'runs/best.pt')
print(f" → 保存最佳模型 (BLEU: {bleu_score:.2f})")
# 保存最终模型
torch.save(model.state_dict(), 'runs/last.pt')
print(f"训练完成! 最佳BLEU: {best_bleu:.2f}")
def warmup_lr(step, config):
"""Warmup学习率调度"""
warmup_steps = 4000
if step < warmup_steps:
return step / warmup_steps
return 1.0 / (step ** 0.5)
if __name__ == "__main__":
train_model()
⚠️ 常见问题与避坑指南
- 词表大小必须与分词模型一致,否则维度不匹配
- batch_size 根据显存调整(12GB → 32,更小显存需减小)
- 约 30 轮收敛,BLEU ≈ 26.3
- 使用
ignore_index忽略 padding 位置的损失 - 建议使用 label smoothing(0.1)提升泛化能力
- 学习率 warmup 有助于训练稳定
💡 个人总结与延伸
模型训练是深度学习的核心环节。本课程使用 WMT 英中翻译数据,约 25 万行,训练 30 轮达到 BLEU 26.3。现代大模型训练需要更大规模数据、更多计算资源和更复杂的训练策略(如分布式训练、混合精度、梯度累积等)。