# Modelado de lenguaje enmascarado

El modelado de lenguaje enmascarado predice un token enmascarado en una secuencia, y el modelo puede prestar atención 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](https://huggingface.co/distilbert/distilroberta-base) en el subconjunto [r/askscience](https://www.reddit.com/r/askscience/) del conjunto de datos [ELI5](https://huggingface.co/datasets/dany0407/eli5_category).
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](https://huggingface.co/tasks/fill-mask)

Antes de comenzar, asegúrate de tener instaladas todas las bibliotecas necesarias:

```bash
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:

```py
>>> 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](https://huggingface.co/datasets/dany0407/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.

```py
>>> 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](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.train_test_split):

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

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

```py
>>> 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`:

```py
>>> from transformers import AutoTokenizer

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

Como se observa en el ejemplo anterior, 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`](https://huggingface.co/docs/datasets/process#flatten):

```py
>>> 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:

```py
>>> 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, use el método [map](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.map) de 🤗 Datasets. Puede 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`. Elimine las columnas que no necesite:

```py
>>> 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.

```py
>>> 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:

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

Ahora crea un lote de ejemplos usando [DataCollatorForLanguageModeling](/docs/transformers/main/es/main_classes/data_collator#transformers.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:

```py
>>> from transformers import DataCollatorForLanguageModeling

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

## Entrenar

Si no estás familiarizado con el ajuste fino de un modelo con [Trainer](/docs/transformers/main/es/main_classes/trainer#transformers.Trainer), ¡echa un vistazo al tutorial básico [aquí](../training)!

¡Ya estás listo para comenzar a entrenar tu modelo! Carga DistilRoBERTa con [AutoModelForMaskedLM](/docs/transformers/main/es/model_doc/auto#transformers.AutoModelForMaskedLM):

```py
>>> 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](/docs/transformers/main/es/main_classes/trainer#transformers.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](/docs/transformers/main/es/main_classes/trainer#transformers.Trainer) junto con el modelo, los conjuntos de datos y el data collator.
3. Llama a [train()](/docs/transformers/main/es/main_classes/trainer#transformers.Trainer.train) para ajustar tu modelo.

```py
>>> 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()](/docs/transformers/main/es/main_classes/trainer#transformers.Trainer.evaluate) para evaluar tu modelo y obtener su perplejidad:

```py
>>> 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()](/docs/transformers/main/es/main_classes/trainer#transformers.Trainer.push_to_hub) para que todo el mundo pueda usarlo:

```py
>>> 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](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/language_modeling.ipynb) 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 usa el token especial `<mask>` para indicar el espacio en blanco:

```py
>>> 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()](/docs/transformers/main/es/main_classes/pipelines#transformers.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:

```py
>>> 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>`:

```py
>>> 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:

```py
>>> 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:

```py
>>> 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.
```

