前言
Transformer已成为NLP领域的主流架构。本教程将用PyTorch实现一个简化的Transformer,用于序列到序列任务(如机器翻译)。
准备工作
环境要求
- Python 3.9+
- PyTorch 2.0+
- 安装:
pip install torch numpy
核心组件实现
1. 多头注意力机制(Multi-Head Attention)
import torch
import torch.nn as nn
import math
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, n_heads):
super().__init__()
assert d_model % n_heads == 0
self.d_k = d_model // n_heads
self.n_heads = n_heads
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=None):
batch_size, seq_len, d_model = x.size()
Q = self.W_q(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1,2)
K = self.W_k(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1,2)
V = self.W_v(x).view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1,2)
scores = torch.matmul(Q, K.transpose(-2,-1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn = torch.softmax(scores, dim=-1)
out = torch.matmul(attn, V).transpose(1,2).contiguous().view(batch_size, seq_len, d_model)
return self.W_o(out)
2. 位置编码(Positional Encoding)
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)
pe = pe.unsqueeze(0)
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:, :x.size(1)]
3. 前馈网络(Feed Forward)
class FeedForward(nn.Module):
def __init__(self, d_model, d_ff):
super().__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
self.relu = nn.ReLU()
def forward(self, x):
return self.linear2(self.relu(self.linear1(x)))
4. 编码器层(Encoder Layer)
class EncoderLayer(nn.Module):
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
self.self_attn = MultiHeadAttention(d_model, n_heads)
self.ffn = FeedForward(d_model, d_ff)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
x = x + self.dropout(self.self_attn(self.norm1(x), mask))
x = x + self.dropout(self.ffn(self.norm2(x)))
return x
5. 完整Transformer模型
class Transformer(nn.Module):
def __init__(self, src_vocab, tgt_vocab, d_model=512, n_heads=8, n_layers=6, d_ff=2048, dropout=0.1):
super().__init__()
self.src_embed = nn.Embedding(src_vocab, d_model)
self.tgt_embed = nn.Embedding(tgt_vocab, d_model)
self.pos_enc = PositionalEncoding(d_model)
self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers)])
self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers)])
self.fc_out = nn.Linear(d_model, tgt_vocab)
self.dropout = nn.Dropout(dropout)
def forward(self, src, tgt, src_mask=None, tgt_mask=None):
src = self.dropout(self.pos_enc(self.src_embed(src)))
tgt = self.dropout(self.pos_enc(self.tgt_embed(tgt)))
enc_output = src
for layer in self.encoder_layers:
enc_output = layer(enc_output, src_mask)
dec_output = tgt
for layer in self.decoder_layers:
dec_output = layer(dec_output, enc_output, tgt_mask)
return self.fc_out(dec_output)
训练与测试
数据准备
使用torchtext加载IWSLT2016数据集(德语-英语)。
训练循环
criterion = nn.CrossEntropyLoss(ignore_index=pad_idx)
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
for epoch in range(10):
for batch in train_loader:
src, tgt = batch.src, batch.tgt
output = model(src, tgt[:, :-1])
loss = criterion(output.reshape(-1, tgt_vocab), tgt[:, 1:].reshape(-1))
optimizer.zero_grad()
loss.backward()
optimizer.step()
总结
本教程实现了Transformer的核心组件。实际应用中可调整参数(如层数、头数)以获得更好效果。完整代码见GitHub。