Que faire lorsque vous obtenez une erreur
Dans cette section, nous allons examiner certaines erreurs courantes qui peuvent se produire lorsque vous essayez de générer des prédictions à partir de votre transformer fraîchement finetuné. Cela vous préparera pour la section 4 de ce chapitre où nous explorerons comment déboguer la phase d’entraînement elle-même.
Nous avons préparé un gabarit de dépôt de modèles pour cette section et si vous voulez exécuter le code de ce chapitre, vous devrez d’abord copier le modèle dans votre compte sur le Hub d’Hugging Face. Pour ce faire, connectez-vous d’abord en exécutant l’une ou l’autre des commandes suivantes dans un notebook Jupyter :
from huggingface_hub import notebook_login
notebook_login()
ou ce qui suit dans votre terminal préféré :
huggingface-cli login
Cela vous demandera d’entrer votre nom d’utilisateur et votre mot de passe, et enregistrera un jeton sous ~/.cache/huggingface/. Une fois que vous vous êtes connecté, vous pouvez copier le gabarit du dépôt avec la fonction suivante :
from distutils.dir_util import copy_tree
from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name
def copy_repository_template():
# Cloner le dépôt et extraire le chemin local
template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"
commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4"
template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash)
# Créer un dépôt vide sur le Hub
model_name = template_repo_id.split("/")[1]
create_repo(model_name, exist_ok=True)
# Cloner le dépôt vide
new_repo_id = get_full_repo_name(model_name)
new_repo_dir = model_name
repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id)
# Copier les fichiers
copy_tree(template_repo_dir, new_repo_dir)
# Pousser sur le Hub
repo.push_to_hub()
Maintenant, lorsque vous appelez copy_repository_template()
, cela va créer une copie du gabarit du dépôt sous votre compte.
Déboguer le pipeline à partir de 🤗 <i> Transformers </i>
Pour donner le coup d’envoi de notre voyage dans le monde merveilleux du débogage de transformers, considérez le scénario suivant : vous travaillez avec un collègue sur un projet de réponse à des questions pour aider les clients d’un site de commerce en ligne à trouver des réponses à des produits. Votre collègue vous envoie un message du genre :
Bonjour ! Je viens de réaliser une expérience en utilisant les techniques du chapitre 7 du cours d’Hugging Face et j’ai obtenu d’excellents résultats sur SQuAD ! Je pense que nous pouvons utiliser ce modèle comme point de départ pour notre projet. L’identifiant du modèle sur le Hub est “lewtun/distillbert-base-uncased-finetuned-squad-d5716d28”. N’hésite pas à le tester :)
et la première chose à laquelle on pense est de charger le modèle en utilisant le pipeline
de 🤗 Transformers :
from transformers import pipeline
model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:
- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co./models'
- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
Oh non, quelque chose semble s’être mal passée ! Si vous êtes novice en programmation, ce genre d’erreurs peut sembler un peu cryptique au début (qu’est-ce qu’une OSError
?!). L’erreur affichée ici n’est que la dernière partie d’un rapport d’erreur beaucoup plus long appelé Python traceback (alias stack trace). Par exemple, si vous exécutez ce code sur Google Colab, vous devriez voir quelque chose comme la capture d’écran suivante :
Il y a beaucoup d’informations dans ces rapports, nous allons donc en parcourir ensemble les éléments clés. La première chose à noter est que les tracebacks doivent être lus de bas en haut. Cela peut sembler bizarre si vous avez l’habitude de lire du texte français de haut en bas mais cela reflète le fait que le traceback montre la séquence d’appels de fonction que le pipeline
fait lors du téléchargement du modèle et du tokenizer. Consultez le chapitre 2 pour plus de détails sur la façon dont le pipeline
fonctionne sous le capot.
🚨 Vous voyez le cadre bleu autour de « 6 frames » dans le traceback de Google Colab ? Il s’agit d’une fonctionnalité spéciale de Colab qui compresse le traceback en frames. Si vous ne parvenez pas à trouver la source d’une erreur, déroulez le traceback en cliquant sur ces deux petites flèches.
Cela signifie que la dernière ligne du traceback indique le dernier message d’erreur et donne le nom de l’exception qui a été levée. Dans ce cas, le type d’exception est OSError
, ce qui indique une erreur liée au système. Si nous lisons le message d’erreur qui l’accompagne, nous pouvons voir qu’il semble y avoir un problème avec le fichier config.json du modèle et deux suggestions nous sont données pour le résoudre :
"""
Make sure that:
- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co./models'
- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
💡 Si vous rencontrez un message d’erreur difficile à comprendre, copiez et collez le message dans Google ou sur Stack Overflow (oui, vraiment !). Il y a de fortes chances que vous ne soyez pas la première personne à rencontrer cette erreur et c’est un bon moyen de trouver des solutions que d’autres membres de la communauté ont publiées. Par exemple, en recherchant OSError : Can't load config for
sur Stack Overflow donne plusieurs réponses qui peuvent être utilisées comme point de départ pour résoudre le problème.
La première suggestion nous demande de vérifier si l’identifiant du modèle est effectivement correct, la première chose à faire est donc de copier l’identifiant et de le coller dans la barre de recherche du Hub :
Hmm, il semble en effet que le modèle de notre collègue ne soit pas sur le Hub… Mais il y a une faute de frappe dans le nom du modèle ! DistilBERT n’a qu’un seul « l » dans son nom alors corrigeons cela et cherchons « lewtun/distilbert-base-uncased-finetuned-squad-d5716d28 » à la place :
Ok, ça a marché. Maintenant essayons de télécharger à nouveau le modèle avec le bon identifiant :
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
"""
OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:
- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co./models'
- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
Argh, encore un échec. Bienvenue dans la vie quotidienne d’un ingénieur en apprentissage machine ! Puisque nous avons corrigé l’identifiant du modèle, le problème doit se situer dans le dépôt lui-même. Une façon rapide d’accéder au contenu d’un dépôt sur le 🤗 Hub est via la fonction list_repo_files()
de la bibliothèque huggingface_hub
:
from huggingface_hub import list_repo_files
list_repo_files(repo_id=model_checkpoint)
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
Intéressant. Il ne semble pas y avoir de fichier config.json dans le dépôt ! Pas étonnant que notre pipeline
n’ait pas pu charger le modèle. Notre collègue a dû oublier de pousser ce fichier vers le Hub après l’avoir finetuné. Dans ce cas, le problème semble assez simple à résoudre : nous pouvons lui demander d’ajouter le fichier, ou, puisque nous pouvons voir à partir de l’identifiant du modèle que le modèle pré-entraîné utilisé est distilbert-base-uncased
, nous pouvons télécharger la configuration de ce modèle et la pousser dans notre dépôt pour voir si cela résout le problème. Essayons cela. En utilisant les techniques apprises dans le chapitre 2, nous pouvons télécharger la configuration du modèle avec la classe AutoConfig
:
from transformers import AutoConfig
pretrained_checkpoint = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(pretrained_checkpoint)
🚨 L’approche que nous adoptons ici n’est pas infaillible puisque notre collègue peut avoir modifié la configuration de distilbert-base-uncased
avant de finetuner le modèle. Dans la vie réelle, nous voudrions vérifier avec lui d’abord, mais pour les besoins de cette section nous supposerons qu’il a utilisé la configuration par défaut.
Nous pouvons ensuite le pousser vers notre dépôt de modèles avec la fonction push_to_hub()
de la configuration :
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
Maintenant, nous pouvons tester si cela a fonctionné en chargeant le modèle depuis le dernier commit de la branche main
:
reader = pipeline("question-answering", model=model_checkpoint, revision="main")
context = r"""
Extractive Question Answering is the task of extracting an answer from a text
given a question. An example of a question answering dataset is the SQuAD
dataset, which is entirely based on that task. If you would like to fine-tune a
model on a SQuAD task, you may leverage the
examples/pytorch/question-answering/run_squad.py script.
🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX
frameworks, so you can use your favourite tools for a wide variety of tasks!
"""
context_fr = r"""
La réponse à des questions consiste à extraire une réponse d'un texte
à partir d'une question. Un exemple de jeu de données de réponse aux questions est le
jeu de données SQuAD qui est entièrement basé sur cette tâche. Si vous souhaitez finetuner
un modèle sur une tâche SQuAD, vous pouvez utiliser le fichier
exemples/pytorch/question-answering/run_squad.py.
🤗 Transformers est interopérable avec les frameworks PyTorch, TensorFlow et JAX.
de sorte que vous pouvez utiliser vos outils préférés pour une grande variété de tâches !
"""
question = "What is extractive question answering?"
# Qu'est-ce que la réponse extractive aux questions ?
reader(question=question, context=context)
{'score': 0.38669535517692566,
'start': 34,
'end': 95,
'answer': 'the task of extracting an answer from a text given a question'}
# la tâche consistant à extraire une réponse d'un texte à partir d'une question.
Woohoo, ça a marché ! Récapitulons ce que vous venez d’apprendre :
- les messages d’erreur en Python sont appelés tracebacks et sont lus de bas en haut. La dernière ligne du message d’erreur contient généralement les informations dont vous avez besoin pour localiser la source du problème,
- si la dernière ligne ne contient pas suffisamment d’informations, remontez dans le traceback et voyez si vous pouvez identifier où l’erreur se produit dans le code source,
- si aucun des messages d’erreur ne peut vous aider à déboguer le problème, essayez de rechercher en ligne une solution à un problème similaire,
- l’
huggingface_hub
fournit une suite d’outils que vous pouvez utiliser pour interagir avec et déboguer les dépôts sur le Hub.
Maintenant que vous savez comment déboguer un pipeline, examinons un exemple plus délicat dans la passe avant du modèle lui-même.
Déboguer la passe avant de votre modèle
Bien que le pipeline
soit parfait pour la plupart des applications où vous devez générer rapidement des prédictions, vous aurez parfois besoin d’accéder aux logits du modèle (par exemple si vous avez un post-traitement personnalisé que vous souhaitez appliquer). Pour voir ce qui peut mal tourner dans ce cas, commençons par récupérer le modèle et le tokenizer de notre pipeline
:
tokenizer = reader.tokenizer model = reader.model
Ensuite, nous avons besoin d’une question, alors voyons si nos frameworks préférés sont supportés :
question = "Which frameworks can I use?" # Quel frameworks puis-je utiliser ?
Comme nous l’avons vu dans le chapitre 7, les étapes habituelles que nous devons suivre sont la tokénisation des entrées, l’extraction des logits des tokens de début et de fin, puis le décodage de l’étendue de la réponse :
import torch
inputs = tokenizer(question, context, add_special_tokens=True)
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Pour obtenir le début de réponse le plus probable avec l'argmax du score
answer_start = torch.argmax(answer_start_scores)
# Pour obtenir la fin de réponse la plus probable avec l'argmax du score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
"""
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_75743/2725838073.py in <module>
1 inputs = tokenizer(question, text, add_special_tokens=True)
2 input_ids = inputs["input_ids"]
----> 3 outputs = model(**inputs)
4 answer_start_scores = outputs.start_logits
5 answer_end_scores = outputs.end_logits
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1050 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051 return forward_call(*input, **kwargs)
1052 # Do not call functions when jit is used
1053 full_backward_hooks, non_full_backward_hooks = [], []
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, start_positions, end_positions, output_attentions, output_hidden_states, return_dict)
723 return_dict = return_dict if return_dict is not None else self.config.use_return_dict
724
--> 725 distilbert_output = self.distilbert(
726 input_ids=input_ids,
727 attention_mask=attention_mask,
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1050 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051 return forward_call(*input, **kwargs)
1052 # Do not call functions when jit is used
1053 full_backward_hooks, non_full_backward_hooks = [], []
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
472 elif input_ids is not None:
--> 473 input_shape = input_ids.size()
474 elif inputs_embeds is not None:
475 input_shape = inputs_embeds.size()[:-1]
AttributeError: 'list' object has no attribute 'size'
"""
Il semble que nous ayons un bug dans notre code ! Mais il ne nous fait pas peur. Nous pouvons utiliser le débogueur Python dans un notebook :
ou dans un terminal :
Ici, la lecture du message d’erreur nous indique que l’objet 'list' n'a pas d'attribut 'size'
, et nous pouvons voir une flèche -->
pointant vers la ligne où le problème a été soulevé dans model(**inputs)
. Vous pouvez déboguer ceci de manière interactive en utilisant le débogueur Python, mais pour l’instant nous allons simplement imprimer une tranche de inputs
pour voir ce que nous avons :
inputs["input_ids"][:5]
[101, 2029, 7705, 2015, 2064]
Cela ressemble certainement à une list
ordinaire en Python mais vérifions le type :
type(inputs["input_ids"])
list
Oui, c’est bien une list
Python. Alors, qu’est-ce qui a mal tourné ? Rappelez-vous que dans le chapitre 2 nous avons vu que les classes AutoModelForXxx
opèrent sur des tenseurs (soit dans PyTorch ou TensorFlow) et qu’une opération commune est d’extraire les dimensions d’un tenseur en utilisant Tensor.size()
. Jetons un autre coup d’oeil au traceback pour voir quelle ligne a déclenché l’exception :
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
472 elif input_ids is not None:
--> 473 input_shape = input_ids.size()
474 elif inputs_embeds is not None:
475 input_shape = inputs_embeds.size()[:-1]
AttributeError: 'list' object has no attribute 'size'
Il semble que notre code ait essayé d’appeler input_ids.size()
, mais cela ne fonctionne clairement pas pour une list
Python qui est juste un conteneur. Comment pouvons-nous résoudre ce problème ? La recherche du message d’erreur sur Stack Overflow donne quelques réponses pertinentes. En cliquant sur la première, une question similaire à la nôtre s’affiche, avec la réponse indiquée dans la capture d’écran ci-dessous :
La réponse recommande d’ajouter return_tensors='pt'
au tokenizer, voyons donc si cela fonctionne pour nous :
inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt")
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Pour obtenir le début de réponse le plus probable avec l'argmax du score
answer_start = torch.argmax(answer_start_scores)
# Pour obtenir la fin de réponse la plus probable avec l'argmax du score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
"""
Question: Which frameworks can I use? # Quels frameworks puis-je utiliser ?
Answer: pytorch, tensorflow, and jax # pytorch, tensorflow et jax
"""
Super, ça a marché ! Voilà un excellent exemple de l’utilité de Stack Overflow : en identifiant un problème similaire, nous avons pu bénéficier de l’expérience d’autres membres de la communauté. Cependant, une recherche de ce type ne donne pas toujours une réponse pertinente. Que faire alors dans ce cas ? Heureusement, il existe une communauté accueillante de développeurs sur le forum d’Hugging Face qui peut vous aider ! Dans la prochaine section, nous verrons comment rédiger de bonnes questions sur les forums pour avoir de bonnes chances d’obtenir une réponse.