数据预处理
(1)分词:将句子分解成单词或词素
en_tokenizer = get_tokenizer('spacy', language='en_core_web_trf')
zh_tokenizer = lambda x: list(jieba.cut(x))
通过以上代码分别使用spacy和jieba对英文和中文进行分词
(2)构建词汇表并添加 特殊标记
def build_vocab(data: List[Tuple[List[str], List[str]]]):
en_vocab = build_vocab_from_iterator(
(en for en, _ in data),
specials=['<unk>', '<pad>', '<bos>', '<eos>']
)
zh_vocab = build_vocab_from_iterator(
(zh for _, zh in data),
specials=['<unk>', '<pad>', '<bos>', '<eos>']
)
en_vocab.set_default_index(en_vocab['<unk>'])
zh_vocab.set_default_index(zh_vocab['<unk>'])
return en_vocab, zh_vocab
def __getitem__(self, idx):
en, zh = self.data[idx]
en_indices = [self.en_vocab['<bos>']] + [self.en_vocab[token] for token in en] + [self.en_vocab['<eos>']]
zh_indices = [self.zh_vocab['<bos>']] + [self.zh_vocab[token] for token in zh] + [self.zh_vocab['<eos>']]
return en_indices, zh_indices
定义build_vocab函数根据数据集构建词汇表,并返回一个英文词汇表和一个中文词汇表,其中包含了特殊标记(如 <unk>,<pad>,<bos>,<eos>)
(3)序列截断和填充
def collate_fn(batch):
en_batch, zh_batch = [], []
for en_item, zh_item in batch:
if en_item and zh_item: # 确保两个序列都不为空
# print("都不为空")
en_batch.append(torch.tensor(en_item))
zh_batch.append(torch.tensor(zh_item))
else:
print("存在为空")
if not en_batch or not zh_batch: # 如果整个批次为空,返回空张量
return torch.tensor([]), torch.tensor([])
en_batch = nn.utils.rnn.pad_sequence(en_batch, batch_first=True, padding_value=en_vocab['<pad>'])
zh_batch = nn.utils.rnn.pad_sequence(zh_batch, batch_first=True, padding_value=zh_vocab['<pad>'])
return en_batch, zh_batch
截断:限制输入序列的长度
填充:然后使用PyTorch的pad_sequence函数对en_batch和zh_batch进行填充,确保序列长度一致,便于批量处理,通常使用使用<pad>进行填充
模型训练
(1)编码器
编码器是负责将输入序列转换为一系列特征表示的部分,其工作流程如下:
-
输入序列表示:将输入序列转换为连续的向量表示,使其能够在模型中传递和处理。
-
特征提取:编码器通过多个层次的神经网络(通常是循环神经网络(RNN)或者注意力机制)来提取输入序列的特征。每一层都会对输入序列进行处理,并逐步捕获更高级别的语义信息。
-
序列建模:在编码器的每个时间步,它会考虑当前输入词以及之前的上下文信息,以在整个序列上建模依赖关系。这有助于编码器捕捉输入序列中的长距离依赖关系,从而更好地理解整个句子。
-
上下文向量生成:最终,编码器将所有时间步的特征表示合并为一个上下文向量(,它包含了整个输入序列的信息。这个上下文向量会被传递给解码器,以便解码器能够生成正确的翻译结果。
(2)解码器
解码器是负责将编码器提取的上下文向量转换为目标语言句子的部分,其工作流程如下:
-
上下文向量接收:解码器首先接收编码器生成的上下文向量,它包含了整个输入序列的信息。这个上下文向量相当于编码器提供的输入序列的语义表示。
-
初始状态设置:解码器在生成目标语言句子之前,会通过一个特殊的起始符号(如<sos>)来指示开始翻译。同时,解码器会设置一个初始的隐藏状态,作为解码器的起始状态。
-
逐步生成目标序列:解码器通过逐步生成目标语言序列中的每个词来完成翻译。在每个时间步,解码器会根据当前解码的词、上一个时间步的隐藏状态以及上下文向量,预测下一个目标词的概率分布。
-
结束条件:解码器需要知道何时停止生成目标序列。通常采用特殊的结束符号(如<eos>)指示解码器完成翻译。
-
最终生成:当解码器生成结束符号或达到最大生成长度时,翻译任务结束,最终得到目标语言的翻译结果。
(3)注意力机制
随着文本序列的进一步增加,翻译性能的评价指标 BLEU 的值就开始出现明显地下降,因此,引入注意力机制更加有效地从编码器向解码器传递源语言信息,提高翻译的准确性和流畅性。
在注意力机制中,通常将输入序列中每个位置的隐藏状态与解码器当前隐藏状态进行操作,计算得到一个权重分布。这个权重分布表示了解码器在当前步骤对输入序列不同位置的关注程度,从而影响解码器生成的词语。
基于注意力机制的 GRU 神经网络机器翻译
- 输入序列经过编码器的GRU层,将输入序列转换为上下文向量,同时保存编码器的所有隐藏状态。
- 解码器的初始隐藏状态为编码器的最后一个隐藏状态,然后在每个时间步骤计算注意力权重,并基于权重和编码器的隐藏状态生成上下文向量。
- 解码器中的GRU单元根据上一个时间步的输出和上下文向量生成当前时间步的隐藏状态。