赞
踩
在Task3中,我们使用了Transformer模型来进行机器翻译,以提升翻译的准确度和翻译的效率。Transformer模型摒弃了循环结构,并完全通过注意力机制完成对源语言序列和目标语言序列全局依赖进行建模。在抽取每个单词的上下文特征时,Transformer 通过自注意力机制(self-attention)衡量上下文中每一个单词对当前单词的重要程度,在这个过程当中没有任何的循环单元参与计算。这种高度可并行化的编码过程使得模型的运行变得十分高效。
Transformer的主要组件包括编码器(Encoder)、解码器(Decoder)和注意力层。其核心是利用多头自注意力机制(Multi-Head Self-Attention),使每个位置的表示不仅依赖于当前位置,还能够直接获取其他位置的表示。自从提出以来,Transformer模型在机器翻译、文本生成等自然语言处理任务中均取得了突破性进展,成为NLP领域新的主流模型。
Transformer模型是机器翻译领域一个非常重要的知识点,是各种笔试面试必考的地方,面试题可以参考transformer模型— 20道面试题自我检测
Transformer模型的首次提出是在论文《Attention Is All You Need》上,有关于论文的讲解和解读可以参考李沐老师的视频Transformer论文逐段精读【论文精读】 ,更多有关论文精读的资料可以见深度学习论文精读
主要的优化部分是将模型结构的代码改成使用Transformer模型。
- !mkdir ../model
- !mkdir ../results
- !pip install torchtext
- !pip install jieba
- !pip install sacrebleu
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- from torch.nn.utils import clip_grad_norm_
- from torchtext.data.metrics import bleu_score
- from torch.utils.data import Dataset, DataLoader
- from torchtext.data.utils import get_tokenizer
- from torchtext.vocab import build_vocab_from_iterator
- from typing import List, Tuple
- import jieba
- import random
- from torch.nn.utils.rnn import pad_sequence
- import sacrebleu
- import time
- import math
- !pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple
- !pip install -U 'spacy[cuda12x]' -i https://mirrors.aliyun.com/pypi/simple
- !pip install ../dataset/en_core_web_trf-3.7.3-py3-none-any.whl
- # !python -m spacy download en_core_web_sm
- # 定义tokenizer
- en_tokenizer = get_tokenizer('spacy', language='en_core_web_trf')
- zh_tokenizer = lambda x: list(jieba.cut(x)) # 使用jieba分词
- # 读取数据函数
- def read_data(file_path: str) -> List[str]:
- with open(file_path, 'r', encoding='utf-8') as f:
- return [line.strip() for line in f]
-
- # 数据预处理函数
- def preprocess_data(en_data: List[str], zh_data: List[str]) -> List[Tuple[List[str], List[str]]]:
- processed_data = []
- for en, zh in zip(en_data, zh_data):
- en_tokens = en_tokenizer(en.lower())[:MAX_LENGTH]
- zh_tokens = zh_tokenizer(zh)[:MAX_LENGTH]
- if en_tokens and zh_tokens: # 确保两个序列都不为空
- processed_data.append((en_tokens, zh_tokens))
- return processed_data
-
- # 构建词汇表
- def build_vocab(data: List[Tuple[List[str], List[str]]]):
- en_vocab = build_vocab_from_iterator(
- (en for en, _ in data),
- specials=['<unk>', '<pad>', '<bos>', '<eos>']
- )
- zh_vocab = build_vocab_from_iterator(
- (zh for _, zh in data),
- specials=['<unk>', '<pad>', '<bos>', '<eos>']
- )
- en_vocab.set_default_index(en_vocab['<unk>'])
- zh_vocab.set_default_index(zh_vocab['<unk>'])
- return en_vocab, zh_vocab
-
-
- class TranslationDataset(Dataset):
- def __init__(self, data: List[Tuple[List[str], List[str]]], en_vocab, zh_vocab):
- self.data = data
- self.en_vocab = en_vocab
- self.zh_vocab = zh_vocab
-
- def __len__(self):
- return len(self.data)
-
- def __getitem__(self, idx):
- en, zh = self.data[idx]
- en_indices = [self.en_vocab['<bos>']] + [self.en_vocab[token] for token in en] + [self.en_vocab['<eos>']]
- zh_indices = [self.zh_vocab['<bos>']] + [self.zh_vocab[token] for token in zh] + [self.zh_vocab['<eos>']]
- return en_indices, zh_indices
- def collate_fn(batch):
- en_batch, zh_batch = [], []
- for en_item, zh_item in batch:
- if en_item and zh_item: # 确保两个序列都不为空
- # print("都不为空")
- en_batch.append(torch.tensor(en_item))
- zh_batch.append(torch.tensor(zh_item))
- else:
- print("存在为空")
- if not en_batch or not zh_batch: # 如果整个批次为空,返回空张量
- return torch.tensor([]), torch.tensor([])
-
- # src_sequences = [item[0] for item in batch]
- # trg_sequences = [item[1] for item in batch]
-
- en_batch = nn.utils.rnn.pad_sequence(en_batch, batch_first=True, padding_value=en_vocab['<pad>'])
- zh_batch = nn.utils.rnn.pad_sequence(zh_batch, batch_first=True, padding_value=zh_vocab['<pad>'])
-
- # en_batch = pad_sequence(en_batch, batch_first=True, padding_value=en_vocab['<pad>'])
- # zh_batch = pad_sequence(zh_batch, batch_first=True, padding_value=zh_vocab['<pad>'])
-
- return en_batch, zh_batch
-
- # 数据加载函数
- def load_data(train_path: str, dev_en_path: str, dev_zh_path: str, test_en_path: str):
- # 读取训练数据
- train_data = read_data(train_path)
- train_en, train_zh = zip(*(line.split('\t') for line in train_data))
-
- # 读取开发集和测试集
- dev_en = read_data(dev_en_path)
- dev_zh = read_data(dev_zh_path)
- test_en = read_data(test_en_path)
-
- # 预处理数据
- train_processed = preprocess_data(train_en, train_zh)
- dev_processed = preprocess_data(dev_en, dev_zh)
- test_processed = [(en_tokenizer(en.lower())[:MAX_LENGTH], []) for en in test_en if en.strip()]
-
- # 构建词汇表
- global en_vocab, zh_vocab
- en_vocab, zh_vocab = build_vocab(train_processed)
-
- # 创建数据集
- train_dataset = TranslationDataset(train_processed, en_vocab, zh_vocab)
- dev_dataset = TranslationDataset(dev_processed, en_vocab, zh_vocab)
- test_dataset = TranslationDataset(test_processed, en_vocab, zh_vocab)
-
- from torch.utils.data import Subset
-
- # 假设你有10000个样本,你只想用前1000个样本进行测试
- indices = list(range(N))
- train_dataset = Subset(train_dataset, indices)
-
- # 创建数据加载器
- train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_fn, drop_last=True)
- dev_loader = DataLoader(dev_dataset, batch_size=BATCH_SIZE, collate_fn=collate_fn, drop_last=True)
- test_loader = DataLoader(test_dataset, batch_size=1, collate_fn=collate_fn, drop_last=True)
-
- return train_loader, dev_loader, test_loader, en_vocab, zh_vocab
- class PositionalEncoding(nn.Module):
- def __init__(self, d_model, dropout=0.1, max_len=5000):
- super(PositionalEncoding, self).__init__()
- self.dropout = nn.Dropout(p=dropout)
-
- pe = torch.zeros(max_len, d_model)
- position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
- div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
- pe[:, 0::2] = torch.sin(position * div_term)
- pe[:, 1::2] = torch.cos(position * div_term)
- pe = pe.unsqueeze(0).transpose(0, 1)
- self.register_buffer('pe', pe)
-
- def forward(self, x):
- x = x + self.pe[:x.size(0), :]
- return self.dropout(x)
-
- class TransformerModel(nn.Module):
- def __init__(self, src_vocab, tgt_vocab, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout):
- super(TransformerModel, self).__init__()
- self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout)
- self.src_embedding = nn.Embedding(len(src_vocab), d_model)
- self.tgt_embedding = nn.Embedding(len(tgt_vocab), d_model)
- self.positional_encoding = PositionalEncoding(d_model, dropout)
- self.fc_out = nn.Linear(d_model, len(tgt_vocab))
- self.src_vocab = src_vocab
- self.tgt_vocab = tgt_vocab
- self.d_model = d_model
-
- def forward(self, src, tgt):
- # 调整src和tgt的维度
- src = src.transpose(0, 1) # (seq_len, batch_size)
- tgt = tgt.transpose(0, 1) # (seq_len, batch_size)
-
- src_mask = self.transformer.generate_square_subsequent_mask(src.size(0)).to(src.device)
- tgt_mask = self.transformer.generate_square_subsequent_mask(tgt.size(0)).to(tgt.device)
-
- src_padding_mask = (src == self.src_vocab['<pad>']).transpose(0, 1)
- tgt_padding_mask = (tgt == self.tgt_vocab['<pad>']).transpose(0, 1)
-
- src_embedded = self.positional_encoding(self.src_embedding(src) * math.sqrt(self.d_model))
- tgt_embedded = self.positional_encoding(self.tgt_embedding(tgt) * math.sqrt(self.d_model))
-
- output = self.transformer(src_embedded, tgt_embedded,
- src_mask, tgt_mask, None, src_padding_mask, tgt_padding_mask, src_padding_mask)
- return self.fc_out(output).transpose(0, 1)
- def initialize_model(src_vocab, tgt_vocab, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6, dim_feedforward=2048, dropout=0.1):
- model = TransformerModel(src_vocab, tgt_vocab, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout)
- return model
- # 定义优化器
- def initialize_optimizer(model, learning_rate=0.001):
- return optim.Adam(model.parameters(), lr=learning_rate)
- # 运行时间
- def epoch_time(start_time, end_time):
- elapsed_time = end_time - start_time
- elapsed_mins = int(elapsed_time / 60)
- elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
- return elapsed_mins, elapsed_secs
- def train(model, iterator, optimizer, criterion, clip):
- model.train()
- epoch_loss = 0
-
- for i, batch in enumerate(iterator):
- src, tgt = batch
- if src.numel() == 0 or tgt.numel() == 0:
- continue
-
- src, tgt = src.to(DEVICE), tgt.to(DEVICE)
-
- optimizer.zero_grad()
- output = model(src, tgt[:, :-1])
-
- output_dim = output.shape[-1]
- output = output.contiguous().view(-1, output_dim)
- tgt = tgt[:, 1:].contiguous().view(-1)
-
- loss = criterion(output, tgt)
- loss.backward()
-
- clip_grad_norm_(model.parameters(), clip)
- optimizer.step()
-
- epoch_loss += loss.item()
-
- return epoch_loss / len(iterator)
-
- def evaluate(model, iterator, criterion):
- model.eval()
- epoch_loss = 0
- with torch.no_grad():
- for i, batch in enumerate(iterator):
- src, tgt = batch
- if src.numel() == 0 or tgt.numel() == 0:
- continue
-
- src, tgt = src.to(DEVICE), tgt.to(DEVICE)
-
- output = model(src, tgt[:, :-1])
-
- output_dim = output.shape[-1]
- output = output.contiguous().view(-1, output_dim)
- tgt = tgt[:, 1:].contiguous().view(-1)
-
- loss = criterion(output, tgt)
- epoch_loss += loss.item()
-
- return epoch_loss / len(iterator)
- def translate_sentence(src_indexes, src_vocab, tgt_vocab, model, device, max_length=50):
- model.eval()
-
- src_tensor = src_indexes.unsqueeze(0).to(device) # 添加批次维度
-
- with torch.no_grad():
- encoder_outputs = model.transformer.encoder(model.positional_encoding(model.src_embedding(src_tensor) * math.sqrt(model.d_model)))
-
- trg_indexes = [tgt_vocab['<bos>']]
- for i in range(max_length):
- trg_tensor = torch.LongTensor(trg_indexes).unsqueeze(0).to(device)
-
- with torch.no_grad():
- output = model(src_tensor, trg_tensor)
-
- pred_token = output.argmax(2)[:, -1].item()
- trg_indexes.append(pred_token)
-
- if pred_token == tgt_vocab['<eos>']:
- break
-
- trg_tokens = [tgt_vocab.get_itos()[i] for i in trg_indexes]
- return trg_tokens[1:-1] # 移除<bos>和<eos>标记
- def calculate_bleu(dev_loader, src_vocab, tgt_vocab, model, device):
- model.eval()
- translations = []
- references = []
-
- with torch.no_grad():
- for src, tgt in dev_loader:
- src = src.to(device)
- for sentence in src:
- translated = translate_sentence(sentence, src_vocab, tgt_vocab, model, device)
- translations.append(' '.join(translated))
-
- for reference in tgt:
- ref_tokens = [tgt_vocab.get_itos()[idx] for idx in reference if idx not in [tgt_vocab['<bos>'], tgt_vocab['<eos>'], tgt_vocab['<pad>']]]
- references.append([' '.join(ref_tokens)])
-
- bleu = sacrebleu.corpus_bleu(translations, references)
- return bleu.score
- # 主训练循环
- def train_model(model, train_iterator, valid_iterator, optimizer, criterion, N_EPOCHS=10, CLIP=1, save_path = '../model/best-model_transformer.pt'):
- best_valid_loss = float('inf')
-
- for epoch in range(N_EPOCHS):
- start_time = time.time()
-
- #print(f"Starting Epoch {epoch + 1}")
- train_loss = train(model, train_iterator, optimizer, criterion, CLIP)
- valid_loss = evaluate(model, valid_iterator, criterion)
-
- end_time = time.time()
- epoch_mins, epoch_secs = epoch_time(start_time, end_time)
-
- if valid_loss < best_valid_loss:
- best_valid_loss = valid_loss
- torch.save(model.state_dict(), save_path)
-
- print(f'Epoch: {epoch+1:02} | Time: {epoch_mins}m {epoch_secs}s')
- print(f'\tTrain Loss: {train_loss:.3f} | Train PPL: {math.exp(train_loss):7.3f}')
- print(f'\t Val. Loss: {valid_loss:.3f} | Val. PPL: {math.exp(valid_loss):7.3f}')
-
- # 定义常量
- MAX_LENGTH = 100 # 最大句子长度
- BATCH_SIZE = 32
- DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
- N = 148363 # 采样训练集的数量,最多148363
-
- train_path = '../dataset/train.txt'
- dev_en_path = '../dataset/dev_en.txt'
- dev_zh_path = '../dataset/dev_zh.txt'
- test_en_path = '../dataset/test_en.txt'
-
- train_loader, dev_loader, test_loader, en_vocab, zh_vocab = load_data(
- train_path, dev_en_path, dev_zh_path, test_en_path
- )
-
-
- print(f"英语词汇表大小: {len(en_vocab)}")
- print(f"中文词汇表大小: {len(zh_vocab)}")
- print(f"训练集大小: {len(train_loader.dataset)}")
- print(f"开发集大小: {len(dev_loader.dataset)}")
- print(f"测试集大小: {len(test_loader.dataset)}")
- # 主函数
- if __name__ == '__main__':
-
- # 模型参数
- D_MODEL = 256
- NHEAD = 8
- NUM_ENCODER_LAYERS = 3
- NUM_DECODER_LAYERS = 3
- DIM_FEEDFORWARD = 512
- DROPOUT = 0.1
-
- N_EPOCHS = 5
- CLIP = 1
-
- # 初始化模型
- model = initialize_model(en_vocab, zh_vocab, D_MODEL, NHEAD, NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS, DIM_FEEDFORWARD, DROPOUT).to(DEVICE)
- print(f'The model has {sum(p.numel() for p in model.parameters() if p.requires_grad):,} trainable parameters')
-
- # 定义损失函数
- criterion = nn.CrossEntropyLoss(ignore_index=zh_vocab['<pad>'])
- # 初始化优化器
- optimizer = optim.Adam(model.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)
-
- # 训练模型
- save_path = '../model/best-model_transformer.pt'
- train_model(model, train_loader, dev_loader, optimizer, criterion, N_EPOCHS, CLIP, save_path=save_path)
-
- print(f"训练完成!模型已保存到:{save_path}")
- # 加载最佳模型
- model.load_state_dict(torch.load('../model/best-model_transformer.pt'))
- save_dir = '../results/submit_task3.txt'
- with open(save_dir, 'w') as f:
- translated_sentences = []
- for batch in test_loader: # 遍历所有数据
- src, _ = batch
- src = src.to(DEVICE)
- translated = translate_sentence(src[0], en_vocab, zh_vocab, model, DEVICE) #翻译结果
- results = "".join(translated)
- f.write(results + '\n') # 将结果写入文件
- print(f"翻译完成,结果已保存到{save_dir}")

代码运行结果如下图所示:
比赛提交分数如下图所示:
最简单的就是调参,将 epochs 调大一点,使用全部训练集,以及调整模型的参数,如head、layers等。如果数据量允许,增加模型的深度(更多的编码器/解码器层)或宽度(更大的隐藏层尺寸),这通常可以提高模型的表达能力和翻译质量,尤其是在处理复杂或专业内容时。
加入术语词典,这是在此竞赛中比较有效的方法,加入术语词典的方法策略也有很多,如:
在模型生成的翻译输出中替换术语,这是最简单的方法
整合到数据预处理流程,确保它们在翻译中保持一致
在模型内部动态地调整术语的嵌入,这涉及到在模型中加入一个额外的层,该层负责查找术语词典中的术语,并为其生成专门的嵌入向量,然后将这些向量与常规的词嵌入结合使用
认真做数据清洗,当前训练集可能会存在脏数据,影响我们的模型训练
数据扩增:
回译(back-translation):将源语言文本先翻译成目标语言,再将目标语言文本翻译回源语言,生成的新文本作为额外的训练数据
同义词替换:随机选择句子中的词,并用其同义词替换
使用句法分析和语义解析技术重新表述句子,保持原意不变
将文本翻译成多种语言后再翻译回原语言,以获得多样化翻译
采用更精细的学习率调度策略(Task1使用的是固定学习率):
Noam Scheduler:结合了warmup阶段和衰减阶段
Step Decay:最简单的一种学习率衰减策略,每隔一定数量的epoch,学习率按固定比例衰减
Cosine Annealing:学习率随周期性变化,通常从初始值下降到接近零,然后再逐渐上升
自己训练一个小的预训练模型,尽量选择 1B 以下小模型,对 GPU 资源要求比较高,仅仅使用魔搭平台可能就满足不了
将训练集上训练出来的模型拿到开发集(dev dataset)上 finetune 可以提高测试集(test dataset)的得分,因为开发集与测试集的分布比较相近
在开发集和测试集上训一个语言模型,用这个语言模型给训练集中的句子打分,选出一些高分句子
集成学习:训练多个不同初始化或架构的模型,并使用集成方法(如投票或平均)来产生最终翻译。这可以减少单一模型的过拟合风险,提高翻译的稳定性。
- # 定义常量
- MAX_LENGTH = 100 # 最大句子长度
- BATCH_SIZE = 64
- DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
- N = 148363 # 采样训练集的数量,最多148363
- # 模型参数
- D_MODEL = 256
- NHEAD = 8
- NUM_ENCODER_LAYERS = 5
- NUM_DECODER_LAYERS = 5
- DIM_FEEDFORWARD = 512
- DROPOUT = 0.1
-
- N_EPOCHS = 10
- CLIP = 1
Transformer模型的核心在于自注意力机制(Self-Attention),其通过允许每个位置上的单词与序列中其他位置的单词进行关联,从而捕捉全局上下文信息。这种机制克服了传统循环神经网络中的长期依赖问题,并且可以并行处理序列中的每个元素,极大地提高了计算效率。
在Transformer中,输入序列被分为多个维度表示,例如词嵌入(Word Embeddings)。自注意力机制的计算过程如下:
由于Transformer没有像RNN那样的序列位置信息,所以需要添加位置编码来表示每个词的位置。位置编码通常是通过添加不同频率的正弦和余弦函数来实现的,使得模型可以理解输入序列中的顺序信息。
Transformer模型通常由多个堆叠的模块组成,主要包括编码器(Encoder)和解码器(Decoder):
最终,解码器的输出会经过线性变换和softmax操作,生成最终的预测输出序列。
Transformer模型广泛应用于自然语言处理领域,如机器翻译、文本生成、问答系统、命名实体识别等任务。它也被扩展到其他领域,如计算机视觉和语音处理,展示了其在序列建模和生成任务中的广泛适用性和效果。
使用大模型进行机器翻译,要比自己训练一个任务简单许多,主要有两种解决方案:
直接调用大模型 API 来解决
本地部署大模型进行推理
可以参考Datawhale 的llm-universe 项目https://github.com/datawhalechina/llm-universe/tree/main/notebook/C2%20%E4%BD%BF%E7%94%A8%20LLM%20API%20%E5%BC%80%E5%8F%91%E5%BA%94%E7%94%A8 实现了ChatGPT、百度文心、讯飞星火、智谱AI等大模型API的调用,包括调用原生 API、封装为 LangChain LLM、封装为 Fastapi 等调用方式。
(Qwen2-7B-Instruct 模型需要显存 16GB +,如果没有这么大可以尝试其他小一点的模型)
- import os
- os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
-
- from transformers import AutoModelForCausalLM, AutoTokenizer
- device = "cuda" # the device to load the model onto
-
- model = AutoModelForCausalLM.from_pretrained(
- "Qwen/Qwen2-7B-Instruct", #"/mnt/data/xuhu/qwen/Qwen2-7B-Instruct"
- torch_dtype="auto",
- device_map="auto"
- )
-
- tokenizer = AutoTokenizer.from_pretrained("/mnt/data/xuhu/qwen/Qwen2-7B-Instruct")
环境变量设置: 代码中设置了一个环境变量HF_ENDPOINT
,值为https://hf-mirror.com
。这可能用于指定模型或数据下载时的镜像或服务端点,确保从指定的地址获取模型或数据。
导入库: 通过导入transformers
库中的AutoModelForCausalLM
和AutoTokenizer
类,代码准备加载预训练的语言模型和其分词器。这些类提供了方便的接口,使得加载和使用预训练模型变得更加简单和高效。
设备选择: 代码指定了设备为cuda
,这意味着希望将模型加载到CUDA设备上进行运算。CUDA通常是NVIDIA GPU的计算能力,用于加速深度学习模型的训练和推理过程。如果没有CUDA设备,可以选择使用cpu
作为替代。
加载预训练模型: 使用AutoModelForCausalLM.from_pretrained
方法加载预训练模型。这个方法接受模型的名称或路径作为参数,并且可以自动选择合适的张量数据类型和设备映射,以优化模型在给定硬件上的性能表现。
加载分词器: 代码加载与模型对应的分词器,通过AutoTokenizer.from_pretrained
方法实现。分词器的作用是将输入的文本数据转换为模型能够理解的标记序列,这是使用预训练模型进行文本处理和生成的必要步骤。
- import pandas as pd
- dc = pd.read_csv('../dataset/en-zh.dic', sep='\t', header=None).set_index(0).to_dict()[1]
-
- from tqdm import tqdm
- lines = open('../dataset/test_en.txt').readlines()
pd.read_csv('../dataset/en-zh.dic', sep='\t', header=None)
: 使用Pandas库的read_csv
函数读取文件../dataset/en-zh.dic
,这是一个以制表符分隔的文本文件,没有列标题(header=None)。.set_index(0)
: 将读取的数据框的第一列设置为索引,这里假设第一列包含英文单词或短语。.to_dict()[1]
: 将数据框转换为字典,并选择字典的第二个值列(即第二列,索引为1),这里假设第二列包含对应的中文翻译。dc
变量成为一个字典,其中英文单词或短语是键,对应的中文翻译是值。open('../dataset/test_en.txt').readlines()
: 打开文件../dataset/test_en.txt
并读取所有行,将每一行作为一个字符串存储在lines
列表中。from tqdm import tqdm
: 导入tqdm
库,用于显示进度条,可能在处理大量文本时特别有用。- result = []
- for line in tqdm(lines):
- sp_words = [x for x in line.lower().split() if x in dc.keys()]
- sp_words_meaning = [dc[x] for x in sp_words]
-
- sp_prompt = '文章字符为:'
- if len(sp_words) > 0:
- for x, y in zip(sp_words, sp_words_meaning):
- sp_prompt += f'{x} 翻译为 {y}; '
- # 主要任务
- messages = [
- {"role": "system", "content": "将英文翻译为中文,不要有其他输出,直接输出翻译后的文本。保留特殊单词的翻译。"},
- ]
-
- # 人工的词典的规则
- if len(sp_prompt) > 0:
- messages.append({"role": "user", "content": sp_prompt})
- messages.append({"role": "user", "content": f"待翻译文本(从英文翻译为中文):{line}"})
-
- text = tokenizer.apply_chat_template(
- messages,
- tokenize=False,
- add_generation_prompt=True
- )
- model_inputs = tokenizer([text], return_tensors="pt").to(device)
-
- generated_ids = model.generate(
- model_inputs.input_ids,
- max_new_tokens=512
- )
- generated_ids = [
- output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
- ]
-
- result_line = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
- result.append(result_line)
-
- with open('../results/qwen_submit.txt', 'w') as f:
- for line in result:
- line = line.strip().replace('\n', '')
- print(line)
- f.write(line + '\n')

循环处理每一行文本:使用for
循环逐行处理lines
列表中的每一行英文文本。tqdm(lines)
用于在处理过程中显示进度条,方便查看处理进度。
提取特定单词和翻译:对当前行的文本进行小写处理,并根据在词典dc
中存在的单词提取它们及其对应的中文翻译。这里使用列表推导式来过滤和映射符合条件的单词和翻译。
构建特殊单词提示:sp_words_meaning): sp_prompt += f'{x} 翻译为 {y}; '
如果存在特殊单词(即在词典dc
中存在的单词),则将其构建成一条提示信息sp_prompt
,格式为“英文单词 翻译为 中文翻译;”。
构建消息列表:构建一个消息列表messages
,其中包含了系统的指导信息和用户的输入信息。如果存在特殊单词的提示信息,将其作为用户输入的一部分添加到消息列表中。
生成模型输入:使用Tokenizer对象的apply_chat_template
方法生成聊天模板的文本,并将其转换为模型的输入。tokenizer
对象被用来对文本进行预处理,并将其转换为适合模型输入的张量表示形式,同时确保在CUDA设备上进行计算。
生成文本:使用预训练的语言生成模型(model
)生成文本。generate
方法接受模型输入的input_ids
和生成的最大令牌数(max_new_tokens
),生成对应的文本输出。
处理生成的文本:将生成的文本通过Tokenizer对象的batch_decode
方法转换为可读的文本,并将结果存储在result_line
中。
将结果写入文件:将每一行处理后的结果result_line
添加到result
列表中,并将最终的翻译结果写入到文件qwen_submit.txt
中。每行结果去除首尾空白并写入文件。
关于更多部署微调大模型的知识,可以学习:
开源大模型食用指南https://github.com/datawhalechina/self-llm
hahaha都看到这里了,要是觉得有用的话就辛苦动动小手点个赞吧!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。