当前位置:   article > 正文

NLP—机器翻译_机器翻译代码

机器翻译代码

目录

一.机器翻译的定义

二.基于attention的机器翻译代码实现

1.读取和预处理数据

2.含注意力机制的编码器—解码器的实现

(1)编码器

(2)注意力机制

​编辑(3)含注意力机制的解码器

3. 训练模型 

4.预测不定长的序列

​编辑 5.评价翻译结果

三.基于self-attention的机器翻译代码实现

1.导入所需的软件包 

2.获取平行数据集

3.准备分词器

4.构建 TorchText 词汇对象并将句子转换为 Torch 张量

5.创建用于训练期间迭代的数据加载器对象。

6.实现Sequence-to-sequence Transformer

7.训练模型

  8.保存词汇表对象和训练好的模型


一.机器翻译的定义

  机器翻译是指将一段文本从一种语言自动翻译到另一种语言。

二.基于attention的机器翻译代码实现

1.读取和预处理数据

  首先定义一些特殊符号,其中“<pad>”(padding)符号用来添加在较短序列后,直到每个序列等长,而“<bos>”和“<eos>”符号分别表示序列的开始和结束。

  1. import collections # 导入collections模块,提供了特殊的容器数据类型,如OrderedDict等。
  2. import os # 导入os模块,用于与操作系统进行交互。
  3. import io # 导入io模块,提供了对文件和流的基本操作。
  4. import math # 导入math模块,提供了数学运算相关的函数。
  5. import torch # 导入PyTorch深度学习框架的核心库。
  6. from torch import nn # 从torch中导入nn模块,用于构建神经网络模型。
  7. import torch.nn.functional as F # 导入torch.nn.functional,包含了各种神经网络的函数接口。
  8. import torchtext.vocab as Vocab # 导入torchtext.vocab模块,用于处理文本的词汇表。
  9. import torch.utils.data as Data # 导入torch.utils.data模块,用于处理数据集。
  10. import sys # 导入sys模块,提供对Python解释器的访问。
  11. # sys.path.append("..") # (可选)将上级目录添加到sys.path中,使得可以导入上级目录中的模块。
  12. import d2lzh_pytorch as d2l # 导入d2lzh_pytorch模块,这是一个封装了Deep Learning相关工具和方法的库。
  13. # 定义常量,用于在序列处理中表示填充符、序列起始符和序列结束符。
  14. PAD, BOS, EOS = '<pad>', '<bos>', '<eos>'
  15. # 设置环境变量,指定使用哪块GPU进行计算。这里设置为使用第一个GPU(编号为0)。
  16. os.environ["CUDA_VISIBLE_DEVICES"] = "0"
  17. # 检查当前是否支持CUDA(即GPU加速),并根据结果选择在CPU还是GPU上进行计算。
  18. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  19. # 打印PyTorch的版本信息和当前选择的计算设备。
  20. print(torch.__version__, device)

     函数 process_one_seq 将一个序列中的所有词记录到 all_tokens 中,方便后续构建词典。同时,它在该序列末尾添加特殊符号 EOS 和 PAD,使得序列长度达到 max_seq_len,然后将处理后的序列保存在 all_seqs 中;函数 build_data 使用 all_tokens 构造一个词典 vocab,然后将 all_seqs 中每个词变换为该词在词典中的索引,并将这些索引构造成一个 Tensor。

  1. # 将一个序列中所有的词记录在all_tokens中以便之后构造词典,
  2. # 然后在该序列后面添加PAD直到序列长度变为max_seq_len,
  3. # 然后将序列保存在all_seqs中
  4. def process_one_seq(seq_tokens, all_tokens, all_seqs, max_seq_len):
  5. # 将当前序列中的所有词添加到all_tokens列表中
  6. all_tokens.extend(seq_tokens)
  7. # 在当前序列末尾添加EOS(序列结束符),
  8. # 然后添加PAD(填充符)直到序列长度达到max_seq_len
  9. seq_tokens += [EOS] + [PAD] * (max_seq_len - len(seq_tokens) - 1)
  10. # 将处理后的序列添加到all_seqs列表中
  11. all_seqs.append(seq_tokens)
  12. # 使用所有的词来构造词典。并将所有序列中的词变换为词索引后构造Tensor
  13. def build_data(all_tokens, all_seqs):
  14. # 使用collections.Counter计算每个词的频率,并基于此构造词典。
  15. # 值得注意的是,这里指定了特别的词汇:PAD、BOS和EOS。
  16. vocab = Vocab.Vocab(collections.Counter(all_tokens),
  17. specials=[PAD, BOS, EOS])
  18. # 将所有序列中的每个词替换为它在词典中的索引,得到一个索引序列的列表
  19. indices = [[vocab.stoi[w] for w in seq] for seq in all_seqs]
  20. # 将索引序列列表转换为PyTorch的Tensor类型,用于后续的模型输入
  21. return vocab, torch.tensor(indices)

    为了演示方便,我们在这里使用一个很小的法语—英语数据集。在这个数据集里,每一行是一对法语句子和它对应的英语句子,中间使用'\t'隔开。在读取数据时,我们在句末附上“<eos>”符号,并可能通过添加“<pad>”符号使每个序列的长度均为max_seq_len。我们为法语词和英语词分别创建词典。法语词的索引和英语词的索引相互独立;将序列的最大长度设成7,然后查看读取到的第一个样本。该样本分别包含法语词索引序列和英语词索引序列。

  1. def read_data(max_seq_len):
  2. # in和out分别是inputoutput的缩写
  3. in_tokens, out_tokens, in_seqs, out_seqs = [], [], [], []
  4. with io.open('fr-en-small.txt') as f:
  5. lines = f.readlines() # 读取文件中的所有行
  6. for line in lines:
  7. in_seq, out_seq = line.rstrip().split('\t') # 将每行按制表符'\t'分割成输入序列和输出序列
  8. in_seq_tokens, out_seq_tokens = in_seq.split(' '), out_seq.split(' ') # 将输入和输出序列分割成词汇列表
  9. if max(len(in_seq_tokens), len(out_seq_tokens)) > max_seq_len - 1:
  10. continue # 如果加上EOS后长于max_seq_len,则忽略掉此样本
  11. process_one_seq(in_seq_tokens, in_tokens, in_seqs, max_seq_len) # 处理输入序列并添加到对应的列表中
  12. process_one_seq(out_seq_tokens, out_tokens, out_seqs, max_seq_len) # 处理输出序列并添加到对应的列表中
  13. in_vocab, in_data = build_data(in_tokens, in_seqs) # 构建输入序列的词典并转换为Tensor数据
  14. out_vocab, out_data = build_data(out_tokens, out_seqs) # 构建输出序列的词典并转换为Tensor数据
  15. return in_vocab, out_vocab, Data.TensorDataset(in_data, out_data)
  16. max_seq_len = 7
  17. in_vocab, out_vocab, dataset = read_data(max_seq_len)
  18. dataset[0]

结果输出如下:

2.含注意力机制的编码器—解码器的实现
(1)编码器

  在编码器中,我们将输入语言的词索引通过词嵌入层得到词的表征,然后输入到一个多层门控循环单元中。我们创建一个批量大小为4、时间步数为7的小批量序列输入。设门控循环单元的隐藏层个数为2,隐藏单元个数为16。编码器对该输入执行前向计算后返回的输出形状为(时间步数, 批量大小, 隐藏单元个数)。门控循环单元在最终时间步的多层隐藏状态的形状为(隐藏层个数, 批量大小, 隐藏单元个数)。对于门控循环单元来说,state就是一个元素,即隐藏状态;如果使用长短期记忆,state是一个元组,包含两个元素即隐藏状态和记忆细胞。

  1. class Encoder(nn.Module):
  2. def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
  3. drop_prob=0, **kwargs):
  4. super(Encoder, self).__init__(**kwargs)
  5. # 初始化编码器的各个层
  6. self.embedding = nn.Embedding(vocab_size, embed_size) # 定义词嵌入层
  7. self.rnn = nn.GRU(embed_size, num_hiddens, num_layers, dropout=drop_prob) # 定义GRU循环神经网络层
  8. def forward(self, inputs, state):
  9. # 前向计算函数,描述了数据在模型中的传播过程
  10. # 输入形状是(批量大小, 时间步数)。将输出互换样本维和时间步维
  11. embedding = self.embedding(inputs.long()).permute(1, 0, 2) # 对输入进行词嵌入并进行维度变换
  12. return self.rnn(embedding, state) # 返回GRU层的输出结果以及更新后的状态
  13. def begin_state(self):
  14. # 初始化RNN的隐藏状态
  15. return None
  16. encoder = Encoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2)
  17. output, state = encoder(torch.zeros((4, 7)), encoder.begin_state())
  18. output.shape, state.shape # GRU的state是h, 而LSTM的是一个元组(h, c)

输出结果如下:

(2)注意力机制

  下图描绘了注意力机制如何为解码器在时间步2计算背景变量。首先,函数声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】

推荐阅读
相关标签