Transformers documentation
Resposta a perguntas
Resposta a perguntas
Tarefas de question answering retornam uma resposta a partir de uma pergunta. Se você já perguntou a um assistente virtual, como Alexa, Siri ou Google, qual é o clima, então você já usou um modelo de question answering antes. Existem dois tipos comuns de tarefas de question answering:
- Extractivo: extrair a resposta do contexto fornecido.
- Abstrativa: gerar uma resposta a partir do contexto que responde corretamente à pergunta.
Este guia mostrará como:
- Ajuste fino do DistilBERT no conjunto de dados SQuAD para resposta a perguntas extrativa.
- Use seu modelo ajustado para inferência.
Para ver todas as arquiteturas e checkpoints compatíveis com esta tarefa, recomendamos consultar a página da tarefa
Antes de começar, verifique se você tem todas as bibliotecas necessárias instaladas:
pip install transformers datasets evaluate
Recomendamos que você faça login na sua conta do Hugging Face para que possa enviar e compartilhar seu modelo com a comunidade. Quando solicitado, insira seu token para fazer login:
>>> from huggingface_hub import notebook_login
>>> notebook_login()Carregar o conjunto de dados SQuAD
Comece carregando um subconjunto menor do conjunto de dados SQuAD a partir da biblioteca 🤗 Datasets. Isso lhe dará a oportunidade de experimentar e garantir que tudo funcione antes de gastar mais tempo treinando no conjunto de dados completo.
>>> from datasets import load_dataset
>>> squad = load_dataset("rajpurkar/squad", split="train[:5000]")Divida o split train do conjunto de dados em um conjunto de treinamento e um conjunto de teste com o método train_test_split:
>>> squad = squad.train_test_split(test_size=0.2)Em seguida, veja um exemplo:
>>> 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'
}Há vários campos importantes aqui:
answers: a posição inicial do token da resposta e o texto da resposta.context: background information from which the model needs to extract the answer.question: a pergunta que o modelo deve responder.
Pré-processar
O próximo passo é carregar um tokenizer DistilBERT para processar os campos question e context:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")Existem algumas etapas de pré-processamento específicas para tarefas de resposta a perguntas que você deve conhecer:
- Alguns exemplos em um conjunto de dados podem ter um
contextmuito longo que excede o comprimento máximo de entrada do modelo. Para lidar com sequências mais longas, trunque apenas ocontextdefinindotruncation="only_second". - Em seguida, mapeie as posições de início e fim da resposta para o
contextoriginal, definindoreturn_offset_mapping=True. - Com o mapeamento em mãos, agora você pode encontrar os tokens de início e fim da resposta. Use o método
sequence_idspara descobrir qual parte do deslocamento corresponde àquestione qual corresponde aocontext.
Aqui está como você pode criar uma função para truncar e mapear os tokens de início e fim da answer para o 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 inputsPara aplicar a função de pré-processamento em todo o conjunto de dados, use a função map do 🤗 Datasets. Você pode acelerar a função map definindo batched=True para processar vários elementos do conjunto de dados de uma vez. Remova quaisquer colunas que você não precise:
>>> tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)Agora, crie um lote de exemplos usando DefaultDataCollator. Diferentemente de outros data collators no 🤗 Transformers, o DefaultDataCollator não aplica nenhum pré-processamento adicional, como padding.
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()Treinar
Se você não está familiarizado com o ajuste fino de um modelo usando o Trainer, confira o tutorial básico aqui!
Agora você está pronto para começar a treinar seu modelo! Carregue o DistilBERT com AutoModelForQuestionAnswering:
>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer
>>> model = AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")Neste ponto, restam apenas três etapas:
- Defina os hiperparâmetros de treinamento em TrainingArguments. O único parâmetro obrigatório é
output_dir, que especifica onde salvar seu modelo. Você enviará este modelo para o Hub definindopush_to_hub=True(é necessário estar conectado ao Hugging Face para enviar seu modelo). - Passe os argumentos de treinamento para o Trainer junto com o modelo, o conjunto de dados, o tokenizer e o data collator.
- Chame train() para fazer o ajuste fino do seu modelo.
>>> 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()Quando o treinamento for concluído, compartilhe seu modelo no Hub com o método push_to_hub() para que todos possam usá-lo:
>>> trainer.push_to_hub()Para um exemplo mais detalhado de como fazer o ajuste fino de um modelo para resposta a perguntas, consulte o notebook do PyTorch correspondente.
Avaliar
A avaliação para question answering exige uma quantidade significativa de pós-processamento. Para não tomar muito do seu tempo, este guia omite a etapa de avaliação. O Trainer ainda calcula a perda de avaliação durante o treinamento, para que você não fique completamente no escuro sobre o desempenho do seu modelo.
Se você tiver mais tempo e estiver interessado em como avaliar seu modelo para question answering, dê uma olhada no capítulo Question answering do Curso 🤗 Hugging Face!
Inferência
Ótimo, agora que você ajustou um modelo, pode usá-lo para inferência!
Pense em uma pergunta e em algum contexto que você gostaria que o modelo previsse:
>>> 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."Tokenize o texto e retorne tensores do PyTorch:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model")
>>> inputs = tokenizer(question, context, return_tensors="pt")Passe suas entradas para o modelo e retorne os logits:
>>> import torch
>>> from transformers import AutoModelForQuestionAnswering
>>> model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model")
>>> with torch.no_grad():
... outputs = model(**inputs)Obtenha a maior probabilidade da saída do modelo para as posições de início e fim:
>>> answer_start_index = outputs.start_logits.argmax()
>>> answer_end_index = outputs.end_logits.argmax()Decodifique os tokens previstos para obter a resposta:
>>> 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'