使用 Trainer API 微調模型
🤗 Transformers提供了一個 Trainer 類來幫助您在自己的數據集上微調任何預訓練模型。完成上一節中的所有數據預處理工作後,您只需要執行幾個步驟來創建 Trainer .最難的部分可能是為 Trainer.train()配置運行環境,因為它在 CPU 上運行速度會非常慢。如果您沒有設置 GPU,您可以訪問免費的 GPU 或 TPUGoogle Colab.
下面的示例假設您已經執行了上一節中的示例。下面這段代碼,概括了您需要提前運行的代碼:
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
Training
在我們定義我們的 Trainer 之前首先要定義一個 TrainingArguments 類,它將包含 Trainer用於訓練和評估的所有超參數。您唯一必須提供的參數是保存訓練模型的目錄,以及訓練過程中的檢查點。對於其餘的參數,您可以保留默認值,這對於基本微調應該非常有效。
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
💡 如果您想在訓練期間自動將模型上傳到 Hub,請將push_to_hub=True添加到TrainingArguments之中. 我們將在第四章中詳細介紹這部分。
第二步是定義我們的模型。正如在[之前的章節](/2_Using Transformers/Introduction)一樣,我們將使用 AutoModelForSequenceClassification 類,它有兩個參數:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
你會注意到,和第二章不一樣的是,在實例化此預訓練模型後會收到警告。這是因為 BERT 沒有在句子對分類方面進行過預訓練,所以預訓練模型的頭部已經被丟棄,而是添加了一個適合句子序列分類的新頭部。警告表明一些權重沒有使用(對應於丟棄的預訓練頭的那些),而其他一些權重被隨機初始化(新頭的那些)。最後鼓勵您訓練模型,這正是我們現在要做的。
一旦我們有了我們的模型,我們就可以定義一個 Trainer 通過將之前構造的所有對象傳遞給它——我們的model 、training_args ,訓練和驗證數據集,data_collator ,和 tokenizer :
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
)
請注意,當您在這裡完成tokenizer後,默認 Trainer使用 的data_collator會使用之前預定義的 DataCollatorWithPadding ,因此您可以在這個例子中跳過 data_collator=data_collator。在第 2 節中向您展示這部分處理仍然很重要!
為了讓預訓練模型在在我們的數據集上微調,我們只需要調用Trainer的train() 方法 :
trainer.train()
這將開始微調(在GPU上應該需要幾分鐘),並每500步報告一次訓練損失。但是,它不會告訴您模型的性能如何(或質量如何)。這是因為:
- 我們沒有通過將evaluation_strategy設置為“steps”(在每次更新參數的時候評估)或“epoch”(在每個epoch結束時評估)來告訴Trainer在訓練期間進行評估。
- 我們沒有為Trainer提供一個compute_metrics()函數來直接計算模型的好壞(否則評估將只輸出loss,這不是一個非常直觀的數字)。
評估
讓我們看看如何構建一個有用的 compute_metrics() 函數並在我們下次訓練時使用它。該函數必須採用 EvalPrediction 對象(帶有 predictions 和 label_ids 字段的參數元組)並將返回一個字符串到浮點數的字典(字符串是返回的指標的名稱,而浮點數是它們的值)。我們可以使用 Trainer.predict() 命令來使用我們的模型進行預測:
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
(408, 2) (408,)
predict() 的輸出結果是具有三個字段的命名元組: predictions , label_ids , 和 metrics .這 metrics 字段將只包含傳遞的數據集的loss,以及一些運行時間(預測所需的總時間和平均時間)。如果我們定義了自己的 compute_metrics() 函數並將其傳遞給 Trainer ,該字段還將包含compute_metrics()的結果。
predict() 方法是具有三個字段的命名元組: predictions , label_ids , 和 metrics .這 metrics 字段將只包含傳遞的數據集的loss,以及一些運行時間(預測所需的總時間和平均時間)。如果我們定義了自己的 compute_metrics() 函數並將其傳遞給 Trainer ,該字段還將包含compute_metrics() 的結果。如你看到的, predictions 是一個形狀為 408 x 2 的二維數組(408 是我們使用的數據集中元素的數量)。這些是我們傳遞給predict()的數據集的每個元素的結果(logits)(正如你在之前的章節看到的情況)。要將我們的預測的可以與真正的標籤進行比較,我們需要在第二個軸上取最大值的索引:
import numpy as np
preds = np.argmax(predictions.predictions, axis=-1)
現在建立我們的 compute_metric() 函數來較為直觀地評估模型的好壞,我們將使用 🤗 Evaluate 庫中的指標。我們可以像加載數據集一樣輕鬆加載與 MRPC 數據集關聯的指標,這次使用 evaluate.load() 函數。返回的對象有一個 compute()方法我們可以用來進行度量計算的方法:
import evaluate
metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
您獲得的確切結果可能會有所不同,因為模型頭的隨機初始化可能會影響最終建立的模型。在這裡,我們可以看到我們的模型在驗證集上的準確率為 85.78%,F1 分數為 89.97。這是用於評估 GLUE 基準的 MRPC 數據集結果的兩個指標。而在BERT 論文中展示的基礎模型的 F1 分數為 88.9。那是 uncased 模型,而我們目前正在使用 cased 模型,通過改進得到了更好的結果。
最後將所有東西打包在一起,我們得到了我們的 compute_metrics() 函數:
def compute_metrics(eval_preds):
metric = evaluate.load("glue", "mrpc")
logits, labels = eval_preds
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
為了查看模型在每個訓練週期結束的好壞,下面是我們如何使用compute_metrics()函數定義一個新的 Trainer :
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
請注意,我們設置了了一個新的 TrainingArguments 它的evaluation_strategy 設置為 epoch 並創建了一個新模型。如果不創建新的模型就直接訓練,就只會繼續訓練之前我們已經訓練過的模型。要啟動新的訓練運行,我們執行:
trainer.train()
這一次,它將在訓練loss之外,還會輸出每個 epoch 結束時的驗證loss和指標。同樣,由於模型的隨機頭部初始化,您達到的準確率/F1 分數可能與我們發現的略有不同,但它應該在同一範圍內。
這 Trainer 將在多個 GPU 或 TPU 上開箱即用,並提供許多選項,例如混合精度訓練(在訓練的參數中使用 fp16 = True )。我們將在第 10 章討論它支持的所有內容。
使用Trainer API微調的介紹到此結束。對最常見的 NLP 任務執行此操作的示例將在第 7 章中給出,但現在讓我們看看如何在純 PyTorch 中執行相同的操作。
✏️ 試試看! 使用您在第 2 節中進行的數據處理,在 GLUE SST-2 數據集上微調模型。