【附源码】基于LSTM-Seq2Seq与注意力机制的英汉机器翻译实现
神经机器翻译(Neural Machine Translation, NMT)自2014年提出以来,逐步取代了传统的统计机器翻译方法,成为当前机器翻译领域的主流技术路线。
本文将介绍一种基于序列到序列(Sequence-to-Sequence, Seq2Seq)架构的英汉翻译系统实现,采用双向LSTM编码器配合注意力机制,在PyTorch框架下完成模型构建与训练。
模型架构设计
整体框架
本项目采用经典的Encoder-Decoder架构。编码器负责将源语言序列(英语)压缩为固定长度的上下文向量,解码器则基于该向量逐词生成目标语言序列(中文)。为提升长句翻译质量,在解码器中引入全局注意力机制,使模型在生成每个目标词时能够动态关注源序列的不同位置。
编码器实现
编码器采用双向LSTM(BiLSTM)结构,其核心优势在于能够同时捕获序列的前向和后向上下文信息。具体实现如下:
class Encoder(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_layers, dropout):
super(Encoder, self).__init__()
self.hidden_dim = hidden_dim
self.num_layers = num_layers
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.lstm = nn.LSTM(
embed_dim, hidden_dim, num_layers,
dropout=dropout if num_layers > 1 else 0,
bidirectional=True,
batch_first=True
)
# 将双向LSTM的输出维度映射回隐藏层维度
self.fc_hidden = nn.Linear(hidden_dim * 2, hidden_dim)
self.fc_cell = nn.Linear(hidden_dim * 2, hidden_dim)
self.dropout = nn.Dropout(dropout)
双向LSTM的输出维度为hidden_dim * 2,需通过全连接层将其映射回hidden_dim,以匹配解码器的输入维度。此外,使用pack_padded_sequence对变长序列进行打包处理,避免填充符参与计算,提升训练效率。
注意力机制
注意力机制的计算采用加性评分函数(Additive Scoring):
class Attention(nn.Module):
def __init__(self, hidden_dim):
super(Attention, self).__init__()
self.attn = nn.Linear(hidden_dim * 3, hidden_dim)
self.v = nn.Linear(hidden_dim, 1, bias=False)
def forward(self, hidden, encoder_outputs, mask=None):
src_len = encoder_outputs.shape[1]
hidden = hidden.unsqueeze(1).repeat(1, src_len, 1)
energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim=2)))
attention = self.v(energy).squeeze(2)
if mask is not None:
attention = attention.masked_fill(mask == 0, -1e10)
return torch.softmax(attention, dim=1)
该模块接收解码器当前隐藏状态与编码器全部输出,计算注意力权重分布。掩码机制用于屏蔽填充位置,确保模型不会关注到无效信息。
解码器实现
解码器为单向LSTM,在每个时间步接收上一时刻的输出(或真实标签,取决于教师强制策略)、前一时刻的隐藏状态与细胞状态,以及经注意力加权后的上下文向量:
class Decoder(nn.Module):
def forward(self, input, hidden, cell, encoder_outputs=None, mask=None):
input = input.unsqueeze(1)
embedded = self.dropout(self.embedding(input))
if self.use_attention and encoder_outputs is not None:
attn_weights = self.attention(hidden[-1], encoder_outputs, mask)
attn_weights = attn_weights.unsqueeze(1)
context = torch.bmm(attn_weights, encoder_outputs)
lstm_input = torch.cat((embedded, context), dim=2)
else:
lstm_input = embedded
context = None
output, (hidden, cell) = self.lstm(lstm_input, (hidden, cell))
# 输出层融合隐藏状态与上下文向量
if self.use_attention and context is not None:
output = torch.cat((output.squeeze(1), context.squeeze(1)), dim=1)
else:
output = output.squeeze(1)
prediction = self.fc_out(output)
return prediction, hidden, cell
输出层将LSTM输出与上下文向量拼接后映射至目标词汇表空间,实现词语预测。
数据预处理
词汇表构建
词汇表管理采用Vocabulary类,维护词到索引的双向映射。特殊标记定义如下:
<PAD>:填充符,索引0<UNK>:未知词,索引1<SOS>:序列起始符,索引2<EOS>:序列终止符,索引3
class Vocabulary:
def __init__(self):
self.word2idx = {PAD_TOKEN: 0, UNK_TOKEN: 1, SOS_TOKEN: 2, EOS_TOKEN: 3}
self.idx2word = {0: PAD_TOKEN, 1: UNK_TOKEN, 2: SOS_TOKEN, 3: EOS_TOKEN}
self.word_count = Counter()
self.n_words = 4
def build(self, min_freq=MIN_FREQ):
for word, count in self.word_count.most_common(MAX_VOCAB_SIZE):
if count >= min_freq:
self.word2idx[word] = self.n_words
self.idx2word[self.n_words] = word
self.n_words += 1
低频词过滤阈值设为2,词汇表上限为50000,兼顾覆盖率与计算效率。
文本分词
英语文本采用空格分词,经正则表达式去除标点并统一小写;中文文本使用jieba分词工具:
en_tokens = re.sub(r"[^a-z0-9\s]", " ", en.lower()).split()
zh_tokens = [t for t in jieba.cut(zh) if t.strip()]
数据加载
自定义TranslationDataset继承自torch.utils.data.Dataset,配合collate_fn实现动态填充:
def collate_fn(batch):
src_batch, tgt_batch = zip(*batch)
src_len, tgt_len = [len(s) for s in src_batch], [len(t) for t in tgt_batch]
src_pad = torch.zeros(len(batch), max(src_len), dtype=torch.long)
for i, seq in enumerate(src_batch):
src_pad[i, :len(seq)] = seq
# 目标序列同理填充
return src_pad, tgt_pad, src_len, tgt_len
训练策略
教师强制
训练过程中采用教师强制(Teacher Forcing)技术,以概率teacher_forcing_ratio决定当前时间步的输入是使用真实标签还是上一时刻的预测结果。这一机制有助于加速收敛,但过高的比例可能导致模型对训练数据过拟合。本实现将该比例设为0.5。
优化与正则化
优化器选用Adam,初始学习率0.001。梯度裁剪阈值设为1.0,防止梯度爆炸。损失函数采用交叉熵损失,忽略填充位置的计算:
criterion = nn.CrossEntropyLoss(ignore_index=0)
训练循环
for epoch in range(NUM_EPOCHS):
train_loss = train_epoch(model, train_loader, optimizer, criterion)
val_loss = evaluate(model, val_loader, criterion)
if val_loss < best_val_loss:
best_val_loss = val_loss
torch.save(model.state_dict(), 'best_model.pt')
模型按7:3比例划分训练集与验证集,共15个epoch,根据验证集损失保存最优模型。
实验配置
| 超参数 | 取值 |
|---|---|
| 嵌入维度 | 256 |
| 隐藏层维度 | 512 |
| LSTM层数 | 2 |
| Dropout | 0.3 |
| 批量大小 | 64 |
| 训练轮数 | 15 |
数据集包含29155条英汉平行句对,经处理后英文词汇表规模为4425,中文词汇表规模为6496。模型总参数量约2980万。
总结
本文实现了一个完整的基于Seq2Seq架构的英汉神经机器翻译系统。双向LSTM编码器有效捕获了源序列的双向语义信息,注意力机制的引入使模型能够处理较长句子,缓解了固定长度上下文向量的信息瓶颈问题。该实现可作为神经机器翻译的基线模型,后续可在此基础上引入Transformer架构、子词分词(BPE)等技术进一步提升翻译质量。
作者:谙弆悕博士(Ailan Anjuxi)
⚠️源码已发布到我的Github !