Transformers 文件
詞元分類
並獲得增強的文件體驗
開始使用
Token 分類
Token 分類為句子中的每個 token 分配一個標籤。最常見的 token 分類任務之一是命名實體識別 (NER)。NER 嘗試為句子中的每個實體(例如人名、地點或組織)查詢標籤。
本指南將向您展示如何:
- 在 WNUT 17 資料集上微調 DistilBERT 以檢測新實體。
- 使用您的微調模型進行推理。
要檢視與此任務相容的所有架構和檢查點,我們建議檢視任務頁面。
在開始之前,請確保您已安裝所有必要的庫
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
字首字母表示實體的 token 位置
B-
表示實體的開頭。I-
表示一個 token 包含在同一個實體中(例如,State
token 是Empire State Building
這樣的實體的一部分)。0
表示 token 不對應任何實體。
預處理
下一步是載入 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]']
然而,這會新增一些特殊 token [CLS]
和 [SEP]
,並且子詞分詞會在輸入和標籤之間建立不匹配。一個對應於單個標籤的單詞現在可能被分成兩個子詞。您需要透過以下方式重新對齊 token 和標籤:
- 使用
word_ids
方法將所有 token 對映到其對應的單詞。 - 將標籤
-100
分配給特殊 token[CLS]
和[SEP]
,以便 PyTorch 損失函式忽略它們(參見 CrossEntropyLoss)。 - 只標記給定單詞的第一個 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 建立一批示例。在整理過程中,將句子動態填充到批次中最長的長度比將整個資料集填充到最大長度更有效。
>>> from transformers import DataCollatorForTokenClassification
>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
>>> 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
函式現在可以使用了,您將在設定訓練時再次用到它。
訓練
在開始訓練模型之前,使用 id2label
和 label2id
建立預期 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,
... }
現在您已準備好開始訓練您的模型!使用 AutoModelForTokenClassification 載入 DistilBERT,並附帶預期標籤的數量和標籤對映
>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer
>>> model = AutoModelForTokenClassification.from_pretrained(
... "distilbert/distilbert-base-uncased", num_labels=13, id2label=id2label, label2id=label2id
... )
此時,只剩下三個步驟
- 在 TrainingArguments 中定義您的訓練超引數。唯一必需的引數是
output_dir
,它指定儲存模型的位置。您將透過設定push_to_hub=True
將此模型推送到 Hub(您需要登入 Hugging Face 才能上傳模型)。在每個 epoch 結束時,Trainer 將評估 seqeval 分數並儲存訓練檢查點。 - 將訓練引數與模型、資料集、分詞器、資料整理器和
compute_metrics
函式一起傳遞給 Trainer。 - 呼叫 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()
如果您不熟悉如何使用 Keras 對模型進行微調,請參閱此處的基本教程!
>>> 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
,使用您的訓練和驗證資料集、epoch 數量和回撥函式來微調模型
>>> model.fit(x=tf_train_set, validation_data=tf_validation_set, epochs=3, callbacks=callbacks)
訓練完成後,您的模型會自動上傳到 Hub,供所有人使用!
有關如何微調模型以進行 token 分類的更深入示例,請檢視相應的 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 張量
>>> 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 張量
>>> 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']