Transformers 文档

Token 分类

Hugging Face's logo
加入 Hugging Face 社区

并获取增强的文档体验

开始使用

Token 分类

Token 分类为句子中的每个 token 分配一个标签。最常见的 token 分类任务之一是命名实体识别 (NER)。NER 尝试为句子中的每个实体找到一个标签,例如人、地点或组织。

本指南将向您展示如何

  1. WNUT 17 数据集上微调 DistilBERT 以检测新实体。
  2. 使用您微调的模型进行推理。

要查看与此任务兼容的所有架构和检查点,我们建议查看任务页面

在开始之前,请确保您已安装所有必要的库

pip install transformers datasets evaluate seqeval

我们鼓励您登录您的 Hugging Face 帐户,以便您可以上传您的模型并与社区分享。出现提示时,输入您的 token 以登录

>>> from huggingface_hub import notebook_login

>>> notebook_login()

加载 WNUT 17 数据集

首先从 🤗 Datasets 库加载 WNUT 17 数据集

>>> from datasets import load_dataset

>>> wnut = load_dataset("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 前缀的字母表示实体的 token 位置

  • B- 表示实体的开头。
  • I- 表示 token 包含在同一实体内部(例如,State token 是像 Empire State Building 这样的实体的一部分)。
  • 0 表示 token 不对应于任何实体。

预处理

下一步是加载 DistilBERT tokenizer 以预处理 tokens 字段

>>> from transformers import AutoTokenizer

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

正如您在上面的示例 tokens 字段中看到的那样,输入看起来已经被 token 化了。但实际上输入尚未被 token 化,您需要设置 is_split_into_words=True 以将单词 token 化为子词。例如

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

但是,这会添加一些特殊 token [CLS][SEP],并且子词 token 化会在输入和标签之间创建不匹配。对应于单个标签的单个单词现在可能被拆分为两个子词。您需要通过以下方式重新对齐 token 和标签:

  1. 使用 word_ids 方法将所有 token 映射到其对应的单词。
  2. 将标签 -100 分配给特殊 token [CLS][SEP],以便 PyTorch 损失函数忽略它们(请参阅 CrossEntropyLoss)。
  3. 仅标记给定单词的第一个 token。将 -100 分配给来自同一单词的其他子 token。

以下是如何创建一个函数来重新对齐 token 和标签,并将序列截断为不超过 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

要将预处理函数应用于整个数据集,请使用 🤗 Datasets map 函数。您可以通过设置 batched=True 来加速 map 函数,以一次处理数据集的多个元素

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

现在使用 DataCollatorWithPadding 创建一批示例。在整理期间将句子动态填充到批次中最长长度,而不是将整个数据集填充到最大长度,这样效率更高。

Pytorch
隐藏 Pytorch 内容
>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
TensorFlow
隐藏 TensorFlow 内容
>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer, return_tensors="tf")

评估

在训练期间包含指标通常有助于评估模型的性能。您可以使用 🤗 Evaluate 库快速加载评估方法。对于此任务,加载 seqeval 框架(请参阅 🤗 Evaluate 快速入门 以了解有关如何加载和计算指标的更多信息)。Seqeval 实际上会生成多个分数:精确率、召回率、F1 值和准确率。

>>> 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 函数现在已准备就绪,当您设置训练时,您将返回到它。

训练

在开始训练模型之前,使用 id2labellabel2id 创建预期 id 到其标签的映射

>>> 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,
... }
Pytorch
隐藏 Pytorch 内容

如果您不熟悉使用 Trainer 微调模型,请查看此处的基本教程!

您现在可以开始训练您的模型了!使用 AutoModelForTokenClassification 加载 DistilBERT,以及预期的标签数量和标签映射

>>> 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,它指定保存模型的位置。您将通过设置 push_to_hub=True 将此模型推送到 Hub(您需要登录 Hugging Face 才能上传您的模型)。在每个 epoch 结束时,Trainer 将评估 seqeval 分数并保存训练检查点。
  2. 将训练参数传递给 Trainer,以及模型、数据集、tokenizer、数据整理器和 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()
TensorFlow
隐藏 TensorFlow 内容

如果您不熟悉使用 Keras 微调模型,请查看此处的基本教程!

要在 TensorFlow 中微调模型,请首先设置优化器函数、学习率计划和一些训练超参数
>>> from transformers import create_optimizer

>>> batch_size = 16
>>> num_train_epochs = 3
>>> num_train_steps = (len(tokenized_wnut["train"]) // batch_size) * num_train_epochs
>>> optimizer, lr_schedule = create_optimizer(
...     init_lr=2e-5,
...     num_train_steps=num_train_steps,
...     weight_decay_rate=0.01,
...     num_warmup_steps=0,
... )

然后,您可以使用 TFAutoModelForTokenClassification 加载 DistilBERT,以及预期的标签数量和标签映射

>>> from transformers import TFAutoModelForTokenClassification

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

使用 prepare_tf_dataset() 将数据集转换为 tf.data.Dataset 格式

>>> tf_train_set = model.prepare_tf_dataset(
...     tokenized_wnut["train"],
...     shuffle=True,
...     batch_size=16,
...     collate_fn=data_collator,
... )

>>> tf_validation_set = model.prepare_tf_dataset(
...     tokenized_wnut["validation"],
...     shuffle=False,
...     batch_size=16,
...     collate_fn=data_collator,
... )

使用 compile 配置模型以进行训练。请注意,Transformers 模型都具有默认的与任务相关的损失函数,因此您无需指定一个,除非您想要指定

>>> import tensorflow as tf

>>> model.compile(optimizer=optimizer)  # No loss argument!

开始训练之前要设置的最后两件事是从预测中计算 seqeval 分数,并提供一种将模型推送到 Hub 的方法。两者都通过使用 Keras 回调来完成。

将您的 compute_metrics 函数传递给 KerasMetricCallback

>>> from transformers.keras_callbacks import KerasMetricCallback

>>> metric_callback = KerasMetricCallback(metric_fn=compute_metrics, eval_dataset=tf_validation_set)

PushToHubCallback 中指定推送模型和 tokenizer 的位置

>>> from transformers.keras_callbacks import PushToHubCallback

>>> push_to_hub_callback = PushToHubCallback(
...     output_dir="my_awesome_wnut_model",
...     tokenizer=tokenizer,
... )

然后将您的回调捆绑在一起

>>> callbacks = [metric_callback, push_to_hub_callback]

最后,您已准备好开始训练您的模型!使用您的训练和验证数据集、epoch 数量和回调调用 fit 以微调模型

>>> model.fit(x=tf_train_set, validation_data=tf_validation_set, epochs=3, callbacks=callbacks)

训练完成后,您的模型将自动上传到 Hub,以便每个人都可以使用它!

有关如何微调模型以进行 token 分类的更深入示例,请查看相应的 PyTorch notebookTensorFlow notebook

推理

太棒了,现在您已经微调了一个模型,您可以将其用于推理!

获取一些您想要运行推理的文本

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

尝试对微调模型进行推理的最简单方法是在 pipeline() 中使用它。使用您的模型实例化一个用于 NER 的 pipeline,并将您的文本传递给它

>>> 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
隐藏 Pytorch 内容

Token 化文本并返回 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']
TensorFlow
隐藏 TensorFlow 内容

Token 化文本并返回 TensorFlow 张量

>>> from transformers import AutoTokenizer

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

将您的输入传递给模型并返回 logits

>>> from transformers import TFAutoModelForTokenClassification

>>> model = TFAutoModelForTokenClassification.from_pretrained("stevhliu/my_awesome_wnut_model")
>>> logits = model(**inputs).logits

获取概率最高的类别,并使用模型的 id2label 映射将其转换为文本标签

>>> predicted_token_class_ids = tf.math.argmax(logits, axis=-1)
>>> predicted_token_class = [model.config.id2label[t] for t in predicted_token_class_ids[0].numpy().tolist()]
>>> 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']
< > 在 GitHub 上更新