Transformers documentation

Классификация токенов

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

Классификация токенов

Классификация токенов присваивает метку отдельным токенам в предложении. Одна из самых распространённых задач классификации токенов — распознавание именованных сущностей (Named Entity Recognition, NER). NER пытается найти метку для каждой сущности в предложении, например, для человека, места или организации.

В этом руководстве показано, как:

  1. Настройте DistilBERT на датасете WNUT 17 для обнаружения новых сущностей.
  2. Используйте вашу дообученную модель для инференса.

Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к странице задачи.

Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:

pip install transformers datasets evaluate seqeval

Мы рекомендуем вам войти в свой аккаунт Hugging Face, чтобы вы могли загрузить свою модель и поделиться ею с сообществом. При появлении запроса введите токен для входа:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Загрузить набор данных WNUT 17

Начните с загрузки набора данных WNUT 17 из библиотеки 🤗 Datasets:

>>> from datasets import load_dataset

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

Теперь рассмотрим пример:

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

Каждое число в ner_tags представляет собой сущность. Преобразуйте числа в имена меток, чтобы узнать, какие это сущности:

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

Буква, предшествующая каждому ner_tag, указывает позицию токена в сущности:

  • B- обозначает начало сущности.
  • I- означает, что токен входит в состав того же сущностного блока (например, токен State является частью сущности, такой как Empire State Building).
  • 0 означает, что токен не соответствует ни одной сущности.

Предобработка

Следующий шаг — загрузить токенизатор DistilBERT для предобработки поля tokens:

>>> from transformers import AutoTokenizer

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

Как вы видели в примере с полем tokens выше, входные данные выглядят уже токенизированными. Однако на самом деле токенизация ещё не выполнена, и вам нужно установить is_split_into_words=True, чтобы разбить слова на подтокены. Например:

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

Однако это добавляет специальные токены [CLS] и [SEP], а субтокенизация приводит к рассинхронизации между входом и метками. Одно слово, соответствующее одной метке, теперь может быть разбито на два субтокена. Вам нужно будет выровнять токены и метки, выполнив следующее:

  1. Сопоставление всех токенов с соответствующими им словами с помощью метода word_ids.
  2. Назначение метки -100 для специальных токенов [CLS] и [SEP], чтобы они игнорировались функцией потерь PyTorch (см. CrossEntropyLoss).
  3. Разметка только первого токена данного слова. Прочим субтокенам из того же слова присваивается -100.

Ниже показано, как создать функцию для выравнивания токенов и меток, а также для усечения последовательностей до максимальной длины входных данных 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

Чтобы применить функцию предобработки ко всему набору данных, используйте функцию map из 🤗 Datasets. Вы можете ускорить работу функции map, установив batched=True, чтобы обрабатывать несколько элементов набора данных одновременно:

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

Теперь создайте батч примеров с помощью DataCollatorWithPadding. При формировании батча эффективнее динамически дополнять предложения до максимальной длины в рамках батча, чем дополнять весь датасет до максимальной длины.

>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)

Оценка

Включение метрики в процесс обучения часто помогает оценить качество модели. Вы можете быстро загрузить метод оценки с помощью библиотеки 🤗 Evaluate. Для этой задачи загрузите фреймворк seqeval (см. быстрый обзор 🤗 Evaluate, чтобы узнать, как загружать и вычислять метрики). Seqeval фактически вычисляет несколько показателей: точность (precision), полноту (recall), F1 и accuracy.

>>> import evaluate

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

Сначала получите метки NER, а затем создайте функцию, которая передаёт ваши истинные предсказания и истинные метки в compute для вычисления метрик:

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

Ваша функция compute_metrics готова, и вы вернётесь к ней при настройке обучения.

Обучение

Прежде чем начинать обучение модели, создайте сопоставление ожидаемых идентификаторов с их метками с помощью id2label и 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,
... }

Если вы не знакомы с дообучением модели с помощью Trainer, загляните в базовое руководство здесь!

Теперь можно приступать к обучению модели! Загрузите DistilBERT с помощью AutoModelForTokenClassification, указав количество ожидаемых меток и соответствия меток:

>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

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

На этом этапе осталось выполнить только три шага:

  1. Определите гиперпараметры обучения в TrainingArguments. Единственный обязательный параметр — output_dir, который указывает, куда сохранять модель. Чтобы загрузить модель на Hub, установите push_to_hub=True (для загрузки модели необходимо войти в аккаунт Hugging Face). В конце каждой эпохи Trainer вычисляет метрики seqeval и сохраняет контрольную точку обучения.
  2. Передайте аргументы обучения в Trainer вместе с моделью, набором данных, токенизатором, data collator и функцией compute_metrics.
  3. Вызовите train(), чтобы дообучить модель.
>>> 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()

После завершения обучения используйте метод push_to_hub(), чтобы опубликовать модель на Hub и дать всем возможность её использовать:

>>> trainer.push_to_hub()

Более подробный пример дообучения модели для классификации токенов можно найти в соответствующем ноутбуке PyTorch.

Вывод

Отлично, теперь, когда вы дообучили модель, вы можете использовать её для инференса!

Возьмите текст, на котором вы хотите выполнить инференс:

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

Самый простой способ проверить вашу дообученную модель для инференса — использовать её в pipeline(). Создайте экземпляр pipeline для NER с вашей моделью и передайте в него текст:

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

Вы также можете вручную воспроизвести результаты pipeline, если хотите:

Токенизируйте текст и верните тензоры PyTorch:

>>> from transformers import AutoTokenizer

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

Передайте входные данные в модель и верните logits:

>>> from transformers import AutoModelForTokenClassification

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

Получите класс с наибольшей вероятностью и используйте отображение id2label модели, чтобы преобразовать его в текстовую метку:

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