Transformers documentation

Clasificación 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

Clasificación de tokens

La clasificación de tokens asigna una etiqueta a los tokens individuales de una oración. Una de las tareas de clasificación de tokens más comunes es el reconocimiento de entidades nombradas (NER). El NER intenta encontrar una etiqueta para cada entidad en una oración, como una persona, un lugar o una organización.

Esta guía le mostrará cómo:

  1. Ajusta DistilBERT en el conjunto de datos WNUT 17 para detectar nuevas entidades.
  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.

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

pip install transformers datasets evaluate seqeval

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:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Cargar el conjunto de datos WNUT 17

Comience cargando el conjunto de datos WNUT 17 desde la biblioteca 🤗 Datasets:

>>> from datasets import load_dataset

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

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

>>> 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 en ner_tags representa una entidad. Convierta los números a sus nombres de etiqueta para descubrir de qué entidades se trata:

>>> 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 letra que antecede a cada ner_tag indica la posición del token dentro de la entidad:

  • B- indica el inicio de una entidad.
  • I- indica que un token está contenido dentro de la misma entidad (por ejemplo, el token State es parte de una entidad como Empire State Building).
  • 0 indica que el token no corresponde a ninguna entidad.

Preprocesar

El siguiente paso es cargar un tokenizador de DistilBERT para preprocesar el campo tokens:

>>> from transformers import AutoTokenizer

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

Como vio en el campo tokens del ejemplo anterior, parece que la entrada ya ha sido tokenizada. Sin embargo, la entrada en realidad aún no ha sido tokenizada y deberá establecer is_split_into_words=True para tokenizar las palabras en subpalabras. Por ejemplo:

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

Sin embargo, esto añade algunos tokens especiales [CLS] y [SEP] y la tokenización de subpalabras crea una desalineación entre la entrada y las etiquetas. Una sola palabra que corresponde a una sola etiqueta ahora puede dividirse en dos subpalabras. Deberás realinear los tokens y las etiquetas mediante:

  1. Mapear todos los tokens a su palabra correspondiente con el método word_ids.
  2. Asignar la etiqueta -100 a los tokens especiales [CLS] y [SEP] para que sean ignorados por la función de pérdida de PyTorch (ver CrossEntropyLoss).
  3. Solo etiquetar el primer token de una palabra dada. Asignar -100 a los demás subtokens de la misma palabra.

A continuación se muestra cómo crear una función para realinear los tokens y las etiquetas, y truncar las secuencias para que no superen la longitud máxima de entrada 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

Para aplicar la función de preprocesamiento a todo el conjunto de datos, utiliza la función map de 🤗 Datasets. Puedes acelerar la función map estableciendo batched=True para procesar varios elementos del conjunto de datos a la vez:

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

Ahora crea un lote de ejemplos usando DataCollatorWithPadding. 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.

>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)

Evaluar

Incluir una métrica durante el entrenamiento suele ser útil para evaluar el rendimiento de tu modelo. Puedes cargar rápidamente un método de evaluación con la biblioteca 🤗 Evaluate. Para esta tarea, carga el marco seqeval (consulta el quick tour de 🤗 Evaluate para saber más sobre cómo cargar y calcular una métrica). Seqeval produce varios puntajes: precisión, recall, F1 y exactitud.

>>> import evaluate

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

Obtén primero las etiquetas NER y, a continuación, crea una función que pase tus predicciones verdaderas y tus etiquetas verdaderas a compute para calcular las puntuaciones:

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

Tu función compute_metrics ya está lista y volverás a ella cuando configures el entrenamiento.

Entrenar

Antes de comenzar a entrenar tu modelo, crea un mapa de los ids esperados a sus etiquetas con id2label y 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 no está familiarizado con el ajuste fino de un modelo con Trainer, ¡eche un vistazo al tutorial básico aquí!

¡Ya está listo para comenzar a entrenar su modelo! Cargue DistilBERT con AutoModelForTokenClassification junto con el número de etiquetas esperadas y los mapeos de etiquetas:

>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

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

En este punto, solo quedan tres pasos:

  1. Define los hiperparámetros de entrenamiento en 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). Al final de cada epoch, Trainer evaluará las puntuaciones de seqeval y guardará el checkpoint de entrenamiento.
  2. Pasa los argumentos de entrenamiento a Trainer junto con el modelo, el conjunto de datos, el tokenizador, el data collator y la función compute_metrics.
  3. Llama a train() para ajustar tu 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()

Una vez completado el entrenamiento, comparte tu modelo en el Hub con el método push_to_hub() para que todo el mundo pueda usarlo:

>>> trainer.push_to_hub()

Para un ejemplo más detallado sobre cómo ajustar un modelo para la clasificación de tokens, consulte el cuaderno de PyTorch correspondiente.

Inferencia

Genial, ahora que has afinado un modelo, ¡puedes usarlo para inferencia!

Guarda el texto sobre el que deseas ejecutar la inferencia:

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

La forma más sencilla de probar tu modelo afinado para inferencia es usarlo en un pipeline(). Crea una instancia de pipeline para NER con tu modelo y pásale tu texto:

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

También puedes replicar manualmente los resultados de pipeline si lo deseas:

Tokeniza el texto y devuelve tensores de PyTorch:

>>> from transformers import AutoTokenizer

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

Pasa tus entradas al modelo y devuelve los logits:

>>> from transformers import AutoModelForTokenClassification

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

Obtén la clase con la probabilidad más alta y usa el mapeo id2label del modelo para convertirla en una etiqueta 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