多项选择任务¶
多项选择任务类似于问答任务,不同之处在于提供了多个候选答案以及一些上下文,模型需要训练以选择正确的答案。
本指南将向您展示如何:
- 在 SWAG 数据集的
regular配置上微调 BERT,以在给定的多个选项和一些上下文中选择最佳答案。 - 使用您微调的模型进行推理。
在开始之前,请确保您已安装所有必要的库:
pip install transformers datasets evaluate
我们鼓励您登录您的 Hugging Face 账户,这样您就可以上传并与社区分享您的模型。当提示时,输入您的令牌以登录:
from huggingface_hub import notebook_login
notebook_login()
加载 SWAG 数据集¶
首先从 🤗 Datasets 库中加载 SWAG 数据集的 regular 配置:
from datasets import load_dataset
swag = load_dataset("swag", "regular")
然后查看一个示例:
swag["train"][0]
虽然这里看起来有很多字段,但实际上非常简单:
sent1和sent2:这些字段显示了句子是如何开始的,如果您将这两个字段放在一起,就会得到startphrase字段。ending:建议一个可能的句子结束方式,但只有一个是正确的。label:标识正确的句子结束。
预处理¶
下一步是加载 BERT 分词器来处理句子开头和四个可能的结尾:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased")
您需要创建的预处理函数需要执行以下操作:
- 制作
sent1字段的四份副本,并将每个副本与sent2结合以重新创建句子开头的方式。 - 将
sent2与每个可能的句子结尾结合。 - 扁平化这两个列表以便进行分词,然后在分词后进行反扁平化,以便每个示例都有相应的
input_ids、attention_mask和labels字段。
ending_names = ["ending0", "ending1", "ending2", "ending3"]
def preprocess_function(examples):
first_sentences = [[context] * 4 for context in examples["sent1"]]
question_headers = examples["sent2"]
second_sentences = [
[f"{header} {examples[end][i]}" for end in ending_names] for i, header in enumerate(question_headers)
]
first_sentences = sum(first_sentences, [])
second_sentences = sum(second_sentences, [])
tokenized_examples = tokenizer(first_sentences, second_sentences, truncation=True)
return {k: [v[i:i + 4] for i in range(0, len(v), 4)] for k, v in tokenized_examples.items()}
要在整个数据集上应用预处理函数,请使用 🤗 Datasets 的 map 方法。您可以通过设置 batched=True 来加速 map 函数,以便一次处理数据集中的多个元素:
tokenized_swag = swag.map(preprocess_function, batched=True)
🤗 Transformers 没有为多项选择提供数据整理器,因此您需要调整 DataCollatorWithPadding 来创建一个示例批次。在整理过程中,动态地将句子填充到批次中最长的长度,而不是将整个数据集填充到最大长度,这样做更有效率。
DataCollatorForMultipleChoice 扁平化所有模型输入,应用填充,然后反扁平化结果:
from dataclasses import dataclass
from transformers.tokenization_utils_base import PreTrainedTokenizerBase, PaddingStrategy
from typing import Optional, Union
import torch
@dataclass
class DataCollatorForMultipleChoice:
"""
Data collator that will dynamically pad the inputs for multiple choice received.
"""
tokenizer: PreTrainedTokenizerBase
padding: Union[bool, str, PaddingStrategy] = True
max_length: Optional[int] = None
pad_to_multiple_of: Optional[int] = None
def __call__(self, features):
label_name = "label" if "label" in features[0].keys() else "labels"
labels = [feature.pop(label_name) for feature in features]
batch_size = len(features)
num_choices = len(features[0]["input_ids"])
flattened_features = [
[{k: v[i] for k, v in feature.items()} for i in range(num_choices)] for feature in features
]
flattened_features = sum(flattened_features, [])
batch = self.tokenizer.pad(
flattened_features,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors="pt",
)
batch = {k: v.view(batch_size, num_choices, -1) for k, v in batch.items()}
batch["labels"] = torch.tensor(labels, dtype=torch.int64)
return batch
评估¶
在训练过程中包含一个指标通常有助于评估您的模型性能。您可以使用 🤗 Evaluate 库快速加载一个评估方法。对于此任务,加载准确性指标(请参阅 🤗 Evaluate 快速入门以了解更多关于如何加载和计算指标的信息):
import evaluate
accuracy = evaluate.load("accuracy")
然后创建一个函数,将您的预测和标签传递给 compute 以计算准确性:
import numpy as np
def compute_metrics(eval_pred):
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return accuracy.compute(predictions=predictions, references=labels)
您的 compute_metrics 函数现在准备好了,当您设置训练时,您将返回到它。
训练¶
如果您不熟悉使用 Trainer 微调模型,请查看基本教程!
您现在可以开始训练您的模型了!使用 AutoModelForMultipleChoice 加载 BERT:
from transformers import AutoModelForMultipleChoice, TrainingArguments, Trainer
model = AutoModelForMultipleChoice.from_pretrained("google-bert/bert-base-uncased")
在这一点上,只剩下三个步骤:
- 在
TrainingArguments中定义您的训练超参数。唯一需要的参数是output_dir,它指定了保存模型的位置。您可以通过设置push_to_hub=True将模型推送到 Hub(您需要登录 Hugging Face 才能上传模型)。在每个 epoch 结束时,Trainer将评估准确性并保存训练检查点。 - 将训练参数传递给
Trainer,以及模型、数据集、分词器、数据整理器和compute_metrics函数。 - 调用
train()以微调您的模型。
training_args = TrainingArguments(
output_dir="my_awesome_swag_model",
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
learning_rate=5e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
weight_decay=0.01,
push_to_hub=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_swag["train"],
eval_dataset=tokenized_swag["validation"],
processing_class=tokenizer,
data_collator=DataCollatorForMultipleChoice(tokenizer=tokenizer),
compute_metrics=compute_metrics,
)
trainer.train()
训练完成后,使用 push_to_hub() 方法将您的模型分享到 Hub,以便每个人都可以使用您的模型:
trainer.push_to_hub()
prompt = "France has a bread law, Le Décret Pain, with strict rules on what is allowed in a traditional baguette."
candidate1 = "The law does not apply to croissants and brioche."
candidate2 = "The law applies to baguettes."
对每个提示和候选答案对进行分词,并返回 PyTorch 张量。您还应该创建一些 labels:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_swag_model")
inputs = tokenizer([[prompt, candidate1], [prompt, candidate2]], return_tensors="pt", padding=True)
labels = torch.tensor(0).unsqueeze(0)
将您的输入和标签传递给模型,并返回 logits:
from transformers import AutoModelForMultipleChoice
model = AutoModelForMultipleChoice.from_pretrained("username/my_awesome_swag_model")
outputs = model(**{k: v.unsqueeze(0) for k, v in inputs.items()}, labels=labels)
logits = outputs.logits
获取概率最高的类别:
predicted_class = logits.argmax().item()
predicted_class