一、数据处理

我的数据集是这样的:

bert微调机器翻译模型 bert 机器翻译_bert微调机器翻译模型


第一列是英文,第二列是对应的法文翻译,第三列是文本来源,所以说第三列是不需要的

1.首先是读取数据,把前两列存入数组中,并把前90%的数据作为训练集,后10%的数据作为验证集

f=open("fra.txt","r",encoding="utf-8").readlines()
en=[]
fre=[]
data=[]
for l in f:
    line=l.strip().split("\t")
    tmp={}
    tmp["en"]=line[0]
    tmp["fr"]=line[1]
    data.append(tmp)
print(len(data))
print(data[0])
train_size=int(len(data)*0.9)
train_data=data[:train_size]
val_data=data[train_size:]

2.把数据存入到对应的文件中

f=open("train.txt","w")
for i in train_data:
    f.write(str(i)+"\n")
f.close()
f=open("val.txt","w")
for i in val_data:
    f.write(str(i)+"\n")
f.close()

3.加载分词器

# 分词,使用已经训练好的helsinki-NLP/opus-mt-en-ro来做翻译任务
from transformers import AutoTokenizer
model_checkpoint="Helsinki-NLP/opus-mt-en-ro"
tokenizer=AutoTokenizer.from_pretrained(model_checkpoint,use_fast=True)

4.定义预处理函数,把数据按源语言和目标语言分开,作为dataset中的input_ids和labels

from datasets import load_dataset
raw_datasets=load_dataset("text",data_files={"train":"train.txt","validation":"val.txt"})

max_input_length=64
max_target_length=64
source_lang="en"
target_lang="fr"

def preprocess_function(examples):
    inputs=[eval(ex)[source_lang] for ex in examples["text"]]
    targets=[eval(ex)[target_lang] for ex in examples["text"]]
    model_inputs=tokenizer(inputs,max_length=max_input_length,truncation=True)
    # 为目标语言设置分词器
    with tokenizer.as_target_tokenizer():
        labels=tokenizer(targets,max_length=max_target_length,truncation=True)
    model_inputs["labels"]=labels["input_ids"]
    return model_inputs    
# 把预处理函数应用到原始的dataset中
tokenized_datasets=raw_datasets.map(preprocess_function,batched=True)

分别输出raw_datasets和tokenized_datasets的结构,可以看到后者多了三列内容,我们只需要关注input_ids和labels就行:

bert微调机器翻译模型 bert 机器翻译_数据_02

二、加载预训练模型,并设置参数

1.预训练模型和训练参数

# 加载预训练模型
from transformers import AutoModelForSeq2SeqLM
model=AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
# 设定训练参数
from transformers import Seq2SeqTrainingArguments
batch_size=8
args=Seq2SeqTrainingArguments(
    "test-translation",
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    weight_decay=0.01,
    save_total_limit=3, # 至多保存的模型个数
    num_train_epochs=10,
    predict_with_generate=True,
    fp16=False,
)

2.载入数据收集器

# 数据收集器data collator,告诉trainer如何从预处理的输入数据中构造batch,我使用数据处理器DataCollatorForSeq2Seq
# 将预处理的输入分batch再次处理后喂给模型
from transformers import DataCollatorForSeq2Seq
data_collator=DataCollatorForSeq2Seq(tokenizer,model=model)

3.加载评估方法

# 定义评估方法,使用bleu指标,利用metric.compute计算该指标对模型进行评估
# 定义postprocess_text函数做一些数据后处理
import numpy as np

from datasets import load_metric
metric=load_metric("sacrebleu")
print("successfully import metric")

4.评估方法只能接收特定格式的数据,因此要把用postprocess_text方法把preds和labels处理成对应的格式

def postprocess_text(preds,labels):
    preds=[pred.strip() for pred in preds]
    labels=[[label.strip()] for label in labels]
    return preds,labels

def compute_metrics(eval_preds):
    preds,labels=eval_preds
    if isinstance(preds,tuple):
        preds=preds[0]
    decoded_preds=tokenizer.batch_decode(preds,skip_special_tokens=True)

    # 如果labels=-100,说明这个label是无法编码的,应该用pad_token_id去进行填充
    labels=np.where(labels!=-100,labels,tokenizer.pad_token_id)
    decoded_labels=tokenizer.batch_decode(labels,skip_special_tokens=True)

    # 把预测后的输出转成适合metric.compute输入的格式
    print("type(decoded_preds)=",type(decoded_preds))
    print("type(decoded_labels)=",type(decoded_labels))
    decoded_preds,decoded_labels=postprocess_text(decoded_preds,decoded_labels)

    result=metric.compute(predictions=decoded_preds,references=decoded_labels)
    result={"bleu":result["score"]}
    
    prediction_lens=[np.count_nonzero(pred!=tokenizer.pad_token_id) for pred in preds]
    result["gen_len"]=np.mean(prediction_lens)
    result={k:round(v,4) for k,v in result.items()}
    print("result is as follow============================")
    print(result)
    return result

三、开始训练(微调模型)

# 开始训练
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
from transformers import Seq2SeqTrainer
trainer=Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"], # 这里要改成自己的训练集
    eval_dataset=tokenized_datasets["validation"], # 这里要改成自己的验证集
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)
trainer.train()

训练结果如下:

bert微调机器翻译模型 bert 机器翻译_bert微调机器翻译模型_03


sacrebleu的评测结果是乘了100后的,所以可以看到评估出来的bleu是在44%左右。

这个实验是用A100跑的,但是跑了10个epoch竟然花了两个半小时。