基于Transformers的自然语言处理(NLP)入门(六)

本文为参加Datawhale组队学习时所写,如若需了解细致内容,请去到Datawhale官方开源课程基于transformers的自然语言处理(NLP)入门 (datawhalechina.github.io)

机器翻译任务

在这个小节中,我们将展示如何使用🤗 Transformers代码库中的模型来解决自然语言处理中的翻译任务。我们将会使用WMT dataset数据集。这是翻译任务最常用的数据集之一。

对于翻译任务,我们将展示如何使用简单的加载数据集,同时针对相应的仍无使用transformer中的Trainer接口对模型进行微调。

1
model_checkpoint = "Helsinki-NLP/opus-mt-en-ro"   # 选择一个模型checkpoint

本文我们使用已经训练好的Helsinki-NLP/opus-mt-en-ro checkpoint来做翻译任务。

加载数据

数据加载和评测方式加载只需要简单使用load_dataset和load_metric即可。我们使用WMT数据集中的English/Romanian双语翻译。

1
2
3
4
from datasets import load_dataset, load_metric

raw_datasets = load_dataset("wmt16", "ro-en")
metric = load_metric("sacrebleu")

dataset结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DatasetDict({
train: Dataset({
features: ['translation'],
num_rows: 610320
})
validation: Dataset({
features: ['translation'],
num_rows: 1999
})
test: Dataset({
features: ['translation'],
num_rows: 1999
})
})

随机选取数据进行展示

我们使用metric中的compute方法来对比predictions和labels,从而计算得分。predictions和labels都需要是一个list。具体格式见下面的例子:

1
2
3
fake_preds = ["hello there", "general kenobi"]
fake_labels = [["hello there"], ["general kenobi"]]
metric.compute(predictions=fake_preds, references=fake_labels)
1
2
3
4
5
6
7
{'score': 0.0,
'counts': [4, 2, 0, 0],
'totals': [4, 2, 0, 0],
'precisions': [100.0, 100.0, 0.0, 0.0],
'bp': 1.0,
'sys_len': 4,
'ref_len': 4}

数据预处理

1
2
3
from transformer import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)

以我们使用的mBART模型为例,我们需要正确设置source语言和target语言。

1
2
3
if "mbart" in model_checkpoint:
tokenizer.src_lang = "en-XX"
tokenizer.tgt_lang = "ro-RO"

token IDs也就是input_ids一般来说随着预训练模型名字的不同而有所不同。原因是不同的预训练模型在预训练的时候设定了不同的规则。但只要tokenizer和model的名字一致,那么tokenizer预处理的输入格式就会满足model需求的。

为了给模型准备好翻译的targets,我们使用as_target_tokenizer来控制targets所对应的特殊token:

1
2
3
4
5
6
with tokenizer.as_target_tokenizer():
print(tokenizer("Hello, this one sentence!"))
model_input = tokenizer("Hello, this one sentence!")
tokens = tokenizer.convert_ids_to_tokens(model_input['input_ids'])
# 打印看一下special toke
print('tokens: {}'.format(tokens))
1
2
{'input_ids': [10334, 1204, 3, 15, 8915, 27, 452, 59, 29579, 581, 23, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
tokens: ['▁Hel', 'lo', ',', '▁', 'this', '▁o', 'ne', '▁se', 'nten', 'ce', '!', '</s>']

如果您使用的是T5预训练模型的checkpoints,需要对特殊的前缀进行检查。T5使用特殊的前缀来告诉模型具体要做的任务,具体前缀例子如下:

1
2
3
4
if model_checkpoint in ["t5-small", "t5-base", "t5-larg", "t5-3b", "t5-11b"]:
prefix = "translate English to Romanian: "
else:
prefix = ""

现在我们可以把所有内容放在一起组成我们的预处理函数了。我们对样本进行预处理的时候,我们还会truncation=True这个参数来确保我们超长的句子被截断。默认情况下,对与比较短的句子我们会自动padding。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
max_input_length = 128
max_target_length = 128
source_lang = "en"
target_lang = "ro"

def preprocess_function(examples):
inputs = [prefix + ex[source_lang] for ex in examples["translation"]]
targets = [ex[target_lang] for ex in examples["translation"]]
model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)

# Setup the tokenizer for targets
with tokenizer.as_target_tokenizer():
labels = tokenizer(targets, max_length=max_target_length, truncation=True)

model_inputs["labels"] = labels["input_ids"]
return model_inputs

接下来对数据集datasets里面的所有样本进行预处理,处理的方式是使用map函数,将预处理函数prepare_train_features应用到(map)所有样本上。

1
tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)

Fine-tuning微调模型

1
2
3
from transformers import AutoModelForSeq2SeqLM, DataCollatorForSeq2Seq, Seq2SeqTrainingArguments, Seq2SeqTrainer

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)

模型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
batch_size = 16
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=1,
predict_with_generate=True,
fp16=False,
)

数据收集器

1
data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)

数据后处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np

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)

# Replace -100 in the labels as we can't decode them.
labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

# Some simple post-processing
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()}
return result
1
2
3
4
5
6
7
8
9
10
11
12
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()

基于Transformers的自然语言处理(NLP)入门(六)
https://www.spacezxy.top/2021/09/28/nlp-transformer/nlp-transformer-6/
作者
Xavier ZXY
发布于
2021年9月28日
许可协议