jp_cn_translation

使用Transformer和PyTorch的日中机器翻译模型

使用Jupyter Notebook、PyTorch、Torchtext和SentencePiece的教程

导入所需包

首先,确保我们的系统中安装了以下包,如果发现某些包缺失,请确保安装它们。

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
import torchtext
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')
device
device(type='cuda')

获取平行数据集

在本教程中,我们将使用从JParaCrawl下载的日英平行数据集,(http://www.kecl.ntt.co.jp/icl/lirg/jparacrawl)该数据集被描述为“由NTT创建的最大公开可用的英日平行语料库。它通过大规模爬取网络并自动对齐平行句子而创建。”你也可以在这里查看相关论文。

# 读取CSV文件,使用制表符作为分隔符,使用python引擎,不使用表头
df = pd.read_csv('zh-ja.bicleaner05.txt', sep='\\t', engine='python', header=None)

# 获取第三列的数据并转换为列表,可选取前10000条数据
trainen = df[2].values.tolist()#[:10000]

# 获取第四列的数据并转换为列表,可选取前10000条数据
trainja = df[3].values.tolist()#[:10000]
# trainen.pop(5972)
# trainja.pop(5972)
print(trainen[500])
print(trainja[500])
Chinese HS Code Harmonized Code System < HS编码 2905 无环醇及其卤化、磺化、硝化或亚硝化衍生物 HS Code List (Harmonized System Code) for US, UK, EU, China, India, France, Japan, Russia, Germany, Korea, Canada ...
Japanese HS Code Harmonized Code System < HSコード 2905 非環式アルコール並びにそのハロゲン化誘導体、スルホン化誘導体、ニトロ化誘導体及びニトロソ化誘導体 HS Code List (Harmonized System Code) for US, UK, EU, China, India, France, Japan, Russia, Germany, Korea, Canada ...

我们也可以使用不同的平行数据集来跟随本文,只需确保我们可以将数据处理成如上所示的两个字符串列表,分别包含日语和英语句子。

准备分词器

与英语或其他字母语言不同,日语句子不包含用于分隔单词的空格。我们可以使用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.encode("All residents aged 20 to 59 years who live in Japan must enroll in public pension system.", out_type=str)
['▁All',
 '▁residents',
 '▁aged',
 '▁20',
 '▁to',
 '▁59',
 '▁years',
 '▁who',
 '▁live',
 '▁in',
 '▁Japan',
 '▁must',
 '▁enroll',
 '▁in',
 '▁public',
 '▁pension',
 '▁system',
 '.']
ja_tokenizer.encode("年金 日本に住んでいる20歳~60歳の全ての人は、公的年金制度に加入しなければなりません。", out_type=str)
['▁',
 '年',
 '金',
 '▁日本',
 'に住んでいる',
 '20',
 '歳',
 '~',
 '60',
 '歳の',
 '全ての',
 '人は',
 '、',
 '公的',
 '年',
 '金',
 '制度',
 'に',
 '加入',
 'しなければなりません',
 '。']

构建TorchText的Vocab对象并将句子转换为Torch张量

使用分词器和原始句子,我们随后构建从TorchText导入的Vocab对象。这个过程可能需要几秒钟或几分钟,具体取决于数据集的大小和计算能力。不同的分词器也会影响构建词汇表所需的时间,我尝试了其他几种日语分词器,但SentencePiece似乎工作得很好且足够快。

def build_vocab(sentences, tokenizer):
    # 创建一个计数器来统计词频
    counter = Counter()
    # 定义特殊标记
    specials = ["<unk>", "<pad>", "<bos>", "<eos>"]
    for sentence in sentences:
      counter.update(tokenizer.encode(sentence, out_type=str))      
    return Vocab(counter)


# 构建日语词汇表
ja_vocab = build_vocab(trainja, ja_tokenizer)
# 构建英语词汇表
en_vocab = build_vocab(trainen, en_tokenizer)

在拥有了词汇表对象之后,我们可以使用词汇表和分词器对象来构建训练数据的张量。

def data_process(ja, en):
  data = []
  # 遍历日语和英语的原始数据
  for (raw_ja, raw_en) in zip(ja, en):
    # 将日语文本转换为张量
    ja_tensor_ = torch.tensor([ja_vocab[token] for token in ja_tokenizer.encode(raw_ja.rstrip("\n"), out_type=str)],
                            dtype=torch.long)
    # 将英语文本转换为张量
    en_tensor_ = torch.tensor([en_vocab[token] for token in en_tokenizer.encode(raw_en.rstrip("\n"), out_type=str)],
                            dtype=torch.long)
    # 将日语和英语张量对添加到数据列表中
    data.append((ja_tensor_, en_tensor_))
  # 返回处理后的数据列表
  return data
# 处理训练数据
train_data = data_process(trainja, trainen)

创建DataLoader对象以便在训练期间迭代

在这里,我将BATCH_SIZE设置为16以防止“cuda out of memory”,但这取决于各种因素,例如机器内存容量、数据大小等,因此可以根据需要自由更改批量大小(注意:PyTorch教程中使用Multi30k德英数据集将批量大小设置为128。)

BATCH_SIZE = 8  # 定义批处理大小
PAD_IDX = ja_vocab['<pad>']  # 获取填充字符的索引
BOS_IDX = ja_vocab['<bos>']  # 获取句子开始字符的索引
EOS_IDX = ja_vocab['<eos>']  # 获取句子结束字符的索引

def generate_batch(data_batch):  # 生成批处理数据的函数
  ja_batch, en_batch = [], []  # 初始化日语和英语的批处理列表
  for (ja_item, en_item) in data_batch:  # 遍历数据批处理中的每一项
    ja_batch.append(torch.cat([torch.tensor([BOS_IDX]), ja_item, torch.tensor([EOS_IDX])], dim=0))  # 将日语句子加上开始和结束标记
    en_batch.append(torch.cat([torch.tensor([BOS_IDX]), en_item, torch.tensor([EOS_IDX])], dim=0))  # 将英语句子加上开始和结束标记
  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  # 返回填充后的日语和英语批处理

train_iter = DataLoader(train_data, batch_size=BATCH_SIZE,  # 创建训练数据加载器
                        shuffle=True, collate_fn=generate_batch)  # 设置随机打乱和批处理生成函数

序列到序列的Transformer

接下来的几段代码和文字解释(以斜体书写)来自原始的PyTorch教程[https://pytorch.org/tutorials/beginner/translation_transformer.html]。除了BATCH_SIZE和将de_vocab改为ja_vocab之外,我没有做任何改动。

Transformer是一种Seq2Seq模型,在“Attention is all you need”论文中被引入,用于解决机器翻译任务。Transformer模型由一个编码器和一个解码器块组成,每个块包含固定数量的层。

编码器通过一系列多头注意力和前馈网络层传播输入序列来处理输入序列。编码器的输出称为记忆,与目标张量一起输入到解码器中。编码器和解码器使用教师强制技术进行端到端训练。

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

class Seq2SeqTransformer(nn.Module):
    """
    定义一个序列到序列的Transformer模型
    """
    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__()
        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)

文本标记通过使用标记嵌入来表示。位置编码被添加到标记嵌入中,以引入词序的概念。

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))
        pos_embedding[:, 0::2] = torch.sin(pos * den)
        pos_embedding[:, 1::2] = torch.cos(pos * den)
        pos_embedding = pos_embedding.unsqueeze(-2)

        self.dropout = nn.Dropout(dropout)
        self.register_buffer('pos_embedding', pos_embedding)

    def forward(self, token_embedding: Tensor):
        # 前向传播方法,将位置编码添加到词嵌入中
        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):
        # 前向传播方法,将输入的词转换为词嵌入向量
        return self.embedding(tokens.long()) * math.sqrt(self.emb_size)

我们创建一个后续词掩码,以阻止目标词关注其后续词。我们还创建掩码,用于掩蔽源和目标填充标记。

def generate_square_subsequent_mask(sz):
    # 生成一个上三角矩阵掩码
    mask = (torch.triu(torch.ones((sz, sz), device=device)) == 1).transpose(0, 1)
    # 将掩码中的0替换为负无穷,1替换为0.0
    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)
    # 生成源序列的掩码,全0表示没有掩码
    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

Define model parameters and instantiate model. 这里我们服务器实在是计算能力有限,按照以下配置可以训练但是效果应该是不行的。如果想要看到训练的效果请使用你自己的带GPU的电脑运行这一套代码。

当你使用自己的GPU的时候,NUM_ENCODER_LAYERS 和 NUM_DECODER_LAYERS 设置为3或者更高,NHEAD设置8,EMB_SIZE设置为512。

import os
# 启用 CUDA 同步调试


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)  # 创建Seq2SeqTransformer模型实例

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(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 = 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)
c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\nn\modules\transformer.py:306: UserWarning: enable_nested_tensor is True, but self.use_nested_tensor is False because encoder_layer.self_attn.batch_first was not True(use batch_first for better inference performance)
  warnings.warn(f"enable_nested_tensor is True, but self.use_nested_tensor is False because {why_not_sparsity_fast_path}")

Start training

开始训练

最后,在准备好必要的类和函数之后,我们就可以开始训练我们的模型了。这不用说,但完成训练所需的时间可能会有很大差异,具体取决于许多因素,例如计算能力、参数和数据集的大小。

当我使用JParaCrawl的完整句子列表(每种语言大约有590万句话)训练模型时,使用单个NVIDIA GeForce RTX 3070 GPU,每个epoch大约需要5小时。

import os
os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
os.environ['TORCH_USE_CUDA_DSA'] = '1'

for epoch in tqdm.tqdm(range(1, NUM_EPOCHS+1)):
  start_time = time.time()
  train_loss = train_epoch(transformer, train_iter, optimizer)
  end_time = time.time()
  print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, "
          f"Epoch time = {(end_time - start_time):.3f}s"))
  0%|          | 0/16 [00:00<?, ?it/s]c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\nn\functional.py:5504: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\cb\pytorch_1000000000000\work\aten\src\ATen\native\transformers\cuda\sdp_utils.cpp:455.)
  attn_output = scaled_dot_product_attention(q, k, v, attn_mask, dropout_p, is_causal)
c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\nn\functional.py:5137: UserWarning: Support for mismatched key_padding_mask and attn_mask is deprecated. Use same type for both instead.
  warnings.warn(
  6%|▋         | 1/16 [07:27<1:51:46, 447.13s/it]

Epoch: 1, Train loss: 4.471, Epoch time = 447.129s


 12%|█▎        | 2/16 [14:55<1:44:26, 447.60s/it]

Epoch: 2, Train loss: 3.497, Epoch time = 447.934s


 19%|█▉        | 3/16 [22:17<1:36:29, 445.31s/it]

Epoch: 3, Train loss: 3.096, Epoch time = 442.577s


 25%|██▌       | 4/16 [29:39<1:28:48, 444.04s/it]

Epoch: 4, Train loss: 2.805, Epoch time = 442.097s


 31%|███▏      | 5/16 [37:02<1:21:21, 443.75s/it]

Epoch: 5, Train loss: 2.583, Epoch time = 443.243s


 31%|███▏      | 5/16 [40:07<1:28:15, 481.45s/it]



---------------------------------------------------------------------------

KeyboardInterrupt                         Traceback (most recent call last)

Cell In[19], line 7
      5 for epoch in tqdm.tqdm(range(1, NUM_EPOCHS+1)):
      6   start_time = time.time()
----> 7   train_loss = train_epoch(transformer, train_iter, optimizer)
      8   end_time = time.time()
      9   print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, "
     10           f"Epoch time = {(end_time - start_time):.3f}s"))


Cell In[18], line 47, in train_epoch(model, train_iter, optimizer)
     45 tgt_out = tgt[1:,:]
     46 loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))
---> 47 loss.backward()
     49 optimizer.step()
     50 losses += loss.item()


File c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\_tensor.py:525, in Tensor.backward(self, gradient, retain_graph, create_graph, inputs)
    515 if has_torch_function_unary(self):
    516     return handle_torch_function(
    517         Tensor.backward,
    518         (self,),
   (...)
    523         inputs=inputs,
    524     )
--> 525 torch.autograd.backward(
    526     self, gradient, retain_graph, create_graph, inputs=inputs
    527 )


File c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\autograd\__init__.py:267, in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)
    262     retain_graph = create_graph
    264 # The reason we repeat the same comment below is that
    265 # some Python versions print out the first line of a multi-line function
    266 # calls in the traceback and some print out the last line
--> 267 _engine_run_backward(
    268     tensors,
    269     grad_tensors_,
    270     retain_graph,
    271     create_graph,
    272     inputs,
    273     allow_unreachable=True,
    274     accumulate_grad=True,
    275 )


File c:\Users\qu\.conda\envs\python39\lib\site-packages\torch\autograd\graph.py:744, in _engine_run_backward(t_outputs, *args, **kwargs)
    742     unregister_hooks = _register_logging_hooks_on_whole_graph(t_outputs)
    743 try:
--> 744     return Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass
    745         t_outputs, *args, **kwargs
    746     )  # Calls into the C++ engine to run the backward pass
    747 finally:
    748     if attach_logging_hooks:


KeyboardInterrupt: 

报错是因为这里我主动停止了,如果不停止效果会更好。

使用训练好的模型尝试翻译日语句子

首先,我们创建翻译新句子的函数,包括获取日语句子、分词、转换为张量、推理,然后将结果解码回英语句子的步骤。

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)

'<unk> ▁H S 代 码 85 15 15 ▁ 包 括 焊 装 焊 装 、 焊 装 焊 装 和 焊 装 ( 电 气 电 气 加 热 加 热 加 热 ) 。 <unk>'
translate(transformer, "(電気式(電気加熱ガス式を含む。)", ja_vocab, en_vocab, ja_tokenizer)

'<unk> ▁ 电 气 热 电 气 热 气 温 度 ( 包 括 电 气 ) 。'
trainen.pop(5)
'Chinese HS Code Harmonized Code System < HS编码 8515 : 电气(包括电热气体)、激光、其他光、光子束、超声波、电子束、磁脉冲或等离子弧焊接机器及装置,不论是否 HS Code List (Harmonized System Code) for US, UK, EU, China, India, France, Japan, Russia, Germany, Korea, Canada ...'
trainja.pop(5)
'Japanese HS Code Harmonized Code System < HSコード 8515 はんだ付け用、ろう付け用又は溶接用の機器(電気式(電気加熱ガス式を含む。)、レーザーその他の光子ビーム式、超音波式、電子ビーム式、 HS Code List (Harmonized System Code) for US, UK, EU, China, India, France, Japan, Russia, Germany, Korea, Canada ...'

保存Vocab对象和训练好的模型

最后,在训练完成后,我们将首先使用Pickle保存Vocab对象(en_vocab和ja_vocab)。

import pickle
# 打开一个文件,用于存储数据
file = open('en_vocab.pkl', 'wb')
# 将信息写入文件
pickle.dump(en_vocab, file)
file.close()
file = open('ja_vocab.pkl', 'wb')
pickle.dump(ja_vocab, file)
file.close()

最后,我们还可以使用PyTorch的保存和加载函数来保存模型以供以后使用。通常,有两种保存模型的方法,具体取决于我们以后如何使用它们。第一种是仅用于推理,我们可以在以后加载模型并使用它进行日语到英语的翻译。

# 保存模型以供推理使用

torch.save(transformer.state_dict(), 'inference_model')

第二种方法也是用于推理,但如果我们想在以后加载模型并恢复训练,也可以使用这种方法。

# 保存模型和检查点以便稍后恢复训练

torch.save({
  'epoch': NUM_EPOCHS,
  'model_state_dict': transformer.state_dict(),
  'optimizer_state_dict': optimizer.state_dict(),
  'loss': train_loss,
  }, 'model_checkpoint.tar')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值