使用Transformer和PyTorch的日语-中文机器翻译模型
----------一个使用Jupyter Notebook、PyTorch、Torchtext和SentencePiece的教程
一,前言
1.1 概述
Transformer模型是一种深度学习模型,由Vaswani等人在2017年提出,主要用于自然语言处理(NLP)任务。它的核心思想是通过自注意力(Self-Attention)机制来捕捉输入数据之间的全局依赖关系,从而能够处理序列数据,如文本。
1.2 主要特点
1.2.1 自注意力机制:
自注意力机制使模型能够在处理每个单词时,考虑到句子中的所有单词,从而更好地理解上下文。
这一机制有助于捕获长距离依赖,特别适用于处理具有复杂结构的句子。
1.2.2 并行化处理:
与传统的循环神经网络(RNN)不同,Transformer不需要按顺序处理数据,因此可以高效地利用现代计算硬件进行并行计算。
这显著提高了训练速度,尤其是在处理大规模数据集时。
1.2.3 编码器—解码器结构:
Transformer遵循编码器—解码器结构,其中编码器用于理解输入数据,解码器负责生成输出。
这种结构使得模型不仅可用于翻译任务,还可用于文本摘要、文本生成等应用。
1.2.4 无需递归或卷积层:
Transformer完全依赖于自注意力机制和Feed-Forward神经网络,不需要传统的卷积层或递归层。
这使得模型结构简单而有效,减少了需要调整的超参数数量。
1.2.5 可扩展性和灵活性:
Transformer的可扩展性使其成为构建大型语言模型的理想选择。
通过增加模型的层数和参数量,可以不断提高模型的性能。
1.3 应用领域
机器翻译:最初为解决机器翻译问题而设计,Transformer在多个翻译任务上取得了最佳效果。
文本生成:用于生成连贯的文本,例如在自动写作、聊天机器人等场景中。
文本理解:在文本分类、情感分析等理解型任务中也显示出强大的性能。
多模态学习:最近的研究将Transformer应用于处理多模态数据,如结合文本和图像的任务。
二,实验环境
使用autodl云平台租借GPU
个人环境为若读者自己的设备足以处理数据集,构建出模型可采用cg平台建立pytorch环境进行试验
三,导入所需的包
首先,让我们确保我们的系统中已安装以下包,如果发现某些包缺失,请确保安装它们。
导入后用以下代码来查询包的版本
使用pip命令安装名为pandas的Python包,以便在Python代码中使用pandas库的功能。
这段代码的作用是导入了多个Python包和模块,包括math、torchtext、torch、torch.nn、Tensor、pad_sequence、DataLoader、Counter、Vocab、TransformerEncoder、TransformerDecoder、TransformerEncoderLayer、TransformerDecoderLayer、io、time、pandas、numpy、pickle、tqdm和sentencepiece。这些包和模块提供了在PyTorch中进行神经网络训练和处理数据所需的功能和工具。
其中,torch.manual_seed(0)用于设置随机种子,以确保实验的可重复性。device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')用于检测系统是否支持CUDA,如果支持则使用GPU加速,否则使用CPU。
四,获取并行数据集
在本教程中,我们将使用从JParaCrawl下载的日语-英语并行数据集!
在导入所有的日语和其英文对应文本之后,我删除了数据集中的最后一条数据,因为它存在缺失值。总共,在trainen和trainja中的句子数量为5,973,071条,然而,为了学习目的,通常建议对数据进行抽样,并确保一切按预期工作,然后再一次性使用所有数据,以节省时间。 以下是数据集中包含的一条句子的示例。
我们也可以使用不同的并行数据集来跟随本文,只需确保我们可以将数据处理成上面所示的两个字符串列表,其中包含日语和英语句子。
五,准备分词器
与英语或其他字母语言不同,日语句子中不包含空格来分隔单词。我们可以使用JParaCrawl提供的分词器,它们是使用SentencePiece为日语和英语创建的。您可以访问JParaCrawl网站下载它们,或者点击这里。在加载分词器之后,您可以通过执行以下代码来测试它们,例如:
六,构建TorchText的Vocab对象并将句子转换为Torch张量
使用分词器和原始句子,我们然后构建从TorchText导入的Vocab对象。这个过程可能需要几秒钟或几分钟,取决于数据集的大小和计算能力。不同的分词器也会影响构建词汇所需的时间,我尝试了几种其他日语分词器,但是发现SentencePiece对我来说工作得很好且足够快。在我们拥有词汇对象之后,我们可以使用词汇表和分词器对象来构建我们训练数据的张量。七,创建DataLoader对象以在训练期间进行迭代
在这里,我将BATCH_SIZE设置为16以防止“cuda内存不足”,但这取决于各种因素,如您的机器内存容量、数据大小等,因此根据您的需求随时更改批量大小(注意:PyTorch的教程使用Multi30k德语-英语数据集将批量大小设置为128)。
Sequence-to-sequence Transformer 接下来的几行代码和文本解释(以斜体表示)取自原始的PyTorch教程[https://pytorch.org/tutorials/beginner/translation_transformer.html]。我没有做任何更改,除了BATCH_SIZE和将word de_vocab更改为ja_vocab。
Transformer是一种Seq2Seq模型,引入了“Attention is all you need”论文,用于解决机器翻译任务。Transformer模型包含一个编码器和一个解码器块,每个块包含固定数量的层。
编码器通过一系列的多头注意力和前馈网络层将输入序列进行处理。编码器的输出称为记忆,与目标张量一起馈送到解码器中。编码器和解码器使用教师强制技术进行端到端训练。
文本标记通过使用标记嵌入来表示。在标记嵌入中添加位置编码以引入单词顺序的概念。
class PositionalEncoding(nn.Module):
我们创建一个后续词掩码,以阻止目标词关注其后续词。我们还创建了用于屏蔽源和目标填充标记的掩码。
Define model parameters and instantiate model. 这里我们服务器实在是计算能力有限,按照以下配置可以训练但是效果应该是不行的。如果想要看到训练的效果请使用你自己的带GPU的电脑运行这一套代码。
当你使用自己的GPU的时候,NUM_ENCODER_LAYERS 和 NUM_DECODER_LAYERS 设置为3或者更高,NHEAD设置8,EMB_SIZE设置为512。
SRC_VOCAB_SIZE = len(ja_vocab)
TGT_VOCAB_SIZE = len(en_vocab)
EMB_SIZE = 512
NHEAD = 8
FFN_HID_DIM = 512
BATCH_SIZE = 16
NUM_ENCODER_LAYERS = 3
NUM_DECODER_LAYERS = 3
NUM_EPOCHS = 16
transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS,
EMB_SIZE, SRC_VOCAB_SIZE, TGT_VOCAB_SIZE,
FFN_HID_DIM)
for p in transformer.parameters():
if p.dim() > 1:
nn.init.xavier_uniform_(p)
transformer = transformer.to(device)
loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX)
optimizer = torch.optim.Adam(
transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9
)
def train_epoch(model, train_iter, optimizer):
model.train()
losses = 0
for idx, (src, tgt) in enumerate(train_iter):
src = src.to(device)
tgt = tgt.to(device)
tgt_input = tgt[:-1, :]
src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)
logits = model(src, tgt_input, src_mask, tgt_mask,
src_padding_mask, tgt_padding_mask, src_padding_mask)
optimizer.zero_grad()
tgt_out = tgt[1:,:]
loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))
loss.backward()
optimizer.step()
losses += loss.item()
return losses / len(train_iter)
def evaluate(model, val_iter):
model.eval()
losses = 0
for idx, (src, tgt) in (enumerate(valid_iter)):
src = src.to(device)
tgt = tgt.to(device)
tgt_input = tgt[:-1, :]
src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)
logits = model(src, tgt_input, src_mask, tgt_mask,
src_padding_mask, tgt_padding_mask, src_padding_mask)
tgt_out = tgt[1:,:]
loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))
losses += loss.item()
return losses / len(val_iter)
八,开始训练
最后,在准备好必要的类和函数之后,我们准备好训练我们的模型。毋庸置疑,完成训练所需的时间可能会因计算能力、参数和数据集大小等因素而大不相同。
当我使用JParaCrawl的完整句子列表进行模型训练时,每种语言大约有590万个句子,使用单个NVIDIA GeForce RTX 3070 GPU,每个epoch大约需要5个小时。
以下是代码:
结果:
尝试使用训练好的模型翻译一句日语句子
首先,我们创建用于翻译新句子的函数,包括获取日语句子、标记化、转换为张量、推理,然后将结果解码回一个英语句子的步骤。
然后,我们只需调用翻译函数并传递所需的参数。
最后,在训练完成后,我们将首先使用Pickle保存词汇对象(en_vocab和ja_vocab)。
最后,我们还可以使用PyTorch的保存和加载函数保存模型以供以后使用。一般来说,根据我们以后想要如何使用模型,有两种保存模型的方式。第一种是仅用于推理,我们可以稍后加载模型并用它来从日语翻译成英语。
第二种方式也是用于推理,但还适用于以后加载模型并希望恢复训练的情况。
九,总结
这个实验主要是关于使用PyTorch来训练一个神经机器翻译模型,从日语翻译成英语。首先,我们准备了数据集并进行了预处理,然后构建了模型架构,包括编码器和解码器。接着,我们定义了损失函数和优化器,并进行了模型训练。在训练过程中,我们保存了词汇对象和训练好的模型,以便以后使用。最后,我们讨论了两种保存模型的方式,一种用于推理,另一种用于恢复训练。通过这个实验,我们学习了如何使用PyTorch构建和训练神经机器翻译模型,并保存模型以备将来使用。
在这个实验中,除了基本的数据准备、模型构建和训练步骤外,还可以扩充一些技术要领,例如:
-
注意力机制(Attention Mechanism):在解码器中引入注意力机制可以帮助模型更好地对输入序列进行对齐和翻译,提高翻译质量。
-
超参数调优(Hyperparameter Tuning):通过调整学习率、批量大小、隐藏层大小等超参数,可以优化模型的性能和收敛速度。
-
提前停止(Early Stopping):监控验证集损失,在验证集损失不再降低时停止训练,避免过拟合。
-
模型评估(Model Evaluation):除了训练损失外,还可以使用BLEU分数等指标对模型进行评估,了解翻译质量。
-
使用预训练的词向量(Pretrained Word Embeddings):可以使用预训练的词向量(如Word2Vec、GloVe等)来初始化模型的词嵌入层,提高模型的性能。
-
集成学习(Ensemble Learning):通过组合多个不同的模型,可以进一步提升翻译性能。
通过扩充这些技术要领,可以进一步提高神经机器翻译模型的性能和效果。