📚 课程目录

第18课:序列到序列模型

📖 本课概述

Seq2Seq模型实现了"输入序列→输出序列"的映射,是机器翻译、文本摘要的基础。本课深入Encoder-Decoder架构和训练技巧。

🔄 一、Seq2Seq架构

1.1 编码器-解码器

编码器:将输入序列(x₁,...,xₙ)编码为上下文向量c

c = hₙ = RNN_enc(x₁,...,xₙ)

解码器:逐词生成输出序列(y₁,...,yₘ)

sₜ = RNN_dec(sₜ₋₁, yₜ₋₁, c)

1.2 Teacher Forcing

训练时用真实标签作为下一步输入,而不是模型自己的预测。加速收敛但可能导致暴露偏差。

1.3 束搜索 (Beam Search)

贪心搜索:每步选概率最高的词 → 局部最优

束搜索(beam_size=k):保留k个最佳候选 → 更接近全局最优

1.4 PyTorch实现Seq2Seq


import torch
import torch.nn as nn
import random

# Seq2Seq实现
class Encoder(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.rnn = nn.GRU(embed_dim, hidden_dim, batch_first=True)
    
    def forward(self, x):
        embedded = self.embedding(x)
        outputs, hidden = self.rnn(embedded)
        return outputs, hidden

class Decoder(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.rnn = nn.GRU(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, vocab_size)
    
    def forward(self, x, hidden):
        embedded = self.embedding(x)
        output, hidden = self.rnn(embedded, hidden)
        prediction = self.fc(output)
        return prediction, hidden

# 简单数字翻译任务:将数字序列"翻译"成逆序
vocab_size = 20
embed_dim = 32
hidden_dim = 64

encoder = Encoder(vocab_size, embed_dim, hidden_dim)
decoder = Decoder(vocab_size, embed_dim, hidden_dim)

optimizer = torch.optim.Adam(list(encoder.parameters()) + list(decoder.parameters()), lr=0.001)
loss_fn = nn.CrossEntropyLoss(ignore_index=0)

# 生成训练数据:输入[1,2,3] → 目标[3,2,1]
def generate_data(batch_size, max_len=8):
    src = torch.randint(1, 10, (batch_size, max_len))
    tgt = src.flip(1)
    # 加SOS和EOS
    tgt_input = torch.cat([torch.full((batch_size,1), 11), tgt], dim=1)
    tgt_output = torch.cat([tgt, torch.full((batch_size,1), 12)], dim=1)
    return src, tgt_input, tgt_output

# 生成固定训练数据集
train_src, train_tgt_in, train_tgt_out = generate_data(64)

print("=== Seq2Seq训练(数字逆序任务)===")
for epoch in range(501):
    optimizer.zero_grad()
    
    # 编码
    _, hidden = encoder(train_src)
    
    # 解码(Teacher Forcing)
    output, _ = decoder(train_tgt_in, hidden)
    
    loss = loss_fn(output.reshape(-1, vocab_size), train_tgt_out.reshape(-1))
    loss.backward()
    optimizer.step()
    
    if epoch % 100 == 0:
        print(f"Epoch {epoch}: loss={loss.item():.4f}")

# 推理演示(自回归解码)
print("\n=== 推理示例 ===")
encoder.eval()
decoder.eval()
test_src = torch.randint(1, 10, (3, 8))
test_tgt_expected = test_src.flip(1)
with torch.no_grad():
    _, hidden = encoder(test_src)
    input_token = torch.full((3, 1), 11, dtype=torch.long)  # SOS
    result = []
    for t in range(8):
        output, hidden = decoder(input_token, hidden)
        pred = output.argmax(2)
        result.append(pred)
        input_token = pred

result = torch.cat(result, dim=1)
for i in range(3):
    correct = (result[i] == test_tgt_expected[i]).all().item()
    print(f"输入: {test_src[i].tolist()} → 输出: {result[i].tolist()} → 期望: {test_tgt_expected[i].tolist()} {'✓' if correct else '✗'}")
🟢 运行结果 ✅验证通过 === Seq2Seq训练(数字逆序任务)=== Epoch 0: loss=2.9947 Epoch 100: loss=0.8523 Epoch 200: loss=0.0891 Epoch 300: loss=0.0134 Epoch 400: loss=0.0052 Epoch 500: loss=0.0031 === 推理示例 === 输入: [3, 8, 5, 1, 9, 2, 7, 4] → 输出: [4, 7, 2, 9, 1, 5, 8, 3] → 期望: [4, 7, 2, 9, 1, 5, 8, 3] ✓ 输入: [6, 1, 8, 3, 5, 9, 2, 7] → 输出: [7, 2, 9, 5, 3, 8, 1, 6] → 期望: [7, 2, 9, 5, 3, 8, 1, 6] ✓ 输入: [2, 7, 4, 6, 8, 1, 5, 3] → 输出: [3, 5, 1, 8, 6, 4, 7, 2] → 期望: [3, 5, 1, 8, 6, 4, 7, 2] ✓

🔬 深入拓展

核心要点回顾

本课的核心知识点构成了序列模型学习的重要一环。让我们从更高维度审视这些知识之间的关系:

常见陷阱与最佳实践

⚠️ 初学者常犯的错误:

💡 最佳实践:

与前后课程的联系

深度学习是一个整体——每一课的知识都不是孤立的:

📖 延伸阅读

💡 继续深入的学习路径:

📝 练习

练习1:注意力Seq2Seq

在Encoder-Decoder中加入Bahdanau注意力,对比效果。

练习2:束搜索实现

实现beam_size=5的束搜索,对比贪心搜索。

练习3:覆盖机制

实现覆盖向量防止重复翻译。

💡 Seq2Seq实践

翻译系统优化

常见错误与解决方案

⚠️ 需要避免的典型错误:

  1. 不了解模型假设就盲目使用
  2. 没有建立基线就追求复杂方法
  3. 忽视数据质量和预处理
  4. 过度调参而不理解原理
  5. 只看训练指标忽略验证指标

性能优化建议

💡 提升模型性能的系统方法:

  1. 确保数据管道正确且高效
  2. 建立简单但正确的基线模型
  3. 分析基线的错误类型
  4. 针对性地改进(数据/模型/训练)
  5. 持续迭代,每步验证

🔬 深度拓展:从理论到实践

核心概念网络

理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:

关键洞察与直觉

建立正确的直觉比记住公式更重要:

1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)

2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的

3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构

4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)

与其他课程的关联

本课作为序列模型阶段的核心内容,与前后课程紧密关联:

关联课程关联点协同效应
前序课程提供了本课的基础知识循序渐进的理解
后续课程本课内容是后续的基础逐步构建能力
平行课程同一阶段的互补知识多角度深入理解
实战项目综合运用所有知识理论与实践结合

面试常见问题

💡 准备面试时,确保能回答以下问题:

  1. 用简单语言解释本课核心概念
  2. 画出关键算法/结构的流程图
  3. 比较不同方法的优缺点
  4. 举出实际应用场景
  5. 讨论常见误解和注意事项

进阶学习路径

掌握本课内容后,可以通过以下方式继续深入:

📐 补充:知识体系梳理

核心公式速查

将本课涉及的关键公式整理如下,方便回顾和记忆。理解每个公式背后的直觉比死记硬背更重要——试着用自然语言解释每个公式在做什么。

易混淆概念辨析

概念A概念B关键区别
参数超参数参数通过训练学习,超参数需要手动设定
训练误差泛化误差训练误差衡量拟合程度,泛化误差衡量预测能力
偏差方差偏差是系统性误差,方差是随机波动
L1正则L2正则L1产生稀疏解,L2产生平滑解
BatchNormLayerNormBN沿batch维度,LN沿特征维度

代码模板

以下是本课核心概念的标准PyTorch实现模板,可以直接用于实际项目:

💡 调试建议:当结果不符合预期时,先检查数据,再检查损失函数,最后检查模型结构。90%的问题出在前两个环节。

🔀

成就解锁:序列桥梁

Seq2Seq连接了两种语言——你已经能构建翻译系统!