聊聊預訓練模型的微調

2023-09-07 13:28:40

翻譯自:Fine-tuning a model with the Trainer API

Transformers 提供了一個 Trainer 類,處理微調在資料集上提供的任何預訓練模型。 完成所有資料預處理工作後,只需執行幾個步驟即可定義 Trainer。 最困難的部分可能是準備執行 Trainer.train() 的環境,因為它在 CPU 上執行速度非常慢。 如果沒有設定 GPU,可以在 Google Colab 上存取免費的 GPU 或 TPU。
下面的程式碼範例假設已經完成了資料預處理的操作:

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)

map函數與DataCollatorWithPadding函數請查閱:Processing the data

Traning(訓練)

定義 Trainer 之前的第一步是定義一個 TrainingArguments 類,該類將包含 Trainer 用於訓練和評估的所有超引數。 必須提供的唯一引數是儲存訓練模型的目錄以及checkpoint。 對於其餘所有內容,可以保留預設值,這對於基本的微調應該非常有效。

from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")

第二步是定義我們的模型。 使用 AutoModelForSequenceClassification 類,它帶有兩個標籤:

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

一旦我們有了模型,我們就可以通過傳遞迄今為止構建的所有物件來定義 Trainer---Modeltraining_argstrainingvalidation datasetsdata_collatortokenizer

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

要在資料集上微調模型,我們只需呼叫訓練器的 train() 方法:

trainer.train()

這將開始微調(在 GPU 上應該需要幾分鐘)並每 500 步報告一次訓練損失。 但是,它不會告訴你模型的表現有多好(或多差)。 這是因為:

  1. 我們沒有告訴訓練器在訓練期間通過將evaluation_strategy設定為「steps」(評估每個eval_steps)或「epoch」(在每個epoch結束時評估)來進行評估。
  2. 我們沒有為訓練器提供compute_metrics()函數來在所述評估期間計算指標(否則評估只會列印損失,這不是一個非常直觀的數位)。

Evaluation(評估)

讓我們看看如何構建一個有用的compute_metrics()函數並在下次訓練時使用它。 該函數必須採用 EvalPrediction 物件(這是一個帶有預測欄位和 label_ids 欄位的命名元組),並將返回一個將字串對映到浮點數的字典(字串是返回的指標的名稱,浮點數是它們的值)。 為了從我們的模型中獲得一些預測,我們可以使用 Trainer.predict() 方法:

predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)

## 輸出
(408, 2) (408,)

Predict() 方法的輸出是另一個具有三個欄位的命名元組:預測、label_ids 和指標。 指標欄位將僅包含傳遞的資料集的損失,以及一些時間指標(預測所需的總時間和平均時間)。 一旦我們完成了compute_metrics()函數並將其傳遞給Trainer,該欄位還將包含compute_metrics()返回的指標。
結果所展示的預測是一個形狀為 408 x 2 的二維陣列(408 是我們使用的資料集中的元素數量)。 這些是我們傳遞給predict()的資料集每個元素的logits。 為了將它們轉換為可以與標籤進行比較的預測,我們需要在第二個軸上獲取最大值的索引:

import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)

我們現在可以將這些預測與標籤進行比較。 為了構建我們的compute_metric()函數,我們將依賴於HuggingFace 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}

獲得的確切結果可能會有所不同,因為Model Head部分的隨機初始化可能會改變它實現的指標。 在這裡,我們可以看到我們的模型在驗證集上的準確率為 85.78%,F1 得分為 89.97。 這些是用於評估 GLUE 基準的 MRPC 資料集結果的兩個指標。 BERT 論文中的表格報告了基本模型的 F1 分數為 88.9,這是非case的模型,而我們目前使用的是case的模型,這解釋了更好的結果。
將所有內容包裝在一起,我們得到了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)

為了檢視它在每個epoch結束時報告指標的實際使用情況,下面是我們如何使用這個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,其評估策略設定為「epoch」和一個新模型 - 否則,我們將繼續訓練已經訓練過的模型。 要啟動新的訓練執行,我們執行:

trainer.train()

這次,除了訓練損失之外,它將在每個時期結束時報告驗證損失和指標。 同樣,由於模型的Model Head初始化,你達到的確切準確度/F1 分數可能與我們發現的有所不同,但它應該處於相同的範圍內。
Trainer 將在多個 GPU 或 TPU 上開箱即用,並提供許多選項,例如混合精度訓練(在訓練引數中使用 fp16 = True)。