
首先,我得回顾一下Transformer的基本结构,确保自己没有记错。Transformer由编码器和解码器组成,每个编码器层包含多头自注意力机制和前馈网络,解码器层则还有编码器-解码器注意力。
接下来,用户需要的是手把手的代码解读,所以必须详细,每一步都不能遗漏。可能需要从数据预处理开始,比如词嵌入、位置编码。然后逐步构建各个组件:自注意力、多头注意力、编码器层、解码器层,最后整合成完整的Transformer模型。
要注意用户提到的系统级指令,特别是LaTeX格式的要求。所有数学公式都要用...或者$$...$$,比如自注意力的计算公式Attention(Q, K, V) = softmax(\\frac{QK\^T}{\\sqrt{d_k}})V,这里要检查是否正确使用括号和符号。
另外,用户要求结构清晰,分步骤讲解。可能需要分成几个部分:嵌入层、位置编码、多头注意力、前馈网络、编码器、解码器,然后是整体模型和训练过程。每个部分都要解释代码的逻辑,比如在PyTorch中如何继承nn.Module,如何初始化参数,前向传播的实现。
需要确保代码示例正确,比如在实现多头注意力时,是否正确地分割了张量,是否计算了注意力分数,并应用了mask。可能还要解释为什么使用缩放点积注意力,以及如何实现残差连接和层归一化。
还要考虑用户可能遇到的常见问题,比如维度不匹配,如何处理mask,训练时的损失函数选择,优化器的设置等。可能需要给出一个简单的示例,说明如何使用构建好的Transformer模型进行训练和推理。
最后,检查是否符合中文回答的要求,避免使用专业术语时直接使用英文而不加解释。比如,提到"embedding"时要翻译为"嵌入"或者"词嵌入"。同时,确保LaTeX语法正确,没有遗漏$符号或者错误使用括号。
以下是用PyTorch实现Transformer模型的详细分步指南,包含关键代码解读:
一、Transformer核心结构
Transformer由编码器(Encoder)和解码器(Decoder)组成,核心公式:
\\text{Attention}(Q,K,V)=\\text{softmax}\\left(\\frac{QK\^T}{\\sqrt{d_k}}\\right)V
二、实现步骤分解
- 导入基础模块
python
import torch
import torch.nn as nn
import torch.nn.functional as F
- 位置编码(Positional Encoding)
解决序列顺序信息的关键:
PE_{(pos,2i)} = \\sin(pos/10000\^{2i/d_{model}})
PE_{(pos,2i+1)} = \\cos(pos/10000\^{2i/d_{model}})
代码实现:
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).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-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):
return x + self.pe[:x.size(1)]
- 多头注意力机制(Multi-Head Attention)
关键步骤:
-
线性投影分割头
-
计算缩放点积注意力
-
合并多头输出
代码实现:
python
class MultiHeadAttention(nn.Module):
def init(self, d_model, num_heads):
super().init()
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.Wq = nn.Linear(d_model, d_model)
self.Wk = nn.Linear(d_model, d_model)
self.Wv = nn.Linear(d_model, d_model)
self.out = nn.Linear(d_model, d_model)
def forward(self, q, k, v, mask=None):
batch_size = q.size(0)
线性投影并分头 [B, L, D] -> [B, L, H, D_k]
q = self.Wq(q).view(batch_size, -1, self.num_heads, self.d_k)
k = self.Wk(k).view(batch_size, -1, self.num_heads, self.d_k)
v = self.Wv(v).view(batch_size, -1, self.num_heads, self.d_k)
计算注意力得分
scores = torch.matmul(q.transpose(1,2), k.transpose(1,2).transpose(2,3))
scores = scores / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
Softmax归一化
attn = F.softmax(scores, dim=-1)
上下文向量计算
context = torch.matmul(attn, v.transpose(1,2))
context = context.transpose(1,2).contiguous().view(batch_size, -1, self.d_model)
return self.out(context)
- 前馈网络(Position-wise FFN)
\\text{FFN}(x) = \\text{ReLU}(xW_1 + b_1)W_2 + b_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)))
- 编码器层完整实现
python
class EncoderLayer(nn.Module):
def init(self, d_model, num_heads, dropout=0.1):
super().init()
self.self_attn = MultiHeadAttention(d_model, num_heads)
self.ffn = FeedForward(d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask):
残差连接+层归一化
attn_output = self.self_attn(x, x, x, mask)
x = self.norm1(x + self.dropout(attn_output))
ffn_output = self.ffn(x)
x = self.norm2(x + self.dropout(ffn_output))
return x
- 解码器层实现(含Encoder-Decoder Attention)
```python
class DecoderLayer(nn.Module):
def init(self, d_model, num_heads, dropout=0.1):
super().init()
self.self_attn = MultiHeadAttention(d_model, num_heads)
self.cross_attn = MultiHeadAttention(d_model, num_heads)
self.ffn = FeedForward(d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, encoder_output, src_mask, tgt_mask):
自注意力
attn_output = self.self_attn(x, x, x, tgt_mask)
x = self.norm1(x + self.dropout(attn_output))
编码器-解码器注意力
attn_output = self.cross_attn(x, encoder_output, encoder_output, src_mask)
x = self.norm2(x + self.dropout(attn_output))
前馈网络
ffn_output = self.ffn(x)
x = self.norm3(x + self.dropout(ffn_output))
return x
三、完整Transformer组装
```python
class Transformer(nn.Module):
def init(self, src_vocab_size, tgt_vocab_size, d_model=512, num_heads=8,
num_layers=6, dropout=0.1):
super().init()
self.encoder_embed = nn.Embedding(src_vocab_size, d_model)
self.decoder_embed = nn.Embedding(tgt_vocab_size, d_model)
self.pos_encoding = PositionalEncoding(d_model)
self.encoder_layers = nn.ModuleList(
EncoderLayer(d_model, num_heads, dropout) for _ in range(num_layers)\]) self.decoder_layers = nn.ModuleList( \[DecoderLayer(d_model, num_heads, dropout) for _ in range(num_layers)\]) self.final_out = nn.Linear(d_model, tgt_vocab_size) def forward(self, src, tgt, src_mask, tgt_mask): # 编码器 src_emb = self.pos_encoding(self.encoder_embed(src)) for layer in self.encoder_layers: src_emb = layer(src_emb, src_mask) # 解码器 tgt_emb = self.pos_encoding(self.decoder_embed(tgt)) for layer in self.decoder_layers: tgt_emb = layer(tgt_emb, src_emb, src_mask, tgt_mask) return self.final_out(tgt_emb) \`\`\` 四、训练关键点 1. \*\*Mask生成\*\*:实现padding mask和look-ahead mask 2. \*\*学习率调度\*\*:使用warmup策略 3. \*\*标签平滑\*\*:提升模型泛化能力 4. \*\*批处理\*\*:利用GPU并行计算 示例训练循环: \`\`\`python model = Transformer(src_vocab_size=10000, tgt_vocab_size=8000) criterion = nn.CrossEntropyLoss(ignore_index=0) optimizer = torch.optim.Adam(model.parameters(), lr=0.0001) for epoch in range(100): for batch in dataloader: src, tgt_in, tgt_out = batch src_mask = (src != 0).unsqueeze(1) tgt_mask = (tgt_in != 0).unsqueeze(1) outputs = model(src, tgt_in, src_mask, tgt_mask) loss = criterion(outputs.view(-1, outputs.size(-1)), tgt_out.view(-1)) optimizer.zero_grad() loss.backward() optimizer.step() \`\`\` 五、常见问题解决 1. \*\*维度不匹配\*\*:检查各层输入输出维度是否一致 2. \*\*梯度消失\*\*:合理使用残差连接和层归一化 3. \*\*过拟合\*\*:增加dropout率或使用权重衰减 4. \*\*训练效率\*\*:采用混合精度训练和梯度累积 建议通过可视化注意力权重来理解模型的决策过程。完整实现约500行代码,建议从简单任务(如数字加法)开始验证模型有效性。