Transformers documentation
Modélisation de langage masqué
Modélisation de langage masqué
Le masked language modeling prédit un jeton masqué dans une séquence, et le modèle peut prêter attention aux jetons de manière bidirectionnelle. Cela signifie que le modèle a un accès complet aux jetons situés à gauche et à droite. Le masked language modeling est idéal pour les tâches qui nécessitent une bonne compréhension contextuelle d'une séquence entière. BERT est un exemple de modèle de masked language modeling.
Ce guide vous montre comment :
- Effectuer le fine-tuning de DistilRoBERTa sur le sous-ensemble r/askscience du jeu de données ELI5.
- 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 le système vous le demande, saisissez votre jeton pour vous connecter :
>>> from huggingface_hub import notebook_login
>>> notebook_login()Charger le jeu de données ELI5
Commencez par charger les 5000 premiers exemples du jeu de données ELI5-Category à l'aide 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 l'ensemble du jeu de données.
>>> from datasets import load_dataset
>>> eli5 = load_dataset("dany0407/eli5_category", 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 :
>>> eli5 = eli5.train_test_split(test_size=0.2)Puis, jetez un œil à un exemple :
>>> 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']}Bien que cela puisse en impressionner, vous n’êtes réellement intéressé que par le champ text. Ce qui est intéressant dans les tâches de modélisation de langage, c’est que vous n’avez pas besoin d’étiquettes (il s’agit d’une tâche non supervisée), car le mot suivant est l’étiquette.
Prétraitement
Pour le masquage de langage, l'étape suivante consiste à charger un tokenizer DistilRoBERTa afin de traiter le sous-champ text :
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilroberta-base")Comme vous pouvez le constater dans l’exemple ci-dessus, le champ text est en réalité imbriqué dans answers. Cela signifie qu’il faudra extraire le sous-champ text de sa structure imbriquée à l’aide de la méthode 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']}Chaque sous-champ est désormais une colonne distincte, comme l’indique le préfixe answers, et le champ text est désormais une liste. Plutôt que de tokeniser chaque phrase séparément, convertissez la liste en chaîne de caractères afin de pouvoir les tokeniser conjointement.
Voici une première fonction de prétraitement pour concaténer la liste de chaînes de caractères de chaque exemple et tokeniser le résultat :
>>> def preprocess_function(examples):
... return tokenizer([" ".join(x) for x in examples["answers.text"]])Pour appliquer cette fonction de prétraitement à l’ensemble du jeu de données, utilisez la méthode map de 🤗 Datasets. Vous pouvez accélérer la fonction map en définissant batched=True pour traiter plusieurs éléments du jeu de données à la fois, et en augmentant le nombre de processus avec num_proc. Supprimez les colonnes dont vous n’avez pas besoin :
>>> tokenized_eli5 = eli5.map(
... preprocess_function,
... batched=True,
... num_proc=4,
... remove_columns=eli5["train"].column_names,
... )Ce jeu de données contient les séquences de jetons, mais certaines d’entre elles sont plus longues que la longueur d’entrée maximale du modèle.
Vous pouvez maintenant utiliser une seconde fonction de prétraitement pour
- concaténer toutes les séquences
- diviser les séquences concaténées en segments plus courts définis par
block_size, qui doivent être à la fois plus courts que la longueur d'entrée maximale et suffisamment courts pour la RAM de votre 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 resultAppliquez la fonction group_texts sur l'ensemble du jeu de données :
>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)Créez maintenant un lot d’exemples à l’aide de DataCollatorForLanguageModeling. Il est plus efficace de rembourrer dynamiquement les phrases à la longueur maximale du lot lors de la collation, plutôt que de rembourrer l’ensemble de données à la longueur maximale.
Utilisez le jeton de fin de séquence comme jeton de bourrage et spécifiez mlm_probability pour masquer aléatoirement les jetons à chaque itération sur les données :
>>> from transformers import DataCollatorForLanguageModeling
>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)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 DistilRoBERTa avec AutoModelForMaskedLM :
>>> from transformers import AutoModelForMaskedLM
>>> model = AutoModelForMaskedLM.from_pretrained("distilbert/distilroberta-base")À 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, les jeux de données et le data collator.
- Appel de train() pour l’ajustement fin de votre modèle.
>>> 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()Une fois l’entraînement terminé, utilisez la méthode evaluate() pour évaluer votre modèle et obtenir sa perplexité :
>>> import math
>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 8.76Ensuite, 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 le masquage de langage, consultez le notebook PyTorch correspondant.
Inférence
Parfait, maintenant que vous avez affiné un modèle, vous pouvez l'utiliser pour l'inférence !
Inventez du texte dans lequel vous souhaitez que le modèle remplisse une lacune, et utilisez le jeton spécial <mask> pour indiquer la lacune :
>>> text = "The Milky Way is a <mask> galaxy."La manière la plus simple de tester votre modèle affiné pour l’inférence est de l’utiliser dans un pipeline(). Instanciez un pipeline pour fill-mask avec votre modèle, puis transmettez-lui votre texte. Si vous le souhaitez, vous pouvez utiliser le paramètre top_k pour spécifier le nombre de prédictions à retourner :
>>> 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.'}]Tokenisez le texte et renvoyez les input_ids sous forme de tenseurs PyTorch. Vous devrez également spécifier la position du jeton <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]Passez vos entrées au modèle et renvoyez les logits du jeton masqué :
>>> 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, :]Retournez ensuite les trois jetons masqués ayant la probabilité la plus élevée et affichez-les :
>>> 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.