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

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


由于模型训练时间很长,我们可以尝试使用GPU加速,我电脑的显卡是RTX 3060,但是显存只有6GB,训练模型至少要8GB再尝试,所以我租了一个AutoDL上的服务器。

我租的是RTX 2080 Ti(11GB),训练大概花了1个小时多。

我使用的环境如下:

python         3.8
pytorch        1.5.1
cuda           10.1
torchvision    0.6.1
torchtext      0.6.0


一些必要的包:

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

一、数据预处理


在本教程中,我们将使用从 JParaCrawl 下载的日英并行数据集JParaCrawl,它被描述为“NTT创建的最大的公开可用的英日平行语料库。它是通过大量抓取网络并自动对齐平行句子而创建的。你也可以在这里看到这篇论文。

df = pd.read_csv('./zh-ja/zh-ja.bicleaner05.txt', sep='\\t', engine='python', header=None)
trainen = df[2].values.tolist()#[:10000]
trainja = df[3].values.tolist()#[:10000]

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

二、模型定义

分词器

与英语或其他字母语言不同,日语句子不包含空格来分隔单词。我们可以使用JParaCrawl提供的分词器,该分词器是使用SentencePiece创建的日语和英语,您可以访问JParaCrawl网站下载它们,或单击此处。

en_tokenizer = spm.SentencePieceProcessor(model_file='enja_spm_models/spm.en.nopretok.model')
ja_tokenizer = spm.SentencePieceProcessor(model_file='enja_spm_models/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',
 '歳の',
 '全ての',
 '人は',
 '、',
 '公的',
 '年',
 '金',
 '制度',
 'に',
 '加入',
 'しなければなりません',
 '。']

Vocab

然后,使用分词器和原始句子,我们构建从 TorchText 导入的 Vocab 对象对象并将句子转换为 Torch 张量。此过程可能需要几秒钟或几分钟,具体取决于我们的数据集大小和计算能力。不同的分词器也会影响构建词汇所需的时间,我尝试了其他几种日语分词器,但 SentencePiece 对我来说似乎运行良好且速度足够快。

def build_vocab(sentences, tokenizer):
  counter = Counter()#计数类
  #遍历训练集每一行
  for sentence in sentences:
    counter.update(tokenizer.encode(sentence, out_type=str))#对每一行进行分词并统计每个分词的数量,制作词典
  return Vocab(counter, specials=['<unk>', '<pad>', '<bos>', '<eos>'])
ja_vocab = build_vocab(trainja, ja_tokenizer)
en_vocab = build_vocab(trainen, en_tokenizer)

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

def data_process(ja, en):
    # 步骤解析:
        # 1. 使用 rstrip("\n") 移除每行末尾的换行符
        # 2. 使用 ja_tokenizer.encode() 将文本编码为标记(token)
        # 3. 将每个标记映射到其在 ja_vocab 中的索引
        # 4. 将索引列表转换为长整型张量
  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 内存不足”,但这取决于各种因素,例如您的机器内存容量、数据大小等,因此请根据需要随意更改批处理大小(注意: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):
    """
    生成批次数据,为每个序列添加开始和结束标记,并进行填充

    :param data_batch: 包含(日语张量, 英语张量)元组的列表
    :return: 填充后的日语批次和英语批次
    """
  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))

  #使用 pad_sequence 函数对批次中的序列进行填充,使它们具有相同的长度
  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 = DataLoader(
    train_data,              # 训练数据
    batch_size=BATCH_SIZE,   # 批次大小
    shuffle=True,            # 随机打乱数据
    collate_fn=generate_batch  # 使用自定义的 generate_batch 函数处理每个批次
)

序列到序列模型

接下来的几个代码和文本说明(用斜体书写)取自原始的 PyTorch 教程 https://pytorch.org/tutorials/beginner/translation_transformer.html. 除了BATCH_SIZE之外,我没有做任何更改,de_vocabwhich 这个词被改成了ja_vocab。
Transformer 是 “Attention is all you need” 论文中介绍的 Seq2Seq 模型,用于解决机器翻译任务。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):
        """
        初始化Seq2Seq Transformer模型

        :param num_encoder_layers: 编码器层数
        :param num_decoder_layers: 解码器层数
        :param emb_size: 嵌入维度
        :param src_vocab_size: 源语言词汇表大小
        :param tgt_vocab_size: 目标语言词汇表大小
        :param dim_feedforward: 前馈网络的维度
        :param dropout: dropout比率
        """
        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)

        # 输出层:将嵌入转换为目标词汇表大小的logits
        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):
        """
        执行前向传播

        :param src: 源语言输入
        :param trg: 目标语言输入
        :param src_mask: 源语言掩码
        :param tgt_mask: 目标语言掩码
        :param src_padding_mask: 源语言填充掩码
        :param tgt_padding_mask: 目标语言填充掩码
        :param memory_key_padding_mask: 记忆键填充掩码
        :return: 模型输出
        """
        # 对源语言和目标语言进行嵌入和位置编码
        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)

        # 生成最

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

import math
import torch
import torch.nn as nn
from torch import Tensor

class PositionalEncoding(nn.Module):
    def __init__(self, emb_size: int, dropout, maxlen: int = 5000):
        """
        初始化位置编码

        :param emb_size: 嵌入维度
        :param dropout: dropout比率
        :param maxlen: 最大序列长度
        """
        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)  # 偶数列使用sin
        pos_embedding[:, 1::2] = torch.cos(pos * den)  # 奇数列使用cos
        
        # 添加批次维度
        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):
        """
        前向传播:将位置编码加到词元嵌入上

        :param token_embedding: 词元嵌入
        :return: 位置编码后的嵌入
        """
        return self.dropout(token_embedding + 
                            self.pos_embedding[:token_embedding.size(0), :])

class TokenEmbedding(nn.Module):
    def __init__(self, vocab_size: int, emb_size):
        """
        初始化词元嵌入

        :param vocab_size: 词汇表大小
        :param emb_size: 嵌入维度
        """
        super(TokenEmbedding, self).__init__()
        self.embedding = nn.Embedding(vocab_size, emb_size)
        self.emb_size = emb_size

    def forward(self, tokens: Tensor):
        """
        前向传播:将词元转换为嵌入向量

        :param tokens: 输入词元
        :return: 词元嵌入
        """
        # 将tokens转换为长整型,并乘以sqrt(emb_size)以保持方差
        return self.embedding(tokens.long()) * math.sqrt(self.emb_size)

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

import torch

def generate_square_subsequent_mask(sz):
    """
    生成一个方形的后续掩码矩阵

    :param sz: 矩阵的大小
    :return: 掩码矩阵
    """
    # 创建一个上三角矩阵
    mask = (torch.triu(torch.ones((sz, sz), device=device)) == 1).transpose(0, 1)
    
    # 将掩码转换为浮点类型,并用 -inf 和 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):
    """
    为源序列和目标序列创建掩码

    :param src: 源序列
    :param tgt: 目标序列
    :return: 源掩码,目标掩码,源填充掩码,目标填充掩码
    """
    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

三、定义模型参数并实例化模型。

# 定义模型参数
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模型
transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS,
                                 EMB_SIZE, SRC_VOCAB_SIZE, TGT_VOCAB_SIZE,
                                 FFN_HID_DIM)

# 使用Xavier初始化方法初始化模型参数
for p in transformer.parameters():
    if p.dim() > 1:
        nn.init.xavier_uniform_(p)

# 将模型移动到指定设备(GPU或CPU)
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):
    """
    训练一个epoch

    :param model: Transformer模型
    :param train_iter: 训练数据迭代器
    :param optimizer: 优化器
    :return: 平均损失
    """
    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:, :]  # 移除第一个词元(<bos>)
        # 计算损失
        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):
    """
    评估模型在验证集上的性能

    :param model: 待评估的Transformer模型
    :param val_iter: 验证数据迭代器
    :return: 平均损失
    """
    model.eval()  # 将模型设置为评估模式
    losses = 0

    with torch.no_grad():  # 禁用梯度计算,减少内存使用并加速计算
        for idx, (src, tgt) in enumerate(val_iter):
            src = src.to(device)  # 将源序列移动到指定设备(GPU或CPU)
            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:, :]  # 准备目标输出,去掉第一个词元(通常是<bos>标记)

            # 计算损失
            # 将logits重塑为二维张量,tgt_out展平为一维
            loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))
            
            losses += loss.item()  # 累加批次损失

    return losses / len(val_iter)  # 返回平均损失

四、训练模型

使用单个 NVIDIA GeForce RTX 2080 Ti 每个 epoch 大约需要6分钟。

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"))
  6%|▋         | 1/16 [06:18<1:34:31, 378.13s/it]

Epoch: 1, Train loss: 4.479, Epoch time = 378.133s


 12%|█▎        | 2/16 [12:40<1:28:48, 380.58s/it]

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


 19%|█▉        | 3/16 [18:59<1:22:21, 380.08s/it]

Epoch: 3, Train loss: 3.100, Epoch time = 379.477s


 25%|██▌       | 4/16 [25:22<1:16:13, 381.15s/it]

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


 31%|███▏      | 5/16 [31:44<1:09:54, 381.32s/it]

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


 38%|███▊      | 6/16 [38:05<1:03:32, 381.26s/it]

Epoch: 6, Train loss: 2.414, Epoch time = 381.122s


 44%|████▍     | 7/16 [44:28<57:15, 381.72s/it]  

Epoch: 7, Train loss: 2.292, Epoch time = 382.671s


 50%|█████     | 8/16 [50:48<50:49, 381.24s/it]

Epoch: 8, Train loss: 2.209, Epoch time = 380.224s


 56%|█████▋    | 9/16 [57:09<44:27, 381.06s/it]

Epoch: 9, Train loss: 2.124, Epoch time = 380.652s


 62%|██████▎   | 10/16 [1:03:27<38:00, 380.13s/it]

Epoch: 10, Train loss: 2.050, Epoch time = 378.054s


 69%|██████▉   | 11/16 [1:09:44<31:35, 379.18s/it]

Epoch: 11, Train loss: 1.990, Epoch time = 377.004s


 75%|███████▌  | 12/16 [1:16:01<25:14, 378.59s/it]

Epoch: 12, Train loss: 1.937, Epoch time = 377.266s


 81%|████████▏ | 13/16 [1:22:22<18:58, 379.42s/it]

Epoch: 13, Train loss: 1.887, Epoch time = 381.329s


 88%|████████▊ | 14/16 [1:28:43<12:39, 379.88s/it]

Epoch: 14, Train loss: 1.845, Epoch time = 380.943s


 94%|█████████▍| 15/16 [1:35:02<06:19, 379.66s/it]

Epoch: 15, Train loss: 1.807, Epoch time = 379.143s


100%|██████████| 16/16 [1:41:21<00:00, 380.07s/it]

Epoch: 16, Train loss: 1.773, Epoch time = 378.421s

五、尝试使用经过训练的模型翻译日语句子

首先,我们创建翻译新句子的函数,包括获取日语句子、标记化、转换为张量、推理等步骤,然后将结果解码回句子,但这次是英语.

def greedy_decode(model, src, src_mask, max_len, start_symbol):
    """
    使用贪婪解码方法生成翻译

    :param model: 训练好的Transformer模型
    :param src: 源语言序列
    :param src_mask: 源语言掩码
    :param max_len: 生成序列的最大长度
    :param start_symbol: 起始符号(通常是BOS_IDX)
    :return: 生成的目标语言序列
    """
    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):
    """
    翻译给定的源语言句子

    :param model: 训练好的Transformer模型
    :param src: 源语言句子
    :param src_vocab: 源语言词汇表
    :param tgt_vocab: 目标语言词汇表
    :param src_tokenizer: 源语言分词器
    :return: 翻译后的目标语言句子
    """
    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>", "
translate(transformer, "HSコード 8515 はんだ付け用、ろう付け用又は溶接用の機器(電気式(電気加熱ガス式を含む。)", ja_vocab, en_vocab, ja_tokenizer)

' ▁H S ▁ 感 应 用 或 焊 接 的 设 备 ▁H S ▁ 码 ▁85 15 是 电 气 加 热 加 热 加 热 器 ( 包 括 电 气 ) 。 ''

这个结果还有些不尽人意,可以再调整参数试试

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 对象和训练的模型

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 save 和 load 函数保存模型以供以后使用。通常,有两种方法可以保存模型,具体取决于我们以后要使用它们的内容。第一个仅用于推理,我们可以稍后加载模型并使用它从日语翻译成英语。

# 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')
  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Transformer是一种用于自然语言处理和其他序列到序列任务的模型架构,它引入了自注意力机制和位置编码来捕捉序列的上下文信息。而PyTorch是一个深度学习框架,提供了用于构建和训练神经网络的工具和库。 TransformerPyTorch之间的区别主要体现在以下几个方面: 1. 功能:Transformer是一种模型架构,用于处理序列到序列的任务,例如机器翻译、文本摘要等。而PyTorch是一个深度学习框架,提供了构建和训练神经网络的功能,包括各种层、优化器和损失函数等。 2. 实现:Transformer可以使用不同的深度学习框架进行实现,包括PyTorchPyTorch提供了Transformer模型的实现代码,可以方便地构建和训练Transformer模型。 3. 应用范围:Transformer可以应用于各种序列到序列的任务,而PyTorch不仅可以用于Transformer模型,还可以用于其他类型的神经网络模型。 4. 灵活性:PyTorch是一个灵活的框架,可以自定义模型的结构和训练过程。而Transformer是一种特定的模型架构,其结构和训练过程已经固定。 5. 社区支持:PyTorch拥有庞大的社区和生态系统,提供了丰富的文档、教程和示例代码。而Transformer作为一种模型架构,其相关的文档和教程相对较少。 总结起来,Transformer是一种模型架构,用于处理序列到序列的任务,而PyTorch是一个深度学习框架,提供了构建和训练神经网络的功能。PyTorch可以用于实现Transformer模型,同时还可以用于其他类型的神经网络模型

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值