Transformers documentation
Réponse aux questions
Réponse aux questions
Les tâches de question answering consistent à retourner une réponse à partir d’une question. Si vous avez déjà demandé à un assistant virtuel comme Alexa, Siri ou Google quel est le temps qu’il fait, vous avez déjà utilisé un modèle de question answering. Il existe deux types courants de tâches de question answering :
- Extractive : extraire la réponse du contexte donné.
- Abstractive : générer une réponse à partir du contexte qui répond correctement à la question.
Ce guide vous montre comment :
- Effectuer le fine-tuning de DistilBERT sur le jeu de données SQuAD pour la question-réponse extractive.
- Utilisez votre modèle affiné pour l'inférence.
Pour consulter toutes les architectures et points de contrôle compatibles avec cette tâche, nous vous recommandons de consulter la page de la tâche
Avant de commencer, assurez-vous d'avoir installé toutes les bibliothèques nécessaires :
pip install transformers datasets evaluate
Nous vous encourage à vous connecter à votre compte Hugging Face afin de pouvoir téléverser et partager votre modèle avec la communauté. Lorsque cela vous est demandé, saisissez votre jeton pour vous connecter :
>>> from huggingface_hub import notebook_login
>>> notebook_login()Charger le jeu de données SQuAD
Commencez par charger un sous-ensemble plus petit du jeu de données SQuAD à partir de la bibliothèque 🤗 Datasets. Cela vous permettra de faire des essais et de vous assurer que tout fonctionne correctement avant de consacrer plus de temps à l’entraînement sur le jeu de données complet.
>>> from datasets import load_dataset
>>> squad = load_dataset("rajpurkar/squad", split="train[:5000]")Divisez le split train du jeu de données en un ensemble d’entraînement et un ensemble de test à l’aide de la méthode train_test_split :
>>> squad = squad.train_test_split(test_size=0.2)Puis, jetez un œil à un exemple :
>>> 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'
}Plusieurs champs importants figurent ici :
answers: la position de départ du jeton de réponse et le texte de la réponse.context: background information from which the model needs to extract the answer.question: la question à laquelle le modèle doit répondre.
Prétraitement
L'étape suivante consiste à charger un tokenizer DistilBERT pour traiter les champs question et context :
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")Il existe quelques étapes de prétraitement spécifiques aux tâches de réponse à des questions dont vous devez tenir compte :
- Certains exemples d’un jeu de données peuvent comporter un
contexttrès long qui dépasse la longueur d’entrée maximale du modèle. Pour traiter des séquences plus longues, tronquez uniquement lecontexten définissanttruncation="only_second". - Ensuite, mappez les positions de début et de fin de la réponse sur le
contextd'origine en définissantreturn_offset_mapping=True. - With the mapping in hand, now you can find the start and end tokens of the answer. Use the
sequence_idsmethod to find which part of the offset corresponds to thequestionand which corresponds to thecontext.
Voici comment créer une fonction pour tronquer et mapper les jetons de début et de fin de la answer vers le 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 inputsPour appliquer la fonction de prétraitement à l’ensemble du jeu de données, utilisez la fonction map de 🤗 Datasets. Vous pouvez accélérer la fonction map en définissant batched=True afin de traiter plusieurs éléments du jeu de données à la fois. Supprimez les colonnes dont vous n’avez pas besoin :
>>> tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)Créez maintenant un lot d’exemples à l’aide de DefaultDataCollator. Contrairement aux autres data collators de 🤗 Transformers, le DefaultDataCollator n’applique aucun prétraitement supplémentaire tel que le padding.
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()Entraînement
Si vous n’êtes pas familier avec l’ajustement fin d’un modèle à l’aide du Trainer, consultez le tutoriel de base ici !
Vous êtes prêt à commencer l’entraînement de votre modèle ! Chargez DistilBERT avec AutoModelForQuestionAnswering :
>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer
>>> model = AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")À ce stade, il ne reste que trois étapes :
- Définissez vos hyperparamètres d'entraînement dans TrainingArguments. Le seul paramètre obligatoire est
output_dir, qui spécifie l'emplacement de sauvegarde de votre modèle. Vous publierez ce modèle sur le Hub en définissantpush_to_hub=True(vous devez être connecté à Hugging Face pour téléverser votre modèle). - Transmettez les arguments d’entraînement à Trainer en même temps que le modèle, le jeu de données, le tokenizer et le data collator.
- Appel de train() pour l’ajustement fin de votre modèle.
>>> 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()Une fois l’entraînement terminé, partagez votre modèle sur le Hub à l’aide de la méthode push_to_hub() afin que chacun puisse l’utiliser :
>>> trainer.push_to_hub()Pour un exemple plus approfondi sur la façon d’affiner un modèle pour la réponse à des questions, consultez le notebook PyTorch correspondant.
Évaluer
L’évaluation pour le question answering nécessite un post-traitement important. Afin de ne pas vous faire perdre trop de temps, ce guide passe l’étape d’évaluation. Le Trainer calcule néanmoins la perte d’évaluation pendant l’entraînement, de sorte que vous ne soyez pas totalement dans l’ignorance des performances de votre modèle.
Si vous avez plus de temps et que vous souhaitez savoir comment évaluer votre modèle pour le question answering, consultez le chapitre Question answering du cours 🤗 Hugging Face !
Inférence
Parfait, maintenant que vous avez affiné un modèle, vous pouvez l'utiliser pour l'inférence !
Formulez une question et un contexte que vous souhaitez que le modèle prédise :
>>> 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."Tokeniser le texte et retourner des tenseurs PyTorch :
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model")
>>> inputs = tokenizer(question, context, return_tensors="pt")Transmettez vos entrées au modèle et renvoyez les logits :
>>> import torch
>>> from transformers import AutoModelForQuestionAnswering
>>> model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model")
>>> with torch.no_grad():
... outputs = model(**inputs)Obtenez la probabilité la plus élevée de la sortie du modèle pour les positions de début et de fin :
>>> answer_start_index = outputs.start_logits.argmax()
>>> answer_end_index = outputs.end_logits.argmax()Décodez les jetons prédits pour obtenir la réponse :
>>> 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'