Transformers 文档

令牌分类

Hugging Face's logo
加入Hugging Face社区

并获得增强的文档体验

开始使用

标记分类

标记分类为句子中的单个标记分配标签。最常见的标记分类任务之一是命名实体识别 (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 数据集

首先从 🤗 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 前缀的字母表示实体的标记位置

  • 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. 为特殊标记 [CLS][SEP] 分配标签 -100,以便 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

要将预处理函数应用于整个数据集,请使用 🤗 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 以及模型、数据集、分词器、数据整理器和 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"],
...     tokenizer=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 中指定要将模型和分词器推送到哪里

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

最后,您就可以开始训练模型了!使用您的训练和验证数据集、时期数和回调调用 fit 以微调模型

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

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

有关如何微调用于标记分类的模型的更深入示例,请查看相应的 PyTorch 笔记本TensorFlow 笔记本

推理

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

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

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

分词文本并返回 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 内容

分词文本并返回 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 上更新