Transformers documentation

Classificação de tokens

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

Classificação de tokens

A classificação de tokens atribui um rótulo a tokens individuais em uma frase. Uma das tarefas mais comuns de classificação de tokens é o Reconhecimento de Entidades Nomeadas (NER). O NER tenta encontrar um rótulo para cada entidade em uma frase, como uma pessoa, local ou organização.

Este guia mostrará como:

  1. Ajuste fino do DistilBERT no conjunto de dados WNUT 17 para detectar novas entidades.
  2. Use seu modelo ajustado para inferência.

Para ver todas as arquiteturas e checkpoints compatíveis com esta tarefa, recomendamos consultar a página da tarefa.

Antes de começar, verifique se você tem todas as bibliotecas necessárias instaladas:

pip install transformers datasets evaluate seqeval

Recomendamos que você faça login na sua conta do Hugging Face para que possa enviar e compartilhar seu modelo com a comunidade. Quando solicitado, insira seu token para fazer login:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Carregar o conjunto de dados WNUT 17

Comece carregando o conjunto de dados WNUT 17 a partir da biblioteca 🤗 Datasets:

>>> from datasets import load_dataset

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

Em seguida, veja um exemplo:

>>> 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', '.']
}

Cada número em ner_tags representa uma entidade. Converta os números em seus nomes de rótulo para descobrir quais são as entidades:

>>> 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",
]

A letra que prefixa cada ner_tag indica a posição do token na entidade:

  • B- indica o início de uma entidade.
  • I- indica que um token está contido dentro da mesma entidade (por exemplo, o token State faz parte de uma entidade como Empire State Building).
  • 0 indica que o token não corresponde a nenhuma entidade.

Pré-processar

O próximo passo é carregar um tokenizer DistilBERT para pré-processar o campo tokens:

>>> from transformers import AutoTokenizer

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

Como você viu no campo tokens do exemplo acima, parece que a entrada já foi tokenizada. Mas a entrada na verdade ainda não foi tokenizada e você precisará definir is_split_into_words=True para tokenizar as palavras em subpalavras. Por exemplo:

>>> 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]']

No entanto, isso adiciona alguns tokens especiais [CLS] e [SEP] e a tokenização em subpalavras cria uma incompatibilidade entre a entrada e os rótulos. Uma única palavra correspondente a um único rótulo pode agora ser dividida em duas subpalavras. Você precisará realinhar os tokens e os rótulos por meio de:

  1. Mapeando todos os tokens para a palavra correspondente com o método word_ids.
  2. Atribuir o rótulo -100 aos tokens especiais [CLS] e [SEP] para que sejam ignorados pela função de perda do PyTorch (veja CrossEntropyLoss).
  3. Etiquetando apenas o primeiro token de uma palavra específica. Atribua -100 aos outros subtokens da mesma palavra.

Aqui está como você pode criar uma função para realinhar os tokens e os rótulos e truncar as sequências para que não excedam o comprimento máximo de entrada do 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

Para aplicar a função de pré-processamento em todo o conjunto de dados, use a função map do 🤗 Datasets. Você pode acelerar a função map definindo batched=True para processar vários elementos do conjunto de dados de uma vez:

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

Agora, crie um lote de exemplos usando DataCollatorWithPadding. É mais eficiente aplicar padding dinamicamente às frases até o comprimento máximo do lote durante a colação, em vez de aplicar padding em todo o conjunto de dados até o comprimento máximo.

>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)

Avaliar

Incluir uma métrica durante o treinamento é frequentemente útil para avaliar o desempenho do seu modelo. Você pode carregar rapidamente um método de avaliação com a biblioteca 🤗 Evaluate. Para esta tarefa, carregue o framework seqeval (veja o quick tour do 🤗 Evaluate para saber mais sobre como carregar e calcular uma métrica). O Seqeval, na verdade, produz várias pontuações: precisão, recall, F1 e acurácia.

>>> import evaluate

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

Obtenha primeiro os rótulos de NER e, em seguida, crie uma função que passa suas previsões verdadeiras e seus rótulos verdadeiros para compute para calcular as pontuações:

>>> 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"],
...     }

Sua função compute_metrics está pronta e você voltará a ela ao configurar o treinamento.

Treinar

Antes de iniciar o treinamento do seu modelo, crie um mapa dos ids esperados para seus rótulos com id2label e 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,
... }

Se você não está familiarizado com o ajuste fino de um modelo usando o Trainer, confira o tutorial básico aqui!

Agora você está pronto para começar a treinar seu modelo! Carregue o DistilBERT com AutoModelForTokenClassification, junto com o número de rótulos esperados e os mapeamentos de rótulos:

>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

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

Neste ponto, restam apenas três etapas:

  1. Defina os hiperparâmetros de treinamento em TrainingArguments. O único parâmetro obrigatório é output_dir, que especifica onde salvar seu modelo. Você enviará este modelo para o Hub definindo push_to_hub=True (é necessário estar conectado ao Hugging Face para enviar seu modelo). Ao final de cada epoch, o Trainer avaliará as pontuações seqeval e salvará o checkpoint de treinamento.
  2. Passe os argumentos de treinamento para o Trainer junto com o modelo, o conjunto de dados, o tokenizer, o data collator e a função compute_metrics.
  3. Chame train() para fazer o ajuste fino do seu modelo.
>>> 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()

Quando o treinamento for concluído, compartilhe seu modelo no Hub com o método push_to_hub() para que todos possam usá-lo:

>>> trainer.push_to_hub()

Para um exemplo mais detalhado de como fazer o ajuste fino de um modelo para classificação de tokens, consulte o notebook do PyTorch correspondente.

Inferência

Ótimo, agora que você ajustou um modelo, pode usá-lo para inferência!

Obtenha um texto no qual você deseja executar inferência:

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

A maneira mais simples de testar seu modelo ajustado para inferência é usá-lo em um pipeline(). Instancie um pipeline para NER com seu modelo e passe seu texto para ele:

>>> 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}]

Você também pode replicar manualmente os resultados do pipeline, se desejar:

Tokenize o texto e retorne tensores do PyTorch:

>>> from transformers import AutoTokenizer

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

Passe suas entradas para o modelo e retorne os logits:

>>> from transformers import AutoModelForTokenClassification

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

Obtenha a classe com a maior probabilidade e use o mapeamento id2label do modelo para convertê-la em um rótulo de texto:

>>> 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