Transformers documentation

问答

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

问答

问答任务根据给定的问题返回答案。如果你曾向 Alexa、Siri 或 Google 等虚拟助手询问天气,那么你就已经使用过问答模型了。问答任务有两种常见类型:

  • 抽取式:从给定的上下文中提取答案。
  • 生成式:根据上下文生成能够正确回答问题的答案。

本指南将向您展示如何:

  1. 在 SQuAD 数据集上微调 DistilBERT,用于抽取式问答。
  2. 使用微调后的模型进行推理。

要查看与此任务兼容的所有架构和检查点,我们建议查阅 任务页面

在开始之前,请确保已安装所有必要的库:

pip install transformers datasets evaluate

我们建议您登录 Hugging Face 账户,以便上传模型并与社区共享。当系统提示时,请输入您的令牌以完成登录:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

加载 SQuAD 数据集

首先,从 🤗 Datasets 库中加载 SQuAD 数据集的一个较小子集。这样你可以先进行实验,确保一切正常运行,然后再花更多时间在完整数据集上进行训练。

>>> from datasets import load_dataset

>>> squad = load_dataset("rajpurkar/squad", split="train[:5000]")

使用 train_test_split 方法将数据集的 train 子集拆分为训练集和测试集:

>>> squad = squad.train_test_split(test_size=0.2)

然后来看一个示例:

>>> squad["train"][0]
{'answers': {'answer_start': [515], 'text': ['Saint Bernadette Soubirous']},
 'context': 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.',
 'id': '5733be284776f41900661182',
 'question': 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?',
 'title': 'University_of_Notre_Dame'
}

这里有几个重要的字段:

  • answers:答案 token 的起始位置以及答案文本。
  • context:模型需要从中提取答案的背景信息。
  • question:模型需要回答的问题。

预处理

下一步是加载 DistilBERT 分词器,以处理 question 和 context 字段:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")

在问答任务中,有几个特定的预处理步骤需要您了解:

  1. 数据集中的某些示例可能具有非常长的 context,超出了模型的最大输入长度。为了处理更长的序列,请设置 truncation="only_second",仅截断 context。
  2. 接下来,通过设置 return_offset_mapping=True,将答案的起始和结束位置映射到原始的 context。
  3. 有了映射关系,现在就可以找到答案的起始和结束 token。使用 sequence_ids 方法来确定偏移量中哪一部分对应 question,哪一部分对应 context。

以下是如何创建一个函数,将 answer 的起始和结束标记截断并映射到 context:

>>> def preprocess_function(examples):
...     questions = [q.strip() for q in examples["question"]]
...     inputs = tokenizer(
...         questions,
...         examples["context"],
...         max_length=384,
...         truncation="only_second",
...         return_offsets_mapping=True,
...         padding="max_length",
...     )

...     offset_mapping = inputs.pop("offset_mapping")
...     answers = examples["answers"]
...     start_positions = []
...     end_positions = []

...     for i, offset in enumerate(offset_mapping):
...         answer = answers[i]
...         start_char = answer["answer_start"][0]
...         end_char = answer["answer_start"][0] + len(answer["text"][0])
...         sequence_ids = inputs.sequence_ids(i)

...         # Find the start and end of the context
...         idx = 0
...         while sequence_ids[idx] != 1:
...             idx += 1
...         context_start = idx
...         while sequence_ids[idx] == 1:
...             idx += 1
...         context_end = idx - 1

...         # If the answer is not fully inside the context, label it (0, 0)
...         if offset[context_start][0] > end_char or offset[context_end][1] < start_char:
...             start_positions.append(0)
...             end_positions.append(0)
...         else:
...             # Otherwise it's the start and end token positions
...             idx = context_start
...             while idx <= context_end and offset[idx][0] <= start_char:
...                 idx += 1
...             start_positions.append(idx - 1)

...             idx = context_end
...             while idx >= context_start and offset[idx][1] >= end_char:
...                 idx -= 1
...             end_positions.append(idx + 1)

...     inputs["start_positions"] = start_positions
...     inputs["end_positions"] = end_positions
...     return inputs

要对整个数据集应用预处理函数,请使用 🤗 Datasets 的 map 函数。通过设置 batched=True 来一次处理数据集中的多个元素,可以加速 map 函数。删除任何不需要的列:

>>> tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)

现在使用 DefaultDataCollator 创建一个示例批次。与 🤗 Transformers 中的其他数据整理器不同,DefaultDataCollator 不会应用填充等额外预处理。

>>> from transformers import DefaultDataCollator

>>> data_collator = DefaultDataCollator()

训练

如果您不熟悉使用 Trainer 对模型进行微调,请查看此处的基础教程!

现在您可以开始训练模型了!使用 AutoModelForQuestionAnswering 加载 DistilBERT:

>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer

>>> model = AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")

此时,只剩下三个步骤:

  1. 在 TrainingArguments 中定义训练超参数。唯一必需的参数是 output_dir,用于指定模型的保存位置。通过设置 push_to_hub=True,您可以将该模型推送到 Hub(上传模型需要登录 Hugging Face 账户)。
  2. 将训练参数与模型、数据集、分词器和数据整理器一起传递给 Trainer。
  3. 调用 train() 来微调你的模型。
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_qa_model",
...     eval_strategy="epoch",
...     learning_rate=2e-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_squad["train"],
...     eval_dataset=tokenized_squad["test"],
...     processing_class=tokenizer,
...     data_collator=data_collator,
... )

>>> trainer.train()

训练完成后,使用 push_to_hub() 方法将您的模型共享到 Hub,以便所有人都能使用您的模型:

>>> trainer.push_to_hub()

如需深入了解如何针对问答任务微调模型,请参阅对应的 PyTorch notebook。

评估

问答任务的评估需要大量的后处理工作。为了不占用您太多时间,本指南将跳过评估步骤。Trainer 仍会在训练过程中计算评估损失,因此您不会完全不了解模型的性能。

如果您有更多时间,并且想了解如何评估问答模型,请查看 🤗 Hugging Face 课程中的 问答 章节!

推理

很好,现在你已经微调了一个模型,可以使用它进行推理了!

提出一个问题以及你希望模型进行预测的上下文:

>>> question = "How many programming languages does BLOOM support?"
>>> context = "BLOOM has 176 billion parameters and can generate text in 46 languages natural languages and 13 programming languages."

对文本进行分词并返回 PyTorch 张量:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model")
>>> inputs = tokenizer(question, context, return_tensors="pt")

将输入传递给模型并返回 logits:

>>> import torch
>>> from transformers import AutoModelForQuestionAnswering

>>> model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model")
>>> with torch.no_grad():
...     outputs = model(**inputs)

从模型输出中获取起始位置和结束位置的最高概率:

>>> answer_start_index = outputs.start_logits.argmax()
>>> answer_end_index = outputs.end_logits.argmax()

将预测的 token 解码以获取答案:

>>> predict_answer_tokens = inputs.input_ids[0, answer_start_index : answer_end_index + 1]
>>> tokenizer.decode(predict_answer_tokens)
'176 billion parameters and can generate text in 46 languages natural languages and 13'
Update on GitHub