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

Ответы на вопросы

Задачи question answering возвращают ответ на заданный вопрос. Если вы когда-либо спрашивали у виртуального ассистента, такого как Alexa, Siri или Google, какая сейчас погода, то вы уже пользовались моделью question answering. Существует два распространённых типа задач question answering:

  • Extractive: извлеките ответ из предоставленного контекста.
  • Abstractive: сгенерировать ответ из контекста, который правильно отвечает на вопрос.

В этом руководстве показано, как:

  1. Настройте DistilBERT на датасете SQuAD для задачи извлечения ответа на вопрос.
  2. Используйте вашу дообученную модель для инференса.

Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к странице задачи

Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:

pip install transformers datasets evaluate

Мы рекомендуем вам войти в свой аккаунт Hugging Face, чтобы вы могли загрузить свою модель и поделиться ею с сообществом. При появлении запроса введите токен для входа:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Загрузка набора данных SQuAD

Начните с загрузки небольшого подмножества набора данных SQuAD из библиотеки 🤗 Datasets. Это даст вам возможность поэкспериментировать и убедиться, что всё работает, прежде чем тратить время на обучение на полном наборе данных.

>>> from datasets import load_dataset

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

Разбейте train-подмножество датасета на обучающую и тестовую выборки с помощью метода train_test_split:

>>> 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: начальная позиция токена ответа и текст ответа.
  • context: фоновая информация, из которой модели необходимо извлечь ответ.
  • question: вопрос, на который должна ответить модель.

Предобработка

Следующий шаг — загрузить токенизатор DistilBERT для обработки полей question и context:

>>> from transformers import AutoTokenizer

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

Существует несколько шагов предобработки, характерных для задач вопросно-ответных систем, о которых следует знать:

  1. Некоторые примеры в наборе данных могут иметь очень длинный context, превышающий максимальную длину входных данных модели. Чтобы обрабатывать более длинные последовательности, обрезайте только context, установив truncation="only_second".
  2. Далее сопоставьте начальные и конечные позиции ответа с исходным context, установив return_offset_mapping=True.
  3. Получив отображение, вы можете найти начальные и конечные токены ответа. Используйте метод 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

Чтобы применить функцию предобработки ко всему набору данных, используйте функцию map из 🤗 Datasets. Вы можете ускорить работу функции map, установив batched=True, чтобы обрабатывать несколько элементов набора данных одновременно. Удалите все столбцы, которые вам не нужны:

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

Теперь создайте пакет примеров с помощью DefaultDataCollator. В отличие от других коллакторов данных в 🤗 Transformers, DefaultDataCollator не выполняет никакого дополнительного предобработки, например, выравнивания (padding).

>>> from transformers import DefaultDataCollator

>>> data_collator = DefaultDataCollator()

Обучение

Если вы не знакомы с дообучением модели с помощью Trainer, загляните в базовое руководство здесь!

Теперь вы готовы приступить к обучению модели! Загрузите DistilBERT с помощью AutoModelForQuestionAnswering:

>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer

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

На этом этапе осталось выполнить только три шага:

  1. Определите гиперпараметры обучения в TrainingArguments. Единственный обязательный параметр — output_dir, который указывает, куда сохранять модель. Вы загрузите эту модель на Hub, установив push_to_hub=True (для загрузки модели необходимо войти в аккаунт Hugging Face).
  2. Передайте аргументы обучения в Trainer вместе с моделью, набором данных, токенизатором и data collator.
  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.

Оценка

Оценка для задачи вопрос-ответ требует значительного количества постобработки. Чтобы не отнимать у вас слишком много времени, в этом руководстве шаг оценки пропущен. Trainer всё равно вычисляет потерю при оценке во время обучения, поэтому вы не остаётесь в полной неопределённости относительно производительности вашей модели.

Если у вас есть больше времени и вы хотите узнать, как оценить свою модель для задачи question answering, загляните в главу Question answering курса 🤗 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()

Декодируйте предсказанные токены, чтобы получить ответ:

>>> 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