日语到中文的机器翻译模型构建方法

目录

导入必要的库和设置环境:

获取并处理数据集:

使用SentencePiece进行分词:

构建词汇表和数据处理:

创建数据加载器:

Seq2Seq Transformer模型构建:

模型训练与验证:

使用训练好的模型进行测试

展示训练数据内容

保存模型

这篇文档是关于使用Transformer和PyTorch实现日语到中文的机器翻译模型的教程。

具体步骤如下:

导入必要的库和设置环境

文档开始部分导入了需要使用的Python库,包括PyTorch、Torchtext、SentencePiece等,用于后续的数据处理和模型构建。

PyTorch 的主要作用包括:

  • 定义和训练模型: PyTorch 提供了灵活的方式来定义各种深度学习模型,包括 Transformer 和其组件(如线性层、嵌入层、注意力机制等)。

  • 自动求导: PyTorch 的自动求导机制使得梯度计算非常方便,这对于梯度下降等优化算法是必不可少的。在模型训练过程中,PyTorch 能够自动跟踪和计算张量的梯度,从而实现反向传播和参数更新。

  • GPU加速支持: PyTorch 提供了对 GPU 计算的原生支持,可以利用 CUDA 架构加速模型训练,尤其对于深度学习模型的大规模训练来说,GPU 的加速作用非常明显。

Torchtext的主要作用包括:

  • 数据加载和预处理: Torchtext 提供了简便的数据加载接口,可以从常见的数据格式(如 CSV、TSV)中加载文本数据。通过 Torchtext,可以轻松地对文本数据进行分词、构建词汇表,并将文本转换为模型可以处理的张量格式。

  • 词汇表管理: Torchtext 可以帮助你构建源语言和目标语言的词汇表,并提供了有效的方法将文本数据转换为模型可以接受的输入形式。这在机器翻译任务中尤为重要,因为模型需要根据词汇表将文本转换为索引化的张量。

  • 数据迭代器: Torchtext 提供了数据迭代器(如 DataLoader),能够将预处理后的数据按批次加载,支持在训练过程中进行数据增强(如随机打乱顺序)等操作。

SentencePiece 的作用主要体现在以下几个方面:

  • 多语言分词支持: SentencePiece 支持多种语言的分词,包括日语和中文等,能够有效地处理各种语言中的复杂文本结构和特点。

  • 子词化(Subword Tokenization): SentencePiece 采用 BPE(Byte Pair Encoding)算法或者 Unigram 语言模型来生成子词单元,可以处理罕见词汇和未登录词,从而提升翻译模型对稀有词汇的处理能力。

  • 词汇表生成: SentencePiece 可以根据语料库自动构建词汇表,并将文本数据编码成子词单元序列。这对于处理语言中的词汇表扩展和未登录词处理至关重要。

综上所述,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
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')

获取并处理数据集

  • 使用了一个从JParaCrawl下载的日语-英语平行语料库。数据集以TSV格式提供,并通过Pandas库读取和处理。
  • 原始数据集包含约597万条句子,但为了演示目的,文档中选择了部分数据(示例中为前10000条)进行处理。
df = pd.read_csv('/kaggle/input/machine-translation/zh-ja.bicleaner05.txt', sep='\\t', engine='python', header=None)

# 提取 DataFrame 中的第三列(索引为2)并转换为列表,保存为 trainen
trainen = df[2].values.tolist()#[:10000]

# 提取 DataFrame 中的第四列(索引为3)并转换为列表,保存为 trainja
trainja = df[3].values.tolist()#[:10000]
# 打印训练数据列表中索引为500的英文句子
print(trainen[500])

# 打印训练数据列表中索引为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 ...

使用SentencePiece进行分词

  • 为了处理日语和英语这种没有明显分词界限的语言,使用了SentencePiece进行分词。分别加载了日语和英语的预训练模型,并进行了简单的分词测试。
# 加载英文分词模型
en_tokenizer = spm.SentencePieceProcessor(model_file='/kaggle/input/machine-translation/spm.en.nopretok.model')

# 加载日文分词模型
ja_tokenizer = spm.SentencePieceProcessor(model_file='/kaggle/input/machine-translation/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)

# 使用日文分词模型对给定句子进行分词
ja_tokenizer.encode("年金 日本に住んでいる20歳~60歳の全ての人は、公的年金制度に加入しなければなりません。", out_type=str)

结果:
英文分词

['▁All',
 '▁residents',
 '▁aged',
 '▁20',
 '▁to',
 '▁59',
 '▁years',
 '▁who',
 '▁live',
 '▁in',
 '▁Japan',
 '▁must',
 '▁enroll',
 '▁in',
 '▁public',
 '▁pension',
 '▁system',
 '.']

日文分词

['▁',
 '年',
 '金',
 '▁日本',
 'に住んでいる',
 '20',
 '歳',
 '~',
 '60',
 '歳の',
 '全ての',
 '人は',
 '、',
 '公的',
 '年',
 '金',
 '制度',
 'に',
 '加入',
 'しなければなりません',
 '。']

构建词汇表和数据处理

  • 使用TorchText的build_vocab_from_iterator方法构建了日语和英语的词汇表(Vocab)。
  • 编写了数据处理函数,将原始文本转换为模型需要的张量形式,同时添加了特殊标记如<bos>(句子开始)和<eos>(句子结束)。
from collections import Counter
from torchtext.vocab import build_vocab_from_iterator

# 定义函数构建词汇表
def build_vocab_iterator(sentences, tokenizer):
    for sentence in sentences:
        yield tokenizer.encode(sentence, out_type=str)

# 构建日文词汇表
ja_vocab = build_vocab_from_iterator(build_vocab_iterator(trainja, ja_tokenizer), specials=['<unk>', '<pad>', '<bos>', '<eos>'])

# 构建英文词汇表
en_vocab = build_vocab_from_iterator(build_vocab_iterator(trainen, en_tokenizer), specials=['<unk>', '<pad>', '<bos>', '<eos>'])

创建数据加载器

  • 使用PyTorch的DataLoader创建了用于训练的数据加载器。数据加载器每次会生成一个批次的数据,这些数据已经通过填充(padding)变成了等长的张量。
# 定义数据处理函数
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)
# 定义批处理大小
BATCH_SIZE = 8

# 获取词汇表中 <pad> 的索引
PAD_IDX = ja_vocab['<pad>']

# 获取词汇表中 <bos> 的索引
BOS_IDX = ja_vocab['<bos>']

# 获取词汇表中 <eos> 的索引
EOS_IDX = ja_vocab['<eos>']

# 定义生成批处理的函数
def generate_batch(data_batch):
    ja_batch, en_batch = [], []
    for (ja_item, en_item) in data_batch:
        # 为每个日文句子添加 <bos> 和 <eos> 标记
        ja_batch.append(torch.cat([torch.tensor([BOS_IDX]), ja_item, torch.tensor([EOS_IDX])], dim=0))
        # 为每个英文句子添加 <bos> 和 <eos> 标记
        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)

Seq2Seq Transformer模型构建

  • 基于Transformer架构,定义了Seq2Seq Transformer模型。该模型包括了编码器(TransformerEncoder)和解码器(TransformerDecoder),以及相应的层和参数初始化方法。
# 导入 Transformer 所需的模块
from torch.nn import (TransformerEncoder, TransformerDecoder, TransformerEncoderLayer, TransformerDecoderLayer)

# 定义 Seq2Seq Transformer 模型
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__()
        # 定义编码器层
        encoder_layer = TransformerEncoderLayer(d_model=emb_size, nhead=NHEAD, dim_feedforward=dim_feedforward)
        # 创建 Transformer 编码器
        self.transformer_encoder = TransformerEncoder(encoder_layer, num_layers=num_encoder_layers)
        # 定义解码器层
        decoder_layer = TransformerDecoderLayer(d_model=emb_size, nhead=NHEAD, dim_feedforward=dim_feedforward)
        # 创建 Transformer 解码器
        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)
  • 通过使用位置编码(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函数)
        pos_embedding[:, 0::2] = torch.sin(pos * den)
        # 计算偶数位上的位置编码值(使用cos函数)
        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):

        # 将输入的词嵌入与对应的位置信息相加,并经过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):

        # 将输入词汇转换为长整型索引,通过嵌入层获取词嵌入,并进行缩放
        return self.embedding(tokens.long()) * math.sqrt(self.emb_size)

模型训练与验证

  • 定义了训练函数和验证函数,分别用于训练和评估模型。
  • 训练过程中使用了Adam优化器和交叉熵损失函数,并实现了基于Teacher Forcing的训练策略。
# 定义各类参数

# 源语言词汇表大小(即日文词汇表大小)
SRC_VOCAB_SIZE = len(ja_vocab)

# 目标语言词汇表大小(即英文词汇表大小)
TGT_VOCAB_SIZE = len(en_vocab)

# 词嵌入的维度,即嵌入层和 Transformer 模型中的维度大小
EMB_SIZE = 512

# 多头注意力机制中头的数量
NHEAD = 8

# 前馈神经网络隐藏层的维度
FFN_HID_DIM = 512

# 批处理大小,指每个批次中的样本数
BATCH_SIZE = 16

# 编码器层的数量
NUM_ENCODER_LAYERS = 3

# 解码器层的数量
NUM_DECODER_LAYERS = 3

# 训练的总轮数,即训练模型时的 epoch 数量
NUM_EPOCHS = 10

# 创建 Seq2Seq Transformer 模型实例,传入之前定义的参数
transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS, EMB_SIZE, SRC_VOCAB_SIZE, TGT_VOCAB_SIZE, FFN_HID_DIM)

# 初始化模型参数
for p in transformer.parameters():
    # 如果参数是二维的(比如权重矩阵),则使用 Xavier 均匀分布进行初始化
    if p.dim() > 1:
        nn.init.xavier_uniform_(p)

# 将模型移动到指定设备(如 GPU 或 CPU)
transformer = transformer.to(device)

# 定义损失函数,使用交叉熵损失,并且忽略 <pad> 索引
loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX)

# 定义优化器,使用 Adam 优化器
optimizer = torch.optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)

# 定义训练单个 epoch 的函数
def train_epoch(model, train_iter, optimizer):
    model.train()  # 设置模型为训练模式
    losses = 0  # 初始化损失为 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  # 初始化损失为 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)  # 返回平均损失

实际训练

  • 最后展示了如何在实际数据集上训练模型。在作者的环境中,使用了单个NVIDIA GeForce RTX 3070 GPU,每个epoch训练大约需要5小时。
# 训练和验证模型
for epoch in tqdm.tqdm(range(1, NUM_EPOCHS+1)):  # 循环训练每个 epoch
    start_time = time.time()  # 记录开始时间
    train_loss = train_epoch(transformer, train_iter, optimizer)  # 训练一个 epoch 并获取训练损失
    end_time = time.time()  # 记录结束时间
    # 打印 epoch 的训练损失和所用时间
    print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, Epoch time = {(end_time - start_time):.3f}s"))

训练结果:
 

 10%|█         | 1/10 [11:03<1:39:31, 663.46s/it]
Epoch: 1, Train loss: 4.478, Epoch time = 663.465s
 20%|██        | 2/10 [22:06<1:28:26, 663.37s/it]
Epoch: 2, Train loss: 3.497, Epoch time = 663.299s
 30%|███       | 3/10 [33:10<1:17:25, 663.70s/it]
Epoch: 3, Train loss: 3.089, Epoch time = 664.094s
 40%|████      | 4/10 [44:14<1:06:21, 663.52s/it]
Epoch: 4, Train loss: 2.793, Epoch time = 663.235s
 50%|█████     | 5/10 [55:17<55:18, 663.65s/it]  
Epoch: 5, Train loss: 2.571, Epoch time = 663.874s
 60%|██████    | 6/10 [1:06:21<44:14, 663.53s/it]
Epoch: 6, Train loss: 2.401, Epoch time = 663.309s
 70%|███████   | 7/10 [1:17:25<33:11, 663.91s/it]
Epoch: 7, Train loss: 2.294, Epoch time = 664.688s
 80%|████████  | 8/10 [1:28:28<22:06, 663.36s/it]
Epoch: 8, Train loss: 2.198, Epoch time = 662.178s
 90%|█████████ | 9/10 [1:39:31<11:03, 663.51s/it]
Epoch: 9, Train loss: 2.113, Epoch time = 663.842s
100%|██████████| 10/10 [1:50:34<00:00, 663.48s/it]

Epoch: 10, Train loss: 2.041, Epoch time = 662.784s

使用训练好的模型进行测试

def greedy_decode(model, src, src_mask, max_len, start_symbol):
    # 将输入张量和掩码移动到指定设备(通常是 GPU 或 CPU)
    src = src.to(device)
    src_mask = src_mask.to(device)
    
    # 编码器对输入进行编码,得到编码器的输出
    memory = model.encode(src, src_mask)
    
    # 初始化目标序列 ys,起始符号为 start_symbol
    ys = torch.ones(1, 1).fill_(start_symbol).type(torch.long).to(device)
    
    # 循环生成每一个后续的单词,直到达到最大长度 max_len-1
    for i in range(max_len - 1):
        # 确保 memory 在正确的设备上
        memory = memory.to(device)
        
        # 生成 memory 的掩码,大小为 (当前生成序列的长度, memory 的长度)
        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 中
        ys = torch.cat([ys, torch.ones(1, 1).type_as(src.data).fill_(next_word)], dim=0)
        
        # 如果生成的单词是结束符号 EOS_IDX,则停止生成
        if next_word == EOS_IDX:
            break
    
    # 返回生成的目标序列
    return ys

def translate(model, src, src_vocab, tgt_vocab, src_tokenizer):
    # 将模型设置为评估模式
    model.eval()
    
    # 将源句子 token 化,并添加起始符号 BOS_IDX 和结束符号 EOS_IDX
    tokens = [BOS_IDX] + [src_vocab.get_stoi()[tok] for tok in src_tokenizer.encode(src, out_type=str)] + [EOS_IDX]
    
    # 计算 token 的数量
    num_tokens = len(tokens)
    
    # 将 token 转换为张量,并调整形状
    src = torch.LongTensor(tokens).reshape(num_tokens, 1)
    
    # 创建源句子的掩码,全为零(因为没有 padding)
    src_mask = torch.zeros(num_tokens, num_tokens).type(torch.bool)
    
    # 使用贪婪解码生成目标句子的 token
    tgt_tokens = greedy_decode(model, src, src_mask, max_len=num_tokens + 5, start_symbol=BOS_IDX).flatten()
    
    # 将目标 token 转换为字符串,并移除起始符号和结束符号
    return " ".join([tgt_vocab.get_itos()[tok] for tok in tgt_tokens]).replace("<bos>", "").replace("<eos>", "")
translate(transformer, "HSコード 8515 はんだ付け用、ろう付け用又は溶接用の機器(電気式(電気加熱ガス式を含む。)", ja_vocab, en_vocab, ja_tokenizer)

原文:

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

翻译结果:

' ▁H S 还 是 用 于 焊 接 、 焊 接 、 焊 接 、 焊 接 设 备 ( 包 括 电 气 加 热 ) 的 用 于 焊 接 设 备 ( 包'

展示训练数据内容

trainen.pop(5)
trainja.pop(5)

输出结果

中文: 
'美国 设施: 停车场, 24小时前台, 健身中心, 报纸, 露台, 禁烟客房, 干洗, 无障碍设施, 免费停车, 上网服务, 电梯, 快速办理入住/退房手续, 保险箱, 暖气, 传真/复印, 行李寄存, 无线网络, 免费无线网络连接, 酒店各处禁烟, 空调, 阳光露台, 自动售货机(饮品), 自动售货机(零食), 每日清洁服务, 内部停车场, 私人停车场, WiFi(覆盖酒店各处), 停车库, 无障碍停车场, 简短描述Gateway Hotel Santa Monica酒店距离海滩2英里(3.2公里),提供24小时健身房。每间客房均提供免费WiFi,客人可以使用酒店的免费地下停车场。 

日文:

'アメリカ合衆国 施設・設備: 駐車場, 24時間対応フロント, フィットネスセンター, 新聞, テラス, 禁煙ルーム, ドライクリーニング, バリアフリー, 無料駐車場, インターネット, エレベーター, エクスプレス・チェックイン / チェックアウト, セーフティボックス, 暖房, FAX / コピー, 荷物預かり, Wi-Fi, 無料Wi-Fi, 全館禁煙, エアコン, サンテラス, 自販機(ドリンク類), 自販機(スナック類), 客室清掃サービス(毎日), 敷地内駐車場, 専用駐車場, Wi-Fi(館内全域), 立体駐車場, 障害者用駐車場, 短い説明Gateway Hotel Santa Monicaはビーチから3.2kmの場所に位置し、24時間利用可能なジム、無料Wi-Fi付きのお部屋、無料の地下駐車場を提供しています。'

保存模型

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

# 保存模型
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')

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值