datawhale - 基于术语词典干预的机器翻译挑战赛 (一)
文章目录
- torchtext 库是干什么用的 ?
- TranslationDataset 类
- 定义 Seq2Seq模型
- Encoder
- Decoder
- Seq2Seq 类
 
 
- load_terminology_dictionary 函数
- 示例用法
 
 
 
- train 函数
- 主程序代码
- 模型评价
- load_sentences 函数
- translate_sentence 函数
- evaluate_bleu 函数
- 主程序
 
- 测试集上进行推理
- inference 函数
- 主程序
 
torchtext 库是干什么用的 ?
torchtext 是一个用于处理文本数据的库,它是 PyTorch 生态系统的一部分。这个库主要用于简化文本数据的预处理和加载过程,使得在深度学习模型中使用文本数据变得更加容易。以下是 torchtext 库的一些主要功能:
-  数据加载: torchtext提供了方便的 API 来加载和处理各种文本数据集,包括常见的数据集格式如 CSV、TSV 等。
-  文本预处理:它包含了一系列的文本预处理工具,如分词、词干提取、词性标注等,这些工具可以帮助你将原始文本转换为模型可以理解的格式。 
-  词汇表管理: torchtext可以自动构建词汇表,并将文本转换为数值表示(如词嵌入),这对于深度学习模型来说是必不可少的。
-  数据迭代器:它提供了数据迭代器,可以方便地将数据分批加载到模型中进行训练,支持多线程和多进程加载,提高了数据加载的效率。 
-  与 PyTorch 集成: torchtext与 PyTorch 深度集成,可以直接将处理好的数据输入到 PyTorch 模型中,简化了整个深度学习流程。
TranslationDataset 类
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchtext.data.utils import get_tokenizer
from collections import Counter
import random
from torch.utils.data import Subset, DataLoader
import time# 定义数据集类
# 修改TranslationDataset类以处理术语
class TranslationDataset(Dataset):def __init__(self, filename, terminology):self.data = []with open(filename, 'r', encoding='utf-8') as f:for line in f:en, zh = line.strip().split('\t')self.data.append((en, zh))
- class TranslationDataset(Dataset)::定义一个继承自- Dataset类的子类- TranslationDataset。
- def __init__(self, filename, terminology)::初始化方法,接受两个参数:- filename(数据文件名)和- terminology(术语词典)。
- self.data = []:初始化一个空列表- self.data,用于存储数据。
- with open(filename, 'r', encoding='utf-8') as f::打开文件,使用UTF-8编码读取。
- for line in f::逐行读取文件内容。
- en, zh = line.strip().split('\t'):去掉行末的换行符,并按制表符分割成英文和中文。
- self.data.append((en, zh)):将英文和中文对添加到- self.data列表中。
        self.terminology = terminology# 创建词汇表,注意这里需要确保术语词典中的词也被包含在词汇表中self.en_tokenizer = get_tokenizer('basic_english')self.zh_tokenizer = list  # 使用字符级分词
- self.terminology = terminology:将传入的术语词典赋值给- self.terminology。
- self.en_tokenizer = get_tokenizer('basic_english'):使用- basic_english分词器对英文进行分词。
- self.zh_tokenizer = list:使用字符级分词,即将中文句子拆分成单个字符。
        en_vocab = Counter(self.terminology.keys())  # 确保术语在词汇表中zh_vocab = Counter()print("en_vocab的值为:", en_vocab)print("zn_vocab的值为:", zn_vocab)
- en_vocab = Counter(self.terminology.keys()):初始化英文词汇表,确保术语词典中的词在词汇表中。
- zh_vocab = Counter():初始化中文词汇表。
- print("en_vocab的值为:", en_vocab):打印英文词汇表的值。
- print("zn_vocab的值为:", zn_vocab):打印中文词汇表的值。
        for en, zh in self.data:en_vocab.update(self.en_tokenizer(en))zh_vocab.update(self.zh_tokenizer(zh))
- for en, zh in self.data::遍历数据集中的每一对英文和中文。
- en_vocab.update(self.en_tokenizer(en)):更新英文词汇表,统计每个词的出现频率。
- zh_vocab.update(self.zh_tokenizer(zh)):更新中文词汇表,统计每个字符的出现频率。
        # 添加术语到词汇表self.en_vocab = ['<pad>', '<sos>', '<eos>'] + list(self.terminology.keys()) + [word for word, _ in en_vocab.most_common(10000)]self.zh_vocab = ['<pad>', '<sos>', '<eos>'] + [word for word, _ in zh_vocab.most_common(10000)]
- self.en_vocab = ['<pad>', '<sos>', '<eos>'] + list(self.terminology.keys()) + [word for word, _ in en_vocab.most_common(10000)]:构建英文词汇表,包含特殊标记(- <pad>,- <sos>,- <eos>)、术语词典中的词以及出现频率最高的10000个词。
- self.zh_vocab = ['<pad>', '<sos>', '<eos>'] + [word for word, _ in zh_vocab.most_common(10000)]:构建中文词汇表,包含特殊标记(- <pad>,- <sos>,- <eos>)以及出现频率最高的10000个字符。
        self.en_word2idx = {word: idx for idx, word in enumerate(self.en_vocab)}self.zh_word2idx = {word: idx for idx, word in enumerate(self.zh_vocab)}
- self.en_word2idx = {word: idx for idx, word in enumerate(self.en_vocab)}:构建英文词到索引的映射字典。
- self.zh_word2idx = {word: idx for idx, word in enumerate(self.zh_vocab)}:构建中文词到索引的映射字典。
    def __len__(self):return len(self.data)
- def __len__(self)::定义数据集的长度方法。
- return len(self.data):返回数据集的长度。
    def __getitem__(self, idx):en, zh = self.data[idx]en_tensor = torch.tensor([self.en_word2idx.get(word, self.en_word2idx['<sos>']) for word in self.en_tokenizer(en)] + [self.en_word2idx['<eos>']])zh_tensor = torch.tensor([self.zh_word2idx.get(word, self.zh_word2idx['<sos>']) for word in self.zh_tokenizer(zh)] + [self.zh_word2idx['<eos>']])return en_tensor, zh_tensor
- def __getitem__(self, idx)::定义获取数据项的方法。
- en, zh = self.data[idx]:获取指定索引的数据项。
- en_tensor = torch.tensor([self.en_word2idx.get(word, self.en_word2idx['<sos>']) for word in self.en_tokenizer(en)] + [self.en_word2idx['<eos>']]):将英文句子转换为索引张量,并在末尾添加- <eos>标记。
- zh_tensor = torch.tensor([self.zh_word2idx.get(word, self.zh_word2idx['<sos>']) for word in self.zh_tokenizer(zh)] + [self.zh_word2idx['<eos>']]):将中文句子转换为索引张量,并在末尾添加- <eos>标记。
- return en_tensor, zh_tensor:返回转换后的英文和中文张量。
def collate_fn(batch):en_batch, zh_batch = [], []for en_item, zh_item in batch:en_batch.append(en_item)zh_batch.append(zh_item)# 对英文和中文序列分别进行填充en_batch = nn.utils.rnn.pad_sequence(en_batch, padding_value=0, batch_first=True)zh_batch = nn.utils.rnn.pad_sequence(zh_batch, padding_value=0, batch_first=True)return en_batch, zh_batch
- def collate_fn(batch)::定义一个用于处理批量数据的函数。
- en_batch, zh_batch = [], []:初始化两个空列表,用于存储批量数据。
- for en_item, zh_item in batch::遍历批量数据中的每一对英文和中文张量。
- en_batch.append(en_item):将英文张量添加到- en_batch列表中。
- zh_batch.append(zh_item):将中文张量添加到- zh_batch列表中。
- en_batch = nn.utils.rnn.pad_sequence(en_batch, padding_value=0, batch_first=True):对英文序列进行填充,使其长度一致。
- zh_batch = nn.utils.rnn.pad_sequence(zh_batch, padding_value=0, batch_first=True):对中文序列进行填充,使其长度一致。
- return en_batch, zh_batch:返回填充后的英文和中文批量数据。
定义 Seq2Seq模型
Encoder
class Encoder(nn.Module):def __init__(self, input_dim, emb_dim, hid_dim, n_layers, dropout):super().__init__()self.embedding = nn.Embedding(input_dim, emb_dim)self.rnn = nn.GRU(emb_dim, hid_dim, n_layers, dropout=dropout, batch_first=True)self.dropout = nn.Dropout(dropout)def forward(self, src):# src shape: [batch_size, src_len]embedded = self.dropout(self.embedding(src))# embedded shape: [batch_size, src_len, emb_dim]outputs, hidden = self.rnn(embedded)# outputs shape: [batch_size, src_len, hid_dim]# hidden shape: [n_layers, batch_size, hid_dim]return outputs, hidden
- 初始化: - input_dim:输入词汇表的大小。
- emb_dim:嵌入层的维度。
- hid_dim:隐藏层的维度。
- n_layers:RNN的层数。
- dropout:Dropout的概率。
 
- 前向传播: - 输入src的形状为[batch_size, src_len]。
- 通过嵌入层和Dropout层,得到embedded,形状为[batch_size, src_len, emb_dim]。
- 通过GRU层,得到outputs和hidden,形状分别为[batch_size, src_len, hid_dim]和[n_layers, batch_size, hid_dim]。
 
- 输入
Decoder
class Decoder(nn.Module):def __init__(self, output_dim, emb_dim, hid_dim, n_layers, dropout):super().__init__()self.output_dim = output_dimself.embedding = nn.Embedding(output_dim, emb_dim)self.rnn = nn.GRU(emb_dim, hid_dim, n_layers, dropout=dropout, batch_first=True)self.fc_out = nn.Linear(hid_dim, output_dim)self.dropout = nn.Dropout(dropout)def forward(self, input, hidden):# input shape: [batch_size, 1]# hidden shape: [n_layers, batch_size, hid_dim]embedded = self.dropout(self.embedding(input))# embedded shape: [batch_size, 1, emb_dim]output, hidden = self.rnn(embedded, hidden)# output shape: [batch_size, 1, hid_dim]# hidden shape: [n_layers, batch_size, hid_dim]prediction = self.fc_out(output.squeeze(1))# prediction shape: [batch_size, output_dim]return prediction, hidden
- 初始化: - output_dim:输出词汇表的大小。
- emb_dim:嵌入层的维度。
- hid_dim:隐藏层的维度。
- n_layers:RNN的层数。
- dropout:Dropout的概率。
 
- 前向传播: - 输入input的形状为[batch_size, 1],hidden的形状为[n_layers, batch_size, hid_dim]。
- 通过嵌入层和Dropout层,得到embedded,形状为[batch_size, 1, emb_dim]。
- 通过GRU层,得到output和hidden,形状分别为[batch_size, 1, hid_dim]和[n_layers, batch_size, hid_dim]。
- 通过全连接层,得到prediction,形状为[batch_size, output_dim]。
 
- 输入
Seq2Seq 类
class Seq2Seq(nn.Module):def __init__(self, encoder, decoder, device):super().__init__()self.encoder = encoderself.decoder = decoderself.device = devicedef forward(self, src, trg, teacher_forcing_ratio=0.5):# src shape: [batch_size, src_len]# trg shape: [batch_size, trg_len]batch_size = src.shape[0]trg_len = trg.shape[1]trg_vocab_size = self.decoder.output_dimoutputs = torch.zeros(batch_size, trg_len, trg_vocab_size).to(self.device)_, hidden = self.encoder(src)input = trg[:, 0].unsqueeze(1)  # Start tokenfor t in range(1, trg_len):output, hidden = self.decoder(input, hidden)outputs[:, t, :] = outputteacher_force = random.random() < teacher_forcing_ratiotop1 = output.argmax(1)input = trg[:, t].unsqueeze(1) if teacher_force else top1.unsqueeze(1)return outputs
- 初始化: - encoder:编码器实例。
- decoder:解码器实例。
- device:设备(CPU或GPU)。
 
- 前向传播: - 输入src的形状为[batch_size, src_len],trg的形状为[batch_size, trg_len]。
- 初始化outputs,形状为[batch_size, trg_len, trg_vocab_size]。
- 通过编码器得到hidden。
- 初始化解码器的输入为trg[:, 0].unsqueeze(1),即目标序列的起始标记。
- 循环解码目标序列的每个时间步: - 通过解码器得到output和hidden。
- 将output存储到outputs中。
- 根据teacher_forcing_ratio决定是否使用教师强制(即使用目标序列的下一个词作为输入,还是使用解码器的预测结果)。
 
- 通过解码器得到
 
- 输入
load_terminology_dictionary 函数
-  函数定义: def load_terminology_dictionary(dict_file):- dict_file: 这是一个字符串参数,表示包含术语词典的文件路径。
 
-  初始化术语词典: terminology = {}- 创建一个空的字典 terminology,用于存储从文件中读取的术语。
 
- 创建一个空的字典 
-  打开文件并读取内容: with open(dict_file, 'r', encoding='utf-8') as f:for line in f:en_term, ch_term = line.strip().split('\t')terminology[en_term] = ch_term- 使用 with open语句打开文件,确保文件在使用后正确关闭。
- 文件以只读模式 ('r') 打开,并指定编码为utf-8。
- 逐行读取文件内容,每行包含一个英文术语和对应的中文术语,用制表符 (\t) 分隔。
- line.strip()用于去除行末的换行符和其他空白字符。
- split('\t')将行按制表符分隔成两个部分,分别赋值给- en_term和- ch_term。
- 将英文术语作为键,中文术语作为值,添加到 terminology字典中。
 
- 使用 
-  返回术语词典: return terminology- 函数返回加载后的术语词典。
 
示例用法
假设有一个文件 terminology.txt,内容如下:
apple  苹果
banana  香蕉
orange  橙子
调用 load_terminology_dictionary 函数:
terminology = load_terminology_dictionary('terminology.txt')
print(terminology)
输出结果将是:
{'apple': '苹果', 'banana': '香蕉', 'orange': '橙子'}
train 函数
def train(model, iterator, optimizer, criterion, clip):
- 定义一个名为 train的函数,该函数接受五个参数:model(模型)、iterator(数据迭代器)、optimizer(优化器)、criterion(损失函数)和clip(梯度裁剪的阈值)。
    model.train()
- 将模型设置为训练模式。这会启用诸如 dropout 和 batch normalization 等训练特有的操作。
    epoch_loss = 0
- 初始化 epoch_loss变量为 0,用于累积整个 epoch 的损失。
    for i, (src, trg) in enumerate(iterator):
- 使用 enumerate遍历数据迭代器iterator,每次迭代获取一个批次的数据(src, trg),其中src是源序列,trg是目标序列。
        src, trg = src.to(device), trg.to(device)
- 将源序列和目标序列移动到指定的设备(如 GPU)上。
        optimizer.zero_grad()
- 将优化器的梯度清零,以防止梯度累积。
        output = model(src, trg)
- 将源序列和目标序列输入模型,得到模型的输出。
        output_dim = output.shape[-1]
- 获取输出张量的最后一个维度的大小,即输出序列的词汇表大小。
        output = output[:, 1:].contiguous().view(-1, output_dim)
- 去掉输出序列的第一个时间步(通常是起始标记),并将剩余部分展平成二维张量,形状为 (batch_size * (sequence_length - 1), output_dim)。
        trg = trg[:, 1:].contiguous().view(-1)
- 去掉目标序列的第一个时间步,并将剩余部分展平成一维张量,形状为 (batch_size * (sequence_length - 1))。
        loss = criterion(output, trg)
- 计算损失,使用定义的损失函数 criterion。
        loss.backward()
- 反向传播,计算梯度。
        torch.nn.utils.clip_grad_norm_(model.parameters(), clip)
- 对模型的梯度进行裁剪,防止梯度爆炸。裁剪的阈值由参数 clip指定。
        optimizer.step()
- 更新模型的参数。
        epoch_loss += loss.item()
- 将当前批次的损失值累加到 epoch_loss中。
    return epoch_loss / len(iterator)
- 返回整个 epoch 的平均损失。
主程序代码
if __name__ == '__main__':
- 这是一个常见的Python惯用法,用于判断当前模块是否是主程序入口。如果是,则执行后续代码。后同。
    start_time = time.time()  # 开始计时
- 记录程序开始执行的时间,用于后续计算总运行时间。
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
- 检查是否有可用的CUDA设备(即GPU),如果有则使用GPU,否则使用CPU。
    terminology = load_terminology_dictionary('../dataset/en-zh.dic')
- 加载术语字典,该字典可能包含特定领域的术语翻译。
    dataset = TranslationDataset('../dataset/train.txt', terminology=terminology)
- 创建一个 TranslationDataset对象,该对象用于加载和处理训练数据。
    N = 1000  # int(len(dataset) * 1)  # 或者你可以设置为数据集大小的一定比例,如 int(len(dataset) * 0.1)subset_indices = list(range(N))subset_dataset = Subset(dataset, subset_indices)
- 选择数据集的前1000个样本进行训练。subset_indices是一个包含前1000个索引的列表,subset_dataset是原始数据集的一个子集。
    train_loader = DataLoader(subset_dataset, batch_size=32, shuffle=True, collate_fn=collate_fn)
- 创建一个 DataLoader对象,用于批量加载数据。batch_size设置为32,shuffle设置为True以打乱数据顺序,collate_fn是一个自定义函数,用于处理批次数据的拼接。
    INPUT_DIM = len(dataset.en_vocab)OUTPUT_DIM = len(dataset.zh_vocab)ENC_EMB_DIM = 256DEC_EMB_DIM = 256HID_DIM = 512N_LAYERS = 2ENC_DROPOUT = 0.5DEC_DROPOUT = 0.5
- 定义模型参数,包括输入和输出维度、嵌入维度、隐藏层维度、层数和 dropout 率。
    enc = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM, N_LAYERS, ENC_DROPOUT)dec = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM, N_LAYERS, DEC_DROPOUT)model = Seq2Seq(enc, dec, device).to(device)
- 初始化编码器和解码器,并将其组合成一个 Seq2Seq模型。然后将模型移动到指定的设备(GPU或CPU)。
    optimizer = optim.Adam(model.parameters())criterion = nn.CrossEntropyLoss(ignore_index=dataset.zh_word2idx['<pad>'])
- 定义优化器和损失函数。优化器使用 Adam,损失函数使用交叉熵损失,并忽略目标序列中的填充标记 <pad>。
    N_EPOCHS = 10CLIP = 1
- 设置训练的 epoch 数和梯度裁剪的阈值。
    for epoch in range(N_EPOCHS):train_loss = train(model, train_loader, optimizer, criterion, CLIP)print(f'Epoch: {epoch+1:02} | Train Loss: {train_loss:.3f}')
- 进行10个 epoch 的训练。每个 epoch 结束后,打印当前 epoch 的训练损失。
    torch.save(model.state_dict(), './translation_model_GRU.pth')
- 在所有 epoch 结束后,保存模型的参数到文件 translation_model_GRU.pth。
    end_time = time.time()  # 结束计时elapsed_time_minute = (end_time - start_time)/60print(f"Total running time: {elapsed_time_minute:.2f} minutes")
- 记录程序结束时间,计算并打印总运行时间(以分钟为单位)。
模型评价
import torch
from sacrebleu.metrics import BLEU
from typing import List
# 假设已经定义了TranslationDataset, Encoder, Decoder, Seq2Seq类
- 导入必要的库和模块。torch用于深度学习,sacrebleu.metrics.BLEU用于评估翻译质量,typing.List用于类型注解。
load_sentences 函数
def load_sentences(file_path: str) -> List[str]:with open(file_path, 'r', encoding='utf-8') as f:return [line.strip() for line in f]
- 定义函数 load_sentences,用于从文件中加载句子。file_path是文件路径,函数返回一个包含所有句子的列表。
translate_sentence 函数
def translate_sentence(sentence: str, model: Seq2Seq, dataset: TranslationDataset, terminology, device: torch.device, max_length: int = 50):
- 定义一个函数 translate_sentence,用于翻译单个句子。参数包括:- sentence: 要翻译的句子。
- model: 预训练的- Seq2Seq模型。
- dataset:- TranslationDataset对象,包含词汇表等信息。
- terminology: 术语词典。
- device: 计算设备(CPU 或 GPU)。
- max_length: 翻译句子的最大长度,默认为 50。
 
    model.eval()
- 将模型设置为评估模式,关闭 dropout 和 batch normalization 等训练特有的操作。
    tokens = dataset.en_tokenizer(sentence)
- 使用 dataset中的en_tokenizer对输入句子进行分词,得到一个 token 列表。
    tensor = torch.LongTensor([dataset.en_word2idx.get(token, dataset.en_word2idx['<sos>']) for token in tokens]).unsqueeze(0).to(device)  # [1, seq_len]
- 将 token 列表转换为索引列表,并转换为 PyTorch 张量。如果 token 不在词汇表中,则使用 <sos>的索引。然后将张量增加一个维度并移动到指定的设备。
    with torch.no_grad():_, hidden = model.encoder(tensor)
- 关闭梯度计算,以节省内存和提高速度。然后使用编码器对输入张量进行编码,得到隐藏状态 hidden。
    translated_tokens = []
- 初始化一个空列表 translated_tokens,用于存储翻译的 token。
    input_token = torch.LongTensor([[dataset.zh_word2idx['<sos>']]]).to(device)  # [1, 1]
- 初始化解码器的输入 token,即 <sos>的索引,并将其转换为张量并移动到指定的设备。
    for _ in range(max_length):
- 开始一个循环,最多迭代 max_length次。
        output, hidden = model.decoder(input_token, hidden)
- 使用解码器对当前输入 token 和隐藏状态进行解码,得到输出 output和新的隐藏状态hidden。
        top_token = output.argmax(1)
- 从输出中选择概率最高的 token 索引。
        translated_token = dataset.zh_vocab[top_token.item()]
- 将索引转换为对应的 token。
        if translated_token == '<eos>':break
- 如果翻译的 token 是 <eos>,则结束循环。
        if translated_token in terminology.values():for en_term, ch_term in terminology.items():if translated_token == ch_term:translated_token = en_termbreak
- 如果翻译的 token 在术语词典中,则使用术语词典中的对应词替换。
        translated_tokens.append(translated_token)
- 将翻译的 token 添加到 translated_tokens列表中。
        input_token = top_token.unsqueeze(1)  # [1, 1]
- 更新输入 token 为当前选择的 token 索引。
    return ''.join(translated_tokens)
- 将翻译的 token 列表连接成一个字符串并返回。
evaluate_bleu 函数
def evaluate_bleu(model: Seq2Seq, dataset: TranslationDataset, src_file: str, ref_file: str, terminology, device: torch.device):
- 定义一个函数 evaluate_bleu,用于评估模型的 BLEU 分数。参数包括:- model: 预训练的- Seq2Seq模型。
- dataset:- TranslationDataset对象,包含词汇表等信息。
- src_file: 包含源语言句子的文件路径。
- ref_file: 包含参考翻译句子的文件路径。
- terminology: 术语词典。
- device: 计算设备(CPU 或 GPU)。
 
    model.eval()
- 将模型设置为评估模式,关闭 dropout 和 batch normalization 等训练特有的操作。
    src_sentences = load_sentences(src_file)
- 调用 load_sentences函数加载源语言句子,返回一个包含所有源句子的列表。
    ref_sentences = load_sentences(ref_file)
- 调用 load_sentences函数加载参考翻译句子,返回一个包含所有参考翻译句子的列表。
    translated_sentences = []
- 初始化一个空列表 translated_sentences,用于存储翻译的句子。
    for src in src_sentences:
- 遍历源语言句子列表。
        translated = translate_sentence(src, model, dataset, terminology, device)
- 调用 translate_sentence函数翻译当前的源句子,返回翻译后的句子。
        translated_sentences.append(translated)
- 将翻译后的句子添加到 translated_sentences列表中。
    bleu = BLEU()
- 创建一个 BLEU对象,用于计算 BLEU 分数。
    score = bleu.corpus_score(translated_sentences, [ref_sentences])
- 调用 corpus_score方法计算翻译句子的 BLEU 分数。translated_sentences是模型生成的翻译句子列表,[ref_sentences]是参考翻译句子列表。
    return score
- 返回计算得到的 BLEU 分数。
主程序
if __name__ == '__main__':
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
- 检查是否有可用的CUDA设备(即GPU),如果有则使用GPU,否则使用CPU。
    terminology = load_terminology_dictionary('../dataset/en-zh.dic')
- 加载术语词典,该字典可能包含特定领域的术语翻译。
    dataset = TranslationDataset('../dataset/train.txt', terminology)
- 创建一个 TranslationDataset对象,该对象用于加载和处理训练数据,并传递术语词典。
    INPUT_DIM = len(dataset.en_vocab)OUTPUT_DIM = len(dataset.zh_vocab)ENC_EMB_DIM = 256DEC_EMB_DIM = 256HID_DIM = 512N_LAYERS = 2ENC_DROPOUT = 0.5DEC_DROPOUT = 0.5
- 定义模型参数,包括输入和输出维度、嵌入维度、隐藏层维度、层数和 dropout 率。
    enc = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM, N_LAYERS, ENC_DROPOUT)dec = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM, N_LAYERS, DEC_DROPOUT)model = Seq2Seq(enc, dec, device).to(device)
- 初始化编码器和解码器,并将其组合成一个 Seq2Seq模型。然后将模型移动到指定的设备(GPU或CPU)。
    model.load_state_dict(torch.load('./translation_model_GRU.pth'))
- 加载预训练的模型参数。
    bleu_score = evaluate_bleu(model, dataset, '../dataset/dev_en.txt', '../dataset/dev_zh.txt', terminology=terminology, device=device)
- 调用 evaluate_bleu函数评估模型的 BLEU 分数。传递的参数包括模型、数据集、源语言文件路径、参考翻译文件路径、术语词典和设备。
    print(f'BLEU-4 score: {bleu_score.score:.2f}')
- 打印评估得到的 BLEU-4 分数,保留两位小数。
测试集上进行推理
inference 函数
def inference(model: Seq2Seq, dataset: TranslationDataset, src_file: str, save_dir: str, terminology, device: torch.device):
- 定义一个函数 inference,用于进行模型推理并将结果保存到文件中。参数包括:- model: 预训练的- Seq2Seq模型。
- dataset:- TranslationDataset对象,包含词汇表等信息。
- src_file: 包含源语言句子的文件路径。
- save_dir: 保存翻译结果的文件路径。
- terminology: 术语词典。
- device: 计算设备(CPU 或 GPU)。
 
    model.eval()
- 将模型设置为评估模式,关闭 dropout 和 batch normalization 等训练特有的操作。
    src_sentences = load_sentences(src_file)
- 调用 load_sentences函数加载源语言句子,返回一个包含所有源句子的列表。
    translated_sentences = []
- 初始化一个空列表 translated_sentences,用于存储翻译的句子。
    for src in src_sentences:
- 遍历源语言句子列表。
        translated = translate_sentence(src, model, dataset, terminology, device)
- 调用 translate_sentence函数翻译当前的源句子,返回翻译后的句子。
        translated_sentences.append(translated)
- 将翻译后的句子添加到 translated_sentences列表中。
    text = '\n'.join(translated_sentences)
- 将 translated_sentences列表中的所有句子连接成一个字符串,每个句子之间用换行符分隔。
    with open(save_dir, 'w', encoding='utf-8') as f:
- 打开一个文件,如果不存在则创建,'w'表示写模式,encoding='utf-8'表示使用 UTF-8 编码。
        f.write(text)
- 将连接后的字符串写入文件。
主程序
if __name__ == '__main__':
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
- 检查是否有可用的CUDA设备(即GPU),如果有则使用GPU,否则使用CPU。
    terminology = load_terminology_dictionary('../dataset/en-zh.dic')
- 加载术语词典,该字典可能包含特定领域的术语翻译。
    dataset = TranslationDataset('../dataset/train.txt', terminology=terminology)
- 创建一个 TranslationDataset对象,该对象用于加载和处理训练数据,并传递术语词典。
    INPUT_DIM = len(dataset.en_vocab)OUTPUT_DIM = len(dataset.zh_vocab)ENC_EMB_DIM = 256DEC_EMB_DIM = 256HID_DIM = 512N_LAYERS = 2ENC_DROPOUT = 0.5DEC_DROPOUT = 0.5
- 定义模型参数,包括输入和输出维度、嵌入维度、隐藏层维度、层数和 dropout 率。
    enc = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM, N_LAYERS, ENC_DROPOUT)dec = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM, N_LAYERS, DEC_DROPOUT)model = Seq2Seq(enc, dec, device).to(device)
- 初始化编码器和解码器,并将其组合成一个 Seq2Seq模型。然后将模型移动到指定的设备(GPU或CPU)。
    model.load_state_dict(torch.load('./translation_model_GRU.pth'))
- 加载预训练的模型参数。
    save_dir = '../dataset/submit.txt'
- 定义保存翻译结果的文件路径。
    inference(model, dataset, src_file="../dataset/test_en.txt", save_dir=save_dir, terminology=terminology, device=device)
- 调用 inference函数进行模型推理,并将翻译结果保存到指定文件中。传递的参数包括模型、数据集、源语言文件路径、保存路径、术语词典和设备。
    print(f"翻译完成!文件已保存到{save_dir}")
- 打印翻译完成的消息,并显示保存文件的路径。
相关文章:
datawhale - 基于术语词典干预的机器翻译挑战赛 (一)
文章目录 torchtext 库是干什么用的 ?TranslationDataset 类定义 Seq2Seq模型EncoderDecoderSeq2Seq 类 load_terminology_dictionary 函数示例用法 train 函数主程序代码模型评价load_sentences 函数translate_sentence 函数evaluate_bleu 函数主程序 测试集上进行…...
【JavaScript脚本宇宙】提升用户体验:探索 JavaScript 命令行界面开发工具
构建交互式命令行:JavaScript 中的 CLI 开发利器 前言 在现代软件开发中,命令行界面(CLI)和终端应用程序的开发变得越来越重要。为了提高用户体验和交互性,使用合适的工具和库是至关重要的。本文将介绍一些用于构建命…...
ubuntu18.04安装显卡驱动后无法进入桌面的解决办法
我没有尝试完美恢复的方法,只尝试了卸载nvidia显卡的方法 1.第一步 进 安开机键进入 1 开机进入 选项界面,选择高级模式(ubuntu 高级选项) 2.第二步 进去以后选择一个括号里面带recovery mode的选项,数字选最高最大的。 3.然后…...
 
javaScript的面试重点--预解析
目录 一.前言 二.预解析案例 一.前言 关于预解析,我们通过今天学习就能够知道解析器运行JS分为哪两步;能够说出变量提升的步骤和运行过程;能够说出函数提升的步骤和运行过程。 二.预解析案例 预解析,简而言之,也就是…...
 
Gitea 仓库事件触发Jenkins远程构建
文章目录 引言I Gitea 仓库事件触发Jenkins远程构建1.1 Jenkins配置1.2 Gitea 配置引言 应用场景:项目部署 I Gitea 仓库事件触发Jenkins远程构建 Gitea支持用于仓库事件的Webhooks 1.1 Jenkins配置 高版本Jenkins需要关闭跨域限制和开启匿名用户访问 在Jenkins启动前加入…...
 
springboot+vue 开发记录(九)后端打包部署运行
本篇文章主要内容是后端项目写好了,怎么打包部署到服务器上运行。 文章目录 1. 在服务器上安装Docker2. 在Docker中装MySQL3. 在Docker中设置网桥,实现容器间的网络通信4. 修改后端配置文件5. 修改pom.xml文件6. 打包7. 编写DockerFile文件8. 上传文件到…...
 
昇思25天学习打卡营第20天 | 基于MindNLP+MusicGen生成自己的个性化音乐
基于MindNLPMusicGen生成个性化音乐 实验简介 MusicGen是Meta AI提出的音乐生成模型,能够根据文本描述或音频提示生成高质量音乐。该模型基于Transformer结构,分为三个阶段:文本编码、音频token预测和音频解码。此实验将演示如何使用MindSpo…...
 
windows USB 设备驱动开发-USB主控制开发(一)
下面介绍主机驱动程序开发的高级概念和任务。 如果你正在编写与 Microsoft 提供的 USB 主机控制器扩展驱动程序 (Ucx01000.sys) 通信的新主机控制器驱动程序,则这部分内容适用于你。 下面是 Windows 中 USB 主机端驱动程序中显示的图表的修改版本。 此版本隐藏 USB…...
Dubbo 负载均衡(Load Balance)
在分布式系统中,负载均衡是确保系统高效稳定运行的关键技术之一。Dubbo 作为一款高性能的 RPC 框架,提供了多种负载均衡策略以满足不同场景的需求。本文将深入介绍 Dubbo 中常用的几种负载均衡策略:随机(Random)、轮询…...
ArcGIS Pro SDK (九)几何 3 点
ArcGIS Pro SDK (九)几何 3 点 文章目录 ArcGIS Pro SDK (九)几何 3 点1 构造地图点2 地图点生成器属性3 地图点的相等性4 缩放至指定点 环境:Visual Studio 2022 .NET6 ArcGIS Pro SDK 3.0 1 构造地图点 // 使用生…...
 
基于神经网络的分类和预测
基于神经网络的分类和预测 一、基础知识(一)引言(二)神经网络的基本概念(1)神经网络(2)神经元(3)常用的激活函数(非线性映射函数)&…...
 
VR头显如何低延迟播放8K的RTSP|RTMP流
技术背景 我们在做Unity平台RTSP、RTMP播放器的时候,有公司提出来这样的技术需求,希望在头显播放全景的8K RTSP|RTMP直播流,8K的数据,对头显和播放器,都提出了新的要求,我们从几个方面,探讨下V…...
 
2、ASPX、.NAT(环境/框架)安全
ASPX、.NAT(环境/框架)安全 源自小迪安全b站公开课 1、搭建组合: WindowsIISaspxsqlserver .NAT基于windows C开发的框架/环境 对抗Java xx.dll <> xx.jar 关键源码封装在dll文件内。 2、.NAT配置调试-信息泄露 功能点…...
 
在家上网IP地址是固定的吗?
在数字化时代,互联网已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐,我们都离不开网络的支持。然而,当我们在家中接入互联网时,可能会产生这样一个疑问:在家上网IP地址是固定的吗?下面一…...
 
交换机和路由器的工作流程
1、交换机工作流程: 将接口中的电流识别为二进制,并转换成数据帧,交换机会记录学习该数据帧的源MAC地址,并将其端口关联起来记录在MAC地址表中。然后查看MAC地址表来查找目标MAC地址,会有一下一些情况: MA…...
 
算法笔记——LCR
一.LCR 152. 验证二叉搜索树的后序遍历序列 题目描述: 给你一个二叉搜索树的后续遍历序列,让你判断该序列是否合法。 解题思路: 根据二叉搜索树的特性,二叉树搜索的每一个结点,大于左子树,小于右子树。…...
ChatGPT对话:如何制作静态网页?
【编者按】编者在很早以前制作过静态网页,之后长期没有使用,已完全不知道最新现状了。所以,从制作工具开始询问ChatGPT,回答非常全面,完全可以解决初学者的问题。 编者虽然长期不制作网页,但一直在编程&…...
 
k8s(二)
五、kubernetes架构(K8S的架构也是master和node模式) 集群里至少需要有一个master节点,即就是主节点。node节点可以多个。 若是多个master节点,worker节点和master的apiserverr进行交互时,就需要通过LB(load banlance)…...
ClickHouse表引擎概述
ClickHouse表引擎概述 表引擎的功能: 数据的存储方式 数据的存储位置 是否可以使用索引 是否可以使用分区 是否支持数据副本 并发数据访问 ClickHouse在建表时必须指定表引擎。 表引擎主要分为四大类:MergeTree系列、Log系列、与其他存储/处理系…...
 
jenkins系列-04-jenkins参数化构建
使用maven build之前,先checkout 指定分支或标签: 拖拽调整顺序:shell执行在前,构建在后: gitee新建标签tag:...
 
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
 
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
 
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
 
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
