pytorch框架下的基于Transformer的日译中翻译模型

目录

一、引言

二、Transformer模型介绍

2.1整体介绍 

2.2 主要组成部分 

2.2.1 编码器-解码器

2.2.2 位置编码 

2.2.3 多头注意力

三、基于Transformer的翻译模型实现

四、小结与致谢


一、引言

        随着深度学习技术的发展,基于Transformer架构的神经机器翻译模型在自然语言处理领域取得了显著的进展。Transformer模型的注意力机制不仅能够处理长距离依赖性,还能有效地学习输入序列的全局信息,使得翻译质量得到显著提升。本文旨在利用PyTorch框架构建一种日语到中文的翻译模型,通过Transformer模型结构实现高效准确的翻译。

二、Transformer模型介绍

2.1整体介绍 

        Transformer模型是一种基于注意力机制的神经网络架构,广泛应用于自然语言处理任务中,特别是机器翻译。相较于传统的循环神经网络(RNN)或者卷积神经网络(CNN),Transformer模型通过自注意力机制(self-attention)实现对输入序列全局信息的建模,有效处理长距离依赖关系。该模型由Vaswani等人在2017年提出,革新了序列到序列学习的范式。

2.2 主要组成部分 

2.2.1 编码器-解码器

        在Transformer中,输入序列经过多层编码器和解码器的交互处理,编码器将输入序列转换为连续的表示,解码器则利用编码器的输出生成目标语言序列。这种结构使得Transformer能够并行处理输入序列,极大地提升了训练速度和效率。

        编码器是Transformer模型的核心组件之一,负责将输入序列转换为连续的表示。它由多个相同的层堆叠而成,每一层包括以下主要步骤:

1. 自注意力机制(Self-Attention):
        在每一层的第一步,输入序列的每个位置都会计算出一个向量表示,该向量考虑了序列中所有其他位置的信息。这种机制允许编码器有效地捕捉输入序列内部的依赖关系,无论其距离有多远。

2. 前馈神经网络(Feedforward Neural Network):
        接着自注意力机制,每个位置的向量表示会经过一个全连接的前馈神经网络。这个网络在每个位置上独立地作用,增强了模型对不同位置的抽象能力。

3. 残差连接(Residual Connection)与层归一化(Layer Normalization):
        在每个子层(自注意力和前馈神经网络)内部,残差连接允许输入直接传递到输出,从而帮助避免梯度消失或爆炸问题。层归一化则有助于加速训练过程,提高模型的泛化能力。

4. 层堆叠(Layer Stacking):
        多个编码器层依次堆叠在一起,每一层的输出作为下一层的输入。通过增加层的数量,模型可以学习更复杂的特征和表示,提高整体的表达能力。

        解码器负责生成目标语言的序列,它也由多个相同的层堆叠而成,每一层结构如下:

1. 自注意力机制(Self-Attention):
        类似编码器中的操作,解码器的自注意力机制帮助模型在生成序列时理解目标语言序列内部的依赖关系,确保生成的每个词都考虑到了其它词的信息。

2.编码器-解码器注意力机制(Encoder-Decoder Attention):
        在解码器的每一层,除了自注意力机制,还包括一个额外的注意力机制,它帮助解码器访问输入序列的编码器输出。这种注意力机制有助于将输入序列的信息传递给解码器,以便更好地生成正确的翻译。

3. 前馈神经网络(Feedforward Neural Network):
        类似编码器,解码器的每个位置向量也会经过一个前馈神经网络,用于映射和处理自注意力机制的输出。

4. 残差连接与层归一化:
        解码器层内部同样应用残差连接和层归一化,以促进信息流动并增强模型的训练稳定性和效果。

5. 层堆叠:
        解码器也是通过堆叠多个层来构建,每一层的输出作为下一层的输入,以逐步生成目标语言序列。

        编码器和解码器在Transformer模型中的结合使用,使得模型能够同时处理输入和输出序列,通过自注意力和层间连接提升了模型的表示能力和学习效率,从而在各种自然语言处理任务中表现出色。

2.2.2 位置编码 

         位置编码(Positional Encoding)在Transformer模型中发挥着重要作用,主要用于提供序列中词语的位置信息。由于Transformer模型不使用传统的循环神经网络(RNN)或卷积神经网络(CNN)来处理序列数据,它缺乏自然的序列顺序信息。因此,需要引入位置编码来补充这一信息。

其中,位置编码的公式如下所示:

        其中Pos表示位置、 i表示维度、d_{model}表示位置向量的向量维度 、2i2i+1表示的是奇偶数(奇偶维度),上图所示就是偶数位置使用sin函数,奇数位置使用cos函数。 这个公式保证了位置编码可以反映出词语的相对和绝对位置,并且支持模型学习到位置的不同尺度。

2.2.3 多头注意力

        多头注意力(Multi-head Attention)是Transformer模型中的关键组件之一,它允许模型同时对输入进行多个不同方式的注意力计算,从而更好地捕捉输入序列中的不同关系。

        在Transformer中,注意力机制用于计算一个输入序列中各个位置之间的依赖关系。多头注意力通过引入多组(头)注意力机制并行工作,每组注意力可以学习不同的注意力权重。每个头都生成一个注意力输出,然后这些输出会被串联或加权组合,最终形成最终的多头注意力输出。

 

        多头相当于把原始信息 Source 放入了多个子空间中,也就是捕捉了多个信息,对于使用 multi-head(多头) attention 的简单回答就是,多头保证了 attention 可以注意到不同子空间的信息,捕捉到更加丰富的特征信息。 

三、基于Transformer的翻译模型实现

        由于本人的电脑配置较低,因此本实验同样在AutoDL平台上完成。

        首先导入模型需要的库以及确认cuda是否可用。

import math
import torchtext
import torch
import torch.nn as nn
from torch import Tensor
from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import DataLoader
from collections import Counter
from torchtext.vocab import Vocab
from torch.nn import TransformerEncoder, TransformerDecoder, TransformerEncoderLayer, TransformerDecoderLayer
import io
import time
import pandas as pd
import numpy as np
import pickle
import tqdm
import sentencepiece as spm
torch.manual_seed(0)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

        相关库的版本如下:

torchtext==0.6.0

torch==2.3.1

torchvision==0.18.0

tqdm==4.65.0

sentencepiece==0.2.0

        然后导入语料文件,本实验选用的是‘zh-ja.bicleaner05.txt’文件

# 导入 pandas 库,并使用 read_csv 函数读取指定路径下的文本文件
import pandas as pd
df = pd.read_csv('zh-ja.bicleaner05.txt', sep='\\t', engine='python', header=None)

# 从数据框中提取第三列(索引为2)的值,并转换为列表类型,存储在 trainen 变量中
trainen = df[2].values.tolist()  #[:10000]

# 从数据框中提取第四列(索引为3)的值,并转换为列表类型,存储在 trainja 变量中
trainja = df[3].values.tolist()  #[:10000]

        部分语料文件示例如下图所示

        与英语或其他字母语言不同,日语句子中没有空格来分隔单词。我们可以使用JParaCrawl提供的分词器,它使用了SentencePiece来处理日语和英语。如果有需要,您可以访问JParaCrawl的网站下载它们。

        下面对该分词器模型进行导入,并测试其功能。

# 初始化一个英文字符嵌入模型处理器,加载指定的模型文件
en_tokenizer = spm.SentencePieceProcessor(model_file='spm.en.nopretok.model')

# 初始化一个日文字符嵌入模型处理器,加载指定的模型文件
ja_tokenizer = spm.SentencePieceProcessor(model_file='spm.ja.nopretok.model')

# 使用 en_tokenizer 对给定的英文文本进行编码,将文本转换为编码后的字符串表示
en_tokenizer.encode("All residents aged 20 to 59 years who live in Japan must enroll in public pension system.", out_type='str')

# 使用 ja_tokenizer 对给定的日文文本进行编码,将文本转换为编码后的字符串表示
ja_tokenizer.encode("年金 日本に住んでいる20歳~60歳の全ての人は、公的年金制度に加入しなければなりません。", out_type='str')

         测试效果如下,左图为英文测试效果,右图为日文测试效果:

                                                              

        使用来自TorchText的标记器和原始句子,我们首先构建Vocab对象。 利用给定的tokenizer和原始句子,统计词频并构建出两种语言(日语和英语)的词汇表,以便后续在文本处理和模型训练中使用。一旦我们有了词汇表对象和标记器对象,我们就可以利用它们来为我们的训练数据构建张量。

def build_vocab(sentences, tokenizer):
    counter = Counter()  # 创建一个计数器对象用于统计词频
    for sentence in sentences:
        counter.update(tokenizer.encode(sentence, out_type=str))  # 使用给定的 tokenizer 对每个句子进行编码,并更新计数器
    return Vocab(counter, specials=['<unk>', '<pad>', '<bos>', '<eos>'])

# 使用 ja_tokenizer 构建日语词汇表,通过统计 trainja 中所有句子的编码词频来实现
ja_vocab = build_vocab(trainja, ja_tokenizer)

# 使用 en_tokenizer 构建英语词汇表,通过统计 trainen 中所有句子的编码词频来实现
en_vocab = build_vocab(trainen, en_tokenizer)

def data_process(ja, en):
    data = []  # 创建一个空列表用于存储处理后的数据
    for (raw_ja, raw_en) in zip(ja, en):
        # 使用 ja_tokenizer 对日语句子进行编码,并转换为 PyTorch 的 LongTensor 格式
        ja_tensor_ = torch.tensor([ja_vocab[token] for token in ja_tokenizer.encode(raw_ja.rstrip("\n"), out_type=str)],
                                  dtype=torch.long)
        # 使用 en_tokenizer 对英语句子进行编码,并转换为 PyTorch 的 LongTensor 格式
        en_tensor_ = torch.tensor([en_vocab[token] for token in en_tokenizer.encode(raw_en.rstrip("\n"), out_type=str)],
                                  dtype=torch.long)
        # 将处理后的日语和英语句子对作为元组加入到 data 列表中
        data.append((ja_tensor_, en_tensor_))
    return data

# 使用 data_process 函数处理训练数据 trainja 和 trainen,生成包含编码后数据的 train_data 列表
train_data = data_process(trainja, trainen)

        接下来我们开始准备训练数据的迭代器train_iter,用于在模型训练过程中加载数据批次,并对日语和英语句子进行预处理、标记化和填充,以便输入到神经网络模型中进行训练。 

BATCH_SIZE = 8  # 定义批量大小为 8
PAD_IDX = ja_vocab['<pad>']  # 获取日语词汇表中 '<pad>' 标记的索引
BOS_IDX = ja_vocab['<bos>']  # 获取日语词汇表中 '<bos>' 标记的索引
EOS_IDX = ja_vocab['<eos>']  # 获取日语词汇表中 '<eos>' 标记的索引

def generate_batch(data_batch):
    ja_batch, en_batch = [], []  # 创建空列表用于存储日语和英语句子批次
    for (ja_item, en_item) in data_batch:
        # 在日语句子的开头和结尾添加 '<bos>' 和 '<eos>' 标记,并将其连接为一个 Tensor
        ja_batch.append(torch.cat([torch.tensor([BOS_IDX]), ja_item, torch.tensor([EOS_IDX])], dim=0))
        # 在英语句子的开头和结尾添加 '<bos>' 和 '<eos>' 标记,并将其连接为一个 Tensor
        en_batch.append(torch.cat([torch.tensor([BOS_IDX]), en_item, torch.tensor([EOS_IDX])], dim=0))
    
    # 对日语句子和英语句子批次进行填充,使用日语词汇表中的 '<pad>' 标记进行填充
    ja_batch = pad_sequence(ja_batch, padding_value=PAD_IDX)
    en_batch = pad_sequence(en_batch, padding_value=PAD_IDX)
    
    return ja_batch, en_batch

# 使用 DataLoader 创建训练数据的迭代器 train_iter,每次迭代会返回一个由 generate_batch 函数生成的批次数据
train_iter = DataLoader(train_data, batch_size=BATCH_SIZE,
                        shuffle=True, collate_fn=generate_batch)

        上述准备工作结束后,我们可以编写一个完整的Transformer架构的编码器-解码器模型,用于处理序列到序列的翻译任务,包括编码器、解码器、词嵌入、位置编码等关键组件的定义和前向传播逻辑的实现。 

from torch.nn import (TransformerEncoder, TransformerDecoder,
                      TransformerEncoderLayer, TransformerDecoderLayer)

class Seq2SeqTransformer(nn.Module):
    def __init__(self, num_encoder_layers: int, num_decoder_layers: int,
                 emb_size: int, src_vocab_size: int, tgt_vocab_size: int,
                 dim_feedforward:int = 512, dropout:float = 0.1):
        super(Seq2SeqTransformer, self).__init__()
        
        # 定义 Transformer 编码器和解码器层
        encoder_layer = TransformerEncoderLayer(d_model=emb_size, nhead=NHEAD,
                                                dim_feedforward=dim_feedforward)
        self.transformer_encoder = TransformerEncoder(encoder_layer, num_layers=num_encoder_layers)
        
        decoder_layer = TransformerDecoderLayer(d_model=emb_size, nhead=NHEAD,
                                                dim_feedforward=dim_feedforward)
        self.transformer_decoder = TransformerDecoder(decoder_layer, num_layers=num_decoder_layers)

        # 定义生成器,将解码器输出映射到目标词汇表的大小
        self.generator = nn.Linear(emb_size, tgt_vocab_size)
        
        # 定义源语言和目标语言的词嵌入层
        self.src_tok_emb = TokenEmbedding(src_vocab_size, emb_size)
        self.tgt_tok_emb = TokenEmbedding(tgt_vocab_size, emb_size)
        
        # 定义位置编码层,用于增强模型对序列顺序的理解
        self.positional_encoding = PositionalEncoding(emb_size, dropout=dropout)

    def forward(self, src: Tensor, trg: Tensor, src_mask: Tensor,
                tgt_mask: Tensor, src_padding_mask: Tensor,
                tgt_padding_mask: Tensor, memory_key_padding_mask: Tensor):
        # 对源语言和目标语言序列进行位置编码
        src_emb = self.positional_encoding(self.src_tok_emb(src))
        tgt_emb = self.positional_encoding(self.tgt_tok_emb(trg))
        
        # 编码器将源语言序列编码为内部表示
        memory = self.transformer_encoder(src_emb, src_mask, src_padding_mask)
        
        # 解码器根据编码器的输出和目标语言序列进行解码
        outs = self.transformer_decoder(tgt_emb, memory, tgt_mask, None,
                                        tgt_padding_mask, memory_key_padding_mask)
        
        # 使用生成器将解码器的输出映射到目标词汇表的维度
        return self.generator(outs)

    def encode(self, src: Tensor, src_mask: Tensor):
        # 编码器单独编码源语言序列,用于生成内部表示或者用于生成翻译时的记忆
        return self.transformer_encoder(self.positional_encoding(
                            self.src_tok_emb(src)), src_mask)

    def decode(self, tgt: Tensor, memory: Tensor, tgt_mask: Tensor):
        # 解码器根据记忆和目标语言序列生成翻译输出
        return self.transformer_decoder(self.positional_encoding(
                          self.tgt_tok_emb(tgt)), memory,
                          tgt_mask)

        而在开始训练之前,我们还需要定义两个类来完成位置编码的定义。这两个类提供了Transformer模型中词嵌入和位置编码的核心功能。其中TokenEmbedding 类负责将单词索引转换为密集的词嵌入向量,并进行适当的缩放;而PositionalEncoding 类引入了位置编码,以增强模型对序列顺序的理解能力,通过添加正弦和余弦函数计算得到的位置信息。

class PositionalEncoding(nn.Module):
    def __init__(self, emb_size: int, dropout, maxlen: int = 5000):
        super(PositionalEncoding, self).__init__()
        
        # 计算位置编码的系数
        den = torch.exp(- torch.arange(0, emb_size, 2) * math.log(10000) / emb_size)
        # 构建位置编码矩阵
        pos = torch.arange(0, maxlen).reshape(maxlen, 1)
        pos_embedding = torch.zeros((maxlen, emb_size))
        # 根据位置和计算得到的系数,分别计算 sin 和 cos 的位置编码
        pos_embedding[:, 0::2] = torch.sin(pos * den)
        pos_embedding[:, 1::2] = torch.cos(pos * den)
        pos_embedding = pos_embedding.unsqueeze(-2)

        # 初始化 dropout 层和位置编码张量
        self.dropout = nn.Dropout(dropout)
        self.register_buffer('pos_embedding', pos_embedding)

    def forward(self, token_embedding: Tensor):
        # 在输入的 token_embedding 上加上位置编码,并通过 dropout 处理后返回
        return self.dropout(token_embedding + self.pos_embedding[:token_embedding.size(0), :])

class TokenEmbedding(nn.Module):
    def __init__(self, vocab_size: int, emb_size):
        super(TokenEmbedding, self).__init__()
        
        # 定义词嵌入层
        self.embedding = nn.Embedding(vocab_size, emb_size)
        self.emb_size = emb_size

    def forward(self, tokens: Tensor):
        # 返回词嵌入乘以 sqrt(词嵌入维度) 的结果
        return self.embedding(tokens.long()) * math.sqrt(self.emb_size)

        同样,我们还需要为Transformer模型的输入序列和目标序列生成了必要的遮掩矩阵,以确保模型在自注意力和注意力机制中能够正确处理序列中的信息和填充部分。

def generate_square_subsequent_mask(sz):
    # 创建一个上三角矩阵
    mask = (torch.triu(torch.ones((sz, sz), device=device)) == 1).transpose(0, 1)
    # 将矩阵转换为浮点型,并根据条件填充特定值
    mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
    return mask

def create_mask(src, tgt):
    # 获取源序列和目标序列的长度
    src_seq_len = src.shape[0]
    tgt_seq_len = tgt.shape[0]

    # 生成目标序列的遮掩矩阵
    tgt_mask = generate_square_subsequent_mask(tgt_seq_len)
    # 创建源序列的零矩阵作为源序列遮掩
    src_mask = torch.zeros((src_seq_len, src_seq_len), device=device).type(torch.bool)

    # 创建源序列和目标序列的填充遮掩
    src_padding_mask = (src == PAD_IDX).transpose(0, 1)
    tgt_padding_mask = (tgt == PAD_IDX).transpose(0, 1)

    return src_mask, tgt_mask, src_padding_mask, tgt_padding_mask

        这下,我们可以实现一个完整的Transformer模型的训练和评估流程了,包括数据加载、遮掩矩阵的生成、模型的前向传播与反向传播优化过程,以及训练和验证损失的计算。 

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
        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(val_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
        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)

        现在,我们执行并监控一个训练周期(epoch)的过程 。

for epoch in tqdm.tqdm(range(1, NUM_EPOCHS+1)):
    start_time = time.time()
    # 开始计时

    # 训练模型一个 epoch
    train_loss = train_epoch(transformer, train_iter, optimizer)
    
    # 结束计时
    end_time = time.time()

    # 打印当前 epoch 的训练损失和训练时间
    print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, "
           f"Epoch time = {(end_time - start_time):.3f}s"))

        训练过程如下所示 :

        训练结束后,我们对训练好的模型进行测试。通过贪婪解码策略实现了简单的机器翻译功能,将源语句翻译成目标语句。它结合了 Transformer 模型的编码器和解码器部分,利用模型生成的能力完成文本序列的转换任务。

def greedy_decode(model, src, src_mask, max_len, start_symbol):
    src = src.to(device)
    src_mask = src_mask.to(device)
    
    # 编码阶段,将输入序列编码成内部表示
    memory = model.encode(src, src_mask)
    
    # 初始化目标序列,以起始符号开始
    ys = torch.ones(1, 1).fill_(start_symbol).type(torch.long).to(device)
    
    # 逐步生成目标序列直到达到最大长度或遇到终止符号
    for i in range(max_len-1):
        memory = memory.to(device)
        
        # 创建目标序列的掩码和记忆掩码
        memory_mask = torch.zeros(ys.shape[0], memory.shape[0]).to(device).type(torch.bool)
        tgt_mask = (generate_square_subsequent_mask(ys.size(0))
                                    .type(torch.bool)).to(device)
        
        # 解码阶段,根据当前目标序列预测下一个词
        out = model.decode(ys, memory, tgt_mask)
        out = out.transpose(0, 1)
        prob = model.generator(out[:, -1])
        _, next_word = torch.max(prob, dim=1)
        next_word = next_word.item()
        
        # 将预测的词添加到目标序列中
        ys = torch.cat([ys,
                        torch.ones(1, 1).type_as(src.data).fill_(next_word)], dim=0)
        
        # 如果预测的词是终止符号,则停止生成
        if next_word == EOS_IDX:
            break
    
    return ys

def translate(model, src, src_vocab, tgt_vocab, src_tokenizer):
    model.eval()
    
    # 对源语句进行分词和转换成词汇索引序列
    tokens = [BOS_IDX] + [src_vocab.stoi[tok] for tok in src_tokenizer.encode(src, out_type=str)]+ [EOS_IDX]
    num_tokens = len(tokens)
    src = (torch.LongTensor(tokens).reshape(num_tokens, 1) )
    src_mask = (torch.zeros(num_tokens, num_tokens)).type(torch.bool)
    
    # 使用贪婪解码生成目标语句的词汇索引序列
    tgt_tokens = greedy_decode(model,  src, src_mask, max_len=num_tokens + 5, start_symbol=BOS_IDX).flatten()
    
    # 将词汇索引序列转换为目标语句字符串
    return " ".join([tgt_vocab.itos[tok] for tok in tgt_tokens]).replace("<bos>", "").replace("<eos>", "")

        接下来我们给出测试用例: 

translate(transformer, "HSコード 8515 はんだ付け用、ろう付け用又は溶接用の機器(電気式(電気加熱ガス式を含む。)", ja_vocab, en_vocab, ja_tokenizer)

        翻译结果如下:

 ' ▁H S ▁ 代 码 ▁85 15 ▁ 用 于 焊 接 应 用 的 焊 接 设 备 ( 包 括 电 热 加 热 ) 。 '

         最后,当翻译完成后,我们会用Pickle保存内容。

import pickle

# 导入pickle库

# 打开一个文件,用于存储数据
file = open('en_vocab.pkl', 'wb')

# 将英语词汇表(en_vocab)的信息存储到文件中
pickle.dump(en_vocab, file)

# 关闭文件
file.close()

# 打开一个文件,用于存储数据
file = open('ja_vocab.pkl', 'wb')

# 将日语词汇表(ja_vocab)的信息存储到文件中
pickle.dump(ja_vocab, file)

# 关闭文件
file.close()

        我们还可以使用 PyTorch 的保存和加载函数来保存模型以备后用。一般来说,根据后续使用的不同,有两种保存模型的方式。第一种是仅用于推理,我们可以稍后加载模型并用它来从日语翻译成英语。第二种方式也是用于推理,但同时也适用于稍后加载模型并且希望恢复训练的情况。

# save model for inference
torch.save(transformer.state_dict(), 'inference_model')

# save model + checkpoint to resume training later
torch.save({
  'epoch': NUM_EPOCHS,
  'model_state_dict': transformer.state_dict(),
  'optimizer_state_dict': optimizer.state_dict(),
  'loss': train_loss,
  }, 'model_checkpoint.tar')

        至此,本次实验全部结束。感兴趣的同学可以复现一下代码或者更深入的了解一下,建议有GPU的情况下进行训练,并且GPU的等级不同训练时长会有较大差距。

四、小结与致谢

        本人是一位NLP新手,初来乍到,刚刚开始学习,欢迎大家对我的博客进行批评指正,也欢迎大家留言讨论。最后,感谢我NLP老师董老师在理论以及代码部分的讲解与教学,感谢各位同学对我的帮助。谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值