Transformers documentation

Classification de jetons

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

Classification de jetons

La classification de jetons attribue un libellé à chaque jeton individuel d'une phrase. L'une des tâches de classification de jetons les plus courantes est la reconnaissance d'entités nommées (NER). Le NER tente de trouver un libellé pour chaque entité d'une phrase, telle qu'une personne, un lieu ou une organisation.

Ce guide vous montre comment :

  1. Effectuer le fine-tuning de DistilBERT sur le jeu de données WNUT 17 pour détecter de nouvelles entités.
  2. 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 seqeval

Nous vous encourage à vous connecter à votre compte Hugging Face afin de pouvoir téléverser et partager votre modèle avec la communauté. Lorsque cela vous est demandé, saisissez votre jeton pour vous connecter :

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Charger le jeu de données WNUT 17

Commencez par charger le jeu de données WNUT 17 à partir de la bibliothèque 🤗 Datasets :

>>> from datasets import load_dataset

>>> wnut = load_dataset("flaitenberger/wnut_17")

Puis, jetez un œil à un exemple :

>>> wnut["train"][0]
{'id': '0',
 'ner_tags': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0],
 'tokens': ['@paulwalk', 'It', "'s", 'the', 'view', 'from', 'where', 'I', "'m", 'living', 'for', 'two', 'weeks', '.', 'Empire', 'State', 'Building', '=', 'ESB', '.', 'Pretty', 'bad', 'storm', 'here', 'last', 'evening', '.']
}

Chaque nombre dans ner_tags représente une entité. Convertissez les nombres en leurs noms d'étiquettes pour identifier les entités :

>>> label_list = wnut["train"].features[f"ner_tags"].feature.names
>>> label_list
[
    "O",
    "B-corporation",
    "I-corporation",
    "B-creative-work",
    "I-creative-work",
    "B-group",
    "I-group",
    "B-location",
    "I-location",
    "B-person",
    "I-person",
    "B-product",
    "I-product",
]

La lettre qui préfixe chaque ner_tag indique la position du jeton au sein de l'entité :

  • B- indique le début d'une entité.
  • I- indique qu'un jeton est contenu à l'intérieur de la même entité (par exemple, le jeton State fait partie d'une entité comme Empire State Building).
  • 0 indique que le jeton ne correspond à aucune entité.

Prétraitement

L'étape suivante consiste à charger un tokenizer DistilBERT pour prétraiter le champ tokens :

>>> from transformers import AutoTokenizer

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

Comme vous l’avez vu dans le champ tokens de l’exemple ci-dessus, l’entrée semble déjà avoir été tokenisée. En réalité, elle ne l’a pas encore été, et vous devrez définir is_split_into_words=True pour tokeniser les mots en sous-mots. Par exemple :

>>> example = wnut["train"][0]
>>> tokenized_input = tokenizer(example["tokens"], is_split_into_words=True)
>>> tokens = tokenizer.convert_ids_to_tokens(tokenized_input["input_ids"])
>>> tokens
['[CLS]', '@', 'paul', '##walk', 'it', "'", 's', 'the', 'view', 'from', 'where', 'i', "'", 'm', 'living', 'for', 'two', 'weeks', '.', 'empire', 'state', 'building', '=', 'es', '##b', '.', 'pretty', 'bad', 'storm', 'here', 'last', 'evening', '.', '[SEP]']

Cependant, cela ajoute des jetons spéciaux [CLS] et [SEP], et la tokenisation en sous-mots crée un décalage entre les entrées et les étiquettes. Un seul mot correspondant à une seule étiquette peut désormais être découpé en deux sous-mots. Vous devrez réaligner les jetons et les étiquettes en :

  1. Cartographie de tous les jetons vers leur mot correspondant à l'aide de la méthode word_ids.
  2. Attribuer l'étiquette -100 aux jetons spéciaux [CLS] et [SEP] afin qu'ils soient ignorés par la fonction de perte de PyTorch (voir CrossEntropyLoss).
  3. Ne marquer que le premier jeton d'un mot donné. Attribuer -100 aux autres sous-jetons du même mot.

Voici comment créer une fonction pour réaligner les jetons et les étiquettes, et tronquer les séquences afin qu’elles ne dépassent pas la longueur d’entrée maximale de DistilBERT :

>>> def tokenize_and_align_labels(examples):
...     tokenized_inputs = tokenizer(examples["tokens"], truncation=True, is_split_into_words=True)

...     labels = []
...     for i, label in enumerate(examples[f"ner_tags"]):
...         word_ids = tokenized_inputs.word_ids(batch_index=i)  # Map tokens to their respective word.
...         previous_word_idx = None
...         label_ids = []
...         for word_idx in word_ids:  # Set the special tokens to -100.
...             if word_idx is None:
...                 label_ids.append(-100)
...             elif word_idx != previous_word_idx:  # Only label the first token of a given word.
...                 label_ids.append(label[word_idx])
...             else:
...                 label_ids.append(-100)
...             previous_word_idx = word_idx
...         labels.append(label_ids)

...     tokenized_inputs["labels"] = labels
...     return tokenized_inputs

Pour appliquer la fonction de prétraitement à l’ensemble du jeu de données, utilisez la fonction 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 :

>>> tokenized_wnut = wnut.map(tokenize_and_align_labels, batched=True)

Créez maintenant un lot d’exemples à l’aide de DataCollatorWithPadding. 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 du jeu de données à la longueur maximale.

>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)

Évaluer

Inclure une métrique pendant l’entraînement est souvent utile pour évaluer les performances de votre modèle. Vous pouvez charger rapidement une méthode d’évaluation avec la bibliothèque 🤗 Evaluate. Pour cette tâche, chargez le framework seqeval (consultez le quick tour de 🤗 Evaluate pour en savoir plus sur la façon de charger et de calculer une métrique). Seqeval produit en réalité plusieurs scores : précision, rappel, F1 et exactitude.

>>> import evaluate

>>> seqeval = evaluate.load("seqeval")

Récupérez d'abord les étiquettes NER, puis créez une fonction qui transmet vos prédictions réelles et vos étiquettes réelles à compute pour calculer les scores :

>>> import numpy as np

>>> labels = [label_list[i] for i in example[f"ner_tags"]]


>>> def compute_metrics(p):
...     predictions, labels = p
...     predictions = np.argmax(predictions, axis=2)

...     true_predictions = [
...         [label_list[p] for (p, l) in zip(prediction, label) if l != -100]
...         for prediction, label in zip(predictions, labels)
...     ]
...     true_labels = [
...         [label_list[l] for (p, l) in zip(prediction, label) if l != -100]
...         for prediction, label in zip(predictions, labels)
...     ]

...     results = seqeval.compute(predictions=true_predictions, references=true_labels)
...     return {
...         "precision": results["overall_precision"],
...         "recall": results["overall_recall"],
...         "f1": results["overall_f1"],
...         "accuracy": results["overall_accuracy"],
...     }

Votre fonction compute_metrics est prête, et vous y reviendrez lors de la configuration de l’entraînement.

Entraînement

Avant de commencer l'entraînement de votre modèle, créez une correspondance entre les ids attendus et leurs libellés à l'aide de id2label et label2id :

>>> id2label = {
...     0: "O",
...     1: "B-corporation",
...     2: "I-corporation",
...     3: "B-creative-work",
...     4: "I-creative-work",
...     5: "B-group",
...     6: "I-group",
...     7: "B-location",
...     8: "I-location",
...     9: "B-person",
...     10: "I-person",
...     11: "B-product",
...     12: "I-product",
... }
>>> label2id = {
...     "O": 0,
...     "B-corporation": 1,
...     "I-corporation": 2,
...     "B-creative-work": 3,
...     "I-creative-work": 4,
...     "B-group": 5,
...     "I-group": 6,
...     "B-location": 7,
...     "I-location": 8,
...     "B-person": 9,
...     "I-person": 10,
...     "B-product": 11,
...     "I-product": 12,
... }

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 DistilBERT avec AutoModelForTokenClassification, en précisant le nombre de classes attendues ainsi que les correspondances des étiquettes :

>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

>>> model = AutoModelForTokenClassification.from_pretrained(
...     "distilbert/distilbert-base-uncased", num_labels=13, id2label=id2label, label2id=label2id
... )

À ce stade, il ne reste que trois étapes :

  1. Définissez vos hyperparamètres d’entraînement dans TrainingArguments. Le seul paramètre obligatoire est output_dir, qui spécifie l’endroit où enregistrer votre modèle. Vous pousserez ce modèle vers le Hub en définissant push_to_hub=True (vous devez être connecté à Hugging Face pour téléverser votre modèle). À la fin de chaque époque, le Trainer évaluera les scores seqeval et enregistrera le point de contrôle d’entraînement.
  2. Transmettez les arguments d’entraînement à Trainer en même temps que le modèle, le jeu de données, le tokenizer, le data collator et la fonction compute_metrics.
  3. Appel de train() pour l’ajustement fin de votre modèle.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_wnut_model",
...     learning_rate=2e-5,
...     per_device_train_batch_size=16,
...     per_device_eval_batch_size=16,
...     num_train_epochs=2,
...     weight_decay=0.01,
...     eval_strategy="epoch",
...     save_strategy="epoch",
...     load_best_model_at_end=True,
...     push_to_hub=True,
... )

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

>>> trainer.train()

Une fois l’entraînement terminé, 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 de la façon de faire un fine-tuning d'un modèle pour la classification de jetons, consultez le notebook PyTorch correspondant.

Inférence

Parfait, maintenant que vous avez affiné un modèle, vous pouvez l'utiliser pour l'inférence !

Récupérez un texte sur lequel vous souhaitez effectuer une inférence :

>>> text = "The Golden State Warriors are an American professional basketball team based in San Francisco."

La manière la plus simple de tester votre modèle fine-tuné pour l’inférence est de l’utiliser dans un pipeline(). Instanciez un pipeline pour la NER avec votre modèle, puis transmettez-lui votre texte :

>>> from transformers import pipeline

>>> classifier = pipeline("ner", model="stevhliu/my_awesome_wnut_model")
>>> classifier(text)
[{'entity': 'B-location',
  'score': 0.42658573,
  'index': 2,
  'word': 'golden',
  'start': 4,
  'end': 10},
 {'entity': 'I-location',
  'score': 0.35856336,
  'index': 3,
  'word': 'state',
  'start': 11,
  'end': 16},
 {'entity': 'B-group',
  'score': 0.3064001,
  'index': 4,
  'word': 'warriors',
  'start': 17,
  'end': 25},
 {'entity': 'B-location',
  'score': 0.65523505,
  'index': 13,
  'word': 'san',
  'start': 80,
  'end': 83},
 {'entity': 'B-location',
  'score': 0.4668663,
  'index': 14,
  'word': 'francisco',
  'start': 84,
  'end': 93}]

Vous pouvez également reproduire manuellement les résultats de pipeline si vous le souhaitez :

Tokeniser le texte et retourner des tenseurs PyTorch :

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("stevhliu/my_awesome_wnut_model")
>>> inputs = tokenizer(text, return_tensors="pt")

Transmettez vos entrées au modèle et renvoyez les logits :

>>> from transformers import AutoModelForTokenClassification

>>> model = AutoModelForTokenClassification.from_pretrained("stevhliu/my_awesome_wnut_model")
>>> with torch.no_grad():
...     logits = model(**inputs).logits

Obtenez la classe ayant la probabilité la plus élevée, puis utilisez le mapping id2label du modèle pour la convertir en libellé texte :

>>> predictions = torch.argmax(logits, dim=2)
>>> predicted_token_class = [model.config.id2label[t.item()] for t in predictions[0]]
>>> predicted_token_class
['O',
 'O',
 'B-location',
 'I-location',
 'B-group',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'B-location',
 'B-location',
 'O',
 'O']
Update on GitHub