Transformers documentation
Respuestas a preguntas
Respuestas a preguntas
Las tareas de respuesta a preguntas devuelven una respuesta a partir de una pregunta. Si alguna vez le has preguntado a un asistente virtual como Alexa, Siri o Google cuál es el clima, ya has utilizado un modelo de respuesta a preguntas. Existen dos tipos comunes de tareas de respuesta a preguntas:
- Extractiva: extrae la respuesta del contexto proporcionado.
- Abstractiva: genera una respuesta a partir del contexto que responda correctamente a la pregunta.
Esta guía le mostrará cómo:
- Ajusta DistilBERT en el conjunto de datos SQuAD para la respuesta extractiva a preguntas.
- Usa tu modelo afinado para la inferencia.
Para ver todas las arquitecturas y puntos de control compatibles con esta tarea, recomendamos consultar la página de la tarea
Antes de comenzar, asegúrate de tener instaladas todas las bibliotecas necesarias:
pip install transformers datasets evaluate
Te animamos a iniciar sesión en tu cuenta de Hugging Face para que puedas subir y compartir tu modelo con la comunidad. Cuando se te solicite, introduce tu token para iniciar sesión:
>>> from huggingface_hub import notebook_login
>>> notebook_login()Cargar el conjunto de datos SQuAD
Comience cargando un subconjunto más pequeño del conjunto de datos SQuAD desde la biblioteca 🤗 Datasets. Esto le dará la oportunidad de experimentar y asegurarse de que todo funcione correctamente antes de dedicar más tiempo a entrenar con el conjunto de datos completo.
>>> from datasets import load_dataset
>>> squad = load_dataset("rajpurkar/squad", split="train[:5000]")Divide la partición train del conjunto de datos en un conjunto de entrenamiento y uno de prueba con el método train_test_split:
>>> squad = squad.train_test_split(test_size=0.2)A continuación, echa un vistazo a un ejemplo:
>>> 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'
}Hay varios campos importantes aquí:
answers: la posición inicial del token de la respuesta y el texto de la respuesta.context: información de fondo de la que el modelo debe extraer la respuesta.question: la pregunta que el modelo debe responder.
Preprocesar
El siguiente paso es cargar un tokenizador de DistilBERT para procesar los campos question y context:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")Hay algunos pasos de preprocesamiento específicos para las tareas de respuesta a preguntas de los que debes estar al tanto:
- Algunos ejemplos de un conjunto de datos pueden tener un
contextmuy largo que excede la longitud máxima de entrada del modelo. Para manejar secuencias más largas, trunca solo elcontextestableciendotruncation="only_second". - A continuación, mapea las posiciones de inicio y fin de la respuesta al
contextoriginal estableciendoreturn_offset_mapping=True. - Con el mapeo en mano, ahora puedes encontrar los tokens de inicio y fin de la respuesta. Usa el método
sequence_idspara determinar qué parte del desplazamiento corresponde a laquestiony cuál corresponde alcontext.
A continuación se muestra cómo crear una función para truncar y mapear los tokens de inicio y fin de answer a 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 la función de preprocesamiento a todo el conjunto de datos, utiliza la función map de 🤗 Datasets. Puedes acelerar la función map estableciendo batched=True para procesar varios elementos del conjunto de datos a la vez. Elimina las columnas que no necesites:
>>> tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)Ahora cree un lote de ejemplos usando DefaultDataCollator. A diferencia de otros data collators en 🤗 Transformers, DefaultDataCollator no aplica ningún preprocesamiento adicional, como el padding.
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()Entrenar
Si no está familiarizado con el ajuste fino de un modelo con Trainer, ¡eche un vistazo al tutorial básico aquí!
¡Ya está listo para comenzar a entrenar su modelo! Cargue DistilBERT con AutoModelForQuestionAnswering:
>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer
>>> model = AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")En este punto, solo quedan tres pasos:
- Define los hiperparámetros de entrenamiento en TrainingArguments. El único parámetro obligatorio es
output_dir, que especifica dónde se guardará tu modelo. Subirás este modelo al Hub configurandopush_to_hub=True(debes iniciar sesión en Hugging Face para subir tu modelo). - Pasa los argumentos de entrenamiento a Trainer junto con el modelo, el conjunto de datos, el tokenizador y el data collator.
- Llama a train() para ajustar tu 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()Una vez completado el entrenamiento, comparte tu modelo en el Hub con el método push_to_hub() para que todo el mundo pueda usarlo:
>>> trainer.push_to_hub()Para un ejemplo más detallado sobre cómo ajustar un modelo para la respuesta a preguntas, consulte el cuaderno de PyTorch correspondiente.
Evaluar
La evaluación para la respuesta a preguntas requiere una cantidad significativa de posprocesamiento. Para no ocupar demasiado su tiempo, esta guía omite el paso de evaluación. El Trainer sigue calculando la pérdida de evaluación durante el entrenamiento, por lo que no queda completamente a ciegas sobre el rendimiento de su modelo.
Si tienes más tiempo y te interesa cómo evaluar tu modelo para la respuesta a preguntas, echa un vistazo al capítulo Question answering del Curso de 🤗 Hugging Face.
Inferencia
Genial, ahora que has afinado un modelo, ¡puedes usarlo para inferencia!
Piensa en una pregunta y en algún contexto que quieras que el modelo prediga:
>>> 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."Tokeniza el texto y devuelve tensores de PyTorch:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model")
>>> inputs = tokenizer(question, context, return_tensors="pt")Pasa tus entradas al modelo y devuelve los logits:
>>> import torch
>>> from transformers import AutoModelForQuestionAnswering
>>> model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model")
>>> with torch.no_grad():
... outputs = model(**inputs)Obtenga la probabilidad más alta de la salida del modelo para las posiciones de inicio y fin:
>>> answer_start_index = outputs.start_logits.argmax()
>>> answer_end_index = outputs.end_logits.argmax()Decodifica los tokens predichos para obtener la respuesta:
>>> 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'