基于Transformer实现机器翻译(日译中)

一、导入所需的包

        首先,让我们确保我们的系统中安装了以下软件包,如果发现缺少某些软件包,请务必安装它们。

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')
# print(torch.cuda.get_device_name(0)) ## 如果你有GPU,请在你自己的电脑上尝试运行这一套代码

二、获取并行数据集 

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

# 读取名为 'zh-ja.bicleaner05.txt' 的文件,文件以制表符(\t)分隔,使用Python引擎来解析,没有表头
df = pd.read_csv('zh-ja.bicleaner05.txt', sep='\\t', engine='python', header=None)

# 将数据框的第三列(索引为2)转换为列表,并赋值给变量 trainen
trainen = df[2].values.tolist()  # 这里可以通过去掉注释 [:10000] 来限制读取前10000行

# 将数据框的第四列(索引为3)转换为列表,并赋值给变量 trainja
trainja = df[3].values.tolist()  # 这里可以通过去掉注释 [:10000] 来限制读取前10000行

# 从训练数据中删除索引为 5972 的项
# 通过 pop 函数删除相应索引的元素
# trainen.pop(5972)
# trainja.pop(5972)

        在导入所有日语和英语对应数据后,我删除了数据集中的最后一个数据,因为它缺少值。总的来说,trainen 和 trainja 中的句子数为 5,973,071,但是,出于学习目的,通常建议在一次性使用所有数据之前对数据进行采样并确保一切按预期工作,以节省时间。

        下面是数据集中包含的句子示例。

print(trainen[500])
print(trainja[500])

三、准备分词器

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

# 使用指定的模型文件 'spm.en.nopretok.model' 创建英文的 SentencePieceProcessor 实例
en_tokenizer = spm.SentencePieceProcessor(model_file='spm.en.nopretok.model')

# 使用指定的模型文件 'spm.ja.nopretok.model' 创建日文的 SentencePieceProcessor 实例
ja_tokenizer = spm.SentencePieceProcessor(model_file='spm.ja.nopretok.model')

         加载分词器后,您可以测试它们,例如,通过执行以下代码。

# 使用英文分词器(en_tokenizer)对给定的句子进行编码,输出类型为字符串(str)
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)对给定的日文句子进行编码,输出类型为字符串(str)
ja_tokenizer.encode("年金 日本に住んでいる20歳~60歳の全ての人は、公的年金制度に加入しなければなりません。", out_type='str')

 

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

        然后,使用分词器和原始句子,我们构建从 TorchText 导入的 Vocab 对象。此过程可能需要几秒钟或几分钟,具体取决于我们的数据集大小和计算能力。不同的分词器也会影响构建词汇所需的时间,我尝试了其他几种日语分词器,但 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>'])
# 使用 build_vocab 函数为日文训练数据创建词汇表
ja_vocab = build_vocab(trainja, ja_tokenizer)
# 使用 build_vocab 函数为英文训练数据创建词汇表
en_vocab = build_vocab(trainen, en_tokenizer)

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

def data_process(ja, en):
  data = []

# 使用 zip 函数将日文和英文的句子配对,并进行迭代
  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
# 使用 data_process 函数处理训练数据
train_data = data_process(trainja, trainen)

五、创建要在训练期间迭代的 DataLoader 对象 

        在这里,我将BATCH_SIZE设置为 16 以防止“cuda 内存不足”,但这取决于各种因素,例如您的机器内存容量、数据大小等,因此请根据需要随意更改批处理大小(注意:PyTorch 的教程使用 Multi30k 德语-英语数据集将批处理大小设置为 128。

# 设置批量大小
BATCH_SIZE = 8
# 获取特殊符号 '<pad>'、'<bos>' 和 '<eos>' 的索引
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:
    # 在每个日文句子的开头和结尾添加 '<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))
   # 使用 pad_sequence 函数将日文批次和英文批次填充到相同的长度,填充值为 '<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
# 创建数据加载器,使用 generate_batch 函数来整理批次
train_iter = DataLoader(train_data, batch_size=BATCH_SIZE,
                        shuffle=True, collate_fn=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)


# 自定义的 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__()
        
        # 定义 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)
        
        # 定义 Transformer 解码层
        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 表示
        memory = self.transformer_encoder(src_emb, src_mask, src_padding_mask)
        
        # 解码器处理目标语言嵌入和 memory 表示,生成输出
        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))
        
        # 为位置编码张量的偶数和奇数索引位置计算 sin 和 cos 值
        pos_embedding[:, 0::2] = torch.sin(pos * den)
        pos_embedding[:, 1::2] = torch.cos(pos * den)
        
        # 增加一个维度以便与 token embedding 进行相加
        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):
        # 对输入 tokens 进行嵌入并进行缩放
        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)
    
    # 将 mask 转换为 float,并填充上三角部分为负无穷,下三角部分为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)
    
    # 为源句子生成全零矩阵掩码(因为编码器不需要下三角掩码)
    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。

# 设定模型超参数
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  # 训练的轮数

# 初始化 Seq2Seq 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)

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

# 定义损失函数,忽略填充索引位置的损失
loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX)

# 定义优化器,使用 Adam 优化算法
optimizer = 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
    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)  # 返回平均损失

七、开始训练

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

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

代码如下:

# 使用 tqdm 包裹训练循环以显示进度条
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"))

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

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

# 贪心解码函数,用于从模型中生成翻译序列
def greedy_decode(model, src, src_mask, max_len, start_symbol):
    src = src.to(device)  # 将源序列移动到指定设备(CPU或GPU)
    src_mask = src_mask.to(device)  # 将源序列掩码移动到指定设备
    memory = model.encode(src, src_mask)  # 编码源序列,生成memory表示
    ys = torch.ones(1, 1).fill_(start_symbol).type(torch.long).to(device)  # 初始化目标序列,以<bos>开始
    
    for i in range(max_len - 1):
        memory = memory.to(device)  # 将memory表示移动到指定设备
        memory_mask = torch.zeros(ys.shape[0], memory.shape[0]).to(device).type(torch.bool)  # 生成memory掩码
        tgt_mask = (generate_square_subsequent_mask(ys.size(0))
                    .type(torch.bool)).to(device)  # 生成目标序列的掩码
        
        out = model.decode(ys, memory, tgt_mask)  # 解码器处理当前的目标序列和memory表示
        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:  # 如果下一个单词是<eos>,则停止解码
            break
    return ys  # 返回生成的目标序列

# 翻译函数,用于将源语言句子翻译成目标语言
def translate(model, src, src_vocab, tgt_vocab, src_tokenizer):
    model.eval()  # 设置模型为评估模式
    # 对源句子进行分词并添加<bos>和<eos>标记
    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()
    
    # 将生成的目标序列转换为目标语言的单词,并去除<bos>和<eos>标记
    return " ".join([tgt_vocab.itos[tok] for tok in tgt_tokens]).replace("<bos>", "").replace("<eos>", "")

 然后,我们可以调用 translate 函数并传递所需的参数。

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

 九、保存 Vocab 对象和训练的模型

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

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

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

# 保存模型状态字典,用于推理
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')  # 保存到文件 'model_checkpoint.tar'

十、总结

 

        通过本次实验,我深刻体会到数据准备、模型配置和训练过程中的每一个细节都对最终结果有重要影响。未来的工作中,我将更加注重这些环节的优化,以提升模型的翻译精度和效率。

        本次实验让我对机器翻译模型的训练有了更深入的理解。尽管遇到了一些挑战,但这些经验为我未来的研究奠定了坚实的基础。期待在未来的实验中,能有更多的资源和时间来进一步优化和提升模型性能 。

  • 23
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Transformer实现机器翻译是一种先进的方法。在Pytorch,可以使用nn.Transformer实现英文到文的机器翻译任务\[1\]。如果想要深入了解nn.Transformer的使用,可以参考一篇博文《Pytorch nn.Transformer的使用详解与Transformer的黑盒讲解》\[1\]。在这篇博文,作者建议先学习CopyTask任务,然后再学习机器翻译任务,这样会更容易理解。 此外,谷歌翻译也在逐步将转换器编码器引入其翻译算法\[2\]。他们提供了一个即用型翻译界面,可以在谷歌翻译网站上使用\[2\]。另外,瓦斯瓦尼等人在2017年的研究发现,Transformer在WMT 2014英德翻译任务和WMT 2014英法翻译任务上取得了最先进的BLEU分数\[3\]。BLEU是一种用于评估机器翻译质量的指标,具体的评估方法可以在《Evaluating machine translation with BLEU》部分找到\[3\]。 综上所述,基于Transformer机器翻译方法在实践取得了很好的效果,并且在Pytorch有相应的实现。同时,谷歌翻译也在逐步引入转换器编码器,并且Transformer机器翻译任务取得了最先进的结果。 #### 引用[.reference_title] - *1* [Pytorch入门实战(5):基于nn.Transformer实现机器翻译(英译汉)](https://blog.csdn.net/zhaohongfei_358/article/details/126175328)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【NLP】第6章 使用 Transformer 进行机器翻译](https://blog.csdn.net/sikh_0529/article/details/127037111)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值