Pytorch
TensorFlow

#
使用 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步報告一次訓練損失。但是，它不會告訴您模型的性能如何（或質量如何）。這是因為:

1. 我們沒有通過將evaluation_strategy設置為“steps”(在每次更新參數的時候評估)或“epoch”(在每個epoch結束時評估)來告訴Trainer在訓練期間進行評估。
2. 我們沒有為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 數據集上微調模型。