1. Transformer Encoder-Decoder 架构概述
在自然语言处理领域,Transformer 架构已经成为序列到序列(Seq2Seq)任务的事实标准。Encoder-Decoder 结构作为 Transformer 的核心设计,广泛应用于机器翻译、文本摘要、问答系统等场景。这种架构将模型分为两个主要部分:编码器负责理解输入序列,解码器负责生成输出序列。
1.1 架构设计理念
Encoder-Decoder 架构的核心思想是将序列处理任务分解为两个阶段:
- 编码阶段:将输入序列(如源语言句子)转换为富含语义的中间表示
- 解码阶段:基于编码器的输出和已生成的部分序列,自回归地生成目标序列
这种设计的关键优势在于:
- 编码器可以专注于理解输入内容的全局信息
- 解码器能够逐步构建输出,同时参考源序列信息
- 两个组件可以独立优化,但又通过注意力机制紧密耦合
1.2 典型应用场景
Encoder-Decoder 架构特别适合以下任务类型:
- 机器翻译:将一种语言的句子转换为另一种语言
- 文本摘要:从长文本生成简洁的摘要
- 对话系统:根据对话历史生成合适的回复
- 代码生成:从自然语言描述生成程序代码
- 语音识别:将语音信号转换为文字
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编码器部分详解
2.1 输入处理流程
编码器的输入处理包含三个关键步骤:
2.1.1 Tokenization 分词
分词过程将原始文本转换为模型可处理的token序列。常见策略包括:
- 词级分词:将文本按词语划分
- 子词分词:使用BPE、WordPiece等算法处理罕见词
- 字符级分词:将文本分解为单个字符
分词后的输出是一个整数ID序列,长度记为L_enc。
2.1.2 Embedding 嵌入
嵌入层将token ID映射为稠密向量表示:
python复制embedding_matrix = nn.Embedding(vocab_size, d_model)
token_embeddings = embedding_matrix(token_ids) # shape: [L_enc, d_model]
关键特性:
- 可学习的参数矩阵
- 语义相似的token在嵌入空间距离相近
- 维度d_model通常为512-12288之间
2.1.3 位置编码
由于Transformer不包含循环结构,需要显式添加位置信息:
python复制class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe)
def forward(self, x):
x = x + self.pe[:x.size(0)]
return x
2.2 编码器层结构
每个编码器层包含两个主要子层:
2.2.1 多头自注意力机制
python复制class MultiHeadAttention(nn.Module):
def __init__(self, d_model, h):
super().__init__()
self.d_k = d_model // h
self.h = h
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x):
# 线性变换得到Q,K,V
Q = self.W_q(x) # [L_enc, d_model]
K = self.W_k(x) # [L_enc, d_model]
V = self.W_v(x) # [L_enc, d_model]
# 分割为多头
Q = Q.view(Q.size(0), self.h, self.d_k) # [L_enc, h, d_k]
K = K.view(K.size(0), self.h, self.d_k)
V = V.view(V.size(0), self.h, self.d_k)
# 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
attn = F.softmax(scores, dim=-1)
# 加权求和
context = torch.matmul(attn, V) # [L_enc, h, d_k]
context = context.view(context.size(0), -1) # [L_enc, d_model]
# 输出投影
output = self.W_o(context)
return output
2.2.2 前馈神经网络
python复制class FeedForward(nn.Module):
def __init__(self, d_model, d_ff=2048):
super().__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
def forward(self, x):
return self.linear2(F.relu(self.linear1(x)))
2.2.3 残差连接与层归一化
python复制class EncoderLayer(nn.Module):
def __init__(self, d_model, h):
super().__init__()
self.self_attn = MultiHeadAttention(d_model, h)
self.ffn = FeedForward(d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
def forward(self, x):
# 自注意力子层
attn_output = self.self_attn(x)
x = self.norm1(x + attn_output)
# 前馈子层
ffn_output = self.ffn(x)
x = self.norm2(x + ffn_output)
return x
3. 解码器部分详解
3.1 解码器层结构
每个解码器层包含三个主要子层:
3.1.1 带掩码的多头自注意力
python复制class MaskedMultiHeadAttention(nn.Module):
def __init__(self, d_model, h):
super().__init__()
self.d_k = d_model // h
self.h = h
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x, mask):
# 线性变换得到Q,K,V
Q = self.W_q(x) # [L_dec, d_model]
K = self.W_k(x) # [L_dec, d_model]
V = self.W_v(x) # [L_dec, d_model]
# 分割为多头
Q = Q.view(Q.size(0), self.h, self.d_k) # [L_dec, h, d_k]
K = K.view(K.size(0), self.h, self.d_k)
V = V.view(V.size(0), self.h, self.d_k)
# 计算注意力分数并应用掩码
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
scores = scores.masked_fill(mask == 0, -1e9)
attn = F.softmax(scores, dim=-1)
# 加权求和
context = torch.matmul(attn, V) # [L_dec, h, d_k]
context = context.view(context.size(0), -1) # [L_dec, d_model]
# 输出投影
output = self.W_o(context)
return output
3.1.2 编码器-解码器注意力
python复制class EncoderDecoderAttention(nn.Module):
def __init__(self, d_model, h):
super().__init__()
self.d_k = d_model // h
self.h = h
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x, encoder_output):
# Q来自解码器输入
Q = self.W_q(x) # [L_dec, d_model]
# K,V来自编码器输出
K = self.W_k(encoder_output) # [L_enc, d_model]
V = self.W_v(encoder_output) # [L_enc, d_model]
# 分割为多头
Q = Q.view(Q.size(0), self.h, self.d_k) # [L_dec, h, d_k]
K = K.view(K.size(0), self.h, self.d_k)
V = V.view(V.size(0), self.h, self.d_k)
# 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
attn = F.softmax(scores, dim=-1)
# 加权求和
context = torch.matmul(attn, V) # [L_dec, h, d_k]
context = context.view(context.size(0), -1) # [L_dec, d_model]
# 输出投影
output = self.W_o(context)
return output
3.1.3 完整解码器层实现
python复制class DecoderLayer(nn.Module):
def __init__(self, d_model, h):
super().__init__()
self.masked_attn = MaskedMultiHeadAttention(d_model, h)
self.enc_dec_attn = EncoderDecoderAttention(d_model, h)
self.ffn = FeedForward(d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
def forward(self, x, encoder_output, mask):
# 掩码自注意力子层
attn_output = self.masked_attn(x, mask)
x = self.norm1(x + attn_output)
# 编码器-解码器注意力子层
enc_dec_output = self.enc_dec_attn(x, encoder_output)
x = self.norm2(x + enc_dec_output)
# 前馈子层
ffn_output = self.ffn(x)
x = self.norm3(x + ffn_output)
return x
3.2 训练与推理流程差异
3.2.1 训练阶段
训练时使用Teacher Forcing策略:
python复制def train_step(encoder, decoder, src, tgt, optimizer, criterion):
# 编码器前向传播
encoder_output = encoder(src)
# 解码器前向传播
outputs = []
for i in range(tgt.size(0)-1):
output = decoder(tgt[:i+1], encoder_output)
outputs.append(output)
# 计算损失
loss = criterion(torch.stack(outputs), tgt[1:])
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss.item()
3.2.2 推理阶段
推理时采用自回归生成:
python复制def generate(encoder, decoder, src, max_len=50):
# 编码器前向传播
encoder_output = encoder(src)
# 初始化解码器输入
tgt = torch.tensor([[SOS_IDX]], device=device)
# 自回归生成
for i in range(max_len):
output = decoder(tgt, encoder_output)
next_token = output.argmax(-1)[-1].unsqueeze(0)
tgt = torch.cat([tgt, next_token.unsqueeze(0)], dim=0)
if next_token.item() == EOS_IDX:
break
return tgt
4. 模型参数计算与分析
4.1 参数分布概览
Transformer模型的参数主要集中在以下几个部分:
- 嵌入层:词嵌入和位置编码
- 注意力机制:Q,K,V投影矩阵和输出投影
- 前馈网络:两个线性变换层
- 层归一化:缩放和偏移参数
4.2 详细参数计算
以d_model=512,h=8,N_enc=N_dec=6的配置为例:
4.2.1 嵌入层参数
- 词嵌入矩阵:vocab_size × d_model
- 假设vocab_size=30000 → 30000×512=15,360,000
- 位置编码:max_len × d_model
- 假设max_len=512 → 512×512=262,144
4.2.2 编码器层参数
单层编码器参数包括:
- 多头注意力:
- Q,K,V投影:3 × d_model × d_model = 3×512×512=786,432
- 输出投影:d_model × d_model = 512×512=262,144
- 前馈网络:
- 第一层:d_model × 4d_model = 512×2048=1,048,576
- 第二层:4d_model × d_model = 2048×512=1,048,576
- 层归一化:
- 2层 × 2 × d_model = 4×512=2,048
单层总计:约3.1M参数
6层编码器:约18.7M参数
4.2.3 解码器层参数
单层解码器参数与编码器类似,但多一个编码器-解码器注意力:
- 掩码自注意力:与编码器自注意力相同
- 编码器-解码器注意力:与自注意力相同
- 前馈网络:与编码器相同
- 层归一化:3层 × 2 × d_model = 6×512=3,072
单层总计:约4.7M参数
6层解码器:约28.2M参数
4.2.4 输出层参数
- 线性层:d_model × vocab_size = 512×30000=15,360,000
4.3 参数优化技巧
在实际应用中,可以采用以下技术优化参数效率:
- 参数共享:
- 编码器与解码器共享词嵌入
- 输出层与词嵌入共享权重
- 低秩近似:
- 对大型矩阵进行低秩分解
- 量化压缩:
- 将浮点参数转换为低精度表示
- 稀疏化:
- 剪枝不重要连接
5. 实际应用中的关键考量
5.1 超参数选择建议
- 模型深度与宽度平衡:
- 浅层宽模型适合简单任务
- 深层窄模型适合复杂任务
- 注意力头数选择:
- 通常d_model能被h整除
- 常见配置为8-16头
- 前馈网络维度:
- 通常为d_model的4倍
- 过大可能导致过拟合
5.2 常见问题与解决方案
5.2.1 训练不稳定
现象:损失值波动大或出现NaN
解决方案:
- 使用更小的学习率
- 增加梯度裁剪
- 检查层归一化的实现
5.2.2 过拟合
现象:训练损失低但验证损失高
解决方案:
- 增加dropout
- 使用标签平滑
- 早停策略
5.2.3 长序列处理
现象:长序列性能下降
解决方案:
- 使用相对位置编码
- 实现稀疏注意力
- 分块处理长序列
5.3 性能优化技巧
- 内存优化:
- 使用梯度检查点
- 激活值重计算
- 计算加速:
- 混合精度训练
- 算子融合
- 批处理策略:
- 动态批处理
- 序列长度分桶
6. 扩展与变体
6.1 经典模型架构
- T5模型:
- 统一的文本到文本框架
- 相对位置编码
- BART模型:
- 去噪自编码预训练
- 更适合生成任务
- M2M-100:
- 多语言翻译模型
- 共享编码器-解码器
6.2 注意力机制改进
- 稀疏注意力:
- Longformer的滑动窗口注意力
- BigBird的随机+局部+全局注意力
- 线性注意力:
- 使用核函数近似softmax
- 降低计算复杂度
- 内存高效注意力:
- FlashAttention优化
- 内存访问模式优化
6.3 结构创新
- 跨层参数共享:
- ALBERT的层间参数共享
- 显著减少参数量
- 模块化设计:
- 专家混合模型
- 条件计算
- 递归结构:
- Universal Transformer
- 自适应计算时间
在实际项目中,选择适合任务需求的架构变体往往能获得更好的性能表现。理解基础Encoder-Decoder架构是掌握这些高级变体的关键。
