Transformers documentation

Modelado de lenguaje enmascarado

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

Modelado de lenguaje enmascarado

El modelado de lenguaje enmascarado predice un token enmascarado en una secuencia, y el modelo puede atender a los tokens de forma bidireccional. Esto significa que el modelo tiene acceso completo a los tokens de la izquierda y de la derecha. El modelado de lenguaje enmascarado es ideal para tareas que requieren una buena comprensión contextual de una secuencia completa. BERT es un ejemplo de un modelo de lenguaje enmascarado.

Esta guía le mostrará cómo:

  1. Ajusta DistilRoBERTa en el subconjunto r/askscience del conjunto de datos ELI5.
  2. 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 ELI5

Comience cargando los primeros 5000 ejemplos del conjunto de datos ELI5-Category con la biblioteca 🤗 Datasets. Esto le dará la oportunidad de experimentar y asegurarse de que todo funcione antes de dedicar más tiempo a entrenar con el conjunto de datos completo.

>>> from datasets import load_dataset

>>> eli5 = load_dataset("dany0407/eli5_category", 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:

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

A continuación, echa un vistazo a un ejemplo:

>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers': {'a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
  'text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
   'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
   'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
   'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
  'score': [21, 19, 5, 3],
  'text_urls': [[],
   [],
   [],
   ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']]},
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Aunque esto puede parecer mucho, en realidad solo te interesa el campo text. Lo interesante de las tareas de modelado de lenguaje es que no necesitas etiquetas (también conocidas como tareas no supervisadas), porque la siguiente palabra es la etiqueta.

Preprocesar

Para el modelado de lenguaje enmascarado, el siguiente paso es cargar un tokenizador de DistilRoBERTa para procesar el subcampo text:

>>> from transformers import AutoTokenizer

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

Notará, a partir del ejemplo anterior, que el campo text está anidado dentro de answers. Esto significa que deberá extraer el subcampo text de su estructura anidada mediante el método flatten:

>>> eli5 = eli5.flatten()
>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers.a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
 'answers.text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
  'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
  'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
  'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
 'answers.score': [21, 19, 5, 3],
 'answers.text_urls': [[],
  [],
  [],
  ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']],
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Cada subcampo ahora es una columna separada, como lo indica el prefijo answers, y el campo text ahora es una lista. En lugar de tokenizar cada oración por separado, convierta la lista en una cadena para poder tokenizarlas conjuntamente.

Aquí hay una primera función de preprocesamiento para unir la lista de cadenas de cada ejemplo y tokenizar el resultado:

>>> def preprocess_function(examples):
...     return tokenizer([" ".join(x) for x in examples["answers.text"]])

Para aplicar esta función de preprocesamiento a todo el conjunto de datos, utiliza el método map de 🤗 Datasets. Puedes acelerar la función map estableciendo batched=True para procesar varios elementos del conjunto de datos a la vez, y aumentando el número de procesos con num_proc. Elimina las columnas que no necesites:

>>> tokenized_eli5 = eli5.map(
...     preprocess_function,
...     batched=True,
...     num_proc=4,
...     remove_columns=eli5["train"].column_names,
... )

Este conjunto de datos contiene las secuencias de tokens, pero algunas de ellas son más largas que la longitud máxima de entrada del modelo.

Ahora puede usar una segunda función de preprocesamiento para

  • concatenar todas las secuencias
  • divida las secuencias concatenadas en fragmentos más cortos definidos por block_size, que deben ser más cortos que la longitud máxima de entrada y lo suficientemente cortos para la RAM de su GPU.
>>> block_size = 128


>>> def group_texts(examples):
...     # Concatenate all texts.
...     concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
...     total_length = len(concatenated_examples[list(examples.keys())[0]])
...     # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can
...     # customize this part to your needs.
...     if total_length >= block_size:
...         total_length = (total_length // block_size) * block_size
...     # Split by chunks of block_size.
...     result = {
...         k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
...         for k, t in concatenated_examples.items()
...     }
...     return result

Aplicar la función group_texts sobre el conjunto de datos completo:

>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)

Ahora crea un lote de ejemplos usando DataCollatorForLanguageModeling. Es más eficiente rellenar dinámicamente las oraciones hasta la longitud más larga dentro de un lote durante la agrupación, en lugar de rellenar todo el conjunto de datos hasta la longitud máxima.

Usa el token de fin de secuencia como token de relleno y especifica mlm_probability para enmascarar tokens aleatoriamente cada vez que iteras sobre los datos:

>>> from transformers import DataCollatorForLanguageModeling

>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)

Entrenar

Si no está familiarizado con el ajuste fino de un modelo con Trainer, ¡eche un vistazo al tutorial básico aquí!

¡Ya estás listo para comenzar a entrenar tu modelo! Carga DistilRoBERTa con AutoModelForMaskedLM:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("distilbert/distilroberta-base")

En este punto, solo quedan tres pasos:

  1. 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 configurando push_to_hub=True (debes iniciar sesión en Hugging Face para subir tu modelo).
  2. Pasa los argumentos de entrenamiento a Trainer junto con el modelo, los conjuntos de datos y el data collator.
  3. Llama a train() para ajustar tu modelo.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_eli5_mlm_model",
...     eval_strategy="epoch",
...     learning_rate=2e-5,
...     num_train_epochs=3,
...     weight_decay=0.01,
...     push_to_hub=True,
... )

>>> trainer = Trainer(
...     model=model,
...     args=training_args,
...     train_dataset=lm_dataset["train"],
...     eval_dataset=lm_dataset["test"],
...     data_collator=data_collator,
...     processing_class=tokenizer,
... )

>>> trainer.train()

Una vez completado el entrenamiento, utiliza el método evaluate() para evaluar tu modelo y obtener su perplejidad:

>>> import math

>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 8.76

Luego, 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 el modelado de lenguaje enmascarado, consulte el cuaderno de PyTorch correspondiente.

Inferencia

Genial, ahora que has afinado un modelo, ¡puedes usarlo para inferencia!

Pensar en algún texto que quieras que el modelo complete y usar el token especial <mask> para indicar el espacio en blanco:

>>> text = "The Milky Way is a <mask> galaxy."

La forma más sencilla de probar tu modelo afinado para inferencia es utilizarlo en un pipeline(). Crea una instancia de pipeline para fill-mask con tu modelo y pásale tu texto. Si lo deseas, puedes usar el parámetro top_k para especificar cuántas predicciones se deben devolver:

>>> from transformers import pipeline

>>> mask_filler = pipeline("fill-mask", "username/my_awesome_eli5_mlm_model")
>>> mask_filler(text, top_k=3)
[{'score': 0.5150994658470154,
  'token': 21300,
  'token_str': ' spiral',
  'sequence': 'The Milky Way is a spiral galaxy.'},
 {'score': 0.07087188959121704,
  'token': 2232,
  'token_str': ' massive',
  'sequence': 'The Milky Way is a massive galaxy.'},
 {'score': 0.06434620916843414,
  'token': 650,
  'token_str': ' small',
  'sequence': 'The Milky Way is a small galaxy.'}]

Tokeniza el texto y devuelve los input_ids como tensores de PyTorch. También deberás especificar la posición del token <mask>:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> inputs = tokenizer(text, return_tensors="pt")
>>> mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]

Pasa tus entradas al modelo y devuelve los logits del token enmascarado:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> logits = model(**inputs).logits
>>> mask_token_logits = logits[0, mask_token_index, :]

Luego, devuelve los tres tokens enmascarados con la mayor probabilidad e imprímelos:

>>> top_3_tokens = torch.topk(mask_token_logits, 3, dim=1).indices[0].tolist()

>>> for token in top_3_tokens:
...     print(text.replace(tokenizer.mask_token, tokenizer.decode([token])))
The Milky Way is a spiral galaxy.
The Milky Way is a massive galaxy.
The Milky Way is a small galaxy.
Update on GitHub